1 /* vi: set sw=4 ts=4: */
3 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 #include <net/if_packet.h>
9 #include <netpacket/packet.h>
10 #include <netinet/if_ether.h>
12 #include "ip_common.h" /* #include "libbb.h" is inside */
17 # define IFLA_LINKINFO 18
18 # define IFLA_INFO_KIND 1
21 /* taken from linux/sockios.h */
22 #define SIOCSIFNAME 0x8923 /* set interface name */
25 static int get_ctl_fd(void)
29 fd
= socket(PF_INET
, SOCK_DGRAM
, 0);
32 fd
= socket(PF_PACKET
, SOCK_DGRAM
, 0);
35 return xsocket(PF_INET6
, SOCK_DGRAM
, 0);
39 static void do_chflags(char *dev
, uint32_t flags
, uint32_t mask
)
44 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
46 xioctl(fd
, SIOCGIFFLAGS
, &ifr
);
47 if ((ifr
.ifr_flags
^ flags
) & mask
) {
48 ifr
.ifr_flags
&= ~mask
;
49 ifr
.ifr_flags
|= mask
& flags
;
50 xioctl(fd
, SIOCSIFFLAGS
, &ifr
);
56 static void do_changename(char *dev
, char *newdev
)
61 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
62 strncpy_IFNAMSIZ(ifr
.ifr_newname
, newdev
);
64 xioctl(fd
, SIOCSIFNAME
, &ifr
);
69 static void set_qlen(char *dev
, int qlen
)
75 memset(&ifr
, 0, sizeof(ifr
));
76 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
78 xioctl(s
, SIOCSIFTXQLEN
, &ifr
);
83 static void set_mtu(char *dev
, int mtu
)
89 memset(&ifr
, 0, sizeof(ifr
));
90 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
92 xioctl(s
, SIOCSIFMTU
, &ifr
);
97 static int get_address(char *dev
, int *htype
)
100 struct sockaddr_ll me
;
104 s
= xsocket(PF_PACKET
, SOCK_DGRAM
, 0);
106 memset(&ifr
, 0, sizeof(ifr
));
107 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
108 xioctl(s
, SIOCGIFINDEX
, &ifr
);
110 memset(&me
, 0, sizeof(me
));
111 me
.sll_family
= AF_PACKET
;
112 me
.sll_ifindex
= ifr
.ifr_ifindex
;
113 me
.sll_protocol
= htons(ETH_P_LOOP
);
114 xbind(s
, (struct sockaddr
*)&me
, sizeof(me
));
116 getsockname(s
, (struct sockaddr
*)&me
, &alen
);
118 //if (getsockname(s, (struct sockaddr*)&me, &alen) == -1)
119 // bb_perror_msg_and_die("getsockname");
121 *htype
= me
.sll_hatype
;
126 static void parse_address(char *dev
, int hatype
, int halen
, char *lla
, struct ifreq
*ifr
)
130 memset(ifr
, 0, sizeof(*ifr
));
131 strncpy_IFNAMSIZ(ifr
->ifr_name
, dev
);
132 ifr
->ifr_hwaddr
.sa_family
= hatype
;
134 alen
= hatype
== 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
135 alen
= ll_addr_a2n((unsigned char *)(ifr
->ifr_hwaddr
.sa_data
), alen
, lla
);
139 bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla
, halen
);
144 static void set_address(struct ifreq
*ifr
, int brd
)
150 xioctl(s
, SIOCSIFHWBROADCAST
, ifr
);
152 xioctl(s
, SIOCSIFHWADDR
, ifr
);
157 static void die_must_be_on_off(const char *msg
) NORETURN
;
158 static void die_must_be_on_off(const char *msg
)
160 bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg
);
163 /* Return value becomes exitcode. It's okay to not return at all */
164 static int do_set(char **argv
)
171 char *newaddr
= NULL
;
173 struct ifreq ifr0
, ifr1
;
174 char *newname
= NULL
;
176 static const char keywords
[] ALIGN1
=
177 "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0"
178 "arp\0""address\0""dev\0";
179 enum { ARG_up
= 0, ARG_down
, ARG_name
, ARG_mtu
, ARG_qlen
, ARG_multicast
,
180 ARG_arp
, ARG_addr
, ARG_dev
};
181 static const char str_on_off
[] ALIGN1
= "on\0""off\0";
182 enum { PARM_on
= 0, PARM_off
};
186 /* substring search ensures that e.g. "addr" and "address"
187 * are both accepted */
188 key
= index_in_substrings(keywords
, *argv
);
192 } else if (key
== ARG_down
) {
195 } else if (key
== ARG_name
) {
198 } else if (key
== ARG_mtu
) {
201 duparg("mtu", *argv
);
202 mtu
= get_unsigned(*argv
, "mtu");
203 } else if (key
== ARG_qlen
) {
206 duparg("qlen", *argv
);
207 qlen
= get_unsigned(*argv
, "qlen");
208 } else if (key
== ARG_addr
) {
211 } else if (key
>= ARG_dev
) {
212 if (key
== ARG_dev
) {
216 duparg2("dev", *argv
);
221 param
= index_in_strings(str_on_off
, *argv
);
222 if (key
== ARG_multicast
) {
224 die_must_be_on_off("multicast");
225 mask
|= IFF_MULTICAST
;
226 if (param
== PARM_on
)
227 flags
|= IFF_MULTICAST
;
229 flags
&= ~IFF_MULTICAST
;
230 } else if (key
== ARG_arp
) {
232 die_must_be_on_off("arp");
234 if (param
== PARM_on
)
244 bb_error_msg_and_die(bb_msg_requires_arg
, "\"dev\"");
247 if (newaddr
|| newbrd
) {
248 halen
= get_address(dev
, &htype
);
250 parse_address(dev
, htype
, halen
, newaddr
, &ifr0
);
251 set_address(&ifr0
, 0);
254 parse_address(dev
, htype
, halen
, newbrd
, &ifr1
);
255 set_address(&ifr1
, 1);
259 if (newname
&& strcmp(dev
, newname
)) {
260 do_changename(dev
, newname
);
270 do_chflags(dev
, flags
, mask
);
274 static int ipaddr_list_link(char **argv
)
276 preferred_family
= AF_PACKET
;
277 return ipaddr_list_or_flush(argv
, 0);
281 #define NLMSG_TAIL(nmsg) \
282 ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
284 /* Return value becomes exitcode. It's okay to not return at all */
285 static int do_change(char **argv
, const unsigned rtm
)
287 static const char keywords
[] ALIGN1
=
288 "link\0""name\0""type\0""dev\0";
295 struct rtnl_handle rth
;
302 char *name_str
= NULL
, *link_str
= NULL
, *type_str
= NULL
, *dev_str
= NULL
;
304 memset(&req
, 0, sizeof(req
));
306 req
.n
.nlmsg_len
= NLMSG_LENGTH(sizeof(struct ifinfomsg
));
307 req
.n
.nlmsg_flags
= NLM_F_REQUEST
;
308 req
.n
.nlmsg_type
= rtm
;
309 req
.i
.ifi_family
= preferred_family
;
310 if (rtm
== RTM_NEWLINK
)
311 req
.n
.nlmsg_flags
|= NLM_F_CREATE
|NLM_F_EXCL
;
314 arg
= index_in_substrings(keywords
, *argv
);
315 if (arg
== ARG_link
) {
318 } else if (arg
== ARG_name
) {
321 } else if (arg
== ARG_type
) {
325 if (arg
== ARG_dev
) {
327 duparg(*argv
, "dev");
337 struct rtattr
*linkinfo
= NLMSG_TAIL(&req
.n
);
339 addattr_l(&req
.n
, sizeof(req
), IFLA_LINKINFO
, NULL
, 0);
340 addattr_l(&req
.n
, sizeof(req
), IFLA_INFO_KIND
, type_str
,
342 linkinfo
->rta_len
= (void *)NLMSG_TAIL(&req
.n
) - (void *)linkinfo
;
344 if (rtm
!= RTM_NEWLINK
) {
346 return 1; /* Need a device to delete */
347 req
.i
.ifi_index
= xll_name_to_index(dev_str
);
352 int idx
= xll_name_to_index(link_str
);
353 addattr_l(&req
.n
, sizeof(req
), IFLA_LINK
, &idx
, 4);
357 const size_t name_len
= strlen(name_str
) + 1;
358 if (name_len
< 2 || name_len
> IFNAMSIZ
)
359 invarg(name_str
, "name");
360 addattr_l(&req
.n
, sizeof(req
), IFLA_IFNAME
, name_str
, name_len
);
362 if (rtnl_talk(&rth
, &req
.n
, 0, 0, NULL
, NULL
, NULL
) < 0)
367 /* Return value becomes exitcode. It's okay to not return at all */
368 int FAST_FUNC
do_iplink(char **argv
)
370 static const char keywords
[] ALIGN1
=
371 "add\0""delete\0""set\0""show\0""lst\0""list\0";
373 smalluint key
= index_in_substrings(keywords
, *argv
);
374 if (key
> 5) /* invalid argument */
375 bb_error_msg_and_die(bb_msg_invalid_arg
, *argv
, applet_name
);
377 if (key
<= 1) /* add/delete */
378 return do_change(argv
, key
? RTM_DELLINK
: RTM_NEWLINK
);
379 else if (key
== 2) /* set */
382 /* show, lst, list */
383 return ipaddr_list_link(argv
);