1 #include <nm_core.h>
2 #include <nm_utils.h>
3 #include <nm_network.h>
4
5 #include <sys/ioctl.h>
6
7 #if defined (NM_OS_LINUX)
8
9 #ifdef _DEFAULT_SOURCE
10 #include <net/if.h>
11 #include <linux/if.h>
12 #else
13 /* Temporary work-around for broken glibc vs. linux kernel header definitions
14 * This is already fixed upstream, remove this when distributions have updated.
15 * net/if.h fuckup should be removed someday in future, when kernels <= 4.2 will not be supported
16 * https://github.com/systemd/systemd/commit/08ce521fb2546921f2642bef067d2cc02158b121
17 * https://github.com/systemd/systemd/commit/6f270e6bd8b78aedf9f77534d6d11141ea0bf8ca
18 */
19 #define _NET_IF_H 1
20 #include <net/if.h>
21 #ifndef IFNAMSIZ
22 #define IFNAMSIZ 16
23 extern unsigned int if_nametoindex (const char *__ifname) __THROW;
24 #endif
25 #include <linux/if.h>
26 #endif
27
28 #include <time.h>
29 #include <sys/socket.h>
30 #include <linux/if_tun.h>
31 #include <linux/netlink.h>
32 #include <linux/rtnetlink.h>
33
34 static const char NM_TUNDEV[] = "/dev/net/tun";
35 static const char NM_NET_MACVTAP[] = "macvtap";
36 static const char NM_NET_VETH[] = "veth";
37 static const int NM_NET_VETH_INFO_PEER = 1;
38
39 enum {
40 NM_MACVTAP_BRIDGE = 1,
41 NM_MACVTAP_PRIVATE = 2
42 };
43
44 enum {
45 NM_MACVTAP_PRIVATE_MODE = 1,
46 NM_MACVTAP_BRIDGE_MODE = 4
47 };
48
49 struct iplink_req {
50 struct nlmsghdr n;
51 struct ifinfomsg i;
52 /* cppcheck-suppress unusedStructMember */
53 char buf[1024];
54 };
55
56 struct ipaddr_req {
57 struct nlmsghdr n;
58 struct ifaddrmsg i;
59 /* cppcheck-suppress unusedStructMember */
60 char buf[256];
61 };
62
63 struct rtnl_handle {
64 int32_t sd;
65 uint32_t seq;
66 struct sockaddr_nl sa;
67 };
68
69 static void nm_net_set_link_status(const nm_str_t *name, int action);
70 static void nm_net_rtnl_open(struct rtnl_handle *rth);
71 static void nm_net_rtnl_talk(struct rtnl_handle *rth, struct nlmsghdr *n,
72 struct nlmsghdr *res, size_t res_len);
73 static int nm_net_add_attr(struct nlmsghdr *n, size_t mlen,
74 int type, const void *data, size_t dlen);
75 static struct rtattr *nm_net_add_attr_nest(struct nlmsghdr *n, size_t mlen,
76 int type);
77 static int nm_net_add_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
78
NLMSG_TAIL(struct nlmsghdr * n)79 static struct rtattr *NLMSG_TAIL(struct nlmsghdr* n)
80 {
81 return (struct rtattr *)((char *)n + NLMSG_ALIGN(n->nlmsg_len));
82 }
83 #else
84 #include <net/if.h>
85 #endif /* NM_OS_LINUX */
86
87 enum tap_on_off {
88 NM_TAP_OFF = 0,
89 NM_TAP_ON
90 };
91
92 enum action {
93 NM_SET_LINK_UP,
94 NM_SET_LINK_DOWN,
95 NM_SET_LINK_ADDR
96 };
97
98 static size_t nm_net_mac_s2a(const nm_str_t *addr, char *res, size_t len);
99 static void nm_net_manage_tap(const nm_str_t *name, int on_off);
100 static void nm_net_addr_change(const nm_str_t *name, const nm_str_t *net,
101 int action);
102
nm_net_iface_exists(const nm_str_t * name)103 int nm_net_iface_exists(const nm_str_t *name)
104 {
105 if (if_nametoindex(name->data) == 0)
106 return NM_ERR;
107
108 return NM_OK;
109 }
110
nm_net_iface_idx(const nm_str_t * name)111 uint32_t nm_net_iface_idx(const nm_str_t *name)
112 {
113 return if_nametoindex(name->data);
114 }
115
nm_net_add_tap(const nm_str_t * name)116 void nm_net_add_tap(const nm_str_t *name)
117 {
118 nm_net_manage_tap(name, NM_TAP_ON);
119 }
120
nm_net_del_tap(const nm_str_t * name)121 void nm_net_del_tap(const nm_str_t *name)
122 {
123 nm_net_manage_tap(name, NM_TAP_OFF);
124 }
125
126 #if defined (NM_OS_LINUX)
nm_net_add_macvtap(const nm_str_t * name,const nm_str_t * parent,const nm_str_t * maddr,int type)127 void nm_net_add_macvtap(const nm_str_t *name, const nm_str_t *parent,
128 const nm_str_t *maddr, int type)
129 {
130 struct iplink_req req;
131 struct rtnl_handle rth;
132 struct rtattr *linkinfo, *data;
133 uint32_t dev_index, mode = 0;
134 size_t mac_len;
135 char macn[32] = {0};
136
137 memset(&req, 0, sizeof(req));
138
139 if ((dev_index = if_nametoindex(parent->data)) == 0)
140 nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
141
142 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
143 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
144 req.n.nlmsg_type = RTM_NEWLINK;
145
146 req.i.ifi_family = AF_UNSPEC;
147 req.i.ifi_flags |= IFF_UP | IFF_MULTICAST | IFF_ALLMULTI;
148
149 mac_len = nm_net_mac_s2a(maddr, macn, sizeof(macn));
150 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_ADDRESS,
151 macn, mac_len) != NM_OK)) {
152 nm_bug("%s: Error add_attr", __func__);
153 }
154
155 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_LINK,
156 &dev_index, sizeof(dev_index)) != NM_OK)) {
157 nm_bug("%s: Error add_attr", __func__);
158 }
159
160 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
161 name->data, name->len) != NM_OK)) {
162 nm_bug("%s: Error add_attr", __func__);
163 }
164
165 linkinfo = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
166 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_INFO_KIND,
167 NM_NET_MACVTAP, strlen(NM_NET_MACVTAP)) != NM_OK)) {
168 nm_bug("%s: Error add_attr", __func__);
169 }
170
171 switch (type) {
172 case NM_MACVTAP_BRIDGE:
173 mode = NM_MACVTAP_BRIDGE_MODE;
174 break;
175 case NM_MACVTAP_PRIVATE:
176 mode = NM_MACVTAP_PRIVATE_MODE;
177 break;
178 }
179
180 data = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_INFO_DATA);
181 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_MACVLAN_MODE,
182 &mode, sizeof(mode)) != NM_OK)) {
183 nm_bug("%s: Error add_attr", __func__);
184 }
185
186 nm_net_add_attr_nest_end(&req.n, data);
187 nm_net_add_attr_nest_end(&req.n, linkinfo);
188
189 nm_net_rtnl_open(&rth);
190 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
191 close(rth.sd);
192 }
193
nm_net_add_veth(const nm_str_t * l_name,const nm_str_t * r_name)194 void nm_net_add_veth(const nm_str_t *l_name, const nm_str_t *r_name)
195 {
196 struct iplink_req req;
197 struct rtnl_handle rth;
198 struct rtattr *linkinfo, *data;
199
200 memset(&req, 0, sizeof(req));
201
202 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
203 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
204 req.n.nlmsg_type = RTM_NEWLINK;
205
206 req.i.ifi_family = AF_UNSPEC;
207 req.i.ifi_index = 0;
208
209 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
210 l_name->data, l_name->len) != NM_OK)) {
211 nm_bug("%s: Error add_attr", __func__);
212 }
213
214 linkinfo = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_LINKINFO);
215 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_INFO_KIND,
216 NM_NET_VETH, strlen(NM_NET_VETH)) != NM_OK)) {
217 nm_bug("%s: Error add_attr", __func__);
218 }
219
220 data = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_INFO_DATA);
221
222 {
223 struct rtattr *vdata;
224 uint32_t ifi_flags, ifi_change;
225 struct ifinfomsg *ifm, *peer_ifm;
226
227 ifm = NLMSG_DATA(&req.n);
228 ifi_flags = ifm->ifi_flags;
229 ifi_change = ifm->ifi_change;
230 ifm->ifi_flags = 0;
231 ifm->ifi_change = 0;
232
233 vdata = NLMSG_TAIL(&req.n);
234 if ((nm_net_add_attr(&req.n, sizeof(req), NM_NET_VETH_INFO_PEER,
235 NULL, 0) != NM_OK)) {
236 nm_bug("%s: Error add_attr", __func__);
237 }
238
239 req.n.nlmsg_len += sizeof(struct ifinfomsg);
240 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
241 r_name->data, r_name->len) != NM_OK)) {
242 nm_bug("%s: Error add_attr", __func__);
243 }
244
245 peer_ifm = RTA_DATA(vdata);
246 peer_ifm->ifi_index = 0;
247 peer_ifm->ifi_flags = ifm->ifi_flags;
248 peer_ifm->ifi_change = ifm->ifi_change;
249 ifm->ifi_flags = ifi_flags;
250 ifm->ifi_change = ifi_change;
251
252 vdata->rta_len = (char *) NLMSG_TAIL(&req.n) - (char *) vdata;
253 }
254
255 nm_net_add_attr_nest_end(&req.n, data);
256 nm_net_add_attr_nest_end(&req.n, linkinfo);
257
258 nm_net_rtnl_open(&rth);
259 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
260 close(rth.sd);
261 }
262
nm_net_del_iface(const nm_str_t * name)263 void nm_net_del_iface(const nm_str_t *name)
264 {
265 struct iplink_req req;
266 struct rtnl_handle rth;
267 uint32_t dev_index;
268
269 memset(&req, 0, sizeof(req));
270
271 if ((dev_index = if_nametoindex(name->data)) == 0)
272 nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
273
274 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
275 req.n.nlmsg_flags = NLM_F_REQUEST;
276 req.n.nlmsg_type = RTM_DELLINK;
277
278 req.i.ifi_family = AF_UNSPEC;
279 req.i.ifi_index = dev_index;
280
281 nm_net_rtnl_open(&rth);
282 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
283 close(rth.sd);
284 }
285 #endif /* NM_OS_LINUX */
286
nm_net_set_ipaddr(const nm_str_t * name,const nm_str_t * addr)287 void nm_net_set_ipaddr(const nm_str_t *name, const nm_str_t *addr)
288 {
289 nm_net_addr_change(name, addr, NM_SET_LINK_ADDR);
290 }
291
nm_net_set_altname(const nm_str_t * name,const nm_str_t * altname)292 void nm_net_set_altname(const nm_str_t *name, const nm_str_t *altname)
293 {
294 #if defined (NM_WITH_NEWLINKPROP)
295 uint32_t dev_index;
296 struct rtnl_handle rth;
297 struct rtattr *props;
298 struct iplink_req req = {
299 .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
300 .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE | NLM_F_APPEND,
301 .n.nlmsg_type = RTM_NEWLINKPROP,
302 .i.ifi_family = AF_UNSPEC
303 };
304
305 props = nm_net_add_attr_nest(&req.n, sizeof(req), IFLA_PROP_LIST | NLA_F_NESTED);
306 nm_net_add_attr(&req.n, sizeof(req), IFLA_ALT_IFNAME,
307 altname->data, altname->len + 1);
308 nm_net_add_attr_nest_end(&req.n, props);
309
310 if ((dev_index = if_nametoindex(name->data)) == 0)
311 nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
312 req.i.ifi_index = dev_index;
313
314 nm_net_rtnl_open(&rth);
315 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
316 close(rth.sd);
317 #else
318 (void) name;
319 (void) altname;
320 #endif /* NM_WITH_NEWLINKPROP */
321 }
322
nm_net_verify_mac(const nm_str_t * mac)323 int nm_net_verify_mac(const nm_str_t *mac)
324 {
325 int rc = NM_ERR;
326 const char *regex = "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$";
327 regex_t reg;
328
329 if (regcomp(®, regex, REG_EXTENDED) != 0) {
330 nm_bug("%s: regcomp failed", __func__);
331 }
332
333 if (regexec(®, mac->data, 0, NULL, 0) == 0)
334 rc = NM_OK;
335
336 regfree(®);
337
338 return rc;
339 }
340
nm_net_verify_ipaddr4(const nm_str_t * src,nm_net_addr_t * net,nm_str_t * err)341 int nm_net_verify_ipaddr4(const nm_str_t *src, nm_net_addr_t *net,
342 nm_str_t *err)
343 {
344 int rc = NM_OK;
345 nm_str_t buf = NM_INIT_STR;
346 nm_str_t addr = NM_INIT_STR;
347 char *token, *saveptr;
348 nm_net_addr_t netaddr = NM_INIT_NETADDR;
349 int n = 0;
350
351 nm_str_copy(&buf, src);
352 saveptr = buf.data;
353
354 if (src->data[src->len - 1] == '/') {
355 nm_str_alloc_text(err, _("Invalid address format: expected IPv4/CIDR"));
356 rc = NM_ERR;
357 goto out;
358 }
359
360 while ((token = strtok_r(saveptr, "/", &saveptr))) {
361 switch (n) {
362 case 0:
363 nm_str_alloc_text(&addr, token);
364 break;
365 case 1:
366 {
367 nm_str_t tmp = NM_INIT_STR;
368 nm_str_alloc_text(&tmp, token);
369 netaddr.cidr = nm_str_stoui(&tmp, 10);
370 nm_str_free(&tmp);
371 }
372 break;
373 default:
374 nm_str_alloc_text(err, _("Invalid address format: expected IPv4/CIDR"));
375 rc = NM_ERR;
376 goto out;
377 }
378 n++;
379 }
380
381 if ((addr.len == 0) ||
382 (inet_pton(AF_INET, addr.data, &netaddr.addr.s_addr) != 1)) {
383 nm_str_alloc_text(err, _("Invalid IPv4 address"));
384 rc = NM_ERR;
385 goto out;
386 }
387
388 if ((netaddr.cidr > 32) || (netaddr.cidr == 0)) {
389 nm_str_alloc_text(err, _("Invalid CIDR: expected [1-32]"));
390 rc = NM_ERR;
391 goto out;
392 }
393
394 if (net != NULL) {
395 net->cidr = netaddr.cidr;
396 memcpy(&net->addr, &netaddr.addr, sizeof(struct in_addr));
397 }
398
399 out:
400 nm_str_free(&addr);
401 nm_str_free(&buf);
402 return rc;
403 }
404
nm_net_fix_tap_name(nm_str_t * name,const nm_str_t * maddr)405 int nm_net_fix_tap_name(nm_str_t *name, const nm_str_t *maddr)
406 {
407 nm_str_t maddr_copy = NM_INIT_STR;
408
409 if (name->len <= 15) /* Linux tap iface max name len */
410 return 0;
411
412 nm_str_copy(&maddr_copy, maddr);
413 nm_str_remove_char(&maddr_copy, ':');
414
415 nm_str_format(name, "vm-%s", maddr_copy.data);
416
417 nm_str_free(&maddr_copy);
418
419 return 1;
420 }
421
nm_net_mac_n2s(uint64_t maddr,nm_str_t * res)422 void nm_net_mac_n2s(uint64_t maddr, nm_str_t *res)
423 {
424 char buf[64] = {0};
425 int pos = 0;
426
427 for (int byte = 0; byte < 6; byte++) {
428 uint32_t octet = ((maddr >> 40) & 0xff);
429
430 int n = snprintf(buf + pos, sizeof(buf) - pos, "%02x:", octet);
431 if (n < 0 || n >= (int)(sizeof(buf) - pos))
432 nm_bug(_("%s: snprintf failed"), __func__);
433
434 pos += n;
435 maddr <<= 8;
436 }
437
438 buf[--pos] = '\0';
439
440 nm_str_alloc_text(res, buf);
441 }
442
nm_net_mac_s2a(const nm_str_t * addr,char * res,size_t len)443 static size_t nm_net_mac_s2a(const nm_str_t *addr, char *res, size_t len)
444 {
445 nm_str_t copy = NM_INIT_STR;
446 size_t n = 0;
447 char *savep;
448
449 nm_str_copy(©, addr);
450 savep = copy.data;
451
452 for (; n < len; n++) {
453 char *cp = strchr(copy.data, ':');
454
455 if (cp) {
456 *cp = '\0';
457 cp++;
458 }
459
460 res[n] = nm_str_stoui(©, 16);
461
462 if (!cp)
463 break;
464 copy.data = cp;
465 }
466
467 copy.data = savep;
468 nm_str_free(©);
469
470 return n + 1;
471 }
472
nm_net_mac_s2n(const nm_str_t * addr)473 uint64_t nm_net_mac_s2n(const nm_str_t *addr)
474 {
475 uint64_t mac = 0;
476 unsigned char buf[6];
477 const size_t buf_len = nm_arr_len(buf);
478
479 nm_net_mac_s2a(addr, (char *)buf, buf_len);
480
481 for (size_t i = 0; i < buf_len; ++i)
482 mac |= ((uint64_t)buf[i]) << 8 * (buf_len - 1 - i);
483
484 return mac;
485 }
486
nm_net_manage_tap(const nm_str_t * name,int on_off)487 static void nm_net_manage_tap(const nm_str_t *name, int on_off)
488 {
489 struct ifreq ifr;
490
491 memset(&ifr, 0, sizeof(ifr));
492 #if defined (NM_OS_LINUX)
493 int fd;
494 ifr.ifr_flags |= (IFF_NO_PI | IFF_TAP);
495 nm_strlcpy(ifr.ifr_name, name->data, IFNAMSIZ);
496
497 if ((fd = open(NM_TUNDEV, O_RDWR)) < 0)
498 nm_bug(_("%s: cannot open TUN device: %s"), __func__, strerror(errno));
499
500 if (ioctl(fd, TUNSETIFF, &ifr) == -1)
501 nm_bug("%s: ioctl(TUNSETIFF): %s", __func__, strerror(errno));
502
503 if (ioctl(fd, TUNSETPERSIST, on_off) == -1)
504 nm_bug("%s: ioctl(TUNSETPERSIST): %s", __func__, strerror(errno));
505
506 if (on_off == NM_TAP_ON)
507 nm_net_link_up(name);
508
509 close(fd);
510 #elif defined (NM_OS_FREEBSD)
511 int sock = socket(AF_INET, SOCK_DGRAM, 0);
512 if (sock == -1)
513 nm_bug("%s: socket: %s", __func__, strerror(errno));
514
515 strlcpy(ifr.ifr_name, name->data, sizeof(ifr.ifr_name));
516
517 if (on_off == NM_TAP_OFF) {
518 if (ioctl(sock, SIOCIFDESTROY, &ifr) == -1)
519 nm_bug("%s: ioctl(SIOCIFDESTROY): %s", __func__, strerror(errno));
520 }
521 close(sock);
522 #endif /* NM_OS_LINUX */
523 }
524
525 #if defined (NM_OS_LINUX)
nm_net_rtnl_open(struct rtnl_handle * rth)526 static void nm_net_rtnl_open(struct rtnl_handle *rth)
527 {
528 memset(rth, 0, sizeof(*rth));
529
530 rth->sa.nl_family = AF_NETLINK;
531 rth->sa.nl_groups = 0;
532
533 if ((rth->sd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE)) == -1)
534 nm_bug("%s: cannot open netlink socket: %s", __func__, strerror(errno));
535
536 if (bind(rth->sd, (struct sockaddr *) &rth->sa, sizeof(rth->sa)) == -1) {
537 close(rth->sd);
538 nm_bug("%s: cannot bind netlink socket: %s", __func__, strerror(errno));
539 }
540
541 rth->seq = time(NULL);
542 }
543
nm_net_add_attr(struct nlmsghdr * n,size_t mlen,int type,const void * data,size_t dlen)544 static int nm_net_add_attr(struct nlmsghdr *n, size_t mlen,
545 int type, const void *data, size_t dlen)
546 {
547 size_t len = RTA_LENGTH(dlen);
548 struct rtattr *rta;
549
550 if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > mlen) {
551 return NM_ERR;
552 }
553
554 rta = (struct rtattr *) (((char *) n) + NLMSG_ALIGN(n->nlmsg_len));
555 rta->rta_type = type;
556 rta->rta_len = len;
557 if (data)
558 memcpy(RTA_DATA(rta), data, dlen);
559 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
560
561 return NM_OK;
562 }
563
nm_net_add_attr_nest(struct nlmsghdr * n,size_t mlen,int type)564 static struct rtattr *nm_net_add_attr_nest(struct nlmsghdr *n, size_t mlen,
565 int type)
566 {
567 struct rtattr *nest = NLMSG_TAIL(n);
568
569 if (nm_net_add_attr(n, mlen, type, NULL, 0) != NM_OK)
570 nm_bug("%s: Error add_attr", __func__);
571
572 return nest;
573 }
574
nm_net_add_attr_nest_end(struct nlmsghdr * n,struct rtattr * nest)575 static int nm_net_add_attr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
576 {
577 nest->rta_len = (char *) NLMSG_TAIL(n) - (char *) nest;
578
579 return n->nlmsg_len;
580 }
581
nm_net_rtnl_talk(struct rtnl_handle * rth,struct nlmsghdr * n,struct nlmsghdr * res,size_t res_len)582 static void nm_net_rtnl_talk(struct rtnl_handle *rth, struct nlmsghdr *n,
583 struct nlmsghdr *res, size_t res_len)
584 {
585 ssize_t len;
586 struct sockaddr_nl sa;
587 struct iovec iov;
588 struct msghdr msg;
589 struct nlmsghdr *nh;
590 char buf[16384] = {0};
591
592 iov.iov_base = (void *) n;
593 iov.iov_len = n->nlmsg_len;
594
595 memset(&msg, 0, sizeof(msg));
596 msg.msg_name = &sa;
597 msg.msg_namelen = sizeof(sa);
598 msg.msg_iov = &iov;
599 msg.msg_iovlen = 1;
600
601 memset(&sa, 0, sizeof(sa));
602 sa.nl_family = AF_NETLINK;
603
604 n->nlmsg_flags |= NLM_F_ACK;
605 n->nlmsg_seq = ++rth->seq;
606
607 if ((len = sendmsg(rth->sd, &msg, 0)) < 0)
608 nm_bug("%s: cannot talk to rtnetlink", __func__);
609
610 memset(buf, 0, sizeof(buf));
611
612 iov.iov_base = buf;
613 iov.iov_len = sizeof(buf);
614
615 len = recvmsg(rth->sd, &msg, 0);
616
617 for (nh = (struct nlmsghdr *) buf; NLMSG_OK(nh, len);
618 nh = NLMSG_NEXT(nh, len)) {
619 if (nh->nlmsg_type == NLMSG_DONE)
620 return;
621 if (nh->nlmsg_type == NLMSG_ERROR) {
622 struct nlmsgerr *nlerr = (struct nlmsgerr *) NLMSG_DATA(nh);
623 if (!nlerr->error)
624 return;
625 nm_bug("%s: RTNETLINK answers: %s",
626 __func__, strerror(-nlerr->error));
627 }
628 if (res)
629 memcpy(res, nh, nm_min(res_len, nh->nlmsg_len));
630 }
631 }
632
nm_net_link_up(const nm_str_t * name)633 void nm_net_link_up(const nm_str_t *name)
634 {
635 nm_net_set_link_status(name, NM_SET_LINK_UP);
636 }
637
nm_net_link_down(const nm_str_t * name)638 void nm_net_link_down(const nm_str_t *name)
639 {
640 nm_net_set_link_status(name, NM_SET_LINK_DOWN);
641 }
642
nm_net_link_status(const nm_str_t * name)643 int nm_net_link_status(const nm_str_t *name)
644 {
645 struct iplink_req req;
646 struct rtnl_handle rth;
647 struct {
648 struct nlmsghdr n;
649 /* cppcheck-suppress unusedStructMember */
650 char buf[16384];
651 } result;
652
653 memset(&req, 0, sizeof(req));
654
655 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
656 req.n.nlmsg_flags = NLM_F_REQUEST;
657 req.n.nlmsg_type = RTM_GETLINK;
658 req.i.ifi_family = AF_UNSPEC;
659
660 if ((nm_net_add_attr(&req.n, sizeof(req), IFLA_IFNAME,
661 name->data, name->len) != NM_OK)) {
662 nm_bug("%s: Error add_attr", __func__);
663 }
664
665 nm_net_rtnl_open(&rth);
666 nm_net_rtnl_talk(&rth, &req.n, &result.n, sizeof(result));
667 close(rth.sd);
668
669 {
670 struct ifinfomsg *ifi = NLMSG_DATA(&result.n);
671 if (!(ifi->ifi_flags & IFF_UP))
672 return NM_ERR;
673 }
674
675 return NM_OK;
676 }
677
nm_net_set_link_status(const nm_str_t * name,int action)678 static void nm_net_set_link_status(const nm_str_t *name, int action)
679 {
680 struct iplink_req req;
681 struct rtnl_handle rth;
682 uint32_t dev_index;
683
684 memset(&req, 0, sizeof(req));
685
686 if ((dev_index = if_nametoindex(name->data)) == 0)
687 nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
688
689 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
690 req.n.nlmsg_flags = NLM_F_REQUEST;
691 req.n.nlmsg_type = RTM_NEWLINK;
692
693 req.i.ifi_family = AF_UNSPEC;
694 req.i.ifi_index = dev_index;
695 req.i.ifi_change |= IFF_UP;
696
697 switch (action) {
698 case NM_SET_LINK_UP:
699 req.i.ifi_flags |= IFF_UP;
700 break;
701 case NM_SET_LINK_DOWN:
702 req.i.ifi_flags &= ~IFF_UP;
703 break;
704 }
705
706 nm_net_rtnl_open(&rth);
707 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
708 close(rth.sd);
709 }
710 #endif /* NM_OS_LINUX */
711
nm_net_addr_change(const nm_str_t * name,const nm_str_t * src,int action)712 static void nm_net_addr_change(const nm_str_t *name, const nm_str_t *src,
713 int action)
714 {
715 #if defined (NM_OS_LINUX)
716 struct ipaddr_req req;
717 struct rtnl_handle rth;
718 uint32_t dev_index;
719
720 memset(&req, 0, sizeof(req));
721
722 if ((dev_index = if_nametoindex(name->data)) == 0)
723 nm_bug("%s: if_nametoindex: %s", __func__, strerror(errno));
724
725 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
726 req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
727 req.n.nlmsg_type = RTM_NEWADDR;
728
729 req.i.ifa_family = AF_INET;
730 req.i.ifa_index = dev_index;
731 req.i.ifa_scope = 0;
732
733 switch (action) {
734 case NM_SET_LINK_ADDR:
735 {
736 struct in_addr brd;
737 struct in_addr mask;
738 nm_net_addr_t net = NM_INIT_NETADDR;
739 nm_str_t errmsg = NM_INIT_STR;
740
741 if (nm_net_verify_ipaddr4(src, &net, &errmsg) != NM_OK)
742 nm_bug("%s: %s", __func__, errmsg.data);
743
744 mask.s_addr = ~((1 << (32 - net.cidr)) - 1);
745 mask.s_addr = htonl(mask.s_addr);
746 brd.s_addr = net.addr.s_addr | (~mask.s_addr);
747 req.i.ifa_prefixlen = net.cidr;
748
749 if ((nm_net_add_attr(&req.n, sizeof(req), IFA_LOCAL,
750 &net.addr.s_addr, sizeof(net.addr.s_addr)) != NM_OK) ||
751 (nm_net_add_attr(&req.n, sizeof(req), IFA_BROADCAST,
752 &brd.s_addr, sizeof(brd.s_addr)) != NM_OK)) {
753 nm_bug("%s: Error add_attr", __func__);
754 }
755
756 nm_str_free(&errmsg);
757 }
758 break;
759 }
760
761 nm_net_rtnl_open(&rth);
762 nm_net_rtnl_talk(&rth, &req.n, NULL, 0);
763 close(rth.sd);
764 #else
765 (void) name;
766 (void) action;
767 (void) src;
768 #endif /* NM_OS_LINUX */
769 }
770
nm_net_check_port(const uint16_t port,const int type,const uint32_t inaddr)771 int nm_net_check_port(const uint16_t port, const int type, const uint32_t inaddr)
772 {
773 int ret = 0;
774 struct sockaddr_in addr;
775
776 int sock = socket(AF_INET, type, 0);
777 if (!sock)
778 nm_bug("%s: couldn't create socket", __func__);
779
780 addr.sin_family = AF_INET;
781 addr.sin_port = htons(port);
782 addr.sin_addr.s_addr = htonl(inaddr);
783
784 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) {
785 ret = 1;
786 shutdown(sock, SHUT_RDWR);
787 }
788
789 close(sock);
790
791 return ret;
792 }
793
794 /* vim:set ts=4 sw=4: */
795