]>
Commit | Line | Data |
---|---|---|
0ad19a3f | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
0ad19a3f | 8 | * |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
250b1eec | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
0ad19a3f | 22 | */ |
23 | #define _GNU_SOURCE | |
24 | #include <stdio.h> | |
25 | #undef _GNU_SOURCe | |
26 | #include <stdlib.h> | |
27 | #include <unistd.h> | |
28 | #include <fcntl.h> | |
29 | #include <errno.h> | |
30 | #include <string.h> | |
31 | #include <stdio.h> | |
32 | #include <ctype.h> | |
dd1d77f9 | 33 | #include <time.h> |
0ad19a3f | 34 | #include <sys/types.h> |
35 | #include <sys/stat.h> | |
36 | #include <sys/socket.h> | |
37 | #include <sys/param.h> | |
38 | #include <sys/ioctl.h> | |
39 | #include <arpa/inet.h> | |
40 | #include <net/if.h> | |
41 | #include <net/if_arp.h> | |
42 | #include <net/ethernet.h> | |
43 | #include <netinet/in.h> | |
44 | #include <linux/netlink.h> | |
45 | #include <linux/rtnetlink.h> | |
46 | #include <linux/sockios.h> | |
f549edcc GK |
47 | |
48 | #include "nl.h" | |
49 | #include "network.h" | |
72d0e1cb | 50 | #include "conf.h" |
0d204771 | 51 | #include "utils.h" |
0ad19a3f | 52 | |
a0265685 SG |
53 | #if HAVE_IFADDRS_H |
54 | #include <ifaddrs.h> | |
55 | #else | |
56 | #include <../include/ifaddrs.h> | |
57 | #endif | |
58 | ||
0ad19a3f | 59 | #ifndef IFLA_LINKMODE |
60 | # define IFLA_LINKMODE 17 | |
61 | #endif | |
62 | ||
63 | #ifndef IFLA_LINKINFO | |
64 | # define IFLA_LINKINFO 18 | |
65 | #endif | |
66 | ||
67 | #ifndef IFLA_NET_NS_PID | |
68 | # define IFLA_NET_NS_PID 19 | |
69 | #endif | |
70 | ||
71 | #ifndef IFLA_INFO_KIND | |
72 | # define IFLA_INFO_KIND 1 | |
73 | #endif | |
74 | ||
26c39028 JHS |
75 | #ifndef IFLA_VLAN_ID |
76 | # define IFLA_VLAN_ID 1 | |
77 | #endif | |
78 | ||
0ad19a3f | 79 | #ifndef IFLA_INFO_DATA |
80 | # define IFLA_INFO_DATA 2 | |
81 | #endif | |
82 | ||
83 | #ifndef VETH_INFO_PEER | |
84 | # define VETH_INFO_PEER 1 | |
85 | #endif | |
86 | ||
e892973e DL |
87 | #ifndef IFLA_MACVLAN_MODE |
88 | # define IFLA_MACVLAN_MODE 1 | |
89 | #endif | |
90 | ||
f8fee0e2 | 91 | |
8d357196 | 92 | int lxc_netdev_move_by_index(int ifindex, pid_t pid, const char* ifname) |
0ad19a3f | 93 | { |
94 | struct nl_handler nlh; | |
95 | struct nlmsg *nlmsg = NULL; | |
06f976ca | 96 | struct ifinfomsg *ifi; |
3cfc0f3a | 97 | int err; |
0ad19a3f | 98 | |
3cfc0f3a MN |
99 | err = netlink_open(&nlh, NETLINK_ROUTE); |
100 | if (err) | |
101 | return err; | |
0ad19a3f | 102 | |
3cfc0f3a | 103 | err = -ENOMEM; |
0ad19a3f | 104 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
105 | if (!nlmsg) | |
106 | goto out; | |
107 | ||
06f976ca SZ |
108 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; |
109 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; | |
110 | ||
111 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
112 | ifi->ifi_family = AF_UNSPEC; | |
113 | ifi->ifi_index = ifindex; | |
0ad19a3f | 114 | |
115 | if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid)) | |
116 | goto out; | |
117 | ||
8d357196 DY |
118 | if (ifname != NULL) { |
119 | if (nla_put_string(nlmsg, IFLA_IFNAME, ifname)) | |
120 | goto out; | |
121 | } | |
122 | ||
3cfc0f3a | 123 | err = netlink_transaction(&nlh, nlmsg, nlmsg); |
0ad19a3f | 124 | out: |
125 | netlink_close(&nlh); | |
126 | nlmsg_free(nlmsg); | |
127 | return err; | |
128 | } | |
129 | ||
e5848d39 SH |
130 | /* |
131 | * If we are asked to move a wireless interface, then | |
132 | * we must actually move its phyN device. Detect | |
133 | * that condition and return the physname here. The | |
134 | * physname will be passed to lxc_netdev_move_wlan() | |
135 | * which will free it when done | |
136 | */ | |
137 | #define PHYSNAME "/sys/class/net/%s/phy80211/name" | |
138 | static char * is_wlan(const char *ifname) | |
139 | { | |
140 | char *path, *physname = NULL; | |
141 | size_t len = strlen(ifname) + strlen(PHYSNAME) - 1; | |
142 | struct stat sb; | |
143 | long physlen; | |
144 | FILE *f; | |
145 | int ret, i; | |
146 | ||
147 | path = alloca(len+1); | |
148 | ret = snprintf(path, len, PHYSNAME, ifname); | |
149 | if (ret < 0 || ret >= len) | |
150 | goto bad; | |
151 | ret = stat(path, &sb); | |
152 | if (ret) | |
153 | goto bad; | |
154 | if (!(f = fopen(path, "r"))) | |
155 | goto bad; | |
156 | // feh - sb.st_size is always 4096 | |
157 | fseek(f, 0, SEEK_END); | |
158 | physlen = ftell(f); | |
159 | fseek(f, 0, SEEK_SET); | |
160 | physname = malloc(physlen+1); | |
161 | if (!physname) | |
162 | goto bad; | |
163 | memset(physname, 0, physlen+1); | |
164 | ret = fread(physname, 1, physlen, f); | |
165 | fclose(f); | |
166 | if (ret < 0) | |
167 | goto bad; | |
168 | ||
169 | for (i = 0; i < physlen; i++) { | |
170 | if (physname[i] == '\n') | |
171 | physname[i] = '\0'; | |
172 | if (physname[i] == '\0') | |
173 | break; | |
174 | } | |
175 | ||
176 | return physname; | |
177 | ||
178 | bad: | |
179 | if (physname) | |
180 | free(physname); | |
181 | return NULL; | |
182 | } | |
183 | ||
184 | static int | |
185 | lxc_netdev_rename_by_name_in_netns(pid_t pid, const char *old, const char *new) | |
186 | { | |
187 | pid_t fpid = fork(); | |
188 | ||
189 | if (fpid < 0) | |
190 | return -1; | |
191 | if (fpid != 0) | |
192 | return wait_for_pid(fpid); | |
193 | if (!switch_to_ns(pid, "net")) | |
194 | return -1; | |
195 | exit(lxc_netdev_rename_by_name(old, new)); | |
196 | } | |
197 | ||
198 | static int | |
199 | lxc_netdev_move_wlan(char *physname, const char *ifname, pid_t pid, const char* newname) | |
200 | { | |
201 | int err = -1; | |
202 | pid_t fpid; | |
203 | char *cmd; | |
204 | ||
205 | /* Move phyN into the container. TODO - do this using netlink. | |
206 | * However, IIUC this involves a bit more complicated work to | |
207 | * talk to the 80211 module, so for now just call out to iw | |
208 | */ | |
209 | cmd = on_path("iw", NULL); | |
210 | if (!cmd) | |
211 | goto out1; | |
212 | free(cmd); | |
213 | ||
214 | fpid = fork(); | |
215 | if (fpid < 0) | |
216 | goto out1; | |
217 | if (fpid == 0) { | |
218 | char pidstr[30]; | |
219 | sprintf(pidstr, "%d", pid); | |
220 | if (execlp("iw", "iw", "phy", physname, "set", "netns", pidstr, NULL)) | |
221 | exit(1); | |
222 | exit(0); // notreached | |
223 | } | |
224 | if (wait_for_pid(fpid)) | |
225 | goto out1; | |
226 | ||
227 | err = 0; | |
228 | if (newname) | |
229 | err = lxc_netdev_rename_by_name_in_netns(pid, ifname, newname); | |
230 | ||
231 | out1: | |
232 | free(physname); | |
233 | return err; | |
234 | } | |
235 | ||
8d357196 | 236 | int lxc_netdev_move_by_name(const char *ifname, pid_t pid, const char* newname) |
8befa924 SH |
237 | { |
238 | int index; | |
e5848d39 | 239 | char *physname; |
8befa924 | 240 | |
8befa924 SH |
241 | if (!ifname) |
242 | return -EINVAL; | |
243 | ||
32571606 | 244 | index = if_nametoindex(ifname); |
49428bf3 DY |
245 | if (!index) |
246 | return -EINVAL; | |
32571606 | 247 | |
e5848d39 SH |
248 | if ((physname = is_wlan(ifname))) |
249 | return lxc_netdev_move_wlan(physname, ifname, pid, newname); | |
250 | ||
8d357196 | 251 | return lxc_netdev_move_by_index(index, pid, newname); |
8befa924 SH |
252 | } |
253 | ||
b84f58b9 | 254 | int lxc_netdev_delete_by_index(int ifindex) |
0ad19a3f | 255 | { |
256 | struct nl_handler nlh; | |
257 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 258 | struct ifinfomsg *ifi; |
b84f58b9 | 259 | int err; |
0ad19a3f | 260 | |
3cfc0f3a MN |
261 | err = netlink_open(&nlh, NETLINK_ROUTE); |
262 | if (err) | |
263 | return err; | |
0ad19a3f | 264 | |
3cfc0f3a | 265 | err = -ENOMEM; |
0ad19a3f | 266 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
267 | if (!nlmsg) | |
268 | goto out; | |
269 | ||
06f976ca | 270 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0ad19a3f | 271 | if (!answer) |
272 | goto out; | |
273 | ||
06f976ca SZ |
274 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; |
275 | nlmsg->nlmsghdr->nlmsg_type = RTM_DELLINK; | |
276 | ||
277 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
278 | ifi->ifi_family = AF_UNSPEC; | |
279 | ifi->ifi_index = ifindex; | |
0ad19a3f | 280 | |
3cfc0f3a | 281 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 282 | out: |
283 | netlink_close(&nlh); | |
284 | nlmsg_free(answer); | |
285 | nlmsg_free(nlmsg); | |
286 | return err; | |
287 | } | |
288 | ||
b84f58b9 DL |
289 | int lxc_netdev_delete_by_name(const char *name) |
290 | { | |
291 | int index; | |
292 | ||
293 | index = if_nametoindex(name); | |
294 | if (!index) | |
295 | return -EINVAL; | |
296 | ||
297 | return lxc_netdev_delete_by_index(index); | |
298 | } | |
299 | ||
300 | int lxc_netdev_rename_by_index(int ifindex, const char *newname) | |
b9a5bb58 DL |
301 | { |
302 | struct nl_handler nlh; | |
303 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 304 | struct ifinfomsg *ifi; |
b84f58b9 | 305 | int len, err; |
b9a5bb58 | 306 | |
3cfc0f3a MN |
307 | err = netlink_open(&nlh, NETLINK_ROUTE); |
308 | if (err) | |
309 | return err; | |
b9a5bb58 | 310 | |
b84f58b9 | 311 | len = strlen(newname); |
dae3fdf6 | 312 | if (len == 1 || len >= IFNAMSIZ) |
b84f58b9 DL |
313 | goto out; |
314 | ||
3cfc0f3a | 315 | err = -ENOMEM; |
b9a5bb58 DL |
316 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
317 | if (!nlmsg) | |
318 | goto out; | |
319 | ||
06f976ca | 320 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
b9a5bb58 DL |
321 | if (!answer) |
322 | goto out; | |
323 | ||
06f976ca SZ |
324 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; |
325 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; | |
326 | ||
327 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
328 | ifi->ifi_family = AF_UNSPEC; | |
329 | ifi->ifi_index = ifindex; | |
b84f58b9 DL |
330 | |
331 | if (nla_put_string(nlmsg, IFLA_IFNAME, newname)) | |
332 | goto out; | |
b9a5bb58 | 333 | |
3cfc0f3a | 334 | err = netlink_transaction(&nlh, nlmsg, answer); |
b9a5bb58 DL |
335 | out: |
336 | netlink_close(&nlh); | |
337 | nlmsg_free(answer); | |
338 | nlmsg_free(nlmsg); | |
339 | return err; | |
340 | } | |
341 | ||
b84f58b9 DL |
342 | int lxc_netdev_rename_by_name(const char *oldname, const char *newname) |
343 | { | |
344 | int len, index; | |
345 | ||
346 | len = strlen(oldname); | |
dae3fdf6 | 347 | if (len == 1 || len >= IFNAMSIZ) |
b84f58b9 DL |
348 | return -EINVAL; |
349 | ||
350 | index = if_nametoindex(oldname); | |
351 | if (!index) | |
352 | return -EINVAL; | |
353 | ||
354 | return lxc_netdev_rename_by_index(index, newname); | |
355 | } | |
356 | ||
8befa924 | 357 | int netdev_set_flag(const char *name, int flag) |
0ad19a3f | 358 | { |
359 | struct nl_handler nlh; | |
360 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 361 | struct ifinfomsg *ifi; |
3cfc0f3a | 362 | int index, len, err; |
0ad19a3f | 363 | |
3cfc0f3a MN |
364 | err = netlink_open(&nlh, NETLINK_ROUTE); |
365 | if (err) | |
366 | return err; | |
0ad19a3f | 367 | |
3cfc0f3a | 368 | err = -EINVAL; |
0ad19a3f | 369 | len = strlen(name); |
dae3fdf6 | 370 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 371 | goto out; |
372 | ||
3cfc0f3a | 373 | err = -ENOMEM; |
0ad19a3f | 374 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
375 | if (!nlmsg) | |
376 | goto out; | |
377 | ||
06f976ca | 378 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0ad19a3f | 379 | if (!answer) |
380 | goto out; | |
381 | ||
3cfc0f3a | 382 | err = -EINVAL; |
0ad19a3f | 383 | index = if_nametoindex(name); |
384 | if (!index) | |
385 | goto out; | |
386 | ||
06f976ca SZ |
387 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; |
388 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; | |
389 | ||
390 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
391 | ifi->ifi_family = AF_UNSPEC; | |
392 | ifi->ifi_index = index; | |
393 | ifi->ifi_change |= IFF_UP; | |
394 | ifi->ifi_flags |= flag; | |
0ad19a3f | 395 | |
396 | err = netlink_transaction(&nlh, nlmsg, answer); | |
0ad19a3f | 397 | out: |
398 | netlink_close(&nlh); | |
399 | nlmsg_free(nlmsg); | |
400 | nlmsg_free(answer); | |
401 | return err; | |
402 | } | |
403 | ||
efa1cf45 DY |
404 | int netdev_get_flag(const char* name, int *flag) |
405 | { | |
406 | struct nl_handler nlh; | |
407 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
a4318300 | 408 | struct ifinfomsg *ifi; |
06f976ca | 409 | int index, len, err; |
efa1cf45 DY |
410 | |
411 | if (!name) | |
412 | return -EINVAL; | |
413 | ||
414 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
415 | if (err) | |
416 | return err; | |
417 | ||
418 | err = -EINVAL; | |
419 | len = strlen(name); | |
420 | if (len == 1 || len >= IFNAMSIZ) | |
421 | goto out; | |
422 | ||
423 | err = -ENOMEM; | |
424 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
425 | if (!nlmsg) | |
426 | goto out; | |
427 | ||
06f976ca | 428 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
efa1cf45 DY |
429 | if (!answer) |
430 | goto out; | |
431 | ||
432 | err = -EINVAL; | |
433 | index = if_nametoindex(name); | |
434 | if (!index) | |
435 | goto out; | |
436 | ||
06f976ca SZ |
437 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST; |
438 | nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; | |
439 | ||
440 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
441 | ifi->ifi_family = AF_UNSPEC; | |
442 | ifi->ifi_index = index; | |
efa1cf45 DY |
443 | |
444 | err = netlink_transaction(&nlh, nlmsg, answer); | |
445 | if (err) | |
446 | goto out; | |
447 | ||
06f976ca | 448 | ifi = NLMSG_DATA(answer->nlmsghdr); |
efa1cf45 DY |
449 | |
450 | *flag = ifi->ifi_flags; | |
451 | out: | |
452 | netlink_close(&nlh); | |
453 | nlmsg_free(nlmsg); | |
454 | nlmsg_free(answer); | |
455 | return err; | |
456 | } | |
457 | ||
458 | /* | |
459 | * \brief Check a interface is up or not. | |
460 | * | |
461 | * \param name: name for the interface. | |
462 | * | |
463 | * \return int. | |
464 | * 0 means interface is down. | |
465 | * 1 means interface is up. | |
466 | * Others means error happened, and ret-value is the error number. | |
467 | */ | |
468 | int lxc_netdev_isup(const char* name) | |
469 | { | |
470 | int flag; | |
471 | int err; | |
472 | ||
473 | err = netdev_get_flag(name, &flag); | |
474 | if (err) | |
475 | goto out; | |
476 | if (flag & IFF_UP) | |
477 | return 1; | |
478 | return 0; | |
479 | out: | |
480 | return err; | |
481 | } | |
482 | ||
0130df54 SH |
483 | int netdev_get_mtu(int ifindex) |
484 | { | |
485 | struct nl_handler nlh; | |
486 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 487 | struct ifinfomsg *ifi; |
0130df54 SH |
488 | struct nlmsghdr *msg; |
489 | int err, res; | |
490 | int recv_len = 0, answer_len; | |
491 | int readmore = 0; | |
492 | ||
493 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
494 | if (err) | |
495 | return err; | |
496 | ||
497 | err = -ENOMEM; | |
498 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
499 | if (!nlmsg) | |
500 | goto out; | |
501 | ||
06f976ca | 502 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0130df54 SH |
503 | if (!answer) |
504 | goto out; | |
505 | ||
506 | /* Save the answer buffer length, since it will be overwritten | |
507 | * on the first receive (and we might need to receive more than | |
508 | * once. */ | |
06f976ca SZ |
509 | answer_len = answer->nlmsghdr->nlmsg_len; |
510 | ||
511 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP; | |
512 | nlmsg->nlmsghdr->nlmsg_type = RTM_GETLINK; | |
0130df54 | 513 | |
06f976ca SZ |
514 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); |
515 | ifi->ifi_family = AF_UNSPEC; | |
0130df54 SH |
516 | |
517 | /* Send the request for addresses, which returns all addresses | |
518 | * on all interfaces. */ | |
519 | err = netlink_send(&nlh, nlmsg); | |
520 | if (err < 0) | |
521 | goto out; | |
522 | ||
523 | do { | |
524 | /* Restore the answer buffer length, it might have been | |
525 | * overwritten by a previous receive. */ | |
06f976ca | 526 | answer->nlmsghdr->nlmsg_len = answer_len; |
0130df54 SH |
527 | |
528 | /* Get the (next) batch of reply messages */ | |
529 | err = netlink_rcv(&nlh, answer); | |
530 | if (err < 0) | |
531 | goto out; | |
532 | ||
533 | recv_len = err; | |
534 | err = 0; | |
535 | ||
536 | /* Satisfy the typing for the netlink macros */ | |
06f976ca | 537 | msg = answer->nlmsghdr; |
0130df54 SH |
538 | |
539 | while (NLMSG_OK(msg, recv_len)) { | |
540 | ||
541 | /* Stop reading if we see an error message */ | |
542 | if (msg->nlmsg_type == NLMSG_ERROR) { | |
543 | struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); | |
544 | err = errmsg->error; | |
545 | goto out; | |
546 | } | |
547 | ||
548 | /* Stop reading if we see a NLMSG_DONE message */ | |
549 | if (msg->nlmsg_type == NLMSG_DONE) { | |
550 | readmore = 0; | |
551 | break; | |
552 | } | |
553 | ||
06f976ca | 554 | ifi = NLMSG_DATA(msg); |
0130df54 SH |
555 | if (ifi->ifi_index == ifindex) { |
556 | struct rtattr *rta = IFLA_RTA(ifi); | |
557 | int attr_len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(*ifi)); | |
558 | res = 0; | |
559 | while(RTA_OK(rta, attr_len)) { | |
560 | /* Found a local address for the requested interface, | |
561 | * return it. */ | |
562 | if (rta->rta_type == IFLA_MTU) { | |
563 | memcpy(&res, RTA_DATA(rta), sizeof(int)); | |
564 | err = res; | |
565 | goto out; | |
566 | } | |
567 | rta = RTA_NEXT(rta, attr_len); | |
568 | } | |
569 | ||
570 | } | |
571 | ||
572 | /* Keep reading more data from the socket if the | |
573 | * last message had the NLF_F_MULTI flag set */ | |
574 | readmore = (msg->nlmsg_flags & NLM_F_MULTI); | |
575 | ||
576 | /* Look at the next message received in this buffer */ | |
577 | msg = NLMSG_NEXT(msg, recv_len); | |
578 | } | |
579 | } while (readmore); | |
580 | ||
581 | /* If we end up here, we didn't find any result, so signal an | |
582 | * error */ | |
583 | err = -1; | |
584 | ||
585 | out: | |
586 | netlink_close(&nlh); | |
587 | nlmsg_free(answer); | |
588 | nlmsg_free(nlmsg); | |
589 | return err; | |
590 | } | |
591 | ||
d472214b | 592 | int lxc_netdev_set_mtu(const char *name, int mtu) |
75d09f83 DL |
593 | { |
594 | struct nl_handler nlh; | |
595 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 596 | struct ifinfomsg *ifi; |
3cfc0f3a | 597 | int index, len, err; |
75d09f83 | 598 | |
3cfc0f3a MN |
599 | err = netlink_open(&nlh, NETLINK_ROUTE); |
600 | if (err) | |
601 | return err; | |
75d09f83 | 602 | |
3cfc0f3a | 603 | err = -EINVAL; |
75d09f83 | 604 | len = strlen(name); |
dae3fdf6 | 605 | if (len == 1 || len >= IFNAMSIZ) |
75d09f83 DL |
606 | goto out; |
607 | ||
3cfc0f3a | 608 | err = -ENOMEM; |
75d09f83 DL |
609 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
610 | if (!nlmsg) | |
611 | goto out; | |
612 | ||
06f976ca | 613 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
75d09f83 DL |
614 | if (!answer) |
615 | goto out; | |
616 | ||
3cfc0f3a | 617 | err = -EINVAL; |
75d09f83 DL |
618 | index = if_nametoindex(name); |
619 | if (!index) | |
620 | goto out; | |
621 | ||
06f976ca SZ |
622 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; |
623 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; | |
624 | ||
625 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
626 | ifi->ifi_family = AF_UNSPEC; | |
627 | ifi->ifi_index = index; | |
75d09f83 DL |
628 | |
629 | if (nla_put_u32(nlmsg, IFLA_MTU, mtu)) | |
630 | goto out; | |
631 | ||
632 | err = netlink_transaction(&nlh, nlmsg, answer); | |
75d09f83 DL |
633 | out: |
634 | netlink_close(&nlh); | |
635 | nlmsg_free(nlmsg); | |
636 | nlmsg_free(answer); | |
637 | return err; | |
638 | } | |
639 | ||
d472214b | 640 | int lxc_netdev_up(const char *name) |
0ad19a3f | 641 | { |
d472214b | 642 | return netdev_set_flag(name, IFF_UP); |
0ad19a3f | 643 | } |
644 | ||
d472214b | 645 | int lxc_netdev_down(const char *name) |
0ad19a3f | 646 | { |
d472214b | 647 | return netdev_set_flag(name, 0); |
0ad19a3f | 648 | } |
649 | ||
497353b6 | 650 | int lxc_veth_create(const char *name1, const char *name2) |
0ad19a3f | 651 | { |
652 | struct nl_handler nlh; | |
653 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 654 | struct ifinfomsg *ifi; |
0ad19a3f | 655 | struct rtattr *nest1, *nest2, *nest3; |
3cfc0f3a | 656 | int len, err; |
0ad19a3f | 657 | |
3cfc0f3a MN |
658 | err = netlink_open(&nlh, NETLINK_ROUTE); |
659 | if (err) | |
660 | return err; | |
0ad19a3f | 661 | |
3cfc0f3a | 662 | err = -EINVAL; |
0ad19a3f | 663 | len = strlen(name1); |
dae3fdf6 | 664 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 665 | goto out; |
666 | ||
667 | len = strlen(name2); | |
dae3fdf6 | 668 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 669 | goto out; |
670 | ||
3cfc0f3a | 671 | err = -ENOMEM; |
0ad19a3f | 672 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
673 | if (!nlmsg) | |
674 | goto out; | |
675 | ||
06f976ca | 676 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0ad19a3f | 677 | if (!answer) |
678 | goto out; | |
679 | ||
06f976ca | 680 | nlmsg->nlmsghdr->nlmsg_flags = |
0ad19a3f | 681 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; |
06f976ca SZ |
682 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; |
683 | ||
684 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
685 | ifi->ifi_family = AF_UNSPEC; | |
0ad19a3f | 686 | |
3cfc0f3a | 687 | err = -EINVAL; |
79e68309 | 688 | nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
0ad19a3f | 689 | if (!nest1) |
690 | goto out; | |
691 | ||
692 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "veth")) | |
693 | goto out; | |
694 | ||
695 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
696 | if (!nest2) | |
697 | goto out; | |
698 | ||
699 | nest3 = nla_begin_nested(nlmsg, VETH_INFO_PEER); | |
700 | if (!nest3) | |
701 | goto out; | |
702 | ||
06f976ca SZ |
703 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); |
704 | if (!ifi) | |
705 | goto out; | |
0ad19a3f | 706 | |
707 | if (nla_put_string(nlmsg, IFLA_IFNAME, name2)) | |
708 | goto out; | |
709 | ||
710 | nla_end_nested(nlmsg, nest3); | |
711 | ||
712 | nla_end_nested(nlmsg, nest2); | |
713 | ||
714 | nla_end_nested(nlmsg, nest1); | |
715 | ||
716 | if (nla_put_string(nlmsg, IFLA_IFNAME, name1)) | |
717 | goto out; | |
718 | ||
3cfc0f3a | 719 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 720 | out: |
721 | netlink_close(&nlh); | |
722 | nlmsg_free(answer); | |
723 | nlmsg_free(nlmsg); | |
724 | return err; | |
725 | } | |
726 | ||
26c39028 | 727 | /* XXX: merge with lxc_macvlan_create */ |
7c11d57a | 728 | int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) |
26c39028 JHS |
729 | { |
730 | struct nl_handler nlh; | |
731 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 732 | struct ifinfomsg *ifi; |
26c39028 | 733 | struct rtattr *nest, *nest2; |
3cfc0f3a | 734 | int lindex, len, err; |
26c39028 | 735 | |
3cfc0f3a MN |
736 | err = netlink_open(&nlh, NETLINK_ROUTE); |
737 | if (err) | |
738 | return err; | |
26c39028 | 739 | |
3cfc0f3a | 740 | err = -EINVAL; |
26c39028 | 741 | len = strlen(master); |
dae3fdf6 | 742 | if (len == 1 || len >= IFNAMSIZ) |
26c39028 JHS |
743 | goto err3; |
744 | ||
745 | len = strlen(name); | |
dae3fdf6 | 746 | if (len == 1 || len >= IFNAMSIZ) |
26c39028 JHS |
747 | goto err3; |
748 | ||
3cfc0f3a | 749 | err = -ENOMEM; |
26c39028 JHS |
750 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
751 | if (!nlmsg) | |
752 | goto err3; | |
753 | ||
06f976ca | 754 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
26c39028 JHS |
755 | if (!answer) |
756 | goto err2; | |
757 | ||
3cfc0f3a | 758 | err = -EINVAL; |
26c39028 JHS |
759 | lindex = if_nametoindex(master); |
760 | if (!lindex) | |
761 | goto err1; | |
762 | ||
06f976ca | 763 | nlmsg->nlmsghdr->nlmsg_flags = |
26c39028 | 764 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; |
06f976ca SZ |
765 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; |
766 | ||
767 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
768 | ifi->ifi_family = AF_UNSPEC; | |
26c39028 | 769 | |
79e68309 | 770 | nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
26c39028 JHS |
771 | if (!nest) |
772 | goto err1; | |
773 | ||
774 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "vlan")) | |
775 | goto err1; | |
776 | ||
777 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
778 | if (!nest2) | |
779 | goto err1; | |
e892973e | 780 | |
26c39028 JHS |
781 | if (nla_put_u16(nlmsg, IFLA_VLAN_ID, vlanid)) |
782 | goto err1; | |
e892973e | 783 | |
26c39028 JHS |
784 | nla_end_nested(nlmsg, nest2); |
785 | ||
786 | nla_end_nested(nlmsg, nest); | |
787 | ||
788 | if (nla_put_u32(nlmsg, IFLA_LINK, lindex)) | |
789 | goto err1; | |
790 | ||
791 | if (nla_put_string(nlmsg, IFLA_IFNAME, name)) | |
792 | goto err1; | |
793 | ||
3cfc0f3a | 794 | err = netlink_transaction(&nlh, nlmsg, answer); |
26c39028 JHS |
795 | err1: |
796 | nlmsg_free(answer); | |
797 | err2: | |
798 | nlmsg_free(nlmsg); | |
799 | err3: | |
800 | netlink_close(&nlh); | |
801 | return err; | |
802 | } | |
803 | ||
e892973e | 804 | int lxc_macvlan_create(const char *master, const char *name, int mode) |
0ad19a3f | 805 | { |
806 | struct nl_handler nlh; | |
807 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 808 | struct ifinfomsg *ifi; |
e892973e | 809 | struct rtattr *nest, *nest2; |
3cfc0f3a | 810 | int index, len, err; |
0ad19a3f | 811 | |
3cfc0f3a MN |
812 | err = netlink_open(&nlh, NETLINK_ROUTE); |
813 | if (err) | |
814 | return err; | |
0ad19a3f | 815 | |
3cfc0f3a | 816 | err = -EINVAL; |
0ad19a3f | 817 | len = strlen(master); |
dae3fdf6 | 818 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 819 | goto out; |
820 | ||
821 | len = strlen(name); | |
dae3fdf6 | 822 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 823 | goto out; |
824 | ||
3cfc0f3a | 825 | err = -ENOMEM; |
0ad19a3f | 826 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
827 | if (!nlmsg) | |
828 | goto out; | |
829 | ||
06f976ca | 830 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0ad19a3f | 831 | if (!answer) |
832 | goto out; | |
833 | ||
3cfc0f3a | 834 | err = -EINVAL; |
0ad19a3f | 835 | index = if_nametoindex(master); |
836 | if (!index) | |
837 | goto out; | |
838 | ||
06f976ca | 839 | nlmsg->nlmsghdr->nlmsg_flags = |
0ad19a3f | 840 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; |
06f976ca SZ |
841 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWLINK; |
842 | ||
843 | ifi = nlmsg_reserve(nlmsg, sizeof(struct ifinfomsg)); | |
844 | ifi->ifi_family = AF_UNSPEC; | |
0ad19a3f | 845 | |
79e68309 | 846 | nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
0ad19a3f | 847 | if (!nest) |
848 | goto out; | |
849 | ||
850 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "macvlan")) | |
851 | goto out; | |
852 | ||
e892973e DL |
853 | if (mode) { |
854 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
855 | if (!nest2) | |
856 | goto out; | |
857 | ||
858 | if (nla_put_u32(nlmsg, IFLA_MACVLAN_MODE, mode)) | |
859 | goto out; | |
860 | ||
861 | nla_end_nested(nlmsg, nest2); | |
862 | } | |
863 | ||
0ad19a3f | 864 | nla_end_nested(nlmsg, nest); |
865 | ||
866 | if (nla_put_u32(nlmsg, IFLA_LINK, index)) | |
867 | goto out; | |
868 | ||
869 | if (nla_put_string(nlmsg, IFLA_IFNAME, name)) | |
870 | goto out; | |
871 | ||
3cfc0f3a | 872 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 873 | out: |
874 | netlink_close(&nlh); | |
875 | nlmsg_free(answer); | |
876 | nlmsg_free(nlmsg); | |
877 | return err; | |
878 | } | |
879 | ||
880 | static int proc_sys_net_write(const char *path, const char *value) | |
881 | { | |
882 | int fd, err = 0; | |
883 | ||
884 | fd = open(path, O_WRONLY); | |
885 | if (fd < 0) | |
886 | return -errno; | |
887 | ||
888 | if (write(fd, value, strlen(value)) < 0) | |
889 | err = -errno; | |
890 | ||
891 | close(fd); | |
892 | return err; | |
893 | } | |
894 | ||
895 | static int ip_forward_set(const char *ifname, int family, int flag) | |
896 | { | |
22ebac19 | 897 | char path[MAXPATHLEN]; |
9ba8130c | 898 | int rc; |
0ad19a3f | 899 | |
900 | if (family != AF_INET && family != AF_INET6) | |
3cfc0f3a | 901 | return -EINVAL; |
0ad19a3f | 902 | |
9ba8130c | 903 | rc = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/forwarding", |
0ad19a3f | 904 | family == AF_INET?"ipv4":"ipv6" , ifname); |
9ba8130c SH |
905 | if (rc >= MAXPATHLEN) |
906 | return -E2BIG; | |
0ad19a3f | 907 | |
22ebac19 | 908 | return proc_sys_net_write(path, flag?"1":"0"); |
0ad19a3f | 909 | } |
910 | ||
497353b6 | 911 | int lxc_ip_forward_on(const char *ifname, int family) |
0ad19a3f | 912 | { |
913 | return ip_forward_set(ifname, family, 1); | |
914 | } | |
915 | ||
497353b6 | 916 | int lxc_ip_forward_off(const char *ifname, int family) |
0ad19a3f | 917 | { |
918 | return ip_forward_set(ifname, family, 0); | |
919 | } | |
920 | ||
921 | static int neigh_proxy_set(const char *ifname, int family, int flag) | |
922 | { | |
923 | char path[MAXPATHLEN]; | |
9ba8130c | 924 | int ret; |
0ad19a3f | 925 | |
926 | if (family != AF_INET && family != AF_INET6) | |
3cfc0f3a | 927 | return -EINVAL; |
0ad19a3f | 928 | |
9ba8130c | 929 | ret = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/%s", |
79e68309 | 930 | family == AF_INET?"ipv4":"ipv6" , ifname, |
0ad19a3f | 931 | family == AF_INET?"proxy_arp":"proxy_ndp"); |
9ba8130c SH |
932 | if (ret < 0 || ret >= MAXPATHLEN) |
933 | return -E2BIG; | |
0ad19a3f | 934 | |
935 | return proc_sys_net_write(path, flag?"1":"0"); | |
936 | } | |
937 | ||
497353b6 | 938 | int lxc_neigh_proxy_on(const char *name, int family) |
0ad19a3f | 939 | { |
940 | return neigh_proxy_set(name, family, 1); | |
941 | } | |
942 | ||
497353b6 | 943 | int lxc_neigh_proxy_off(const char *name, int family) |
0ad19a3f | 944 | { |
945 | return neigh_proxy_set(name, family, 0); | |
946 | } | |
947 | ||
948 | int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr) | |
949 | { | |
1f1b18e7 DL |
950 | unsigned char *data; |
951 | char c; | |
952 | int i = 0; | |
953 | unsigned val; | |
954 | ||
955 | sockaddr->sa_family = ARPHRD_ETHER; | |
956 | data = (unsigned char *)sockaddr->sa_data; | |
957 | ||
958 | while ((*macaddr != '\0') && (i < ETH_ALEN)) { | |
959 | val = 0; | |
960 | c = *macaddr++; | |
961 | if (isdigit(c)) | |
962 | val = c - '0'; | |
963 | else if (c >= 'a' && c <= 'f') | |
964 | val = c - 'a' + 10; | |
965 | else if (c >= 'A' && c <= 'F') | |
966 | val = c - 'A' + 10; | |
967 | else { | |
968 | return -EINVAL; | |
969 | } | |
970 | val <<= 4; | |
971 | c = *macaddr; | |
972 | if (isdigit(c)) | |
973 | val |= c - '0'; | |
974 | else if (c >= 'a' && c <= 'f') | |
975 | val |= c - 'a' + 10; | |
976 | else if (c >= 'A' && c <= 'F') | |
977 | val |= c - 'A' + 10; | |
978 | else if (c == ':' || c == 0) | |
979 | val >>= 4; | |
980 | else { | |
981 | return -EINVAL; | |
982 | } | |
983 | if (c != 0) | |
984 | macaddr++; | |
985 | *data++ = (unsigned char) (val & 0377); | |
986 | i++; | |
987 | ||
988 | if (*macaddr == ':') | |
989 | macaddr++; | |
0ad19a3f | 990 | } |
0ad19a3f | 991 | |
1f1b18e7 | 992 | return 0; |
0ad19a3f | 993 | } |
994 | ||
1f1b18e7 DL |
995 | static int ip_addr_add(int family, int ifindex, |
996 | void *addr, void *bcast, void *acast, int prefix) | |
0ad19a3f | 997 | { |
998 | struct nl_handler nlh; | |
0ad19a3f | 999 | struct nlmsg *nlmsg = NULL, *answer = NULL; |
06f976ca | 1000 | struct ifaddrmsg *ifa; |
4bf1968d | 1001 | int addrlen; |
3cfc0f3a | 1002 | int err; |
0ad19a3f | 1003 | |
4bf1968d DL |
1004 | addrlen = family == AF_INET ? sizeof(struct in_addr) : |
1005 | sizeof(struct in6_addr); | |
1006 | ||
3cfc0f3a MN |
1007 | err = netlink_open(&nlh, NETLINK_ROUTE); |
1008 | if (err) | |
1009 | return err; | |
0ad19a3f | 1010 | |
3cfc0f3a | 1011 | err = -ENOMEM; |
0ad19a3f | 1012 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
1013 | if (!nlmsg) | |
1014 | goto out; | |
1015 | ||
06f976ca | 1016 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
0ad19a3f | 1017 | if (!answer) |
1018 | goto out; | |
1019 | ||
06f976ca | 1020 | nlmsg->nlmsghdr->nlmsg_flags = |
0ad19a3f | 1021 | NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; |
06f976ca SZ |
1022 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWADDR; |
1023 | ||
1024 | ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); | |
1025 | ifa->ifa_prefixlen = prefix; | |
1026 | ifa->ifa_index = ifindex; | |
1027 | ifa->ifa_family = family; | |
1028 | ifa->ifa_scope = 0; | |
0ad19a3f | 1029 | |
3cfc0f3a | 1030 | err = -EINVAL; |
4bf1968d | 1031 | if (nla_put_buffer(nlmsg, IFA_LOCAL, addr, addrlen)) |
0ad19a3f | 1032 | goto out; |
1033 | ||
4bf1968d | 1034 | if (nla_put_buffer(nlmsg, IFA_ADDRESS, addr, addrlen)) |
0ad19a3f | 1035 | goto out; |
1036 | ||
d8948a52 | 1037 | if (nla_put_buffer(nlmsg, IFA_BROADCAST, bcast, addrlen)) |
1f1b18e7 DL |
1038 | goto out; |
1039 | ||
1040 | /* TODO : multicast, anycast with ipv6 */ | |
7ddc8f24 | 1041 | err = -EPROTONOSUPPORT; |
79881dc6 DL |
1042 | if (family == AF_INET6 && |
1043 | (memcmp(bcast, &in6addr_any, sizeof(in6addr_any)) || | |
1044 | memcmp(acast, &in6addr_any, sizeof(in6addr_any)))) | |
1f1b18e7 | 1045 | goto out; |
0ad19a3f | 1046 | |
3cfc0f3a | 1047 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 1048 | out: |
1049 | netlink_close(&nlh); | |
1050 | nlmsg_free(answer); | |
1051 | nlmsg_free(nlmsg); | |
1052 | return err; | |
1053 | } | |
1054 | ||
1f1b18e7 DL |
1055 | int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, |
1056 | struct in6_addr *mcast, | |
1057 | struct in6_addr *acast, int prefix) | |
1058 | { | |
1059 | return ip_addr_add(AF_INET6, ifindex, addr, mcast, acast, prefix); | |
1060 | } | |
1061 | ||
1062 | int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, | |
1063 | struct in_addr *bcast, int prefix) | |
1064 | { | |
1065 | return ip_addr_add(AF_INET, ifindex, addr, bcast, NULL, prefix); | |
1066 | } | |
1067 | ||
19a26f82 MK |
1068 | /* Find an IFA_LOCAL (or IFA_ADDRESS if not IFA_LOCAL is present) |
1069 | * address from the given RTM_NEWADDR message. Allocates memory for the | |
1070 | * address and stores that pointer in *res (so res should be an | |
1071 | * in_addr** or in6_addr**). | |
1072 | */ | |
06f976ca SZ |
1073 | static int ifa_get_local_ip(int family, struct nlmsghdr *msg, void** res) { |
1074 | struct ifaddrmsg *ifa = NLMSG_DATA(msg); | |
1075 | struct rtattr *rta = IFA_RTA(ifa); | |
1076 | int attr_len = NLMSG_PAYLOAD(msg, sizeof(struct ifaddrmsg)); | |
19a26f82 MK |
1077 | int addrlen; |
1078 | ||
06f976ca | 1079 | if (ifa->ifa_family != family) |
19a26f82 MK |
1080 | return 0; |
1081 | ||
1082 | addrlen = family == AF_INET ? sizeof(struct in_addr) : | |
1083 | sizeof(struct in6_addr); | |
1084 | ||
1085 | /* Loop over the rtattr's in this message */ | |
1086 | while(RTA_OK(rta, attr_len)) { | |
1087 | /* Found a local address for the requested interface, | |
1088 | * return it. */ | |
1089 | if (rta->rta_type == IFA_LOCAL || rta->rta_type == IFA_ADDRESS) { | |
1090 | /* Sanity check. The family check above should | |
1091 | * make sure the address length is correct, but | |
1092 | * check here just in case */ | |
1093 | if (RTA_PAYLOAD(rta) != addrlen) | |
1094 | return -1; | |
1095 | ||
1096 | /* We might have found an IFA_ADDRESS before, | |
1097 | * which we now overwrite with an IFA_LOCAL. */ | |
dd66e5ad | 1098 | if (!*res) { |
19a26f82 | 1099 | *res = malloc(addrlen); |
dd66e5ad DE |
1100 | if (!*res) |
1101 | return -1; | |
1102 | } | |
19a26f82 MK |
1103 | |
1104 | memcpy(*res, RTA_DATA(rta), addrlen); | |
1105 | ||
1106 | if (rta->rta_type == IFA_LOCAL) | |
1107 | break; | |
1108 | } | |
1109 | rta = RTA_NEXT(rta, attr_len); | |
1110 | } | |
1111 | return 0; | |
1112 | } | |
1113 | ||
1114 | static int ip_addr_get(int family, int ifindex, void **res) | |
1115 | { | |
1116 | struct nl_handler nlh; | |
1117 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 1118 | struct ifaddrmsg *ifa; |
19a26f82 MK |
1119 | struct nlmsghdr *msg; |
1120 | int err; | |
1121 | int recv_len = 0, answer_len; | |
1122 | int readmore = 0; | |
1123 | ||
1124 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
1125 | if (err) | |
1126 | return err; | |
1127 | ||
1128 | err = -ENOMEM; | |
1129 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
1130 | if (!nlmsg) | |
1131 | goto out; | |
1132 | ||
06f976ca | 1133 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
19a26f82 MK |
1134 | if (!answer) |
1135 | goto out; | |
1136 | ||
1137 | /* Save the answer buffer length, since it will be overwritten | |
1138 | * on the first receive (and we might need to receive more than | |
1139 | * once. */ | |
06f976ca SZ |
1140 | answer_len = answer->nlmsghdr->nlmsg_len; |
1141 | ||
1142 | nlmsg->nlmsghdr->nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT; | |
1143 | nlmsg->nlmsghdr->nlmsg_type = RTM_GETADDR; | |
19a26f82 | 1144 | |
06f976ca SZ |
1145 | ifa = nlmsg_reserve(nlmsg, sizeof(struct ifaddrmsg)); |
1146 | ifa->ifa_family = family; | |
19a26f82 MK |
1147 | |
1148 | /* Send the request for addresses, which returns all addresses | |
1149 | * on all interfaces. */ | |
1150 | err = netlink_send(&nlh, nlmsg); | |
1151 | if (err < 0) | |
1152 | goto out; | |
19a26f82 MK |
1153 | |
1154 | do { | |
1155 | /* Restore the answer buffer length, it might have been | |
1156 | * overwritten by a previous receive. */ | |
06f976ca | 1157 | answer->nlmsghdr->nlmsg_len = answer_len; |
19a26f82 MK |
1158 | |
1159 | /* Get the (next) batch of reply messages */ | |
1160 | err = netlink_rcv(&nlh, answer); | |
1161 | if (err < 0) | |
1162 | goto out; | |
1163 | ||
1164 | recv_len = err; | |
1165 | err = 0; | |
1166 | ||
1167 | /* Satisfy the typing for the netlink macros */ | |
06f976ca | 1168 | msg = answer->nlmsghdr; |
19a26f82 MK |
1169 | |
1170 | while (NLMSG_OK(msg, recv_len)) { | |
1171 | /* Stop reading if we see an error message */ | |
1172 | if (msg->nlmsg_type == NLMSG_ERROR) { | |
1173 | struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); | |
1174 | err = errmsg->error; | |
1175 | goto out; | |
1176 | } | |
1177 | ||
1178 | /* Stop reading if we see a NLMSG_DONE message */ | |
1179 | if (msg->nlmsg_type == NLMSG_DONE) { | |
1180 | readmore = 0; | |
1181 | break; | |
1182 | } | |
1183 | ||
1184 | if (msg->nlmsg_type != RTM_NEWADDR) { | |
1185 | err = -1; | |
1186 | goto out; | |
1187 | } | |
1188 | ||
06f976ca SZ |
1189 | ifa = (struct ifaddrmsg *)NLMSG_DATA(msg); |
1190 | if (ifa->ifa_index == ifindex) { | |
1191 | if (ifa_get_local_ip(family, msg, res) < 0) { | |
51e7a874 SG |
1192 | err = -1; |
1193 | goto out; | |
1194 | } | |
1195 | ||
19a26f82 MK |
1196 | /* Found a result, stop searching */ |
1197 | if (*res) | |
1198 | goto out; | |
1199 | } | |
1200 | ||
1201 | /* Keep reading more data from the socket if the | |
1202 | * last message had the NLF_F_MULTI flag set */ | |
1203 | readmore = (msg->nlmsg_flags & NLM_F_MULTI); | |
1204 | ||
1205 | /* Look at the next message received in this buffer */ | |
1206 | msg = NLMSG_NEXT(msg, recv_len); | |
1207 | } | |
1208 | } while (readmore); | |
1209 | ||
1210 | /* If we end up here, we didn't find any result, so signal an | |
1211 | * error */ | |
1212 | err = -1; | |
1213 | ||
1214 | out: | |
1215 | netlink_close(&nlh); | |
1216 | nlmsg_free(answer); | |
1217 | nlmsg_free(nlmsg); | |
1218 | return err; | |
1219 | } | |
1220 | ||
1221 | int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res) | |
1222 | { | |
1223 | return ip_addr_get(AF_INET6, ifindex, (void**)res); | |
1224 | } | |
1225 | ||
1226 | int lxc_ipv4_addr_get(int ifindex, struct in_addr** res) | |
1227 | { | |
1228 | return ip_addr_get(AF_INET, ifindex, (void**)res); | |
1229 | } | |
1230 | ||
f8fee0e2 MK |
1231 | static int ip_gateway_add(int family, int ifindex, void *gw) |
1232 | { | |
1233 | struct nl_handler nlh; | |
1234 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 1235 | struct rtmsg *rt; |
f8fee0e2 MK |
1236 | int addrlen; |
1237 | int err; | |
1238 | ||
1239 | addrlen = family == AF_INET ? sizeof(struct in_addr) : | |
1240 | sizeof(struct in6_addr); | |
1241 | ||
1242 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
1243 | if (err) | |
1244 | return err; | |
1245 | ||
1246 | err = -ENOMEM; | |
1247 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
1248 | if (!nlmsg) | |
1249 | goto out; | |
1250 | ||
06f976ca | 1251 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
f8fee0e2 MK |
1252 | if (!answer) |
1253 | goto out; | |
1254 | ||
06f976ca | 1255 | nlmsg->nlmsghdr->nlmsg_flags = |
f8fee0e2 | 1256 | NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; |
06f976ca SZ |
1257 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; |
1258 | ||
1259 | rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); | |
1260 | rt->rtm_family = family; | |
1261 | rt->rtm_table = RT_TABLE_MAIN; | |
1262 | rt->rtm_scope = RT_SCOPE_UNIVERSE; | |
1263 | rt->rtm_protocol = RTPROT_BOOT; | |
1264 | rt->rtm_type = RTN_UNICAST; | |
f8fee0e2 | 1265 | /* "default" destination */ |
06f976ca | 1266 | rt->rtm_dst_len = 0; |
f8fee0e2 MK |
1267 | |
1268 | err = -EINVAL; | |
1269 | if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) | |
1270 | goto out; | |
1271 | ||
1272 | /* Adding the interface index enables the use of link-local | |
1273 | * addresses for the gateway */ | |
1274 | if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) | |
1275 | goto out; | |
1276 | ||
1277 | err = netlink_transaction(&nlh, nlmsg, answer); | |
1278 | out: | |
1279 | netlink_close(&nlh); | |
1280 | nlmsg_free(answer); | |
1281 | nlmsg_free(nlmsg); | |
1282 | return err; | |
1283 | } | |
1284 | ||
1285 | int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw) | |
1286 | { | |
1287 | return ip_gateway_add(AF_INET, ifindex, gw); | |
1288 | } | |
1289 | ||
1290 | int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw) | |
1291 | { | |
1292 | return ip_gateway_add(AF_INET6, ifindex, gw); | |
1293 | } | |
1294 | ||
77dcf03a GL |
1295 | static int ip_route_dest_add(int family, int ifindex, void *dest) |
1296 | { | |
1297 | struct nl_handler nlh; | |
1298 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
06f976ca | 1299 | struct rtmsg *rt; |
77dcf03a GL |
1300 | int addrlen; |
1301 | int err; | |
1302 | ||
1303 | addrlen = family == AF_INET ? sizeof(struct in_addr) : | |
1304 | sizeof(struct in6_addr); | |
1305 | ||
1306 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
1307 | if (err) | |
1308 | return err; | |
1309 | ||
1310 | err = -ENOMEM; | |
1311 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
1312 | if (!nlmsg) | |
1313 | goto out; | |
1314 | ||
06f976ca | 1315 | answer = nlmsg_alloc_reserve(NLMSG_GOOD_SIZE); |
77dcf03a GL |
1316 | if (!answer) |
1317 | goto out; | |
1318 | ||
06f976ca | 1319 | nlmsg->nlmsghdr->nlmsg_flags = |
77dcf03a | 1320 | NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; |
06f976ca SZ |
1321 | nlmsg->nlmsghdr->nlmsg_type = RTM_NEWROUTE; |
1322 | ||
1323 | rt = nlmsg_reserve(nlmsg, sizeof(struct rtmsg)); | |
1324 | rt->rtm_family = family; | |
1325 | rt->rtm_table = RT_TABLE_MAIN; | |
1326 | rt->rtm_scope = RT_SCOPE_LINK; | |
1327 | rt->rtm_protocol = RTPROT_BOOT; | |
1328 | rt->rtm_type = RTN_UNICAST; | |
1329 | rt->rtm_dst_len = addrlen*8; | |
77dcf03a GL |
1330 | |
1331 | err = -EINVAL; | |
1332 | if (nla_put_buffer(nlmsg, RTA_DST, dest, addrlen)) | |
1333 | goto out; | |
1334 | if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) | |
1335 | goto out; | |
1336 | err = netlink_transaction(&nlh, nlmsg, answer); | |
1337 | out: | |
1338 | netlink_close(&nlh); | |
1339 | nlmsg_free(answer); | |
1340 | nlmsg_free(nlmsg); | |
1341 | return err; | |
1342 | } | |
1343 | ||
1344 | int lxc_ipv4_dest_add(int ifindex, struct in_addr *dest) | |
1345 | { | |
1346 | return ip_route_dest_add(AF_INET, ifindex, dest); | |
1347 | } | |
1348 | ||
1349 | int lxc_ipv6_dest_add(int ifindex, struct in6_addr *dest) | |
1350 | { | |
1351 | return ip_route_dest_add(AF_INET6, ifindex, dest); | |
1352 | } | |
1353 | ||
0d204771 SH |
1354 | static bool is_ovs_bridge(const char *bridge) |
1355 | { | |
1356 | char brdirname[22 + IFNAMSIZ + 1] = {0}; | |
1357 | struct stat sb; | |
1358 | ||
1359 | snprintf(brdirname, 22 +IFNAMSIZ + 1, "/sys/class/net/%s/bridge", bridge); | |
1360 | if (stat(brdirname, &sb) == -1 && errno == ENOENT) | |
1361 | return true; | |
1362 | return false; | |
1363 | } | |
1364 | ||
1365 | static int attach_to_ovs_bridge(const char *bridge, const char *nic) | |
1366 | { | |
1367 | pid_t pid; | |
6ad22d06 SH |
1368 | char *cmd; |
1369 | ||
1370 | cmd = on_path("ovs-vsctl", NULL); | |
1371 | if (!cmd) | |
1372 | return -1; | |
1373 | free(cmd); | |
0d204771 SH |
1374 | |
1375 | pid = fork(); | |
1376 | if (pid < 0) | |
1377 | return -1; | |
1378 | if (pid > 0) | |
1379 | return wait_for_pid(pid); | |
1380 | ||
6ad22d06 | 1381 | if (execlp("ovs-vsctl", "ovs-vsctl", "add-port", bridge, nic, NULL)) |
0d204771 SH |
1382 | exit(1); |
1383 | // not reached | |
1384 | exit(1); | |
1385 | } | |
0d204771 | 1386 | |
7d163508 MN |
1387 | /* |
1388 | * There is a lxc_bridge_attach, but no need of a bridge detach | |
d472214b | 1389 | * as automatically done by kernel when a netdev is deleted. |
7d163508 MN |
1390 | */ |
1391 | int lxc_bridge_attach(const char *bridge, const char *ifname) | |
0ad19a3f | 1392 | { |
1393 | int fd, index, err; | |
1394 | struct ifreq ifr; | |
1395 | ||
dae3fdf6 | 1396 | if (strlen(ifname) >= IFNAMSIZ) |
3cfc0f3a | 1397 | return -EINVAL; |
0ad19a3f | 1398 | |
1399 | index = if_nametoindex(ifname); | |
1400 | if (!index) | |
3cfc0f3a | 1401 | return -EINVAL; |
0ad19a3f | 1402 | |
0d204771 SH |
1403 | if (is_ovs_bridge(bridge)) |
1404 | return attach_to_ovs_bridge(bridge, ifname); | |
1405 | ||
0ad19a3f | 1406 | fd = socket(AF_INET, SOCK_STREAM, 0); |
1407 | if (fd < 0) | |
3cfc0f3a | 1408 | return -errno; |
0ad19a3f | 1409 | |
5da6aa8c DE |
1410 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ-1); |
1411 | ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
0ad19a3f | 1412 | ifr.ifr_ifindex = index; |
7d163508 | 1413 | err = ioctl(fd, SIOCBRADDIF, &ifr); |
0ad19a3f | 1414 | close(fd); |
3cfc0f3a MN |
1415 | if (err) |
1416 | err = -errno; | |
0ad19a3f | 1417 | |
1418 | return err; | |
1419 | } | |
72d0e1cb | 1420 | |
74a3920a | 1421 | static const char* const lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { |
b343592b | 1422 | [LXC_NET_EMPTY] = "empty", |
72d0e1cb SG |
1423 | [LXC_NET_VETH] = "veth", |
1424 | [LXC_NET_MACVLAN] = "macvlan", | |
72d0e1cb | 1425 | [LXC_NET_PHYS] = "phys", |
b343592b BP |
1426 | [LXC_NET_VLAN] = "vlan", |
1427 | [LXC_NET_NONE] = "none", | |
72d0e1cb SG |
1428 | }; |
1429 | ||
1430 | const char *lxc_net_type_to_str(int type) | |
1431 | { | |
1432 | if (type < 0 || type > LXC_NET_MAXCONFTYPE) | |
1433 | return NULL; | |
1434 | return lxc_network_types[type]; | |
1435 | } | |
8befa924 | 1436 | |
74a3920a | 1437 | static const char padchar[] = |
a0265685 SG |
1438 | "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
1439 | ||
1440 | char *lxc_mkifname(char *template) | |
1441 | { | |
1442 | char *name = NULL; | |
1443 | int i = 0; | |
1444 | FILE *urandom; | |
1445 | unsigned int seed; | |
1446 | struct ifaddrs *ifaddr, *ifa; | |
1447 | int ifexists = 0; | |
1448 | ||
1449 | /* Get all the network interfaces */ | |
1450 | getifaddrs(&ifaddr); | |
1451 | ||
1452 | /* Initialize the random number generator */ | |
a0265685 | 1453 | urandom = fopen ("/dev/urandom", "r"); |
a0265685 SG |
1454 | if (urandom != NULL) { |
1455 | if (fread (&seed, sizeof(seed), 1, urandom) <= 0) | |
1456 | seed = time(0); | |
a0265685 | 1457 | fclose(urandom); |
a0265685 SG |
1458 | } |
1459 | else | |
1460 | seed = time(0); | |
1461 | ||
1462 | #ifndef HAVE_RAND_R | |
1463 | srand(seed); | |
1464 | #endif | |
1465 | ||
1466 | /* Generate random names until we find one that doesn't exist */ | |
1467 | while(1) { | |
1468 | ifexists = 0; | |
1469 | name = strdup(template); | |
1470 | ||
1471 | if (name == NULL) | |
1472 | return NULL; | |
1473 | ||
1474 | for (i = 0; i < strlen(name); i++) { | |
1475 | if (name[i] == 'X') { | |
1476 | #ifdef HAVE_RAND_R | |
1477 | name[i] = padchar[rand_r(&seed) % (strlen(padchar) - 1)]; | |
1478 | #else | |
1479 | name[i] = padchar[rand() % (strlen(padchar) - 1)]; | |
1480 | #endif | |
1481 | } | |
1482 | } | |
1483 | ||
1484 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { | |
1485 | if (strcmp(ifa->ifa_name, name) == 0) { | |
1486 | ifexists = 1; | |
1487 | break; | |
1488 | } | |
1489 | } | |
1490 | ||
1491 | if (ifexists == 0) | |
1492 | break; | |
1493 | ||
1494 | free(name); | |
1495 | } | |
1496 | ||
1497 | freeifaddrs(ifaddr); | |
1498 | return name; | |
1499 | } | |
1500 | ||
8befa924 SH |
1501 | int setup_private_host_hw_addr(char *veth1) |
1502 | { | |
1503 | struct ifreq ifr; | |
1504 | int err; | |
1505 | int sockfd; | |
1506 | ||
8befa924 | 1507 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); |
8befa924 SH |
1508 | if (sockfd < 0) |
1509 | return -errno; | |
1510 | ||
1511 | snprintf((char *)ifr.ifr_name, IFNAMSIZ, "%s", veth1); | |
1512 | err = ioctl(sockfd, SIOCGIFHWADDR, &ifr); | |
1513 | if (err < 0) { | |
8befa924 | 1514 | close(sockfd); |
8befa924 SH |
1515 | return -errno; |
1516 | } | |
1517 | ||
1518 | ifr.ifr_hwaddr.sa_data[0] = 0xfe; | |
1519 | err = ioctl(sockfd, SIOCSIFHWADDR, &ifr); | |
8befa924 | 1520 | close(sockfd); |
8befa924 SH |
1521 | if (err < 0) |
1522 | return -errno; | |
1523 | ||
1524 | return 0; | |
1525 | } |