]>
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 | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
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> | |
33 | #include <sys/types.h> | |
34 | #include <sys/stat.h> | |
35 | #include <sys/socket.h> | |
36 | #include <sys/param.h> | |
37 | #include <sys/ioctl.h> | |
38 | #include <arpa/inet.h> | |
39 | #include <net/if.h> | |
40 | #include <net/if_arp.h> | |
41 | #include <net/ethernet.h> | |
42 | #include <netinet/in.h> | |
43 | #include <linux/netlink.h> | |
44 | #include <linux/rtnetlink.h> | |
45 | #include <linux/sockios.h> | |
46 | #include <linux/if_bridge.h> | |
f549edcc GK |
47 | |
48 | #include "nl.h" | |
49 | #include "network.h" | |
72d0e1cb | 50 | #include "conf.h" |
0ad19a3f | 51 | |
52 | #ifndef IFLA_LINKMODE | |
53 | # define IFLA_LINKMODE 17 | |
54 | #endif | |
55 | ||
56 | #ifndef IFLA_LINKINFO | |
57 | # define IFLA_LINKINFO 18 | |
58 | #endif | |
59 | ||
60 | #ifndef IFLA_NET_NS_PID | |
61 | # define IFLA_NET_NS_PID 19 | |
62 | #endif | |
63 | ||
64 | #ifndef IFLA_INFO_KIND | |
65 | # define IFLA_INFO_KIND 1 | |
66 | #endif | |
67 | ||
26c39028 JHS |
68 | #ifndef IFLA_VLAN_ID |
69 | # define IFLA_VLAN_ID 1 | |
70 | #endif | |
71 | ||
0ad19a3f | 72 | #ifndef IFLA_INFO_DATA |
73 | # define IFLA_INFO_DATA 2 | |
74 | #endif | |
75 | ||
76 | #ifndef VETH_INFO_PEER | |
77 | # define VETH_INFO_PEER 1 | |
78 | #endif | |
79 | ||
e892973e DL |
80 | #ifndef IFLA_MACVLAN_MODE |
81 | # define IFLA_MACVLAN_MODE 1 | |
82 | #endif | |
83 | ||
0ad19a3f | 84 | struct link_req { |
85 | struct nlmsg nlmsg; | |
86 | struct ifinfomsg ifinfomsg; | |
87 | }; | |
88 | ||
89 | struct ip_req { | |
90 | struct nlmsg nlmsg; | |
91 | struct ifaddrmsg ifa; | |
92 | }; | |
93 | ||
f8fee0e2 MK |
94 | struct rt_req { |
95 | struct nlmsg nlmsg; | |
96 | struct rtmsg rt; | |
97 | }; | |
98 | ||
d472214b | 99 | int lxc_netdev_move_by_index(int ifindex, pid_t pid) |
0ad19a3f | 100 | { |
101 | struct nl_handler nlh; | |
102 | struct nlmsg *nlmsg = NULL; | |
103 | struct link_req *link_req; | |
3cfc0f3a | 104 | int err; |
0ad19a3f | 105 | |
3cfc0f3a MN |
106 | err = netlink_open(&nlh, NETLINK_ROUTE); |
107 | if (err) | |
108 | return err; | |
0ad19a3f | 109 | |
3cfc0f3a | 110 | err = -ENOMEM; |
0ad19a3f | 111 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
112 | if (!nlmsg) | |
113 | goto out; | |
114 | ||
0ad19a3f | 115 | link_req = (struct link_req *)nlmsg; |
116 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
82d5ae15 | 117 | link_req->ifinfomsg.ifi_index = ifindex; |
0ad19a3f | 118 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); |
119 | nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; | |
120 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
121 | ||
122 | if (nla_put_u32(nlmsg, IFLA_NET_NS_PID, pid)) | |
123 | goto out; | |
124 | ||
3cfc0f3a | 125 | err = netlink_transaction(&nlh, nlmsg, nlmsg); |
0ad19a3f | 126 | out: |
127 | netlink_close(&nlh); | |
128 | nlmsg_free(nlmsg); | |
129 | return err; | |
130 | } | |
131 | ||
b84f58b9 | 132 | int lxc_netdev_delete_by_index(int ifindex) |
0ad19a3f | 133 | { |
134 | struct nl_handler nlh; | |
135 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
136 | struct link_req *link_req; | |
b84f58b9 | 137 | int err; |
0ad19a3f | 138 | |
3cfc0f3a MN |
139 | err = netlink_open(&nlh, NETLINK_ROUTE); |
140 | if (err) | |
141 | return err; | |
0ad19a3f | 142 | |
3cfc0f3a | 143 | err = -ENOMEM; |
0ad19a3f | 144 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
145 | if (!nlmsg) | |
146 | goto out; | |
147 | ||
148 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
149 | if (!answer) | |
150 | goto out; | |
151 | ||
0ad19a3f | 152 | link_req = (struct link_req *)nlmsg; |
153 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
b84f58b9 | 154 | link_req->ifinfomsg.ifi_index = ifindex; |
0ad19a3f | 155 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); |
156 | nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; | |
157 | nlmsg->nlmsghdr.nlmsg_type = RTM_DELLINK; | |
158 | ||
3cfc0f3a | 159 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 160 | out: |
161 | netlink_close(&nlh); | |
162 | nlmsg_free(answer); | |
163 | nlmsg_free(nlmsg); | |
164 | return err; | |
165 | } | |
166 | ||
b84f58b9 DL |
167 | int lxc_netdev_delete_by_name(const char *name) |
168 | { | |
169 | int index; | |
170 | ||
171 | index = if_nametoindex(name); | |
172 | if (!index) | |
173 | return -EINVAL; | |
174 | ||
175 | return lxc_netdev_delete_by_index(index); | |
176 | } | |
177 | ||
178 | int lxc_netdev_rename_by_index(int ifindex, const char *newname) | |
b9a5bb58 DL |
179 | { |
180 | struct nl_handler nlh; | |
181 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
182 | struct link_req *link_req; | |
b84f58b9 | 183 | int len, err; |
b9a5bb58 | 184 | |
3cfc0f3a MN |
185 | err = netlink_open(&nlh, NETLINK_ROUTE); |
186 | if (err) | |
187 | return err; | |
b9a5bb58 | 188 | |
b84f58b9 | 189 | len = strlen(newname); |
dae3fdf6 | 190 | if (len == 1 || len >= IFNAMSIZ) |
b84f58b9 DL |
191 | goto out; |
192 | ||
3cfc0f3a | 193 | err = -ENOMEM; |
b9a5bb58 DL |
194 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
195 | if (!nlmsg) | |
196 | goto out; | |
197 | ||
198 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
199 | if (!answer) | |
200 | goto out; | |
201 | ||
202 | link_req = (struct link_req *)nlmsg; | |
203 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
204 | link_req->ifinfomsg.ifi_index = ifindex; | |
205 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
206 | nlmsg->nlmsghdr.nlmsg_flags = NLM_F_ACK|NLM_F_REQUEST; | |
b84f58b9 DL |
207 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; |
208 | ||
209 | if (nla_put_string(nlmsg, IFLA_IFNAME, newname)) | |
210 | goto out; | |
b9a5bb58 | 211 | |
3cfc0f3a | 212 | err = netlink_transaction(&nlh, nlmsg, answer); |
b9a5bb58 DL |
213 | out: |
214 | netlink_close(&nlh); | |
215 | nlmsg_free(answer); | |
216 | nlmsg_free(nlmsg); | |
217 | return err; | |
218 | } | |
219 | ||
b84f58b9 DL |
220 | int lxc_netdev_rename_by_name(const char *oldname, const char *newname) |
221 | { | |
222 | int len, index; | |
223 | ||
224 | len = strlen(oldname); | |
dae3fdf6 | 225 | if (len == 1 || len >= IFNAMSIZ) |
b84f58b9 DL |
226 | return -EINVAL; |
227 | ||
228 | index = if_nametoindex(oldname); | |
229 | if (!index) | |
230 | return -EINVAL; | |
231 | ||
232 | return lxc_netdev_rename_by_index(index, newname); | |
233 | } | |
234 | ||
d472214b | 235 | static int netdev_set_flag(const char *name, int flag) |
0ad19a3f | 236 | { |
237 | struct nl_handler nlh; | |
238 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
239 | struct link_req *link_req; | |
3cfc0f3a | 240 | int index, len, err; |
0ad19a3f | 241 | |
3cfc0f3a MN |
242 | err = netlink_open(&nlh, NETLINK_ROUTE); |
243 | if (err) | |
244 | return err; | |
0ad19a3f | 245 | |
3cfc0f3a | 246 | err = -EINVAL; |
0ad19a3f | 247 | len = strlen(name); |
dae3fdf6 | 248 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 249 | goto out; |
250 | ||
3cfc0f3a | 251 | err = -ENOMEM; |
0ad19a3f | 252 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
253 | if (!nlmsg) | |
254 | goto out; | |
255 | ||
256 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
257 | if (!answer) | |
258 | goto out; | |
259 | ||
3cfc0f3a | 260 | err = -EINVAL; |
0ad19a3f | 261 | index = if_nametoindex(name); |
262 | if (!index) | |
263 | goto out; | |
264 | ||
265 | link_req = (struct link_req *)nlmsg; | |
266 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
267 | link_req->ifinfomsg.ifi_index = index; | |
268 | link_req->ifinfomsg.ifi_change |= IFF_UP; | |
269 | link_req->ifinfomsg.ifi_flags |= flag; | |
270 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
271 | nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; | |
272 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
273 | ||
274 | err = netlink_transaction(&nlh, nlmsg, answer); | |
0ad19a3f | 275 | out: |
276 | netlink_close(&nlh); | |
277 | nlmsg_free(nlmsg); | |
278 | nlmsg_free(answer); | |
279 | return err; | |
280 | } | |
281 | ||
d472214b | 282 | int lxc_netdev_set_mtu(const char *name, int mtu) |
75d09f83 DL |
283 | { |
284 | struct nl_handler nlh; | |
285 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
286 | struct link_req *link_req; | |
3cfc0f3a | 287 | int index, len, err; |
75d09f83 | 288 | |
3cfc0f3a MN |
289 | err = netlink_open(&nlh, NETLINK_ROUTE); |
290 | if (err) | |
291 | return err; | |
75d09f83 | 292 | |
3cfc0f3a | 293 | err = -EINVAL; |
75d09f83 | 294 | len = strlen(name); |
dae3fdf6 | 295 | if (len == 1 || len >= IFNAMSIZ) |
75d09f83 DL |
296 | goto out; |
297 | ||
3cfc0f3a | 298 | err = -ENOMEM; |
75d09f83 DL |
299 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
300 | if (!nlmsg) | |
301 | goto out; | |
302 | ||
303 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
304 | if (!answer) | |
305 | goto out; | |
306 | ||
3cfc0f3a | 307 | err = -EINVAL; |
75d09f83 DL |
308 | index = if_nametoindex(name); |
309 | if (!index) | |
310 | goto out; | |
311 | ||
312 | link_req = (struct link_req *)nlmsg; | |
313 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
314 | link_req->ifinfomsg.ifi_index = index; | |
315 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
316 | nlmsg->nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; | |
317 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
318 | ||
319 | if (nla_put_u32(nlmsg, IFLA_MTU, mtu)) | |
320 | goto out; | |
321 | ||
322 | err = netlink_transaction(&nlh, nlmsg, answer); | |
75d09f83 DL |
323 | out: |
324 | netlink_close(&nlh); | |
325 | nlmsg_free(nlmsg); | |
326 | nlmsg_free(answer); | |
327 | return err; | |
328 | } | |
329 | ||
d472214b | 330 | int lxc_netdev_up(const char *name) |
0ad19a3f | 331 | { |
d472214b | 332 | return netdev_set_flag(name, IFF_UP); |
0ad19a3f | 333 | } |
334 | ||
d472214b | 335 | int lxc_netdev_down(const char *name) |
0ad19a3f | 336 | { |
d472214b | 337 | return netdev_set_flag(name, 0); |
0ad19a3f | 338 | } |
339 | ||
497353b6 | 340 | int lxc_veth_create(const char *name1, const char *name2) |
0ad19a3f | 341 | { |
342 | struct nl_handler nlh; | |
343 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
344 | struct link_req *link_req; | |
345 | struct rtattr *nest1, *nest2, *nest3; | |
3cfc0f3a | 346 | int len, err; |
0ad19a3f | 347 | |
3cfc0f3a MN |
348 | err = netlink_open(&nlh, NETLINK_ROUTE); |
349 | if (err) | |
350 | return err; | |
0ad19a3f | 351 | |
3cfc0f3a | 352 | err = -EINVAL; |
0ad19a3f | 353 | len = strlen(name1); |
dae3fdf6 | 354 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 355 | goto out; |
356 | ||
357 | len = strlen(name2); | |
dae3fdf6 | 358 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 359 | goto out; |
360 | ||
3cfc0f3a | 361 | err = -ENOMEM; |
0ad19a3f | 362 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
363 | if (!nlmsg) | |
364 | goto out; | |
365 | ||
366 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
367 | if (!answer) | |
368 | goto out; | |
369 | ||
370 | link_req = (struct link_req *)nlmsg; | |
371 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
372 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
79e68309 | 373 | nlmsg->nlmsghdr.nlmsg_flags = |
0ad19a3f | 374 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; |
375 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
376 | ||
3cfc0f3a | 377 | err = -EINVAL; |
79e68309 | 378 | nest1 = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
0ad19a3f | 379 | if (!nest1) |
380 | goto out; | |
381 | ||
382 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "veth")) | |
383 | goto out; | |
384 | ||
385 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
386 | if (!nest2) | |
387 | goto out; | |
388 | ||
389 | nest3 = nla_begin_nested(nlmsg, VETH_INFO_PEER); | |
390 | if (!nest3) | |
391 | goto out; | |
392 | ||
393 | nlmsg->nlmsghdr.nlmsg_len += sizeof(struct ifinfomsg); | |
394 | ||
395 | if (nla_put_string(nlmsg, IFLA_IFNAME, name2)) | |
396 | goto out; | |
397 | ||
398 | nla_end_nested(nlmsg, nest3); | |
399 | ||
400 | nla_end_nested(nlmsg, nest2); | |
401 | ||
402 | nla_end_nested(nlmsg, nest1); | |
403 | ||
404 | if (nla_put_string(nlmsg, IFLA_IFNAME, name1)) | |
405 | goto out; | |
406 | ||
3cfc0f3a | 407 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 408 | out: |
409 | netlink_close(&nlh); | |
410 | nlmsg_free(answer); | |
411 | nlmsg_free(nlmsg); | |
412 | return err; | |
413 | } | |
414 | ||
26c39028 | 415 | /* XXX: merge with lxc_macvlan_create */ |
7c11d57a | 416 | int lxc_vlan_create(const char *master, const char *name, unsigned short vlanid) |
26c39028 JHS |
417 | { |
418 | struct nl_handler nlh; | |
419 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
420 | struct link_req *link_req; | |
421 | struct rtattr *nest, *nest2; | |
3cfc0f3a | 422 | int lindex, len, err; |
26c39028 | 423 | |
3cfc0f3a MN |
424 | err = netlink_open(&nlh, NETLINK_ROUTE); |
425 | if (err) | |
426 | return err; | |
26c39028 | 427 | |
3cfc0f3a | 428 | err = -EINVAL; |
26c39028 | 429 | len = strlen(master); |
dae3fdf6 | 430 | if (len == 1 || len >= IFNAMSIZ) |
26c39028 JHS |
431 | goto err3; |
432 | ||
433 | len = strlen(name); | |
dae3fdf6 | 434 | if (len == 1 || len >= IFNAMSIZ) |
26c39028 JHS |
435 | goto err3; |
436 | ||
3cfc0f3a | 437 | err = -ENOMEM; |
26c39028 JHS |
438 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
439 | if (!nlmsg) | |
440 | goto err3; | |
441 | ||
442 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
443 | if (!answer) | |
444 | goto err2; | |
445 | ||
3cfc0f3a | 446 | err = -EINVAL; |
26c39028 JHS |
447 | lindex = if_nametoindex(master); |
448 | if (!lindex) | |
449 | goto err1; | |
450 | ||
451 | link_req = (struct link_req *)nlmsg; | |
452 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
453 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
454 | nlmsg->nlmsghdr.nlmsg_flags = | |
455 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; | |
456 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
457 | ||
79e68309 | 458 | nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
26c39028 JHS |
459 | if (!nest) |
460 | goto err1; | |
461 | ||
462 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "vlan")) | |
463 | goto err1; | |
464 | ||
465 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
466 | if (!nest2) | |
467 | goto err1; | |
e892973e | 468 | |
26c39028 JHS |
469 | if (nla_put_u16(nlmsg, IFLA_VLAN_ID, vlanid)) |
470 | goto err1; | |
e892973e | 471 | |
26c39028 JHS |
472 | nla_end_nested(nlmsg, nest2); |
473 | ||
474 | nla_end_nested(nlmsg, nest); | |
475 | ||
476 | if (nla_put_u32(nlmsg, IFLA_LINK, lindex)) | |
477 | goto err1; | |
478 | ||
479 | if (nla_put_string(nlmsg, IFLA_IFNAME, name)) | |
480 | goto err1; | |
481 | ||
3cfc0f3a | 482 | err = netlink_transaction(&nlh, nlmsg, answer); |
26c39028 JHS |
483 | err1: |
484 | nlmsg_free(answer); | |
485 | err2: | |
486 | nlmsg_free(nlmsg); | |
487 | err3: | |
488 | netlink_close(&nlh); | |
489 | return err; | |
490 | } | |
491 | ||
e892973e | 492 | int lxc_macvlan_create(const char *master, const char *name, int mode) |
0ad19a3f | 493 | { |
494 | struct nl_handler nlh; | |
495 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
496 | struct link_req *link_req; | |
e892973e | 497 | struct rtattr *nest, *nest2; |
3cfc0f3a | 498 | int index, len, err; |
0ad19a3f | 499 | |
3cfc0f3a MN |
500 | err = netlink_open(&nlh, NETLINK_ROUTE); |
501 | if (err) | |
502 | return err; | |
0ad19a3f | 503 | |
3cfc0f3a | 504 | err = -EINVAL; |
0ad19a3f | 505 | len = strlen(master); |
dae3fdf6 | 506 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 507 | goto out; |
508 | ||
509 | len = strlen(name); | |
dae3fdf6 | 510 | if (len == 1 || len >= IFNAMSIZ) |
0ad19a3f | 511 | goto out; |
512 | ||
3cfc0f3a | 513 | err = -ENOMEM; |
0ad19a3f | 514 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
515 | if (!nlmsg) | |
516 | goto out; | |
517 | ||
518 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
519 | if (!answer) | |
520 | goto out; | |
521 | ||
3cfc0f3a | 522 | err = -EINVAL; |
0ad19a3f | 523 | index = if_nametoindex(master); |
524 | if (!index) | |
525 | goto out; | |
526 | ||
527 | link_req = (struct link_req *)nlmsg; | |
528 | link_req->ifinfomsg.ifi_family = AF_UNSPEC; | |
529 | nlmsg->nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
530 | nlmsg->nlmsghdr.nlmsg_flags = | |
531 | NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK; | |
532 | nlmsg->nlmsghdr.nlmsg_type = RTM_NEWLINK; | |
533 | ||
79e68309 | 534 | nest = nla_begin_nested(nlmsg, IFLA_LINKINFO); |
0ad19a3f | 535 | if (!nest) |
536 | goto out; | |
537 | ||
538 | if (nla_put_string(nlmsg, IFLA_INFO_KIND, "macvlan")) | |
539 | goto out; | |
540 | ||
e892973e DL |
541 | if (mode) { |
542 | nest2 = nla_begin_nested(nlmsg, IFLA_INFO_DATA); | |
543 | if (!nest2) | |
544 | goto out; | |
545 | ||
546 | if (nla_put_u32(nlmsg, IFLA_MACVLAN_MODE, mode)) | |
547 | goto out; | |
548 | ||
549 | nla_end_nested(nlmsg, nest2); | |
550 | } | |
551 | ||
0ad19a3f | 552 | nla_end_nested(nlmsg, nest); |
553 | ||
554 | if (nla_put_u32(nlmsg, IFLA_LINK, index)) | |
555 | goto out; | |
556 | ||
557 | if (nla_put_string(nlmsg, IFLA_IFNAME, name)) | |
558 | goto out; | |
559 | ||
3cfc0f3a | 560 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 561 | out: |
562 | netlink_close(&nlh); | |
563 | nlmsg_free(answer); | |
564 | nlmsg_free(nlmsg); | |
565 | return err; | |
566 | } | |
567 | ||
568 | static int proc_sys_net_write(const char *path, const char *value) | |
569 | { | |
570 | int fd, err = 0; | |
571 | ||
572 | fd = open(path, O_WRONLY); | |
573 | if (fd < 0) | |
574 | return -errno; | |
575 | ||
576 | if (write(fd, value, strlen(value)) < 0) | |
577 | err = -errno; | |
578 | ||
579 | close(fd); | |
580 | return err; | |
581 | } | |
582 | ||
583 | static int ip_forward_set(const char *ifname, int family, int flag) | |
584 | { | |
22ebac19 | 585 | char path[MAXPATHLEN]; |
9ba8130c | 586 | int rc; |
0ad19a3f | 587 | |
588 | if (family != AF_INET && family != AF_INET6) | |
3cfc0f3a | 589 | return -EINVAL; |
0ad19a3f | 590 | |
9ba8130c | 591 | rc = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/forwarding", |
0ad19a3f | 592 | family == AF_INET?"ipv4":"ipv6" , ifname); |
9ba8130c SH |
593 | if (rc >= MAXPATHLEN) |
594 | return -E2BIG; | |
0ad19a3f | 595 | |
22ebac19 | 596 | return proc_sys_net_write(path, flag?"1":"0"); |
0ad19a3f | 597 | } |
598 | ||
497353b6 | 599 | int lxc_ip_forward_on(const char *ifname, int family) |
0ad19a3f | 600 | { |
601 | return ip_forward_set(ifname, family, 1); | |
602 | } | |
603 | ||
497353b6 | 604 | int lxc_ip_forward_off(const char *ifname, int family) |
0ad19a3f | 605 | { |
606 | return ip_forward_set(ifname, family, 0); | |
607 | } | |
608 | ||
609 | static int neigh_proxy_set(const char *ifname, int family, int flag) | |
610 | { | |
611 | char path[MAXPATHLEN]; | |
9ba8130c | 612 | int ret; |
0ad19a3f | 613 | |
614 | if (family != AF_INET && family != AF_INET6) | |
3cfc0f3a | 615 | return -EINVAL; |
0ad19a3f | 616 | |
9ba8130c | 617 | ret = snprintf(path, MAXPATHLEN, "/proc/sys/net/%s/conf/%s/%s", |
79e68309 | 618 | family == AF_INET?"ipv4":"ipv6" , ifname, |
0ad19a3f | 619 | family == AF_INET?"proxy_arp":"proxy_ndp"); |
9ba8130c SH |
620 | if (ret < 0 || ret >= MAXPATHLEN) |
621 | return -E2BIG; | |
0ad19a3f | 622 | |
623 | return proc_sys_net_write(path, flag?"1":"0"); | |
624 | } | |
625 | ||
497353b6 | 626 | int lxc_neigh_proxy_on(const char *name, int family) |
0ad19a3f | 627 | { |
628 | return neigh_proxy_set(name, family, 1); | |
629 | } | |
630 | ||
497353b6 | 631 | int lxc_neigh_proxy_off(const char *name, int family) |
0ad19a3f | 632 | { |
633 | return neigh_proxy_set(name, family, 0); | |
634 | } | |
635 | ||
636 | int lxc_convert_mac(char *macaddr, struct sockaddr *sockaddr) | |
637 | { | |
1f1b18e7 DL |
638 | unsigned char *data; |
639 | char c; | |
640 | int i = 0; | |
641 | unsigned val; | |
642 | ||
643 | sockaddr->sa_family = ARPHRD_ETHER; | |
644 | data = (unsigned char *)sockaddr->sa_data; | |
645 | ||
646 | while ((*macaddr != '\0') && (i < ETH_ALEN)) { | |
647 | val = 0; | |
648 | c = *macaddr++; | |
649 | if (isdigit(c)) | |
650 | val = c - '0'; | |
651 | else if (c >= 'a' && c <= 'f') | |
652 | val = c - 'a' + 10; | |
653 | else if (c >= 'A' && c <= 'F') | |
654 | val = c - 'A' + 10; | |
655 | else { | |
656 | return -EINVAL; | |
657 | } | |
658 | val <<= 4; | |
659 | c = *macaddr; | |
660 | if (isdigit(c)) | |
661 | val |= c - '0'; | |
662 | else if (c >= 'a' && c <= 'f') | |
663 | val |= c - 'a' + 10; | |
664 | else if (c >= 'A' && c <= 'F') | |
665 | val |= c - 'A' + 10; | |
666 | else if (c == ':' || c == 0) | |
667 | val >>= 4; | |
668 | else { | |
669 | return -EINVAL; | |
670 | } | |
671 | if (c != 0) | |
672 | macaddr++; | |
673 | *data++ = (unsigned char) (val & 0377); | |
674 | i++; | |
675 | ||
676 | if (*macaddr == ':') | |
677 | macaddr++; | |
0ad19a3f | 678 | } |
0ad19a3f | 679 | |
1f1b18e7 | 680 | return 0; |
0ad19a3f | 681 | } |
682 | ||
1f1b18e7 DL |
683 | static int ip_addr_add(int family, int ifindex, |
684 | void *addr, void *bcast, void *acast, int prefix) | |
0ad19a3f | 685 | { |
686 | struct nl_handler nlh; | |
0ad19a3f | 687 | struct nlmsg *nlmsg = NULL, *answer = NULL; |
688 | struct ip_req *ip_req; | |
4bf1968d | 689 | int addrlen; |
3cfc0f3a | 690 | int err; |
0ad19a3f | 691 | |
4bf1968d DL |
692 | addrlen = family == AF_INET ? sizeof(struct in_addr) : |
693 | sizeof(struct in6_addr); | |
694 | ||
3cfc0f3a MN |
695 | err = netlink_open(&nlh, NETLINK_ROUTE); |
696 | if (err) | |
697 | return err; | |
0ad19a3f | 698 | |
3cfc0f3a | 699 | err = -ENOMEM; |
0ad19a3f | 700 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); |
701 | if (!nlmsg) | |
702 | goto out; | |
703 | ||
704 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
705 | if (!answer) | |
706 | goto out; | |
707 | ||
0ad19a3f | 708 | ip_req = (struct ip_req *)nlmsg; |
1f1b18e7 DL |
709 | ip_req->nlmsg.nlmsghdr.nlmsg_len = |
710 | NLMSG_LENGTH(sizeof(struct ifaddrmsg)); | |
0ad19a3f | 711 | ip_req->nlmsg.nlmsghdr.nlmsg_flags = |
712 | NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; | |
713 | ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_NEWADDR; | |
714 | ip_req->ifa.ifa_prefixlen = prefix; | |
715 | ip_req->ifa.ifa_index = ifindex; | |
4bf1968d | 716 | ip_req->ifa.ifa_family = family; |
0ad19a3f | 717 | ip_req->ifa.ifa_scope = 0; |
718 | ||
3cfc0f3a | 719 | err = -EINVAL; |
4bf1968d | 720 | if (nla_put_buffer(nlmsg, IFA_LOCAL, addr, addrlen)) |
0ad19a3f | 721 | goto out; |
722 | ||
4bf1968d | 723 | if (nla_put_buffer(nlmsg, IFA_ADDRESS, addr, addrlen)) |
0ad19a3f | 724 | goto out; |
725 | ||
d8948a52 | 726 | if (nla_put_buffer(nlmsg, IFA_BROADCAST, bcast, addrlen)) |
1f1b18e7 DL |
727 | goto out; |
728 | ||
729 | /* TODO : multicast, anycast with ipv6 */ | |
7ddc8f24 | 730 | err = -EPROTONOSUPPORT; |
79881dc6 DL |
731 | if (family == AF_INET6 && |
732 | (memcmp(bcast, &in6addr_any, sizeof(in6addr_any)) || | |
733 | memcmp(acast, &in6addr_any, sizeof(in6addr_any)))) | |
1f1b18e7 | 734 | goto out; |
0ad19a3f | 735 | |
3cfc0f3a | 736 | err = netlink_transaction(&nlh, nlmsg, answer); |
0ad19a3f | 737 | out: |
738 | netlink_close(&nlh); | |
739 | nlmsg_free(answer); | |
740 | nlmsg_free(nlmsg); | |
741 | return err; | |
742 | } | |
743 | ||
1f1b18e7 DL |
744 | int lxc_ipv6_addr_add(int ifindex, struct in6_addr *addr, |
745 | struct in6_addr *mcast, | |
746 | struct in6_addr *acast, int prefix) | |
747 | { | |
748 | return ip_addr_add(AF_INET6, ifindex, addr, mcast, acast, prefix); | |
749 | } | |
750 | ||
751 | int lxc_ipv4_addr_add(int ifindex, struct in_addr *addr, | |
752 | struct in_addr *bcast, int prefix) | |
753 | { | |
754 | return ip_addr_add(AF_INET, ifindex, addr, bcast, NULL, prefix); | |
755 | } | |
756 | ||
19a26f82 MK |
757 | /* Find an IFA_LOCAL (or IFA_ADDRESS if not IFA_LOCAL is present) |
758 | * address from the given RTM_NEWADDR message. Allocates memory for the | |
759 | * address and stores that pointer in *res (so res should be an | |
760 | * in_addr** or in6_addr**). | |
761 | */ | |
762 | static int ifa_get_local_ip(int family, struct ip_req *ip_info, void** res) { | |
763 | struct rtattr *rta = IFA_RTA(&ip_info->ifa); | |
764 | int attr_len = IFA_PAYLOAD(&ip_info->nlmsg.nlmsghdr); | |
765 | int addrlen; | |
766 | ||
767 | if (ip_info->ifa.ifa_family != family) | |
768 | return 0; | |
769 | ||
770 | addrlen = family == AF_INET ? sizeof(struct in_addr) : | |
771 | sizeof(struct in6_addr); | |
772 | ||
773 | /* Loop over the rtattr's in this message */ | |
774 | while(RTA_OK(rta, attr_len)) { | |
775 | /* Found a local address for the requested interface, | |
776 | * return it. */ | |
777 | if (rta->rta_type == IFA_LOCAL || rta->rta_type == IFA_ADDRESS) { | |
778 | /* Sanity check. The family check above should | |
779 | * make sure the address length is correct, but | |
780 | * check here just in case */ | |
781 | if (RTA_PAYLOAD(rta) != addrlen) | |
782 | return -1; | |
783 | ||
784 | /* We might have found an IFA_ADDRESS before, | |
785 | * which we now overwrite with an IFA_LOCAL. */ | |
dd66e5ad | 786 | if (!*res) { |
19a26f82 | 787 | *res = malloc(addrlen); |
dd66e5ad DE |
788 | if (!*res) |
789 | return -1; | |
790 | } | |
19a26f82 MK |
791 | |
792 | memcpy(*res, RTA_DATA(rta), addrlen); | |
793 | ||
794 | if (rta->rta_type == IFA_LOCAL) | |
795 | break; | |
796 | } | |
797 | rta = RTA_NEXT(rta, attr_len); | |
798 | } | |
799 | return 0; | |
800 | } | |
801 | ||
802 | static int ip_addr_get(int family, int ifindex, void **res) | |
803 | { | |
804 | struct nl_handler nlh; | |
805 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
806 | struct ip_req *ip_req, *ip_info; | |
807 | struct nlmsghdr *msg; | |
808 | int err; | |
809 | int recv_len = 0, answer_len; | |
810 | int readmore = 0; | |
811 | ||
812 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
813 | if (err) | |
814 | return err; | |
815 | ||
816 | err = -ENOMEM; | |
817 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
818 | if (!nlmsg) | |
819 | goto out; | |
820 | ||
821 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
822 | if (!answer) | |
823 | goto out; | |
824 | ||
825 | /* Save the answer buffer length, since it will be overwritten | |
826 | * on the first receive (and we might need to receive more than | |
827 | * once. */ | |
828 | answer_len = answer->nlmsghdr.nlmsg_len; | |
829 | ||
830 | ip_req = (struct ip_req *)nlmsg; | |
831 | ip_req->nlmsg.nlmsghdr.nlmsg_len = | |
832 | NLMSG_LENGTH(sizeof(struct ifaddrmsg)); | |
833 | ip_req->nlmsg.nlmsghdr.nlmsg_flags = NLM_F_REQUEST|NLM_F_ROOT; | |
834 | ip_req->nlmsg.nlmsghdr.nlmsg_type = RTM_GETADDR; | |
835 | ip_req->ifa.ifa_family = family; | |
836 | ||
837 | /* Send the request for addresses, which returns all addresses | |
838 | * on all interfaces. */ | |
839 | err = netlink_send(&nlh, nlmsg); | |
840 | if (err < 0) | |
841 | goto out; | |
19a26f82 MK |
842 | |
843 | do { | |
844 | /* Restore the answer buffer length, it might have been | |
845 | * overwritten by a previous receive. */ | |
846 | answer->nlmsghdr.nlmsg_len = answer_len; | |
847 | ||
848 | /* Get the (next) batch of reply messages */ | |
849 | err = netlink_rcv(&nlh, answer); | |
850 | if (err < 0) | |
851 | goto out; | |
852 | ||
853 | recv_len = err; | |
854 | err = 0; | |
855 | ||
856 | /* Satisfy the typing for the netlink macros */ | |
857 | msg = &answer->nlmsghdr; | |
858 | ||
859 | while (NLMSG_OK(msg, recv_len)) { | |
860 | /* Stop reading if we see an error message */ | |
861 | if (msg->nlmsg_type == NLMSG_ERROR) { | |
862 | struct nlmsgerr *errmsg = (struct nlmsgerr*)NLMSG_DATA(msg); | |
863 | err = errmsg->error; | |
864 | goto out; | |
865 | } | |
866 | ||
867 | /* Stop reading if we see a NLMSG_DONE message */ | |
868 | if (msg->nlmsg_type == NLMSG_DONE) { | |
869 | readmore = 0; | |
870 | break; | |
871 | } | |
872 | ||
873 | if (msg->nlmsg_type != RTM_NEWADDR) { | |
874 | err = -1; | |
875 | goto out; | |
876 | } | |
877 | ||
878 | ip_info = (struct ip_req *)msg; | |
879 | if (ip_info->ifa.ifa_index == ifindex) { | |
880 | ifa_get_local_ip(family, ip_info, res); | |
881 | /* Found a result, stop searching */ | |
882 | if (*res) | |
883 | goto out; | |
884 | } | |
885 | ||
886 | /* Keep reading more data from the socket if the | |
887 | * last message had the NLF_F_MULTI flag set */ | |
888 | readmore = (msg->nlmsg_flags & NLM_F_MULTI); | |
889 | ||
890 | /* Look at the next message received in this buffer */ | |
891 | msg = NLMSG_NEXT(msg, recv_len); | |
892 | } | |
893 | } while (readmore); | |
894 | ||
895 | /* If we end up here, we didn't find any result, so signal an | |
896 | * error */ | |
897 | err = -1; | |
898 | ||
899 | out: | |
900 | netlink_close(&nlh); | |
901 | nlmsg_free(answer); | |
902 | nlmsg_free(nlmsg); | |
903 | return err; | |
904 | } | |
905 | ||
906 | int lxc_ipv6_addr_get(int ifindex, struct in6_addr **res) | |
907 | { | |
908 | return ip_addr_get(AF_INET6, ifindex, (void**)res); | |
909 | } | |
910 | ||
911 | int lxc_ipv4_addr_get(int ifindex, struct in_addr** res) | |
912 | { | |
913 | return ip_addr_get(AF_INET, ifindex, (void**)res); | |
914 | } | |
915 | ||
f8fee0e2 MK |
916 | static int ip_gateway_add(int family, int ifindex, void *gw) |
917 | { | |
918 | struct nl_handler nlh; | |
919 | struct nlmsg *nlmsg = NULL, *answer = NULL; | |
920 | struct rt_req *rt_req; | |
921 | int addrlen; | |
922 | int err; | |
923 | ||
924 | addrlen = family == AF_INET ? sizeof(struct in_addr) : | |
925 | sizeof(struct in6_addr); | |
926 | ||
927 | err = netlink_open(&nlh, NETLINK_ROUTE); | |
928 | if (err) | |
929 | return err; | |
930 | ||
931 | err = -ENOMEM; | |
932 | nlmsg = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
933 | if (!nlmsg) | |
934 | goto out; | |
935 | ||
936 | answer = nlmsg_alloc(NLMSG_GOOD_SIZE); | |
937 | if (!answer) | |
938 | goto out; | |
939 | ||
940 | rt_req = (struct rt_req *)nlmsg; | |
941 | rt_req->nlmsg.nlmsghdr.nlmsg_len = | |
942 | NLMSG_LENGTH(sizeof(struct rtmsg)); | |
943 | rt_req->nlmsg.nlmsghdr.nlmsg_flags = | |
944 | NLM_F_ACK|NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL; | |
945 | rt_req->nlmsg.nlmsghdr.nlmsg_type = RTM_NEWROUTE; | |
946 | rt_req->rt.rtm_family = family; | |
947 | rt_req->rt.rtm_table = RT_TABLE_MAIN; | |
948 | rt_req->rt.rtm_scope = RT_SCOPE_UNIVERSE; | |
949 | rt_req->rt.rtm_protocol = RTPROT_BOOT; | |
950 | rt_req->rt.rtm_type = RTN_UNICAST; | |
951 | /* "default" destination */ | |
952 | rt_req->rt.rtm_dst_len = 0; | |
953 | ||
954 | err = -EINVAL; | |
955 | if (nla_put_buffer(nlmsg, RTA_GATEWAY, gw, addrlen)) | |
956 | goto out; | |
957 | ||
958 | /* Adding the interface index enables the use of link-local | |
959 | * addresses for the gateway */ | |
960 | if (nla_put_u32(nlmsg, RTA_OIF, ifindex)) | |
961 | goto out; | |
962 | ||
963 | err = netlink_transaction(&nlh, nlmsg, answer); | |
964 | out: | |
965 | netlink_close(&nlh); | |
966 | nlmsg_free(answer); | |
967 | nlmsg_free(nlmsg); | |
968 | return err; | |
969 | } | |
970 | ||
971 | int lxc_ipv4_gateway_add(int ifindex, struct in_addr *gw) | |
972 | { | |
973 | return ip_gateway_add(AF_INET, ifindex, gw); | |
974 | } | |
975 | ||
976 | int lxc_ipv6_gateway_add(int ifindex, struct in6_addr *gw) | |
977 | { | |
978 | return ip_gateway_add(AF_INET6, ifindex, gw); | |
979 | } | |
980 | ||
7d163508 MN |
981 | /* |
982 | * There is a lxc_bridge_attach, but no need of a bridge detach | |
d472214b | 983 | * as automatically done by kernel when a netdev is deleted. |
7d163508 MN |
984 | */ |
985 | int lxc_bridge_attach(const char *bridge, const char *ifname) | |
0ad19a3f | 986 | { |
987 | int fd, index, err; | |
988 | struct ifreq ifr; | |
989 | ||
dae3fdf6 | 990 | if (strlen(ifname) >= IFNAMSIZ) |
3cfc0f3a | 991 | return -EINVAL; |
0ad19a3f | 992 | |
993 | index = if_nametoindex(ifname); | |
994 | if (!index) | |
3cfc0f3a | 995 | return -EINVAL; |
0ad19a3f | 996 | |
997 | fd = socket(AF_INET, SOCK_STREAM, 0); | |
998 | if (fd < 0) | |
3cfc0f3a | 999 | return -errno; |
0ad19a3f | 1000 | |
1001 | strncpy(ifr.ifr_name, bridge, IFNAMSIZ); | |
1002 | ifr.ifr_ifindex = index; | |
7d163508 | 1003 | err = ioctl(fd, SIOCBRADDIF, &ifr); |
0ad19a3f | 1004 | close(fd); |
3cfc0f3a MN |
1005 | if (err) |
1006 | err = -errno; | |
0ad19a3f | 1007 | |
1008 | return err; | |
1009 | } | |
72d0e1cb SG |
1010 | |
1011 | static char* lxc_network_types[LXC_NET_MAXCONFTYPE + 1] = { | |
1012 | [LXC_NET_VETH] = "veth", | |
1013 | [LXC_NET_MACVLAN] = "macvlan", | |
1014 | [LXC_NET_VLAN] = "vlan", | |
1015 | [LXC_NET_PHYS] = "phys", | |
1016 | [LXC_NET_EMPTY] = "empty", | |
1017 | }; | |
1018 | ||
1019 | const char *lxc_net_type_to_str(int type) | |
1020 | { | |
1021 | if (type < 0 || type > LXC_NET_MAXCONFTYPE) | |
1022 | return NULL; | |
1023 | return lxc_network_types[type]; | |
1024 | } |