ASP.NET Core修改CentOS的IP地址
最近做的一个产品中有个需求,就是客户使用的时候可以通过Web网页修改服务器的IP地址(客户是普通使用者,没有Linux使用经验,我们的产品作为一台服务器部署到客户机房,客户通过HTTP方式访问使用)。
在Linux中一切皆是文件,修改IP地址同样是只要修改了对应网卡的配置文件后重启网络服务,只要配置正确无IP冲突,即可成功启用新的IP地址。
所以我们实现的思路是访问网卡配置文件,获取当前网卡的配置信息展示(如果有多个网卡就展示多个网卡信息,用tab来切换网卡信息),然后用户可以修改对应的网卡网络信息提交保存,后台通过修改网卡对应的文件内容,即可达到修改网卡IP地址的功能。
当然因为要访问修改网卡配置文件和执行重启网络的命令,对Web的权限有一定的要求,因为客户是内网运行,简单粗暴的使用了root用户来启动Web程序。
以下代码实现了ASP.NET Core修改CentOS的IP地址功能。
前端使用的是LayUI
1 @model ViewModel.IPVm 2 @{ 3 var ipList = Model.IpList; 4 } 5 6 <div class="layui-bg-gray" style="padding: 30px;"> 7 <div class="layui-row layui-col-space15"> 8 <div class="layui-col-md6"> 9 <div class="layui-card" style="min-height: 280px;"> 10 <div class="layui-card-header"> 11 配置服务器IP 12 </div> 13 <div class="layui-card-body"> 14 <div class="layui-tab-item layui-show" id="tbcontent_0" lay-filter="tbcontent_0filter"> 15 @{ 16 17 if (ipList != null && ipList.Any()) 18 { 19 <ul class="layui-tab-title"> 20 21 @{ 22 for (int i = 0; i < ipList.Count; i++) 23 { 24 var ip = ipList[i]; 25 <li>@ip.IpName</li> 26 } 27 } 28 </ul> 29 30 <div class="layui-tab-content"> 31 @{ 32 for (int i = 0; i < ipList.Count; i++) 33 { 34 var ip = ipList[i]; 35 var i1 = i; 36 <div class="layui-tab-item" id="tbcontent_@i" lay-filter="tbcontent_1filter"> 37 38 <div class="layui-form-item layui-form" lay-filter="Ipv4div_@i1"> 39 <label for="Ipv4div_@i1" class="layui-form-label" style="width: 100px">IPv4地址:</label> 40 <div class="layui-input-block" style="margin-left: 130px"> 41 <input autocomplete="off" type="text" name="Ipv4div_txt_@i1" value="@ip.Ipv4" maxlength="15" placeholder="请填写IPv4的IP地址" class="layui-input" id="Ipv4div_txt_@i1"> 42 </div> 43 </div> 44 <div class="layui-form-item layui-form" lay-filter="IpMaskdiv_@i1"> 45 <label for="IpMaskdiv_@i1" class="layui-form-label" style="width: 100px">子网掩码:</label> 46 <div class="layui-input-block" style="margin-left: 130px"> 47 <input autocomplete="off" type="text" name="IpMaskdiv_txt_@i1" value="@ip.IpMask" maxlength="15" placeholder="请填写IPv4的子网掩码" class="layui-input" id="IpMaskdiv_txt_@i1"> 48 </div> 49 </div> 50 <div class="layui-form-item layui-form" lay-filter="IpGetWaydiv_@i1"> 51 <label for="IpGetWaydiv_@i1" class="layui-form-label" style="width: 100px">网关地址:</label> 52 <div class="layui-input-block" style="margin-left: 130px"> 53 <input autocomplete="off" type="text" name="IpGetWay_txt_@i1" value="@ip.IpGetWay" maxlength="15" placeholder="请填写IPv4的网关地址" class="layui-input" id="IpGetWay_txt_@i1"> 54 </div> 55 </div> 56 <div class="layui-form-item layui-form" lay-filter="IpDnsdiv_@i1"> 57 <label for="IpDnsdiv_@i1" class="layui-form-label" style="width: 100px">DNS地址:</label> 58 <div class="layui-input-block" style="margin-left: 130px"> 59 <input autocomplete="off" type="text" name="IpDnsdiv_txt_@i1" value="@ip.IpDns" maxlength="15" placeholder="请填写IPv4的DNS地址" class="layui-input" id="IpDnsdiv_txt_@i1"> 60 </div> 61 </div> 62 <input type="hidden" value="@ip.IpGuid" maxlength="32" name="IpGuid_@i1" id="IpGuid_@i1" /> 63 <input type="hidden" value="@ip.IpName" maxlength="32" name="IpName_@i1" id="IpName_@i1" /> 64 <button onclick="SaveIpConfig(this);" type="button" class="layui-btn" id="btn_@i" lay-filter="btn_@i filter">提交修改</button> 65 </div> 66 } 67 } 68 </div> 69 } 70 } 71 </div> 72 </div> 73 </div> 74 </div> 75 </div> 76 </div> 77 <script type="text/javascript"> 78 var element = layui.element; 79 layui.use('layer'); 80 81 function SaveIpConfig(e) { 82 var btnId = $(e).attr('id'); 83 84 if (btnId == '' || btnId == undefined) return; 85 86 var id = btnId.split('_'); 87 88 if (id.length != 2) return; 89 90 var ip = $("#Ipv4div_txt_" + id[1]).val(); 91 var mask = $("#IpMaskdiv_txt_" + id[1]).val(); 92 var getWay = $("#IpGetWay_txt_" + id[1]).val(); 93 var dns = $("#IpDnsdiv_txt_" + id[1]).val(); 94 var nid = $("#IpGuid_" + id[1]).val(); 95 var ipName = $("#IpName_" + id[1]).val(); 96 97 if ($.trim(ip) == '') { 98 layer.alert('请填写IP地址', 99 { 100 title: '错误' 101 }); 102 return; 103 } else { 104 if (!validateIP($.trim(ip))) { 105 layer.alert('IP地址格式不正确', 106 { 107 title: '错误' 108 }); 109 return; 110 } 111 } 112 113 if (mask.trim() == '') { 114 layer.alert('请填写子网掩码', { 115 title: '错误' 116 }); 117 return; 118 } else { 119 if (!validateIP($.trim(mask))) { 120 layer.alert('子网掩码格式不正确', 121 { 122 title: '错误' 123 }); 124 return; 125 } 126 } 127 128 if ($.trim(getWay) != '' && !validateIP($.trim(getWay))) { 129 layer.alert('网关地址格式不正确', { 130 title: '错误' 131 }); 132 return; 133 } 134 135 if ($.trim(dns) != '' && !validateIP($.trim(dns))) { 136 layer.alert('DNS格式不正确', { 137 title: '错误' 138 }); 139 return; 140 } 141 142 layer.confirm('确定要修改服务器IP吗?!', 143 { icon: 3, title: '信息' }, 144 function(index) { 145 layer.close(index); 146 index = layer.load(0); 147 $.ajax({ 148 type: 'POST', 149 url: '/ChangeIp/SaveIpConfig', 150 data: { ip: ip, mask: mask, getWay: getWay, dns: dns, nid: nid, ipName: ipName}, 151 dataType: "json", 152 success: function(result) { 153 if (result != '' && result != 'undefined') { 154 if (result.code != "200") { 155 layer.alert(result.msg, 156 { 157 title: '错误' 158 }); 159 return; 160 } else { 161 layer.alert('修改成功,请3秒后访问新的IP地址来使用系统。', 162 { 163 title: '成功' 164 }); 165 } 166 } 167 else { 168 layer.alert('返回数据错误。', { 169 title: '错误' 170 }); 171 } 172 }, 173 complete: function(xhr, ts) { 174 layer.close(index); 175 } 176 }); 177 return false; 178 }); 179 } 180 function validateIP(str) { 181 var re = /^(?:(?:2[0-4][0-9]\.)|(?:25[0-5]\.)|(?:1[0-9][0-9]\.)|(?:[1-9][0-9]\.)|(?:[0-9]\.)){3}(?:(?:2[0-4][0-9])|(?:25[0-5])|(?:1[0-9][0-9])|(?:[1-9][0-9])|(?:[0-9]))$/; 182 return re.test(str); 183 } 184 </script>
后台VM实体
1 namespace ViewModel 2 { 3 public class IPVm 4 { 3 /// <summary> 4 /// 本地IP地址 5 /// </summary> 6 public List<IpModel> IpList { get; set; } 7 } 8 }
IpModel实体
namespace Model { /// <summary> /// 获取本地IP地址信息 /// </summary> public class IpModel { /// <summary> /// IP配置文件路径 /// </summary> public string IpConfigFile { get; set; } /// <summary> /// 名称 /// </summary> public string IpName { get; set; } /// <summary> /// 一个随机编号 /// </summary> public string IpGuid { get; set; } /// <summary> /// IPv4的地址 /// </summary> public string Ipv4 { get; set; } /// <summary> /// IPv6的地址 /// </summary> public string Ipv6 { get; set; } /// <summary> /// 子网掩码 /// </summary> public string IpMask { get; set; } /// <summary> /// 网关地址 /// </summary> public string IpGetWay { get; set; } /// <summary> /// DNS地址 /// </summary> public string IpDns { get; set; } } }
后台Controller实现修改IP地址
1 using Microsoft.AspNetCore.Mvc; 2 using Microsoft.Extensions.Logging; 3 using MidSoft.Sfrz.Model; 4 using System; 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.IO; 8 using System.Linq; 9 using System.Net; 10 using System.Net.NetworkInformation; 11 using System.Threading.Tasks; 12 13 namespace Controllers 14 { 15 public class ChangeIpController : Controller 16 { 17 private readonly ILogger<CshSjkController> _logger; 18 public ChangeIpController( ILogger<CshSjkController> logger) 19 { 20 _logger = logger; 21 } 22 23 public IActionResult Index() 24 { 25 #region 获取IP地址 26 27 var IpList = new List<IpModel>(); 28 try 29 { 30 var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); 31 foreach (var nic in networkInterfaces) 32 { 33 if (string.Equals("loopback", nic.Name) || string.Equals("lo", nic.Name)) continue; 34 35 var ipModel = new IpModel(); 36 37 var file = Path.Combine("/", "etc", "sysconfig", "network-scripts", $"ifcfg-{nic.Name}");//这里是网卡的配置文件名称,可能每台服务器的开头不一样,比如我这里的网卡都是ifcfg-开头的文件,记得确认一下 38 39 ipModel.IpConfigFile = file; 40 41 ipModel.IpDns = ""; 42 ipModel.IpGetWay = ""; 43 ipModel.IpMask = ""; 44 ipModel.Ipv4 = ""; 45 ipModel.Ipv6 = ""; 46 ipModel.IpName = nic.Name; 47 ipModel.IpGuid = Guid.NewGuid().ToString("D"); 48 49 if (System.IO.File.Exists(file)) 50 { 51 var ipDictionary = new Dictionary<string, string>(); 52 #if DEBUG 53 _logger.LogInformation("==========IP============"); 54 _logger.LogInformation($"FilePath={file}"); 55 #endif 56 57 using (var streamReader = new StreamReader(file)) 58 { 59 var line = string.Empty; 60 while ((line = streamReader.ReadLine()) != null) 61 { 62 var split = line.Split('='); 63 if (split.Length > 1) 64 { 65 ipDictionary.Add(split[0], split[1]); 66 } 67 } 68 } 69 #if DEBUG 70 _logger.LogInformation($"DicLength:{ipDictionary.Count}"); 71 _logger.LogInformation("==========IPEnd============"); 72 #endif 73 if (ipDictionary.Any()) 74 { 75 if (ipDictionary.ContainsKey("NAME")) ipModel.IpName = ipDictionary["NAME"].Replace("\"", ""); 76 if (ipDictionary.ContainsKey("UUID")) ipModel.IpGuid = ipDictionary["UUID"].Replace("\"", ""); 77 if (ipDictionary.ContainsKey("IPADDR")) ipModel.Ipv4 = ipDictionary["IPADDR"].Replace("\"", ""); 78 if (ipDictionary.ContainsKey("PREFIX")) 79 { 80 int.TryParse(ipDictionary["PREFIX"].Replace("\"", ""), out var ipMask); 81 ipMask = Math.Abs(ipMask); 82 if (ipMask == 0 || ipMask > 32) 83 { 84 ipModel.IpMask = "255.255.255.0"; 85 } 86 else 87 { 88 var ip = Int2IpMask(ipMask); 89 ipModel.IpMask = $"{ip.Item1}.{ip.Item2}.{ip.Item3}.{ip.Item4}"; 90 } 91 92 } 93 if (ipDictionary.ContainsKey("GATEWAY")) ipModel.IpGetWay = ipDictionary["GATEWAY"].Replace("\"", ""); 94 if (ipDictionary.ContainsKey("DNS1")) ipModel.IpDns = ipDictionary["DNS1"].Replace("\"", ""); 95 if (ipDictionary.ContainsKey("DNS2")) ipModel.IpDns = ipDictionary["DNS2"].Replace("\"", ""); 96 } 97 } 98 99 100 IpList.Add(ipModel); 101 } 102 } 103 catch (Exception e) 104 { 105 _logger.LogError($"加载IP地址配置文件时出错:{e}"); 106 107 } 108 109 #endregion 110 111 return View(IpList); 112 } 113 114 #region 保存服务器网卡IP配置 115 /// <summary> 116 /// 保存服务器网卡IP配置 117 /// </summary> 118 /// <returns></returns> 119 [HttpPost] 120 121 public async Task<JsonResult> SaveIpConfig() 122 { 123 var ip = Request.Form["ip"]; 124 var mask = Request.Form["mask"]; 125 var getWay = Request.Form["getWay"]; 126 var dns = Request.Form["dns"]; 127 var nid = Request.Form["nid"]; 128 var ipName = Request.Form["ipName"]; 129 130 if (string.IsNullOrWhiteSpace(ip)) 131 { 132 return new JsonResult(new { code = "300", msg = "请填写IP地址" }); 133 } 134 if (!IPAddress.TryParse(ip, out _)) 135 { 136 return new JsonResult(new { code = "300", msg = "IP地址格式不正确" }); 137 } 138 if (!IPAddress.TryParse(mask, out _)) 139 { 140 return new JsonResult(new { code = "300", msg = "子网掩码地址格式不正确" }); 141 } 142 143 if (string.IsNullOrEmpty(mask) || mask.ToString().Split('.').Length != 4) 144 { 145 return null; 146 } 147 148 var ips = mask.ToString().Split('.'); 149 150 var ipMask = 0; 151 var ip1 = Ip2Mask(ips[0]); 152 var ip2 = Ip2Mask(ips[1]); 153 var ip3 = Ip2Mask(ips[2]); 154 var ip4 = Ip2Mask(ips[3]); 155 if (ip1 == null|| ip2 == null|| ip3 == null|| ip4 == null) 156 { 157 return new JsonResult(new { code = "300", msg = "子网掩码填写不正确" }); 158 } 159 160 ipMask = (int)ip1 + (int)ip2 + (int)ip3 + (int)ip4; 161 162 if (ipMask == 0) 163 { 164 return new JsonResult(new { code = "300", msg = "子网掩码填写不正确" }); 165 } 166 167 if (!string.IsNullOrWhiteSpace(getWay) && !IPAddress.TryParse(ip, out _)) 168 { 169 return new JsonResult(new { code = "300", msg = "网关地址格式不正确" }); 170 } 171 172 if (!string.IsNullOrWhiteSpace(dns) && !IPAddress.TryParse(dns, out _)) 173 { 174 return new JsonResult(new { code = "300", msg = "DNS格式不正确" }); 175 } 176 177 if (string.IsNullOrWhiteSpace(ipName)) 178 { 179 ipName = "enp1s0"; 180 } 181 if (string.IsNullOrWhiteSpace(nid)) 182 { 183 nid = Guid.NewGuid().ToString("D"); 184 } 185 186 Task.Run(async () => 187 { 188 189 var file = Path.Combine("/", "etc", "sysconfig", "network-scripts", $"ifcfg-{ipName}"); 190 await Task.Delay(200); 191 try 192 { 193 using (var fileStream = new FileStream(file, FileMode.Create)) 194 { 195 using (var fileStreamWriter = new StreamWriter(fileStream)) 196 { 197 await fileStreamWriter.WriteLineAsync(@"TYPE=""Ethernet"""); 198 await fileStreamWriter.WriteLineAsync(@"PROXY_METHOD=""none"""); 199 await fileStreamWriter.WriteLineAsync(@"BROWSER_ONLY=""no"""); 200 await fileStreamWriter.WriteLineAsync(@"BOOTPROTO=""none"""); 201 await fileStreamWriter.WriteLineAsync(@"DEFROUTE=""yes"""); 202 await fileStreamWriter.WriteLineAsync(@"IPV4_FAILURE_FATAL=""no"""); 203 await fileStreamWriter.WriteLineAsync(@"IPV6INIT=""yes"""); 204 await fileStreamWriter.WriteLineAsync(@"IPV6_AUTOCONF=""yes"""); 205 await fileStreamWriter.WriteLineAsync(@"IPV6_DEFROUTE=""yes"""); 206 await fileStreamWriter.WriteLineAsync(@"IPV6_FAILURE_FATAL=""no"""); 207 await fileStreamWriter.WriteLineAsync(@"IPV6_ADDR_GEN_MODE=""stable-privacy"""); 208 await fileStreamWriter.WriteLineAsync($@"NAME=""{ipName}"""); 209 await fileStreamWriter.WriteLineAsync($@"UUID=""{nid}"""); 210 await fileStreamWriter.WriteLineAsync($@"DEVICE=""{ipName}"""); 211 await fileStreamWriter.WriteLineAsync(@"ONBOOT=""yes"""); 212 await fileStreamWriter.WriteLineAsync($@"IPADDR=""{ip}"""); 213 await fileStreamWriter.WriteLineAsync($@"PREFIX=""{ipMask}"""); 214 await fileStreamWriter.WriteLineAsync($@"GATEWAY=""{getWay}"""); 215 await fileStreamWriter.WriteLineAsync($@"DNS1=""{dns}"""); 216 await fileStreamWriter.WriteLineAsync(@"IPV6_PRIVACY=""no"""); 217 } 218 } 219 220 await Process.Start("service", " network restart")?.WaitForExitAsync()!; 221 } 222 catch (Exception e) 223 { 224 _logger.LogError($"修改服务器IP地址出错:{e}"); 225 } 226 227 }); 228 return new JsonResult(new { code = "200" }); 229 } 230 231 #endregion 232 233 #region 子网掩码位数转子网掩码IP 234 235 private Tuple<int, int, int, int> Int2IpMask(int ipLength) 236 { 237 int ip1 = 0, ip2 = 0, ip3 = 0, ip4 = 0; 238 var tmpvar = ipLength; 239 if (tmpvar > 32 || tmpvar < 0) 240 { 241 return null; 242 } 243 244 if (tmpvar >= 8) 245 { 246 ip1 = 255; 247 tmpvar -= 8; 248 } 249 else 250 { 251 ip1 = Int2BitMask(tmpvar); 252 return new Tuple<int, int, int, int>(ip1, 0, 0, 0); 253 } 254 if (tmpvar >= 8) 255 { 256 ip2 = 255; 257 tmpvar -= 8; 258 } 259 else 260 { 261 ip2 = Int2BitMask(tmpvar); 262 return new Tuple<int, int, int, int>(ip1, ip2, 0, 0); 263 } 264 if (tmpvar >= 8) 265 { 266 ip3 = 255; 267 tmpvar -= 8; 268 } 269 else 270 { 271 ip3 = Int2BitMask(tmpvar); 272 return new Tuple<int, int, int, int>(ip1, ip2, ip3, 0); 273 } 274 ip4 = Int2BitMask(tmpvar); 275 return new Tuple<int, int, int, int>(ip1, ip2, ip3, ip4); 276 } 277 278 private int Int2BitMask(int num) 279 { 280 if (num >= 8) 281 { 282 return (255); 283 } 284 var pianyiliang = 0xff00; 285 while (num > 0) 286 { 287 pianyiliang = pianyiliang >> 1; 288 num--; 289 } 290 return (pianyiliang & 0xff); 291 } 292 #endregion 293 294 #region 子网掩码IP地址转位数 295 private int? Ip2Mask(string ip) 296 { 297 int.TryParse(ip, out var num); 298 if (num == 255) 299 { 300 return 8; 301 } 302 var pianyiliang = 0xff00; 303 var i = 0; 304 while (i < 8) 305 { 306 if (num == (pianyiliang & 0xff)) 307 { 308 return i; 309 } 310 pianyiliang >>= 1; 311 i++; 312 } 313 return null; 314 } 315 #endregion 316 } 317 }