最近做的一个产品中有个需求,就是客户使用的时候可以通过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 }