summaryrefslogtreecommitdiff
path: root/networking/libiproute
diff options
context:
space:
mode:
authorGlenn L McGrath <bug1@ihug.co.nz>2002-11-10 01:33:55 +0000
committerGlenn L McGrath <bug1@ihug.co.nz>2002-11-10 01:33:55 +0000
commit9a2d27249cc2235f7e001a9ea8d4605406bc5f38 (patch)
treeb7b2917c3cf46ac3fa25df5f9a27a9a9fbfb0398 /networking/libiproute
parent021fa7db9139bff3b4bf404dfd7d2b1541ed71f8 (diff)
downloadbusybox-w32-9a2d27249cc2235f7e001a9ea8d4605406bc5f38.tar.gz
busybox-w32-9a2d27249cc2235f7e001a9ea8d4605406bc5f38.tar.bz2
busybox-w32-9a2d27249cc2235f7e001a9ea8d4605406bc5f38.zip
IP applet by Bastian Blank <waldi@debian.org>
Diffstat (limited to 'networking/libiproute')
-rw-r--r--networking/libiproute/Makefile30
-rw-r--r--networking/libiproute/Makefile.in43
-rw-r--r--networking/libiproute/ip_common.h20
-rw-r--r--networking/libiproute/ipaddress.c723
-rw-r--r--networking/libiproute/iplink.c349
-rw-r--r--networking/libiproute/iproute.c674
-rw-r--r--networking/libiproute/iptunnel.c548
-rw-r--r--networking/libiproute/libnetlink.c521
-rw-r--r--networking/libiproute/libnetlink.h46
-rw-r--r--networking/libiproute/linux/pkt_sched.h413
-rw-r--r--networking/libiproute/ll_addr.c92
-rw-r--r--networking/libiproute/ll_map.c169
-rw-r--r--networking/libiproute/ll_map.h12
-rw-r--r--networking/libiproute/ll_proto.c118
-rw-r--r--networking/libiproute/ll_types.c121
-rw-r--r--networking/libiproute/rt_names.c389
-rw-r--r--networking/libiproute/rt_names.h30
-rw-r--r--networking/libiproute/rtm_map.c116
-rw-r--r--networking/libiproute/rtm_map.h10
-rw-r--r--networking/libiproute/utils.c368
-rw-r--r--networking/libiproute/utils.h101
21 files changed, 4893 insertions, 0 deletions
diff --git a/networking/libiproute/Makefile b/networking/libiproute/Makefile
new file mode 100644
index 000000000..29419fd4a
--- /dev/null
+++ b/networking/libiproute/Makefile
@@ -0,0 +1,30 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2002 Erik Andersen <andersee@debian.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20TOPDIR:= ../../
21LIBIPROUTE_DIR:=./
22include $(TOPDIR).config
23include $(TOPDIR)Rules.mak
24include Makefile.in
25all: $(libraries-y)
26-include $(TOPDIR).depend
27
28clean:
29 rm -f *.o *.a $(AR_TARGET)
30
diff --git a/networking/libiproute/Makefile.in b/networking/libiproute/Makefile.in
new file mode 100644
index 000000000..9f782af16
--- /dev/null
+++ b/networking/libiproute/Makefile.in
@@ -0,0 +1,43 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2002 by Erik Andersen <andersee@debian.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LIBIPROUTE_AR:=libiproute.a
21ifndef $(LIBIPROUTE_DIR)
22LIBIPROUTE_DIR:=$(TOPDIR)networking/libiproute/
23endif
24
25LIBIPROUTE-y:= \
26 ipaddress.o \
27 iplink.o \
28 iproute.o \
29 iptunnel.o \
30 libnetlink.o \
31 ll_addr.o \
32 ll_map.o \
33 ll_proto.o \
34 ll_types.o \
35 rt_names.o \
36 rtm_map.o \
37 utils.o
38
39libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR)
40
41$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
42 $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y))
43
diff --git a/networking/libiproute/ip_common.h b/networking/libiproute/ip_common.h
new file mode 100644
index 000000000..5ac43218e
--- /dev/null
+++ b/networking/libiproute/ip_common.h
@@ -0,0 +1,20 @@
1extern int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
2extern int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
3extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
4extern int ipaddr_list(int argc, char **argv);
5extern int ipaddr_list_link(int argc, char **argv);
6extern int iproute_monitor(int argc, char **argv);
7extern void iplink_usage(void) __attribute__((noreturn));
8extern void iproute_reset_filter(void);
9extern void ipaddr_reset_filter(int);
10extern void ipneigh_reset_filter(void);
11extern int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
12extern int do_ipaddr(int argc, char **argv);
13extern int do_iproute(int argc, char **argv);
14extern int do_iprule(int argc, char **argv);
15extern int do_ipneigh(int argc, char **argv);
16extern int do_iptunnel(int argc, char **argv);
17extern int do_iplink(int argc, char **argv);
18extern int do_ipmonitor(int argc, char **argv);
19extern int do_multiaddr(int argc, char **argv);
20extern int do_multiroute(int argc, char **argv);
diff --git a/networking/libiproute/ipaddress.c b/networking/libiproute/ipaddress.c
new file mode 100644
index 000000000..8f491f327
--- /dev/null
+++ b/networking/libiproute/ipaddress.c
@@ -0,0 +1,723 @@
1/*
2 * ipaddress.c "ip address".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 * Changes:
12 * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
13 */
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <unistd.h>
18#include <syslog.h>
19#include <fcntl.h>
20#include <sys/ioctl.h>
21#include <sys/socket.h>
22#include <sys/ioctl.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <string.h>
26#include <fnmatch.h>
27
28#include <linux/netdevice.h>
29#include <linux/if_arp.h>
30#include <linux/sockios.h>
31
32#include "rt_names.h"
33#include "utils.h"
34#include "ll_map.h"
35#include "ip_common.h"
36
37#include "busybox.h"
38
39static struct
40{
41 int ifindex;
42 int family;
43 int oneline;
44 int showqueue;
45 inet_prefix pfx;
46 int scope, scopemask;
47 int flags, flagmask;
48 int up;
49 char *label;
50 struct rtnl_handle *rth;
51} filter;
52
53static int do_link;
54
55void print_link_flags(FILE *fp, unsigned flags, unsigned mdown)
56{
57 fprintf(fp, "<");
58 flags &= ~IFF_RUNNING;
59#define _PF(f) if (flags&IFF_##f) { \
60 flags &= ~IFF_##f ; \
61 fprintf(fp, #f "%s", flags ? "," : ""); }
62 _PF(LOOPBACK);
63 _PF(BROADCAST);
64 _PF(POINTOPOINT);
65 _PF(MULTICAST);
66 _PF(NOARP);
67#if 0
68 _PF(ALLMULTI);
69 _PF(PROMISC);
70 _PF(MASTER);
71 _PF(SLAVE);
72 _PF(DEBUG);
73 _PF(DYNAMIC);
74 _PF(AUTOMEDIA);
75 _PF(PORTSEL);
76 _PF(NOTRAILERS);
77#endif
78 _PF(UP);
79#undef _PF
80 if (flags)
81 fprintf(fp, "%x", flags);
82 if (mdown)
83 fprintf(fp, ",M-DOWN");
84 fprintf(fp, "> ");
85}
86
87void print_queuelen(char *name)
88{
89 struct ifreq ifr;
90 int s;
91
92 s = socket(AF_INET, SOCK_STREAM, 0);
93 if (s < 0)
94 return;
95
96 memset(&ifr, 0, sizeof(ifr));
97 strcpy(ifr.ifr_name, name);
98 if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) {
99 perror("SIOCGIFXQLEN");
100 close(s);
101 return;
102 }
103 close(s);
104
105 if (ifr.ifr_qlen)
106 printf("qlen %d", ifr.ifr_qlen);
107}
108
109int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
110{
111 FILE *fp = (FILE*)arg;
112 struct ifinfomsg *ifi = NLMSG_DATA(n);
113 struct rtattr * tb[IFLA_MAX+1];
114 int len = n->nlmsg_len;
115 unsigned m_flag = 0;
116
117 if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
118 return 0;
119
120 len -= NLMSG_LENGTH(sizeof(*ifi));
121 if (len < 0)
122 return -1;
123
124 if (filter.ifindex && ifi->ifi_index != filter.ifindex)
125 return 0;
126 if (filter.up && !(ifi->ifi_flags&IFF_UP))
127 return 0;
128
129 memset(tb, 0, sizeof(tb));
130 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
131 if (tb[IFLA_IFNAME] == NULL) {
132 fprintf(stderr, "BUG: nil ifname\n");
133 return -1;
134 }
135 if (filter.label &&
136 (!filter.family || filter.family == AF_PACKET) &&
137 fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0))
138 return 0;
139
140 if (n->nlmsg_type == RTM_DELLINK)
141 fprintf(fp, "Deleted ");
142
143 fprintf(fp, "%d: %s", ifi->ifi_index,
144 tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>");
145
146 if (tb[IFLA_LINK]) {
147 SPRINT_BUF(b1);
148 int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
149 if (iflink == 0)
150 fprintf(fp, "@NONE: ");
151 else {
152 fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1));
153 m_flag = ll_index_to_flags(iflink);
154 m_flag = !(m_flag & IFF_UP);
155 }
156 } else {
157 fprintf(fp, ": ");
158 }
159 print_link_flags(fp, ifi->ifi_flags, m_flag);
160
161 if (tb[IFLA_MTU])
162 fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
163 if (tb[IFLA_QDISC])
164 fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
165#ifdef IFLA_MASTER
166 if (tb[IFLA_MASTER]) {
167 SPRINT_BUF(b1);
168 fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1));
169 }
170#endif
171 if (filter.showqueue)
172 print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME]));
173
174 if (!filter.family || filter.family == AF_PACKET) {
175 SPRINT_BUF(b1);
176 fprintf(fp, "%s", _SL_);
177 fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1)));
178
179 if (tb[IFLA_ADDRESS]) {
180 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
181 RTA_PAYLOAD(tb[IFLA_ADDRESS]),
182 ifi->ifi_type,
183 b1, sizeof(b1)));
184 }
185 if (tb[IFLA_BROADCAST]) {
186 if (ifi->ifi_flags&IFF_POINTOPOINT)
187 fprintf(fp, " peer ");
188 else
189 fprintf(fp, " brd ");
190 fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
191 RTA_PAYLOAD(tb[IFLA_BROADCAST]),
192 ifi->ifi_type,
193 b1, sizeof(b1)));
194 }
195 }
196 fprintf(fp, "\n");
197 fflush(fp);
198 return 0;
199}
200
201int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
202{
203 FILE *fp = (FILE*)arg;
204 struct ifaddrmsg *ifa = NLMSG_DATA(n);
205 int len = n->nlmsg_len;
206 struct rtattr * rta_tb[IFA_MAX+1];
207 char abuf[256];
208 SPRINT_BUF(b1);
209
210 if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
211 return 0;
212 len -= NLMSG_LENGTH(sizeof(*ifa));
213 if (len < 0) {
214 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
215 return -1;
216 }
217
218 memset(rta_tb, 0, sizeof(rta_tb));
219 parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
220
221 if (!rta_tb[IFA_LOCAL])
222 rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
223 if (!rta_tb[IFA_ADDRESS])
224 rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
225
226 if (filter.ifindex && filter.ifindex != ifa->ifa_index)
227 return 0;
228 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
229 return 0;
230 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
231 return 0;
232 if (filter.label) {
233 SPRINT_BUF(b1);
234 const char *label;
235 if (rta_tb[IFA_LABEL])
236 label = RTA_DATA(rta_tb[IFA_LABEL]);
237 else
238 label = ll_idx_n2a(ifa->ifa_index, b1);
239 if (fnmatch(filter.label, label, 0) != 0)
240 return 0;
241 }
242 if (filter.pfx.family) {
243 if (rta_tb[IFA_LOCAL]) {
244 inet_prefix dst;
245 memset(&dst, 0, sizeof(dst));
246 dst.family = ifa->ifa_family;
247 memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
248 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
249 return 0;
250 }
251 }
252
253 if (n->nlmsg_type == RTM_DELADDR)
254 fprintf(fp, "Deleted ");
255
256 if (filter.oneline)
257 fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
258 if (ifa->ifa_family == AF_INET)
259 fprintf(fp, " inet ");
260 else if (ifa->ifa_family == AF_INET6)
261 fprintf(fp, " inet6 ");
262 else
263 fprintf(fp, " family %d ", ifa->ifa_family);
264
265 if (rta_tb[IFA_LOCAL]) {
266 fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family,
267 RTA_PAYLOAD(rta_tb[IFA_LOCAL]),
268 RTA_DATA(rta_tb[IFA_LOCAL]),
269 abuf, sizeof(abuf)));
270
271 if (rta_tb[IFA_ADDRESS] == NULL ||
272 memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) {
273 fprintf(fp, "/%d ", ifa->ifa_prefixlen);
274 } else {
275 fprintf(fp, " peer %s/%d ",
276 rt_addr_n2a(ifa->ifa_family,
277 RTA_PAYLOAD(rta_tb[IFA_ADDRESS]),
278 RTA_DATA(rta_tb[IFA_ADDRESS]),
279 abuf, sizeof(abuf)),
280 ifa->ifa_prefixlen);
281 }
282 }
283
284 if (rta_tb[IFA_BROADCAST]) {
285 fprintf(fp, "brd %s ",
286 rt_addr_n2a(ifa->ifa_family,
287 RTA_PAYLOAD(rta_tb[IFA_BROADCAST]),
288 RTA_DATA(rta_tb[IFA_BROADCAST]),
289 abuf, sizeof(abuf)));
290 }
291 if (rta_tb[IFA_ANYCAST]) {
292 fprintf(fp, "any %s ",
293 rt_addr_n2a(ifa->ifa_family,
294 RTA_PAYLOAD(rta_tb[IFA_ANYCAST]),
295 RTA_DATA(rta_tb[IFA_ANYCAST]),
296 abuf, sizeof(abuf)));
297 }
298 fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1)));
299 if (ifa->ifa_flags&IFA_F_SECONDARY) {
300 ifa->ifa_flags &= ~IFA_F_SECONDARY;
301 fprintf(fp, "secondary ");
302 }
303 if (ifa->ifa_flags&IFA_F_TENTATIVE) {
304 ifa->ifa_flags &= ~IFA_F_TENTATIVE;
305 fprintf(fp, "tentative ");
306 }
307 if (ifa->ifa_flags&IFA_F_DEPRECATED) {
308 ifa->ifa_flags &= ~IFA_F_DEPRECATED;
309 fprintf(fp, "deprecated ");
310 }
311 if (!(ifa->ifa_flags&IFA_F_PERMANENT)) {
312 fprintf(fp, "dynamic ");
313 } else
314 ifa->ifa_flags &= ~IFA_F_PERMANENT;
315 if (ifa->ifa_flags)
316 fprintf(fp, "flags %02x ", ifa->ifa_flags);
317 if (rta_tb[IFA_LABEL])
318 fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL]));
319 if (rta_tb[IFA_CACHEINFO]) {
320 struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
321 char buf[128];
322 fprintf(fp, "%s", _SL_);
323 if (ci->ifa_valid == 0xFFFFFFFFU)
324 sprintf(buf, "valid_lft forever");
325 else
326 sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
327 if (ci->ifa_prefered == 0xFFFFFFFFU)
328 sprintf(buf+strlen(buf), " preferred_lft forever");
329 else
330 sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
331 fprintf(fp, " %s", buf);
332 }
333 fprintf(fp, "\n");
334 fflush(fp);
335 return 0;
336}
337
338
339struct nlmsg_list
340{
341 struct nlmsg_list *next;
342 struct nlmsghdr h;
343};
344
345int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp)
346{
347 for ( ;ainfo ; ainfo = ainfo->next) {
348 struct nlmsghdr *n = &ainfo->h;
349 struct ifaddrmsg *ifa = NLMSG_DATA(n);
350
351 if (n->nlmsg_type != RTM_NEWADDR)
352 continue;
353
354 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
355 return -1;
356
357 if (ifa->ifa_index != ifindex ||
358 (filter.family && filter.family != ifa->ifa_family))
359 continue;
360
361 print_addrinfo(NULL, n, fp);
362 }
363 return 0;
364}
365
366
367int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
368{
369 struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
370 struct nlmsg_list *h;
371 struct nlmsg_list **lp;
372
373 h = malloc(n->nlmsg_len+sizeof(void*));
374 if (h == NULL)
375 return -1;
376
377 memcpy(&h->h, n, n->nlmsg_len);
378 h->next = NULL;
379
380 for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
381 *lp = h;
382
383 ll_remember_index(who, n, NULL);
384 return 0;
385}
386
387int ipaddr_list(int argc, char **argv)
388{
389 struct nlmsg_list *linfo = NULL;
390 struct nlmsg_list *ainfo = NULL;
391 struct nlmsg_list *l;
392 struct rtnl_handle rth;
393 char *filter_dev = NULL;
394 int no_link = 0;
395
396 ipaddr_reset_filter(oneline);
397 filter.showqueue = 1;
398
399 if (filter.family == AF_UNSPEC)
400 filter.family = preferred_family;
401
402 while (argc > 0) {
403 if (strcmp(*argv, "to") == 0) {
404 NEXT_ARG();
405 get_prefix(&filter.pfx, *argv, filter.family);
406 if (filter.family == AF_UNSPEC)
407 filter.family = filter.pfx.family;
408 } else if (strcmp(*argv, "scope") == 0) {
409 int scope = 0;
410 NEXT_ARG();
411 filter.scopemask = -1;
412 if (rtnl_rtscope_a2n(&scope, *argv)) {
413 if (strcmp(*argv, "all") != 0)
414 invarg("invalid \"scope\"\n", *argv);
415 scope = RT_SCOPE_NOWHERE;
416 filter.scopemask = 0;
417 }
418 filter.scope = scope;
419 } else if (strcmp(*argv, "up") == 0) {
420 filter.up = 1;
421 } else if (strcmp(*argv, "label") == 0) {
422 NEXT_ARG();
423 filter.label = *argv;
424 } else {
425 if (strcmp(*argv, "dev") == 0) {
426 NEXT_ARG();
427 }
428 if (filter_dev)
429 duparg2("dev", *argv);
430 filter_dev = *argv;
431 }
432 argv++; argc--;
433 }
434
435 if (rtnl_open(&rth, 0) < 0)
436 exit(1);
437
438 if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) {
439 perror("Cannot send dump request");
440 exit(1);
441 }
442
443 if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
444 fprintf(stderr, "Dump terminated\n");
445 exit(1);
446 }
447
448 if (filter_dev) {
449 filter.ifindex = ll_name_to_index(filter_dev);
450 if (filter.ifindex <= 0) {
451 fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev);
452 return -1;
453 }
454 }
455
456 if (filter.family != AF_PACKET) {
457 if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
458 perror("Cannot send dump request");
459 exit(1);
460 }
461
462 if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) {
463 fprintf(stderr, "Dump terminated\n");
464 exit(1);
465 }
466 }
467
468
469 if (filter.family && filter.family != AF_PACKET) {
470 struct nlmsg_list **lp;
471 lp=&linfo;
472
473 if (filter.oneline)
474 no_link = 1;
475
476 while ((l=*lp)!=NULL) {
477 int ok = 0;
478 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
479 struct nlmsg_list *a;
480
481 for (a=ainfo; a; a=a->next) {
482 struct nlmsghdr *n = &a->h;
483 struct ifaddrmsg *ifa = NLMSG_DATA(n);
484
485 if (ifa->ifa_index != ifi->ifi_index ||
486 (filter.family && filter.family != ifa->ifa_family))
487 continue;
488 if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
489 continue;
490 if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
491 continue;
492 if (filter.pfx.family || filter.label) {
493 struct rtattr *tb[IFA_MAX+1];
494 memset(tb, 0, sizeof(tb));
495 parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
496 if (!tb[IFA_LOCAL])
497 tb[IFA_LOCAL] = tb[IFA_ADDRESS];
498
499 if (filter.pfx.family && tb[IFA_LOCAL]) {
500 inet_prefix dst;
501 memset(&dst, 0, sizeof(dst));
502 dst.family = ifa->ifa_family;
503 memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
504 if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen))
505 continue;
506 }
507 if (filter.label) {
508 SPRINT_BUF(b1);
509 const char *label;
510 if (tb[IFA_LABEL])
511 label = RTA_DATA(tb[IFA_LABEL]);
512 else
513 label = ll_idx_n2a(ifa->ifa_index, b1);
514 if (fnmatch(filter.label, label, 0) != 0)
515 continue;
516 }
517 }
518
519 ok = 1;
520 break;
521 }
522 if (!ok)
523 *lp = l->next;
524 else
525 lp = &l->next;
526 }
527 }
528
529 for (l=linfo; l; l = l->next) {
530 if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) {
531 struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
532 if (filter.family != AF_PACKET)
533 print_selected_addrinfo(ifi->ifi_index, ainfo, stdout);
534 }
535 fflush(stdout);
536 }
537
538 exit(0);
539}
540
541int ipaddr_list_link(int argc, char **argv)
542{
543 preferred_family = AF_PACKET;
544 do_link = 1;
545 return ipaddr_list(argc, argv);
546}
547
548void ipaddr_reset_filter(int oneline)
549{
550 memset(&filter, 0, sizeof(filter));
551 filter.oneline = oneline;
552}
553
554int default_scope(inet_prefix *lcl)
555{
556 if (lcl->family == AF_INET) {
557 if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127)
558 return RT_SCOPE_HOST;
559 }
560 return 0;
561}
562
563int ipaddr_modify(int cmd, int argc, char **argv)
564{
565 struct rtnl_handle rth;
566 struct {
567 struct nlmsghdr n;
568 struct ifaddrmsg ifa;
569 char buf[256];
570 } req;
571 char *d = NULL;
572 char *l = NULL;
573 inet_prefix lcl;
574 inet_prefix peer;
575 int local_len = 0;
576 int peer_len = 0;
577 int brd_len = 0;
578 int any_len = 0;
579 int scoped = 0;
580
581 memset(&req, 0, sizeof(req));
582
583 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
584 req.n.nlmsg_flags = NLM_F_REQUEST;
585 req.n.nlmsg_type = cmd;
586 req.ifa.ifa_family = preferred_family;
587
588 while (argc > 0) {
589 if (strcmp(*argv, "peer") == 0 ||
590 strcmp(*argv, "remote") == 0) {
591 NEXT_ARG();
592
593 if (peer_len)
594 duparg("peer", *argv);
595 get_prefix(&peer, *argv, req.ifa.ifa_family);
596 peer_len = peer.bytelen;
597 if (req.ifa.ifa_family == AF_UNSPEC)
598 req.ifa.ifa_family = peer.family;
599 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
600 req.ifa.ifa_prefixlen = peer.bitlen;
601 } else if (matches(*argv, "broadcast") == 0 ||
602 strcmp(*argv, "brd") == 0) {
603 inet_prefix addr;
604 NEXT_ARG();
605 if (brd_len)
606 duparg("broadcast", *argv);
607 if (strcmp(*argv, "+") == 0)
608 brd_len = -1;
609 else if (strcmp(*argv, "-") == 0)
610 brd_len = -2;
611 else {
612 get_addr(&addr, *argv, req.ifa.ifa_family);
613 if (req.ifa.ifa_family == AF_UNSPEC)
614 req.ifa.ifa_family = addr.family;
615 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
616 brd_len = addr.bytelen;
617 }
618 } else if (strcmp(*argv, "anycast") == 0) {
619 inet_prefix addr;
620 NEXT_ARG();
621 if (any_len)
622 duparg("anycast", *argv);
623 get_addr(&addr, *argv, req.ifa.ifa_family);
624 if (req.ifa.ifa_family == AF_UNSPEC)
625 req.ifa.ifa_family = addr.family;
626 addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
627 any_len = addr.bytelen;
628 } else if (strcmp(*argv, "scope") == 0) {
629 int scope = 0;
630 NEXT_ARG();
631 if (rtnl_rtscope_a2n(&scope, *argv))
632 invarg(*argv, "invalid scope value.");
633 req.ifa.ifa_scope = scope;
634 scoped = 1;
635 } else if (strcmp(*argv, "dev") == 0) {
636 NEXT_ARG();
637 d = *argv;
638 } else if (strcmp(*argv, "label") == 0) {
639 NEXT_ARG();
640 l = *argv;
641 addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1);
642 } else {
643 if (strcmp(*argv, "local") == 0) {
644 NEXT_ARG();
645 }
646 if (local_len)
647 duparg2("local", *argv);
648 get_prefix(&lcl, *argv, req.ifa.ifa_family);
649 if (req.ifa.ifa_family == AF_UNSPEC)
650 req.ifa.ifa_family = lcl.family;
651 addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
652 local_len = lcl.bytelen;
653 }
654 argc--; argv++;
655 }
656 if (d == NULL) {
657 fprintf(stderr, "Not enough information: \"dev\" argument is required.\n");
658 return -1;
659 }
660 if (l && matches(d, l) != 0) {
661 fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l);
662 exit(1);
663 }
664
665 if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
666 peer = lcl;
667 addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
668 }
669 if (req.ifa.ifa_prefixlen == 0)
670 req.ifa.ifa_prefixlen = lcl.bitlen;
671
672 if (brd_len < 0 && cmd != RTM_DELADDR) {
673 inet_prefix brd;
674 int i;
675 if (req.ifa.ifa_family != AF_INET) {
676 fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n");
677 return -1;
678 }
679 brd = peer;
680 if (brd.bitlen <= 30) {
681 for (i=31; i>=brd.bitlen; i--) {
682 if (brd_len == -1)
683 brd.data[0] |= htonl(1<<(31-i));
684 else
685 brd.data[0] &= ~htonl(1<<(31-i));
686 }
687 addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
688 brd_len = brd.bytelen;
689 }
690 }
691 if (!scoped && cmd != RTM_DELADDR)
692 req.ifa.ifa_scope = default_scope(&lcl);
693
694 if (rtnl_open(&rth, 0) < 0)
695 exit(1);
696
697 ll_init_map(&rth);
698
699 if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) {
700 fprintf(stderr, "Cannot find device \"%s\"\n", d);
701 return -1;
702 }
703
704 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
705 exit(2);
706
707 exit(0);
708}
709
710int do_ipaddr(int argc, char **argv)
711{
712 if (argc < 1)
713 return ipaddr_list(0, NULL);
714 if (matches(*argv, "add") == 0)
715 return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1);
716 if (matches(*argv, "delete") == 0)
717 return ipaddr_modify(RTM_DELADDR, argc-1, argv+1);
718 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
719 || matches(*argv, "lst") == 0)
720 return ipaddr_list(argc-1, argv+1);
721 fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv);
722 exit(-1);
723}
diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c
new file mode 100644
index 000000000..90d60b44c
--- /dev/null
+++ b/networking/libiproute/iplink.c
@@ -0,0 +1,349 @@
1/*
2 * iplink.c "ip link".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <errno.h>
19#include <sys/socket.h>
20#include <linux/if.h>
21#include <linux/if_packet.h>
22#include <linux/if_ether.h>
23#include <linux/sockios.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <string.h>
27#include <sys/ioctl.h>
28#include <linux/sockios.h>
29
30#include "rt_names.h"
31#include "utils.h"
32#include "ip_common.h"
33
34#include "busybox.h"
35
36static int on_off(char *msg)
37{
38 fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
39 return -1;
40}
41
42static int get_ctl_fd(void)
43{
44 int s_errno;
45 int fd;
46
47 fd = socket(PF_INET, SOCK_DGRAM, 0);
48 if (fd >= 0)
49 return fd;
50 s_errno = errno;
51 fd = socket(PF_PACKET, SOCK_DGRAM, 0);
52 if (fd >= 0)
53 return fd;
54 fd = socket(PF_INET6, SOCK_DGRAM, 0);
55 if (fd >= 0)
56 return fd;
57 errno = s_errno;
58 perror("Cannot create control socket");
59 return -1;
60}
61
62static int do_chflags(char *dev, __u32 flags, __u32 mask)
63{
64 struct ifreq ifr;
65 int fd;
66 int err;
67
68 strcpy(ifr.ifr_name, dev);
69 fd = get_ctl_fd();
70 if (fd < 0)
71 return -1;
72 err = ioctl(fd, SIOCGIFFLAGS, &ifr);
73 if (err) {
74 perror("SIOCGIFFLAGS");
75 close(fd);
76 return -1;
77 }
78 if ((ifr.ifr_flags^flags)&mask) {
79 ifr.ifr_flags &= ~mask;
80 ifr.ifr_flags |= mask&flags;
81 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
82 if (err)
83 perror("SIOCSIFFLAGS");
84 }
85 close(fd);
86 return err;
87}
88
89static int do_changename(char *dev, char *newdev)
90{
91 struct ifreq ifr;
92 int fd;
93 int err;
94
95 strcpy(ifr.ifr_name, dev);
96 strcpy(ifr.ifr_newname, newdev);
97 fd = get_ctl_fd();
98 if (fd < 0)
99 return -1;
100 err = ioctl(fd, SIOCSIFNAME, &ifr);
101 if (err) {
102 perror("SIOCSIFNAME");
103 close(fd);
104 return -1;
105 }
106 close(fd);
107 return err;
108}
109
110static int set_qlen(char *dev, int qlen)
111{
112 struct ifreq ifr;
113 int s;
114
115 s = get_ctl_fd();
116 if (s < 0)
117 return -1;
118
119 memset(&ifr, 0, sizeof(ifr));
120 strcpy(ifr.ifr_name, dev);
121 ifr.ifr_qlen = qlen;
122 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
123 perror("SIOCSIFXQLEN");
124 close(s);
125 return -1;
126 }
127 close(s);
128
129 return 0;
130}
131
132static int set_mtu(char *dev, int mtu)
133{
134 struct ifreq ifr;
135 int s;
136
137 s = get_ctl_fd();
138 if (s < 0)
139 return -1;
140
141 memset(&ifr, 0, sizeof(ifr));
142 strcpy(ifr.ifr_name, dev);
143 ifr.ifr_mtu = mtu;
144 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
145 perror("SIOCSIFMTU");
146 close(s);
147 return -1;
148 }
149 close(s);
150
151 return 0;
152}
153
154static int get_address(char *dev, int *htype)
155{
156 struct ifreq ifr;
157 struct sockaddr_ll me;
158 int alen;
159 int s;
160
161 s = socket(PF_PACKET, SOCK_DGRAM, 0);
162 if (s < 0) {
163 perror("socket(PF_PACKET)");
164 return -1;
165 }
166
167 memset(&ifr, 0, sizeof(ifr));
168 strcpy(ifr.ifr_name, dev);
169 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
170 perror("SIOCGIFINDEX");
171 close(s);
172 return -1;
173 }
174
175 memset(&me, 0, sizeof(me));
176 me.sll_family = AF_PACKET;
177 me.sll_ifindex = ifr.ifr_ifindex;
178 me.sll_protocol = htons(ETH_P_LOOP);
179 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
180 perror("bind");
181 close(s);
182 return -1;
183 }
184
185 alen = sizeof(me);
186 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
187 perror("getsockname");
188 close(s);
189 return -1;
190 }
191 close(s);
192 *htype = me.sll_hatype;
193 return me.sll_halen;
194}
195
196static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
197{
198 int alen;
199
200 memset(ifr, 0, sizeof(*ifr));
201 strcpy(ifr->ifr_name, dev);
202 ifr->ifr_hwaddr.sa_family = hatype;
203 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
204 if (alen < 0)
205 return -1;
206 if (alen != halen) {
207 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
208 return -1;
209 }
210 return 0;
211}
212
213static int set_address(struct ifreq *ifr, int brd)
214{
215 int s;
216
217 s = get_ctl_fd();
218 if (s < 0)
219 return -1;
220 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
221 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
222 close(s);
223 return -1;
224 }
225 close(s);
226 return 0;
227}
228
229
230static int do_set(int argc, char **argv)
231{
232 char *dev = NULL;
233 __u32 mask = 0;
234 __u32 flags = 0;
235 int qlen = -1;
236 int mtu = -1;
237 char *newaddr = NULL;
238 char *newbrd = NULL;
239 struct ifreq ifr0, ifr1;
240 char *newname = NULL;
241 int htype, halen;
242
243 while (argc > 0) {
244 if (strcmp(*argv, "up") == 0) {
245 mask |= IFF_UP;
246 flags |= IFF_UP;
247 } else if (strcmp(*argv, "down") == 0) {
248 mask |= IFF_UP;
249 flags &= ~IFF_UP;
250 } else if (strcmp(*argv, "name") == 0) {
251 NEXT_ARG();
252 newname = *argv;
253 } else if (strcmp(*argv, "mtu") == 0) {
254 NEXT_ARG();
255 if (mtu != -1)
256 duparg("mtu", *argv);
257 if (get_integer(&mtu, *argv, 0))
258 invarg("Invalid \"mtu\" value\n", *argv);
259 } else if (strcmp(*argv, "multicast") == 0) {
260 NEXT_ARG();
261 mask |= IFF_MULTICAST;
262 if (strcmp(*argv, "on") == 0) {
263 flags |= IFF_MULTICAST;
264 } else if (strcmp(*argv, "off") == 0) {
265 flags &= ~IFF_MULTICAST;
266 } else
267 return on_off("multicast");
268 } else if (strcmp(*argv, "arp") == 0) {
269 NEXT_ARG();
270 mask |= IFF_NOARP;
271 if (strcmp(*argv, "on") == 0) {
272 flags &= ~IFF_NOARP;
273 } else if (strcmp(*argv, "off") == 0) {
274 flags |= IFF_NOARP;
275 } else
276 return on_off("noarp");
277 } else {
278 if (strcmp(*argv, "dev") == 0) {
279 NEXT_ARG();
280 }
281 if (dev)
282 duparg2("dev", *argv);
283 dev = *argv;
284 }
285 argc--; argv++;
286 }
287
288 if (!dev) {
289 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
290 exit(-1);
291 }
292
293 if (newaddr || newbrd) {
294 halen = get_address(dev, &htype);
295 if (halen < 0)
296 return -1;
297 if (newaddr) {
298 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
299 return -1;
300 }
301 if (newbrd) {
302 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
303 return -1;
304 }
305 }
306
307 if (newname && strcmp(dev, newname)) {
308 if (do_changename(dev, newname) < 0)
309 return -1;
310 dev = newname;
311 }
312 if (qlen != -1) {
313 if (set_qlen(dev, qlen) < 0)
314 return -1;
315 }
316 if (mtu != -1) {
317 if (set_mtu(dev, mtu) < 0)
318 return -1;
319 }
320 if (newaddr || newbrd) {
321 if (newbrd) {
322 if (set_address(&ifr1, 1) < 0)
323 return -1;
324 }
325 if (newaddr) {
326 if (set_address(&ifr0, 0) < 0)
327 return -1;
328 }
329 }
330 if (mask)
331 return do_chflags(dev, flags, mask);
332 return 0;
333}
334
335int do_iplink(int argc, char **argv)
336{
337 if (argc > 0) {
338 if (matches(*argv, "set") == 0)
339 return do_set(argc-1, argv+1);
340 if (matches(*argv, "show") == 0 ||
341 matches(*argv, "lst") == 0 ||
342 matches(*argv, "list") == 0)
343 return ipaddr_list_link(argc-1, argv+1);
344 } else
345 return ipaddr_list_link(0, NULL);
346
347 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
348 exit(-1);
349}
diff --git a/networking/libiproute/iproute.c b/networking/libiproute/iproute.c
new file mode 100644
index 000000000..e38abcd01
--- /dev/null
+++ b/networking/libiproute/iproute.c
@@ -0,0 +1,674 @@
1/*
2 * iproute.c "ip route".
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <unistd.h>
21#include <syslog.h>
22#include <fcntl.h>
23#include <string.h>
24#include <sys/time.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <netinet/ip.h>
28#include <arpa/inet.h>
29#include <linux/in_route.h>
30
31#include "rt_names.h"
32#include "utils.h"
33#include "ip_common.h"
34
35#include "busybox.h"
36
37#ifndef RTAX_RTTVAR
38#define RTAX_RTTVAR RTAX_HOPS
39#endif
40
41
42static struct
43{
44 int tb;
45 int flushp;
46 int flushe;
47 struct rtnl_handle *rth;
48 int protocol, protocolmask;
49 int scope, scopemask;
50 int type, typemask;
51 int tos, tosmask;
52 int iif, iifmask;
53 int oif, oifmask;
54 int realm, realmmask;
55 inet_prefix rprefsrc;
56 inet_prefix rvia;
57 inet_prefix rdst;
58 inet_prefix mdst;
59 inet_prefix rsrc;
60 inet_prefix msrc;
61} filter;
62
63int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
64{
65 FILE *fp = (FILE*)arg;
66 struct rtmsg *r = NLMSG_DATA(n);
67 int len = n->nlmsg_len;
68 struct rtattr * tb[RTA_MAX+1];
69 char abuf[256];
70 int host_len = -1;
71 SPRINT_BUF(b1);
72
73
74 if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
75 fprintf(stderr, "Not a route: %08x %08x %08x\n",
76 n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
77 return 0;
78 }
79 len -= NLMSG_LENGTH(sizeof(*r));
80 if (len < 0) {
81 fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
82 return -1;
83 }
84
85 if (r->rtm_family == AF_INET6)
86 host_len = 128;
87 else if (r->rtm_family == AF_INET)
88 host_len = 32;
89
90 if (r->rtm_family == AF_INET6) {
91 if (filter.tb) {
92 if (filter.tb < 0) {
93 if (!(r->rtm_flags&RTM_F_CLONED))
94 return 0;
95 } else {
96 if (r->rtm_flags&RTM_F_CLONED)
97 return 0;
98 if (filter.tb == RT_TABLE_LOCAL) {
99 if (r->rtm_type != RTN_LOCAL)
100 return 0;
101 } else if (filter.tb == RT_TABLE_MAIN) {
102 if (r->rtm_type == RTN_LOCAL)
103 return 0;
104 } else {
105 return 0;
106 }
107 }
108 }
109 } else {
110 if (filter.tb > 0 && filter.tb != r->rtm_table)
111 return 0;
112 }
113 if (filter.rdst.family &&
114 (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len))
115 return 0;
116 if (filter.mdst.family &&
117 (r->rtm_family != filter.mdst.family ||
118 (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len)))
119 return 0;
120 if (filter.rsrc.family &&
121 (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len))
122 return 0;
123 if (filter.msrc.family &&
124 (r->rtm_family != filter.msrc.family ||
125 (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len)))
126 return 0;
127
128 memset(tb, 0, sizeof(tb));
129 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
130
131 if (n->nlmsg_type == RTM_DELROUTE)
132 fprintf(fp, "Deleted ");
133 if (r->rtm_type != RTN_UNICAST && !filter.type)
134 fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
135
136 if (tb[RTA_DST]) {
137 if (r->rtm_dst_len != host_len) {
138 fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family,
139 RTA_PAYLOAD(tb[RTA_DST]),
140 RTA_DATA(tb[RTA_DST]),
141 abuf, sizeof(abuf)),
142 r->rtm_dst_len
143 );
144 } else {
145 fprintf(fp, "%s ", format_host(r->rtm_family,
146 RTA_PAYLOAD(tb[RTA_DST]),
147 RTA_DATA(tb[RTA_DST]),
148 abuf, sizeof(abuf))
149 );
150 }
151 } else if (r->rtm_dst_len) {
152 fprintf(fp, "0/%d ", r->rtm_dst_len);
153 } else {
154 fprintf(fp, "default ");
155 }
156 if (tb[RTA_SRC]) {
157 if (r->rtm_src_len != host_len) {
158 fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family,
159 RTA_PAYLOAD(tb[RTA_SRC]),
160 RTA_DATA(tb[RTA_SRC]),
161 abuf, sizeof(abuf)),
162 r->rtm_src_len
163 );
164 } else {
165 fprintf(fp, "from %s ", format_host(r->rtm_family,
166 RTA_PAYLOAD(tb[RTA_SRC]),
167 RTA_DATA(tb[RTA_SRC]),
168 abuf, sizeof(abuf))
169 );
170 }
171 } else if (r->rtm_src_len) {
172 fprintf(fp, "from 0/%u ", r->rtm_src_len);
173 }
174 if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) {
175 fprintf(fp, "via %s ",
176 format_host(r->rtm_family,
177 RTA_PAYLOAD(tb[RTA_GATEWAY]),
178 RTA_DATA(tb[RTA_GATEWAY]),
179 abuf, sizeof(abuf)));
180 }
181 if (tb[RTA_OIF] && filter.oifmask != -1)
182 fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF])));
183
184 if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) {
185 /* Do not use format_host(). It is our local addr
186 and symbolic name will not be useful.
187 */
188 fprintf(fp, " src %s ",
189 rt_addr_n2a(r->rtm_family,
190 RTA_PAYLOAD(tb[RTA_PREFSRC]),
191 RTA_DATA(tb[RTA_PREFSRC]),
192 abuf, sizeof(abuf)));
193 }
194 if (tb[RTA_PRIORITY])
195 fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY]));
196 if (r->rtm_family == AF_INET6) {
197 struct rta_cacheinfo *ci = NULL;
198 if (tb[RTA_CACHEINFO])
199 ci = RTA_DATA(tb[RTA_CACHEINFO]);
200 if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
201 static int hz;
202 if (!hz)
203 hz = get_hz();
204 if (r->rtm_flags & RTM_F_CLONED)
205 fprintf(fp, "%s cache ", _SL_);
206 if (ci->rta_expires)
207 fprintf(fp, " expires %dsec", ci->rta_expires/hz);
208 if (ci->rta_error != 0)
209 fprintf(fp, " error %d", ci->rta_error);
210 } else if (ci) {
211 if (ci->rta_error != 0)
212 fprintf(fp, " error %d", ci->rta_error);
213 }
214 }
215 if (tb[RTA_IIF] && filter.iifmask != -1) {
216 fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
217 }
218 fprintf(fp, "\n");
219 fflush(fp);
220 return 0;
221}
222
223int iproute_modify(int cmd, unsigned flags, int argc, char **argv)
224{
225 struct rtnl_handle rth;
226 struct {
227 struct nlmsghdr n;
228 struct rtmsg r;
229 char buf[1024];
230 } req;
231 char mxbuf[256];
232 struct rtattr * mxrta = (void*)mxbuf;
233 unsigned mxlock = 0;
234 char *d = NULL;
235 int gw_ok = 0;
236 int dst_ok = 0;
237 //int nhs_ok = 0;
238 //int scope_ok = 0;
239 //int table_ok = 0;
240 int proto_ok = 0;
241 int type_ok = 0;
242
243 memset(&req, 0, sizeof(req));
244
245 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
246 req.n.nlmsg_flags = NLM_F_REQUEST|flags;
247 req.n.nlmsg_type = cmd;
248 req.r.rtm_family = preferred_family;
249 req.r.rtm_table = RT_TABLE_MAIN;
250 req.r.rtm_scope = RT_SCOPE_NOWHERE;
251
252 if (cmd != RTM_DELROUTE) {
253 req.r.rtm_protocol = RTPROT_BOOT;
254 req.r.rtm_scope = RT_SCOPE_UNIVERSE;
255 req.r.rtm_type = RTN_UNICAST;
256 }
257
258 mxrta->rta_type = RTA_METRICS;
259 mxrta->rta_len = RTA_LENGTH(0);
260
261 while (argc > 0) {
262 if (strcmp(*argv, "src") == 0) {
263 inet_prefix addr;
264 NEXT_ARG();
265 get_addr(&addr, *argv, req.r.rtm_family);
266 if (req.r.rtm_family == AF_UNSPEC)
267 req.r.rtm_family = addr.family;
268 addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
269 } else if (strcmp(*argv, "via") == 0) {
270 inet_prefix addr;
271 gw_ok = 1;
272 NEXT_ARG();
273 get_addr(&addr, *argv, req.r.rtm_family);
274 if (req.r.rtm_family == AF_UNSPEC)
275 req.r.rtm_family = addr.family;
276 addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
277 } else if (strcmp(*argv, "mtu") == 0) {
278 unsigned mtu;
279 NEXT_ARG();
280 if (strcmp(*argv, "lock") == 0) {
281 mxlock |= (1<<RTAX_MTU);
282 NEXT_ARG();
283 }
284 if (get_unsigned(&mtu, *argv, 0))
285 invarg("\"mtu\" value is invalid\n", *argv);
286 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
287 } else if (matches(*argv, "protocol") == 0) {
288 int prot;
289 NEXT_ARG();
290 if (rtnl_rtprot_a2n(&prot, *argv))
291 invarg("\"protocol\" value is invalid\n", *argv);
292 req.r.rtm_protocol = prot;
293 proto_ok =1;
294 } else if (strcmp(*argv, "dev") == 0 ||
295 strcmp(*argv, "oif") == 0) {
296 NEXT_ARG();
297 d = *argv;
298 } else {
299 int type;
300 inet_prefix dst;
301
302 if (strcmp(*argv, "to") == 0) {
303 NEXT_ARG();
304 }
305 if ((**argv < '0' || **argv > '9') &&
306 rtnl_rtntype_a2n(&type, *argv) == 0) {
307 NEXT_ARG();
308 req.r.rtm_type = type;
309 type_ok = 1;
310 }
311
312 if (dst_ok)
313 duparg2("to", *argv);
314 get_prefix(&dst, *argv, req.r.rtm_family);
315 if (req.r.rtm_family == AF_UNSPEC)
316 req.r.rtm_family = dst.family;
317 req.r.rtm_dst_len = dst.bitlen;
318 dst_ok = 1;
319 if (dst.bytelen)
320 addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
321 }
322 argc--; argv++;
323 }
324
325 if (rtnl_open(&rth, 0) < 0)
326 exit(1);
327
328 if (mxrta->rta_len > RTA_LENGTH(0)) {
329 if (mxlock)
330 rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
331 addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
332 }
333
334 if (req.r.rtm_family == AF_UNSPEC)
335 req.r.rtm_family = AF_INET;
336
337 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
338 exit(2);
339
340 return 0;
341}
342
343static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
344{
345 struct {
346 struct nlmsghdr nlh;
347 struct rtmsg rtm;
348 } req;
349 struct sockaddr_nl nladdr;
350
351 memset(&nladdr, 0, sizeof(nladdr));
352 memset(&req, 0, sizeof(req));
353 nladdr.nl_family = AF_NETLINK;
354
355 req.nlh.nlmsg_len = sizeof(req);
356 req.nlh.nlmsg_type = RTM_GETROUTE;
357 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
358 req.nlh.nlmsg_pid = 0;
359 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
360 req.rtm.rtm_family = family;
361 req.rtm.rtm_flags |= RTM_F_CLONED;
362
363 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
364}
365
366static int iproute_list(int argc, char **argv)
367{
368 int do_ipv6 = preferred_family;
369 struct rtnl_handle rth;
370 char *id = NULL;
371 char *od = NULL;
372
373 iproute_reset_filter();
374 filter.tb = RT_TABLE_MAIN;
375
376 while (argc > 0) {
377 if (matches(*argv, "protocol") == 0) {
378 int prot = 0;
379 NEXT_ARG();
380 filter.protocolmask = -1;
381 if (rtnl_rtprot_a2n(&prot, *argv)) {
382 if (strcmp(*argv, "all") != 0)
383 invarg("invalid \"protocol\"\n", *argv);
384 prot = 0;
385 filter.protocolmask = 0;
386 }
387 filter.protocol = prot;
388 } else if (strcmp(*argv, "dev") == 0 ||
389 strcmp(*argv, "oif") == 0) {
390 NEXT_ARG();
391 od = *argv;
392 } else if (strcmp(*argv, "iif") == 0) {
393 NEXT_ARG();
394 id = *argv;
395 } else if (matches(*argv, "from") == 0) {
396 NEXT_ARG();
397 if (matches(*argv, "root") == 0) {
398 NEXT_ARG();
399 get_prefix(&filter.rsrc, *argv, do_ipv6);
400 } else if (matches(*argv, "match") == 0) {
401 NEXT_ARG();
402 get_prefix(&filter.msrc, *argv, do_ipv6);
403 } else {
404 if (matches(*argv, "exact") == 0) {
405 NEXT_ARG();
406 }
407 get_prefix(&filter.msrc, *argv, do_ipv6);
408 filter.rsrc = filter.msrc;
409 }
410 } else {
411 if (matches(*argv, "to") == 0) {
412 NEXT_ARG();
413 }
414 if (matches(*argv, "root") == 0) {
415 NEXT_ARG();
416 get_prefix(&filter.rdst, *argv, do_ipv6);
417 } else if (matches(*argv, "match") == 0) {
418 NEXT_ARG();
419 get_prefix(&filter.mdst, *argv, do_ipv6);
420 } else {
421 if (matches(*argv, "exact") == 0) {
422 NEXT_ARG();
423 }
424 get_prefix(&filter.mdst, *argv, do_ipv6);
425 filter.rdst = filter.mdst;
426 }
427 }
428 argc--; argv++;
429 }
430
431 if (do_ipv6 == AF_UNSPEC && filter.tb)
432 do_ipv6 = AF_INET;
433
434 if (rtnl_open(&rth, 0) < 0)
435 exit(1);
436
437 ll_init_map(&rth);
438
439 if (id || od) {
440 int idx;
441
442 if (id) {
443 if ((idx = ll_name_to_index(id)) == 0) {
444 fprintf(stderr, "Cannot find device \"%s\"\n", id);
445 return -1;
446 }
447 filter.iif = idx;
448 filter.iifmask = -1;
449 }
450 if (od) {
451 if ((idx = ll_name_to_index(od)) == 0) {
452 fprintf(stderr, "Cannot find device \"%s\"\n", od);
453 return -1;
454 }
455 filter.oif = idx;
456 filter.oifmask = -1;
457 }
458 }
459
460 if (filter.tb != -1) {
461 if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
462 perror("Cannot send dump request");
463 exit(1);
464 }
465 } else {
466 if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
467 perror("Cannot send dump request");
468 exit(1);
469 }
470 }
471
472 if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) {
473 fprintf(stderr, "Dump terminated\n");
474 exit(1);
475 }
476
477 exit(0);
478}
479
480
481int iproute_get(int argc, char **argv)
482{
483 struct rtnl_handle rth;
484 struct {
485 struct nlmsghdr n;
486 struct rtmsg r;
487 char buf[1024];
488 } req;
489 char *idev = NULL;
490 char *odev = NULL;
491 int connected = 0;
492 int from_ok = 0;
493
494 memset(&req, 0, sizeof(req));
495
496 iproute_reset_filter();
497
498 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
499 req.n.nlmsg_flags = NLM_F_REQUEST;
500 req.n.nlmsg_type = RTM_GETROUTE;
501 req.r.rtm_family = preferred_family;
502 req.r.rtm_table = 0;
503 req.r.rtm_protocol = 0;
504 req.r.rtm_scope = 0;
505 req.r.rtm_type = 0;
506 req.r.rtm_src_len = 0;
507 req.r.rtm_dst_len = 0;
508 req.r.rtm_tos = 0;
509
510 while (argc > 0) {
511 if (matches(*argv, "from") == 0) {
512 inet_prefix addr;
513 NEXT_ARG();
514 from_ok = 1;
515 get_prefix(&addr, *argv, req.r.rtm_family);
516 if (req.r.rtm_family == AF_UNSPEC)
517 req.r.rtm_family = addr.family;
518 if (addr.bytelen)
519 addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
520 req.r.rtm_src_len = addr.bitlen;
521 } else if (matches(*argv, "iif") == 0) {
522 NEXT_ARG();
523 idev = *argv;
524 } else if (matches(*argv, "oif") == 0 ||
525 strcmp(*argv, "dev") == 0) {
526 NEXT_ARG();
527 odev = *argv;
528 } else if (matches(*argv, "notify") == 0) {
529 req.r.rtm_flags |= RTM_F_NOTIFY;
530 } else if (matches(*argv, "connected") == 0) {
531 connected = 1;
532 } else {
533 inet_prefix addr;
534 if (strcmp(*argv, "to") == 0) {
535 NEXT_ARG();
536 }
537 get_prefix(&addr, *argv, req.r.rtm_family);
538 if (req.r.rtm_family == AF_UNSPEC)
539 req.r.rtm_family = addr.family;
540 if (addr.bytelen)
541 addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
542 req.r.rtm_dst_len = addr.bitlen;
543 }
544 argc--; argv++;
545 }
546
547 if (req.r.rtm_dst_len == 0) {
548 fprintf(stderr, "need at least destination address\n");
549 exit(1);
550 }
551
552 if (rtnl_open(&rth, 0) < 0)
553 exit(1);
554
555 ll_init_map(&rth);
556
557 if (idev || odev) {
558 int idx;
559
560 if (idev) {
561 if ((idx = ll_name_to_index(idev)) == 0) {
562 fprintf(stderr, "Cannot find device \"%s\"\n", idev);
563 return -1;
564 }
565 addattr32(&req.n, sizeof(req), RTA_IIF, idx);
566 }
567 if (odev) {
568 if ((idx = ll_name_to_index(odev)) == 0) {
569 fprintf(stderr, "Cannot find device \"%s\"\n", odev);
570 return -1;
571 }
572 addattr32(&req.n, sizeof(req), RTA_OIF, idx);
573 }
574 }
575
576 if (req.r.rtm_family == AF_UNSPEC)
577 req.r.rtm_family = AF_INET;
578
579 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
580 exit(2);
581
582 if (connected && !from_ok) {
583 struct rtmsg *r = NLMSG_DATA(&req.n);
584 int len = req.n.nlmsg_len;
585 struct rtattr * tb[RTA_MAX+1];
586
587 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
588 fprintf(stderr, "An error :-)\n");
589 exit(1);
590 }
591
592 if (req.n.nlmsg_type != RTM_NEWROUTE) {
593 fprintf(stderr, "Not a route?\n");
594 return -1;
595 }
596 len -= NLMSG_LENGTH(sizeof(*r));
597 if (len < 0) {
598 fprintf(stderr, "Wrong len %d\n", len);
599 return -1;
600 }
601
602 memset(tb, 0, sizeof(tb));
603 parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
604
605 if (tb[RTA_PREFSRC]) {
606 tb[RTA_PREFSRC]->rta_type = RTA_SRC;
607 r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
608 } else if (!tb[RTA_SRC]) {
609 fprintf(stderr, "Failed to connect the route\n");
610 return -1;
611 }
612 if (!odev && tb[RTA_OIF])
613 tb[RTA_OIF]->rta_type = 0;
614 if (tb[RTA_GATEWAY])
615 tb[RTA_GATEWAY]->rta_type = 0;
616 if (!idev && tb[RTA_IIF])
617 tb[RTA_IIF]->rta_type = 0;
618 req.n.nlmsg_flags = NLM_F_REQUEST;
619 req.n.nlmsg_type = RTM_GETROUTE;
620
621 if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0)
622 exit(2);
623 }
624
625 if (print_route(NULL, &req.n, (void*)stdout) < 0) {
626 fprintf(stderr, "An error :-)\n");
627 exit(1);
628 }
629
630 exit(0);
631}
632
633void iproute_reset_filter()
634{
635 memset(&filter, 0, sizeof(filter));
636 filter.mdst.bitlen = -1;
637 filter.msrc.bitlen = -1;
638}
639
640int do_iproute(int argc, char **argv)
641{
642 if (argc < 1)
643 return iproute_list(0, NULL);
644
645 if (matches(*argv, "add") == 0)
646 return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL,
647 argc-1, argv+1);
648 if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0)
649 return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE,
650 argc-1, argv+1);
651 if (matches(*argv, "replace") == 0)
652 return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE,
653 argc-1, argv+1);
654 if (matches(*argv, "prepend") == 0)
655 return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE,
656 argc-1, argv+1);
657 if (matches(*argv, "append") == 0)
658 return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND,
659 argc-1, argv+1);
660 if (matches(*argv, "test") == 0)
661 return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL,
662 argc-1, argv+1);
663 if (matches(*argv, "delete") == 0)
664 return iproute_modify(RTM_DELROUTE, 0,
665 argc-1, argv+1);
666 if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0
667 || matches(*argv, "lst") == 0)
668 return iproute_list(argc-1, argv+1);
669 if (matches(*argv, "get") == 0)
670 return iproute_get(argc-1, argv+1);
671 fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv);
672 exit(-1);
673}
674
diff --git a/networking/libiproute/iptunnel.c b/networking/libiproute/iptunnel.c
new file mode 100644
index 000000000..beb8bb675
--- /dev/null
+++ b/networking/libiproute/iptunnel.c
@@ -0,0 +1,548 @@
1/*
2 * iptunnel.c "ip tunnel"
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
16 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
17 */
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <syslog.h>
24#include <fcntl.h>
25#include <sys/socket.h>
26#include <sys/ioctl.h>
27#include <netinet/in.h>
28#include <netinet/ip.h>
29#include <arpa/inet.h>
30
31#include <linux/if.h>
32#include <linux/if_arp.h>
33#include <linux/if_tunnel.h>
34
35#include "rt_names.h"
36#include "utils.h"
37
38#include "busybox.h"
39
40static int do_ioctl_get_ifindex(char *dev)
41{
42 struct ifreq ifr;
43 int fd;
44 int err;
45
46 strcpy(ifr.ifr_name, dev);
47 fd = socket(AF_INET, SOCK_DGRAM, 0);
48 err = ioctl(fd, SIOCGIFINDEX, &ifr);
49 if (err) {
50 perror("ioctl");
51 return 0;
52 }
53 close(fd);
54 return ifr.ifr_ifindex;
55}
56
57static int do_ioctl_get_iftype(char *dev)
58{
59 struct ifreq ifr;
60 int fd;
61 int err;
62
63 strcpy(ifr.ifr_name, dev);
64 fd = socket(AF_INET, SOCK_DGRAM, 0);
65 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
66 if (err) {
67 perror("ioctl");
68 return -1;
69 }
70 close(fd);
71 return ifr.ifr_addr.sa_family;
72}
73
74
75static char * do_ioctl_get_ifname(int idx)
76{
77 static struct ifreq ifr;
78 int fd;
79 int err;
80
81 ifr.ifr_ifindex = idx;
82 fd = socket(AF_INET, SOCK_DGRAM, 0);
83 err = ioctl(fd, SIOCGIFNAME, &ifr);
84 if (err) {
85 perror("ioctl");
86 return NULL;
87 }
88 close(fd);
89 return ifr.ifr_name;
90}
91
92
93
94static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p)
95{
96 struct ifreq ifr;
97 int fd;
98 int err;
99
100 strcpy(ifr.ifr_name, basedev);
101 ifr.ifr_ifru.ifru_data = (void*)p;
102 fd = socket(AF_INET, SOCK_DGRAM, 0);
103 err = ioctl(fd, SIOCGETTUNNEL, &ifr);
104 if (err)
105 perror("ioctl");
106 close(fd);
107 return err;
108}
109
110static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p)
111{
112 struct ifreq ifr;
113 int fd;
114 int err;
115
116 if (cmd == SIOCCHGTUNNEL && p->name[0])
117 strcpy(ifr.ifr_name, p->name);
118 else
119 strcpy(ifr.ifr_name, basedev);
120 ifr.ifr_ifru.ifru_data = (void*)p;
121 fd = socket(AF_INET, SOCK_DGRAM, 0);
122 err = ioctl(fd, cmd, &ifr);
123 if (err)
124 perror("ioctl");
125 close(fd);
126 return err;
127}
128
129static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p)
130{
131 struct ifreq ifr;
132 int fd;
133 int err;
134
135 if (p->name[0])
136 strcpy(ifr.ifr_name, p->name);
137 else
138 strcpy(ifr.ifr_name, basedev);
139 ifr.ifr_ifru.ifru_data = (void*)p;
140 fd = socket(AF_INET, SOCK_DGRAM, 0);
141 err = ioctl(fd, SIOCDELTUNNEL, &ifr);
142 if (err)
143 perror("ioctl");
144 close(fd);
145 return err;
146}
147
148static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
149{
150 int count = 0;
151 char medium[IFNAMSIZ];
152
153 memset(p, 0, sizeof(*p));
154 memset(&medium, 0, sizeof(medium));
155
156 p->iph.version = 4;
157 p->iph.ihl = 5;
158#ifndef IP_DF
159#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
160#endif
161 p->iph.frag_off = htons(IP_DF);
162
163 while (argc > 0) {
164 if (strcmp(*argv, "mode") == 0) {
165 NEXT_ARG();
166 if (strcmp(*argv, "ipip") == 0 ||
167 strcmp(*argv, "ip/ip") == 0) {
168 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
169 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
170 exit(-1);
171 }
172 p->iph.protocol = IPPROTO_IPIP;
173 } else if (strcmp(*argv, "gre") == 0 ||
174 strcmp(*argv, "gre/ip") == 0) {
175 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
176 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
177 exit(-1);
178 }
179 p->iph.protocol = IPPROTO_GRE;
180 } else if (strcmp(*argv, "sit") == 0 ||
181 strcmp(*argv, "ipv6/ip") == 0) {
182 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
183 fprintf(stderr,"You managed to ask for more than one tunnel mode.\n");
184 exit(-1);
185 }
186 p->iph.protocol = IPPROTO_IPV6;
187 } else {
188 fprintf(stderr,"Cannot guess tunnel mode.\n");
189 exit(-1);
190 }
191 } else if (strcmp(*argv, "key") == 0) {
192 unsigned uval;
193 NEXT_ARG();
194 p->i_flags |= GRE_KEY;
195 p->o_flags |= GRE_KEY;
196 if (strchr(*argv, '.'))
197 p->i_key = p->o_key = get_addr32(*argv);
198 else {
199 if (get_unsigned(&uval, *argv, 0)<0) {
200 fprintf(stderr, "invalid value of \"key\"\n");
201 exit(-1);
202 }
203 p->i_key = p->o_key = htonl(uval);
204 }
205 } else if (strcmp(*argv, "ikey") == 0) {
206 unsigned uval;
207 NEXT_ARG();
208 p->i_flags |= GRE_KEY;
209 if (strchr(*argv, '.'))
210 p->o_key = get_addr32(*argv);
211 else {
212 if (get_unsigned(&uval, *argv, 0)<0) {
213 fprintf(stderr, "invalid value of \"ikey\"\n");
214 exit(-1);
215 }
216 p->i_key = htonl(uval);
217 }
218 } else if (strcmp(*argv, "okey") == 0) {
219 unsigned uval;
220 NEXT_ARG();
221 p->o_flags |= GRE_KEY;
222 if (strchr(*argv, '.'))
223 p->o_key = get_addr32(*argv);
224 else {
225 if (get_unsigned(&uval, *argv, 0)<0) {
226 fprintf(stderr, "invalid value of \"okey\"\n");
227 exit(-1);
228 }
229 p->o_key = htonl(uval);
230 }
231 } else if (strcmp(*argv, "seq") == 0) {
232 p->i_flags |= GRE_SEQ;
233 p->o_flags |= GRE_SEQ;
234 } else if (strcmp(*argv, "iseq") == 0) {
235 p->i_flags |= GRE_SEQ;
236 } else if (strcmp(*argv, "oseq") == 0) {
237 p->o_flags |= GRE_SEQ;
238 } else if (strcmp(*argv, "csum") == 0) {
239 p->i_flags |= GRE_CSUM;
240 p->o_flags |= GRE_CSUM;
241 } else if (strcmp(*argv, "icsum") == 0) {
242 p->i_flags |= GRE_CSUM;
243 } else if (strcmp(*argv, "ocsum") == 0) {
244 p->o_flags |= GRE_CSUM;
245 } else if (strcmp(*argv, "nopmtudisc") == 0) {
246 p->iph.frag_off = 0;
247 } else if (strcmp(*argv, "pmtudisc") == 0) {
248 p->iph.frag_off = htons(IP_DF);
249 } else if (strcmp(*argv, "remote") == 0) {
250 NEXT_ARG();
251 if (strcmp(*argv, "any"))
252 p->iph.daddr = get_addr32(*argv);
253 } else if (strcmp(*argv, "local") == 0) {
254 NEXT_ARG();
255 if (strcmp(*argv, "any"))
256 p->iph.saddr = get_addr32(*argv);
257 } else if (strcmp(*argv, "dev") == 0) {
258 NEXT_ARG();
259 strncpy(medium, *argv, IFNAMSIZ-1);
260 } else if (strcmp(*argv, "ttl") == 0) {
261 unsigned uval;
262 NEXT_ARG();
263 if (strcmp(*argv, "inherit") != 0) {
264 if (get_unsigned(&uval, *argv, 0))
265 invarg("invalid TTL\n", *argv);
266 if (uval > 255)
267 invarg("TTL must be <=255\n", *argv);
268 p->iph.ttl = uval;
269 }
270 } else if (strcmp(*argv, "tos") == 0 ||
271 matches(*argv, "dsfield") == 0) {
272 __u32 uval;
273 NEXT_ARG();
274 if (strcmp(*argv, "inherit") != 0) {
275 if (rtnl_dsfield_a2n(&uval, *argv))
276 invarg("bad TOS value", *argv);
277 p->iph.tos = uval;
278 } else
279 p->iph.tos = 1;
280 } else {
281 if (strcmp(*argv, "name") == 0) {
282 NEXT_ARG();
283 }
284 if (p->name[0])
285 duparg2("name", *argv);
286 strncpy(p->name, *argv, IFNAMSIZ);
287 if (cmd == SIOCCHGTUNNEL && count == 0) {
288 struct ip_tunnel_parm old_p;
289 memset(&old_p, 0, sizeof(old_p));
290 if (do_get_ioctl(*argv, &old_p))
291 return -1;
292 *p = old_p;
293 }
294 }
295 count++;
296 argc--; argv++;
297 }
298
299
300 if (p->iph.protocol == 0) {
301 if (memcmp(p->name, "gre", 3) == 0)
302 p->iph.protocol = IPPROTO_GRE;
303 else if (memcmp(p->name, "ipip", 4) == 0)
304 p->iph.protocol = IPPROTO_IPIP;
305 else if (memcmp(p->name, "sit", 3) == 0)
306 p->iph.protocol = IPPROTO_IPV6;
307 }
308
309 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
310 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
311 fprintf(stderr, "Keys are not allowed with ipip and sit.\n");
312 return -1;
313 }
314 }
315
316 if (medium[0]) {
317 p->link = do_ioctl_get_ifindex(medium);
318 if (p->link == 0)
319 return -1;
320 }
321
322 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
323 p->i_key = p->iph.daddr;
324 p->i_flags |= GRE_KEY;
325 }
326 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
327 p->o_key = p->iph.daddr;
328 p->o_flags |= GRE_KEY;
329 }
330 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
331 fprintf(stderr, "Broadcast tunnel requires a source address.\n");
332 return -1;
333 }
334 return 0;
335}
336
337
338static int do_add(int cmd, int argc, char **argv)
339{
340 struct ip_tunnel_parm p;
341
342 if (parse_args(argc, argv, cmd, &p) < 0)
343 return -1;
344
345 if (p.iph.ttl && p.iph.frag_off == 0) {
346 fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n");
347 return -1;
348 }
349
350 switch (p.iph.protocol) {
351 case IPPROTO_IPIP:
352 return do_add_ioctl(cmd, "tunl0", &p);
353 case IPPROTO_GRE:
354 return do_add_ioctl(cmd, "gre0", &p);
355 case IPPROTO_IPV6:
356 return do_add_ioctl(cmd, "sit0", &p);
357 default:
358 fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n");
359 return -1;
360 }
361 return -1;
362}
363
364int do_del(int argc, char **argv)
365{
366 struct ip_tunnel_parm p;
367
368 if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0)
369 return -1;
370
371 switch (p.iph.protocol) {
372 case IPPROTO_IPIP:
373 return do_del_ioctl("tunl0", &p);
374 case IPPROTO_GRE:
375 return do_del_ioctl("gre0", &p);
376 case IPPROTO_IPV6:
377 return do_del_ioctl("sit0", &p);
378 default:
379 return do_del_ioctl(p.name, &p);
380 }
381 return -1;
382}
383
384void print_tunnel(struct ip_tunnel_parm *p)
385{
386 char s1[256];
387 char s2[256];
388 char s3[64];
389 char s4[64];
390
391 format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
392 format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
393 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
394 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
395
396 printf("%s: %s/ip remote %s local %s ",
397 p->name,
398 p->iph.protocol == IPPROTO_IPIP ? "ip" :
399 (p->iph.protocol == IPPROTO_GRE ? "gre" :
400 (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
401 p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
402 if (p->link) {
403 char *n = do_ioctl_get_ifname(p->link);
404 if (n)
405 printf(" dev %s ", n);
406 }
407 if (p->iph.ttl)
408 printf(" ttl %d ", p->iph.ttl);
409 else
410 printf(" ttl inherit ");
411 if (p->iph.tos) {
412 SPRINT_BUF(b1);
413 printf(" tos");
414 if (p->iph.tos&1)
415 printf(" inherit");
416 if (p->iph.tos&~1)
417 printf("%c%s ", p->iph.tos&1 ? '/' : ' ',
418 rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1)));
419 }
420 if (!(p->iph.frag_off&htons(IP_DF)))
421 printf(" nopmtudisc");
422
423 if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key)
424 printf(" key %s", s3);
425 else if ((p->i_flags|p->o_flags)&GRE_KEY) {
426 if (p->i_flags&GRE_KEY)
427 printf(" ikey %s ", s3);
428 if (p->o_flags&GRE_KEY)
429 printf(" okey %s ", s4);
430 }
431
432 if (p->i_flags&GRE_SEQ)
433 printf("%s Drop packets out of sequence.\n", _SL_);
434 if (p->i_flags&GRE_CSUM)
435 printf("%s Checksum in received packet is required.", _SL_);
436 if (p->o_flags&GRE_SEQ)
437 printf("%s Sequence packets on output.", _SL_);
438 if (p->o_flags&GRE_CSUM)
439 printf("%s Checksum output packets.", _SL_);
440}
441
442static int do_tunnels_list(struct ip_tunnel_parm *p)
443{
444 char name[IFNAMSIZ];
445 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
446 rx_fifo, rx_frame,
447 tx_bytes, tx_packets, tx_errs, tx_drops,
448 tx_fifo, tx_colls, tx_carrier, rx_multi;
449 int type;
450 struct ip_tunnel_parm p1;
451
452 char buf[512];
453 FILE *fp = fopen("/proc/net/dev", "r");
454 if (fp == NULL) {
455 perror("fopen");
456 return -1;
457 }
458
459 fgets(buf, sizeof(buf), fp);
460 fgets(buf, sizeof(buf), fp);
461
462 while (fgets(buf, sizeof(buf), fp) != NULL) {
463 char *ptr;
464 buf[sizeof(buf) - 1] = 0;
465 if ((ptr = strchr(buf, ':')) == NULL ||
466 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
467 fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n");
468 return -1;
469 }
470 if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld",
471 &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
472 &rx_fifo, &rx_frame, &rx_multi,
473 &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
474 &tx_fifo, &tx_colls, &tx_carrier) != 14)
475 continue;
476 if (p->name[0] && strcmp(p->name, name))
477 continue;
478 type = do_ioctl_get_iftype(name);
479 if (type == -1) {
480 fprintf(stderr, "Failed to get type of [%s]\n", name);
481 continue;
482 }
483 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
484 continue;
485 memset(&p1, 0, sizeof(p1));
486 if (do_get_ioctl(name, &p1))
487 continue;
488 if ((p->link && p1.link != p->link) ||
489 (p->name[0] && strcmp(p1.name, p->name)) ||
490 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
491 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
492 (p->i_key && p1.i_key != p->i_key))
493 continue;
494 print_tunnel(&p1);
495 printf("\n");
496 }
497 return 0;
498}
499
500static int do_show(int argc, char **argv)
501{
502 int err;
503 struct ip_tunnel_parm p;
504
505 if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0)
506 return -1;
507
508 switch (p.iph.protocol) {
509 case IPPROTO_IPIP:
510 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
511 break;
512 case IPPROTO_GRE:
513 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
514 break;
515 case IPPROTO_IPV6:
516 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
517 break;
518 default:
519 do_tunnels_list(&p);
520 return 0;
521 }
522 if (err)
523 return -1;
524
525 print_tunnel(&p);
526 printf("\n");
527 return 0;
528}
529
530int do_iptunnel(int argc, char **argv)
531{
532 if (argc > 0) {
533 if (matches(*argv, "add") == 0)
534 return do_add(SIOCADDTUNNEL, argc-1, argv+1);
535 if (matches(*argv, "change") == 0)
536 return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
537 if (matches(*argv, "del") == 0)
538 return do_del(argc-1, argv+1);
539 if (matches(*argv, "show") == 0 ||
540 matches(*argv, "lst") == 0 ||
541 matches(*argv, "list") == 0)
542 return do_show(argc-1, argv+1);
543 } else
544 return do_show(0, NULL);
545
546 fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv);
547 exit(-1);
548}
diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c
new file mode 100644
index 000000000..a1f39d409
--- /dev/null
+++ b/networking/libiproute/libnetlink.c
@@ -0,0 +1,521 @@
1/*
2 * libnetlink.c RTnetlink service routines.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <net/if_arp.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21#include <string.h>
22#include <errno.h>
23#include <time.h>
24#include <sys/uio.h>
25
26#include "libnetlink.h"
27
28void rtnl_close(struct rtnl_handle *rth)
29{
30 close(rth->fd);
31}
32
33int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
34{
35 int addr_len;
36
37 memset(rth, 0, sizeof(rth));
38
39 rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
40 if (rth->fd < 0) {
41 perror("Cannot open netlink socket");
42 return -1;
43 }
44
45 memset(&rth->local, 0, sizeof(rth->local));
46 rth->local.nl_family = AF_NETLINK;
47 rth->local.nl_groups = subscriptions;
48
49 if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
50 perror("Cannot bind netlink socket");
51 return -1;
52 }
53 addr_len = sizeof(rth->local);
54 if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
55 perror("Cannot getsockname");
56 return -1;
57 }
58 if (addr_len != sizeof(rth->local)) {
59 fprintf(stderr, "Wrong address length %d\n", addr_len);
60 return -1;
61 }
62 if (rth->local.nl_family != AF_NETLINK) {
63 fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
64 return -1;
65 }
66 rth->seq = time(NULL);
67 return 0;
68}
69
70int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
71{
72 struct {
73 struct nlmsghdr nlh;
74 struct rtgenmsg g;
75 } req;
76 struct sockaddr_nl nladdr;
77
78 memset(&nladdr, 0, sizeof(nladdr));
79 nladdr.nl_family = AF_NETLINK;
80
81 req.nlh.nlmsg_len = sizeof(req);
82 req.nlh.nlmsg_type = type;
83 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
84 req.nlh.nlmsg_pid = 0;
85 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
86 req.g.rtgen_family = family;
87
88 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
89}
90
91int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
92{
93 struct sockaddr_nl nladdr;
94
95 memset(&nladdr, 0, sizeof(nladdr));
96 nladdr.nl_family = AF_NETLINK;
97
98 return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
99}
100
101int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
102{
103 struct nlmsghdr nlh;
104 struct sockaddr_nl nladdr;
105 struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
106 struct msghdr msg = {
107 (void*)&nladdr, sizeof(nladdr),
108 iov, 2,
109 NULL, 0,
110 0
111 };
112
113 memset(&nladdr, 0, sizeof(nladdr));
114 nladdr.nl_family = AF_NETLINK;
115
116 nlh.nlmsg_len = NLMSG_LENGTH(len);
117 nlh.nlmsg_type = type;
118 nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
119 nlh.nlmsg_pid = 0;
120 nlh.nlmsg_seq = rth->dump = ++rth->seq;
121
122 return sendmsg(rth->fd, &msg, 0);
123}
124
125int rtnl_dump_filter(struct rtnl_handle *rth,
126 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
127 void *arg1,
128 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
129 void *arg2)
130{
131 char buf[8192];
132 struct sockaddr_nl nladdr;
133 struct iovec iov = { buf, sizeof(buf) };
134
135 while (1) {
136 int status;
137 struct nlmsghdr *h;
138
139 struct msghdr msg = {
140 (void*)&nladdr, sizeof(nladdr),
141 &iov, 1,
142 NULL, 0,
143 0
144 };
145
146 status = recvmsg(rth->fd, &msg, 0);
147
148 if (status < 0) {
149 if (errno == EINTR)
150 continue;
151 perror("OVERRUN");
152 continue;
153 }
154 if (status == 0) {
155 fprintf(stderr, "EOF on netlink\n");
156 return -1;
157 }
158 if (msg.msg_namelen != sizeof(nladdr)) {
159 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
160 exit(1);
161 }
162
163 h = (struct nlmsghdr*)buf;
164 while (NLMSG_OK(h, status)) {
165 int err;
166
167 if (h->nlmsg_pid != rth->local.nl_pid ||
168 h->nlmsg_seq != rth->dump) {
169 if (junk) {
170 err = junk(&nladdr, h, arg2);
171 if (err < 0)
172 return err;
173 }
174 goto skip_it;
175 }
176
177 if (h->nlmsg_type == NLMSG_DONE)
178 return 0;
179 if (h->nlmsg_type == NLMSG_ERROR) {
180 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
181 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
182 fprintf(stderr, "ERROR truncated\n");
183 } else {
184 errno = -err->error;
185 perror("RTNETLINK answers");
186 }
187 return -1;
188 }
189 err = filter(&nladdr, h, arg1);
190 if (err < 0)
191 return err;
192
193skip_it:
194 h = NLMSG_NEXT(h, status);
195 }
196 if (msg.msg_flags & MSG_TRUNC) {
197 fprintf(stderr, "Message truncated\n");
198 continue;
199 }
200 if (status) {
201 fprintf(stderr, "!!!Remnant of size %d\n", status);
202 exit(1);
203 }
204 }
205}
206
207int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
208 unsigned groups, struct nlmsghdr *answer,
209 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
210 void *jarg)
211{
212 int status;
213 unsigned seq;
214 struct nlmsghdr *h;
215 struct sockaddr_nl nladdr;
216 struct iovec iov = { (void*)n, n->nlmsg_len };
217 char buf[8192];
218 struct msghdr msg = {
219 (void*)&nladdr, sizeof(nladdr),
220 &iov, 1,
221 NULL, 0,
222 0
223 };
224
225 memset(&nladdr, 0, sizeof(nladdr));
226 nladdr.nl_family = AF_NETLINK;
227 nladdr.nl_pid = peer;
228 nladdr.nl_groups = groups;
229
230 n->nlmsg_seq = seq = ++rtnl->seq;
231 if (answer == NULL)
232 n->nlmsg_flags |= NLM_F_ACK;
233
234 status = sendmsg(rtnl->fd, &msg, 0);
235
236 if (status < 0) {
237 perror("Cannot talk to rtnetlink");
238 return -1;
239 }
240
241 iov.iov_base = buf;
242
243 while (1) {
244 iov.iov_len = sizeof(buf);
245 status = recvmsg(rtnl->fd, &msg, 0);
246
247 if (status < 0) {
248 if (errno == EINTR)
249 continue;
250 perror("OVERRUN");
251 continue;
252 }
253 if (status == 0) {
254 fprintf(stderr, "EOF on netlink\n");
255 return -1;
256 }
257 if (msg.msg_namelen != sizeof(nladdr)) {
258 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
259 exit(1);
260 }
261 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
262 int err;
263 int len = h->nlmsg_len;
264 int l = len - sizeof(*h);
265
266 if (l<0 || len>status) {
267 if (msg.msg_flags & MSG_TRUNC) {
268 fprintf(stderr, "Truncated message\n");
269 return -1;
270 }
271 fprintf(stderr, "!!!malformed message: len=%d\n", len);
272 exit(1);
273 }
274
275 if (h->nlmsg_pid != rtnl->local.nl_pid ||
276 h->nlmsg_seq != seq) {
277 if (junk) {
278 err = junk(&nladdr, h, jarg);
279 if (err < 0)
280 return err;
281 }
282 continue;
283 }
284
285 if (h->nlmsg_type == NLMSG_ERROR) {
286 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
287 if (l < sizeof(struct nlmsgerr)) {
288 fprintf(stderr, "ERROR truncated\n");
289 } else {
290 errno = -err->error;
291 if (errno == 0) {
292 if (answer)
293 memcpy(answer, h, h->nlmsg_len);
294 return 0;
295 }
296 perror("RTNETLINK answers");
297 }
298 return -1;
299 }
300 if (answer) {
301 memcpy(answer, h, h->nlmsg_len);
302 return 0;
303 }
304
305 fprintf(stderr, "Unexpected reply!!!\n");
306
307 status -= NLMSG_ALIGN(len);
308 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
309 }
310 if (msg.msg_flags & MSG_TRUNC) {
311 fprintf(stderr, "Message truncated\n");
312 continue;
313 }
314 if (status) {
315 fprintf(stderr, "!!!Remnant of size %d\n", status);
316 exit(1);
317 }
318 }
319}
320
321int rtnl_listen(struct rtnl_handle *rtnl,
322 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
323 void *jarg)
324{
325 int status;
326 struct nlmsghdr *h;
327 struct sockaddr_nl nladdr;
328 struct iovec iov;
329 char buf[8192];
330 struct msghdr msg = {
331 (void*)&nladdr, sizeof(nladdr),
332 &iov, 1,
333 NULL, 0,
334 0
335 };
336
337 memset(&nladdr, 0, sizeof(nladdr));
338 nladdr.nl_family = AF_NETLINK;
339 nladdr.nl_pid = 0;
340 nladdr.nl_groups = 0;
341
342
343 iov.iov_base = buf;
344
345 while (1) {
346 iov.iov_len = sizeof(buf);
347 status = recvmsg(rtnl->fd, &msg, 0);
348
349 if (status < 0) {
350 if (errno == EINTR)
351 continue;
352 perror("OVERRUN");
353 continue;
354 }
355 if (status == 0) {
356 fprintf(stderr, "EOF on netlink\n");
357 return -1;
358 }
359 if (msg.msg_namelen != sizeof(nladdr)) {
360 fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
361 exit(1);
362 }
363 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
364 int err;
365 int len = h->nlmsg_len;
366 int l = len - sizeof(*h);
367
368 if (l<0 || len>status) {
369 if (msg.msg_flags & MSG_TRUNC) {
370 fprintf(stderr, "Truncated message\n");
371 return -1;
372 }
373 fprintf(stderr, "!!!malformed message: len=%d\n", len);
374 exit(1);
375 }
376
377 err = handler(&nladdr, h, jarg);
378 if (err < 0)
379 return err;
380
381 status -= NLMSG_ALIGN(len);
382 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
383 }
384 if (msg.msg_flags & MSG_TRUNC) {
385 fprintf(stderr, "Message truncated\n");
386 continue;
387 }
388 if (status) {
389 fprintf(stderr, "!!!Remnant of size %d\n", status);
390 exit(1);
391 }
392 }
393}
394
395int rtnl_from_file(FILE *rtnl,
396 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
397 void *jarg)
398{
399 int status;
400 struct sockaddr_nl nladdr;
401 char buf[8192];
402 struct nlmsghdr *h = (void*)buf;
403
404 memset(&nladdr, 0, sizeof(nladdr));
405 nladdr.nl_family = AF_NETLINK;
406 nladdr.nl_pid = 0;
407 nladdr.nl_groups = 0;
408
409 while (1) {
410 int err, len, type;
411 int l;
412
413 status = fread(&buf, 1, sizeof(*h), rtnl);
414
415 if (status < 0) {
416 if (errno == EINTR)
417 continue;
418 perror("rtnl_from_file: fread");
419 return -1;
420 }
421 if (status == 0)
422 return 0;
423
424 len = h->nlmsg_len;
425 type= h->nlmsg_type;
426 l = len - sizeof(*h);
427
428 if (l<0 || len>sizeof(buf)) {
429 fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
430 len, ftell(rtnl));
431 return -1;
432 }
433
434 status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
435
436 if (status < 0) {
437 perror("rtnl_from_file: fread");
438 return -1;
439 }
440 if (status < l) {
441 fprintf(stderr, "rtnl-from_file: truncated message\n");
442 return -1;
443 }
444
445 err = handler(&nladdr, h, jarg);
446 if (err < 0)
447 return err;
448 }
449}
450
451int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
452{
453 int len = RTA_LENGTH(4);
454 struct rtattr *rta;
455 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
456 return -1;
457 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
458 rta->rta_type = type;
459 rta->rta_len = len;
460 memcpy(RTA_DATA(rta), &data, 4);
461 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
462 return 0;
463}
464
465int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
466{
467 int len = RTA_LENGTH(alen);
468 struct rtattr *rta;
469
470 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
471 return -1;
472 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
473 rta->rta_type = type;
474 rta->rta_len = len;
475 memcpy(RTA_DATA(rta), data, alen);
476 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
477 return 0;
478}
479
480int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
481{
482 int len = RTA_LENGTH(4);
483 struct rtattr *subrta;
484
485 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
486 return -1;
487 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
488 subrta->rta_type = type;
489 subrta->rta_len = len;
490 memcpy(RTA_DATA(subrta), &data, 4);
491 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
492 return 0;
493}
494
495int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
496{
497 struct rtattr *subrta;
498 int len = RTA_LENGTH(alen);
499
500 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
501 return -1;
502 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
503 subrta->rta_type = type;
504 subrta->rta_len = len;
505 memcpy(RTA_DATA(subrta), data, alen);
506 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
507 return 0;
508}
509
510
511int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
512{
513 while (RTA_OK(rta, len)) {
514 if (rta->rta_type <= max)
515 tb[rta->rta_type] = rta;
516 rta = RTA_NEXT(rta,len);
517 }
518 if (len)
519 fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
520 return 0;
521}
diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h
new file mode 100644
index 000000000..45d3ad2bc
--- /dev/null
+++ b/networking/libiproute/libnetlink.h
@@ -0,0 +1,46 @@
1#ifndef __LIBNETLINK_H__
2#define __LIBNETLINK_H__ 1
3
4#include <asm/types.h>
5#include <linux/netlink.h>
6#include <linux/rtnetlink.h>
7
8struct rtnl_handle
9{
10 int fd;
11 struct sockaddr_nl local;
12 struct sockaddr_nl peer;
13 __u32 seq;
14 __u32 dump;
15};
16
17extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
18extern void rtnl_close(struct rtnl_handle *rth);
19extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
20extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
21extern int rtnl_dump_filter(struct rtnl_handle *rth,
22 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
23 void *arg1,
24 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
25 void *arg2);
26extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
27 unsigned groups, struct nlmsghdr *answer,
28 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
29 void *jarg);
30extern int rtnl_send(struct rtnl_handle *rth, char *buf, int);
31
32
33extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
34extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen);
35extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
36extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen);
37
38extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
39
40extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
41 void *jarg);
42extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
43 void *jarg);
44
45#endif /* __LIBNETLINK_H__ */
46
diff --git a/networking/libiproute/linux/pkt_sched.h b/networking/libiproute/linux/pkt_sched.h
new file mode 100644
index 000000000..e174588f5
--- /dev/null
+++ b/networking/libiproute/linux/pkt_sched.h
@@ -0,0 +1,413 @@
1#ifndef __LINUX_PKT_SCHED_H
2#define __LINUX_PKT_SCHED_H
3
4/* Logical priority bands not depending on specific packet scheduler.
5 Every scheduler will map them to real traffic classes, if it has
6 no more precise mechanism to classify packets.
7
8 These numbers have no special meaning, though their coincidence
9 with obsolete IPv6 values is not occasional :-). New IPv6 drafts
10 preferred full anarchy inspired by diffserv group.
11
12 Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy
13 class, actually, as rule it will be handled with more care than
14 filler or even bulk.
15 */
16
17#include <asm/types.h>
18
19#define TC_PRIO_BESTEFFORT 0
20#define TC_PRIO_FILLER 1
21#define TC_PRIO_BULK 2
22#define TC_PRIO_INTERACTIVE_BULK 4
23#define TC_PRIO_INTERACTIVE 6
24#define TC_PRIO_CONTROL 7
25
26#define TC_PRIO_MAX 15
27
28/* Generic queue statistics, available for all the elements.
29 Particular schedulers may have also their private records.
30 */
31
32struct tc_stats
33{
34 __u64 bytes; /* NUmber of enqueues bytes */
35 __u32 packets; /* Number of enqueued packets */
36 __u32 drops; /* Packets dropped because of lack of resources */
37 __u32 overlimits; /* Number of throttle events when this
38 * flow goes out of allocated bandwidth */
39 __u32 bps; /* Current flow byte rate */
40 __u32 pps; /* Current flow packet rate */
41 __u32 qlen;
42 __u32 backlog;
43#ifdef __KERNEL__
44 spinlock_t *lock;
45#endif
46};
47
48struct tc_estimator
49{
50 char interval;
51 unsigned char ewma_log;
52};
53
54/* "Handles"
55 ---------
56
57 All the traffic control objects have 32bit identifiers, or "handles".
58
59 They can be considered as opaque numbers from user API viewpoint,
60 but actually they always consist of two fields: major and
61 minor numbers, which are interpreted by kernel specially,
62 that may be used by applications, though not recommended.
63
64 F.e. qdisc handles always have minor number equal to zero,
65 classes (or flows) have major equal to parent qdisc major, and
66 minor uniquely identifying class inside qdisc.
67
68 Macros to manipulate handles:
69 */
70
71#define TC_H_MAJ_MASK (0xFFFF0000U)
72#define TC_H_MIN_MASK (0x0000FFFFU)
73#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK)
74#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK)
75#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK))
76
77#define TC_H_UNSPEC (0U)
78#define TC_H_ROOT (0xFFFFFFFFU)
79#define TC_H_INGRESS (0xFFFFFFF1U)
80
81struct tc_ratespec
82{
83 unsigned char cell_log;
84 unsigned char __reserved;
85 unsigned short feature;
86 short addend;
87 unsigned short mpu;
88 __u32 rate;
89};
90
91/* FIFO section */
92
93struct tc_fifo_qopt
94{
95 __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
96};
97
98/* PRIO section */
99
100#define TCQ_PRIO_BANDS 16
101
102struct tc_prio_qopt
103{
104 int bands; /* Number of bands */
105 __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */
106};
107
108/* CSZ section */
109
110struct tc_csz_qopt
111{
112 int flows; /* Maximal number of guaranteed flows */
113 unsigned char R_log; /* Fixed point position for round number */
114 unsigned char delta_log; /* Log of maximal managed time interval */
115 __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */
116};
117
118struct tc_csz_copt
119{
120 struct tc_ratespec slice;
121 struct tc_ratespec rate;
122 struct tc_ratespec peakrate;
123 __u32 limit;
124 __u32 buffer;
125 __u32 mtu;
126};
127
128enum
129{
130 TCA_CSZ_UNSPEC,
131 TCA_CSZ_PARMS,
132 TCA_CSZ_RTAB,
133 TCA_CSZ_PTAB,
134};
135
136/* TBF section */
137
138struct tc_tbf_qopt
139{
140 struct tc_ratespec rate;
141 struct tc_ratespec peakrate;
142 __u32 limit;
143 __u32 buffer;
144 __u32 mtu;
145};
146
147enum
148{
149 TCA_TBF_UNSPEC,
150 TCA_TBF_PARMS,
151 TCA_TBF_RTAB,
152 TCA_TBF_PTAB,
153};
154
155
156/* TEQL section */
157
158/* TEQL does not require any parameters */
159
160/* SFQ section */
161
162struct tc_sfq_qopt
163{
164 unsigned quantum; /* Bytes per round allocated to flow */
165 int perturb_period; /* Period of hash perturbation */
166 __u32 limit; /* Maximal packets in queue */
167 unsigned divisor; /* Hash divisor */
168 unsigned flows; /* Maximal number of flows */
169};
170
171/*
172 * NOTE: limit, divisor and flows are hardwired to code at the moment.
173 *
174 * limit=flows=128, divisor=1024;
175 *
176 * The only reason for this is efficiency, it is possible
177 * to change these parameters in compile time.
178 */
179
180/* RED section */
181
182enum
183{
184 TCA_RED_UNSPEC,
185 TCA_RED_PARMS,
186 TCA_RED_STAB,
187};
188
189struct tc_red_qopt
190{
191 __u32 limit; /* HARD maximal queue length (bytes) */
192 __u32 qth_min; /* Min average length threshold (bytes) */
193 __u32 qth_max; /* Max average length threshold (bytes) */
194 unsigned char Wlog; /* log(W) */
195 unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
196 unsigned char Scell_log; /* cell size for idle damping */
197 unsigned char flags;
198#define TC_RED_ECN 1
199};
200
201struct tc_red_xstats
202{
203 __u32 early; /* Early drops */
204 __u32 pdrop; /* Drops due to queue limits */
205 __u32 other; /* Drops due to drop() calls */
206 __u32 marked; /* Marked packets */
207};
208
209/* GRED section */
210
211#define MAX_DPs 16
212
213enum
214{
215 TCA_GRED_UNSPEC,
216 TCA_GRED_PARMS,
217 TCA_GRED_STAB,
218 TCA_GRED_DPS,
219};
220
221#define TCA_SET_OFF TCA_GRED_PARMS
222struct tc_gred_qopt
223{
224 __u32 limit; /* HARD maximal queue length (bytes)
225*/
226 __u32 qth_min; /* Min average length threshold (bytes)
227*/
228 __u32 qth_max; /* Max average length threshold (bytes)
229*/
230 __u32 DP; /* upto 2^32 DPs */
231 __u32 backlog;
232 __u32 qave;
233 __u32 forced;
234 __u32 early;
235 __u32 other;
236 __u32 pdrop;
237
238 unsigned char Wlog; /* log(W) */
239 unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
240 unsigned char Scell_log; /* cell size for idle damping */
241 __u8 prio; /* prio of this VQ */
242 __u32 packets;
243 __u32 bytesin;
244};
245/* gred setup */
246struct tc_gred_sopt
247{
248 __u32 DPs;
249 __u32 def_DP;
250 __u8 grio;
251};
252
253/* HTB section */
254#define TC_HTB_NUMPRIO 4
255#define TC_HTB_MAXDEPTH 4
256
257struct tc_htb_opt
258{
259 struct tc_ratespec rate;
260 struct tc_ratespec ceil;
261 __u32 buffer;
262 __u32 cbuffer;
263 __u32 quantum; /* out only */
264 __u32 level; /* out only */
265 __u8 prio;
266 __u8 injectd; /* inject class distance */
267 __u8 pad[2];
268};
269struct tc_htb_glob
270{
271 __u32 rate2quantum; /* bps->quantum divisor */
272 __u32 defcls; /* default class number */
273 __u32 use_dcache; /* use dequeue cache ? */
274 __u32 debug; /* debug flags */
275
276
277 /* stats */
278 __u32 deq_rate; /* dequeue rate */
279 __u32 utilz; /* dequeue utilization */
280 __u32 trials; /* deq_prio trials per dequeue */
281 __u32 dcache_hits;
282 __u32 direct_pkts; /* count of non shapped packets */
283};
284enum
285{
286 TCA_HTB_UNSPEC,
287 TCA_HTB_PARMS,
288 TCA_HTB_INIT,
289 TCA_HTB_CTAB,
290 TCA_HTB_RTAB,
291};
292struct tc_htb_xstats
293{
294 __u32 lends;
295 __u32 borrows;
296 __u32 giants; /* too big packets (rate will not be accurate) */
297 __u32 injects; /* how many times leaf used injected bw */
298 __u32 tokens;
299 __u32 ctokens;
300};
301
302/* CBQ section */
303
304#define TC_CBQ_MAXPRIO 8
305#define TC_CBQ_MAXLEVEL 8
306#define TC_CBQ_DEF_EWMA 5
307
308struct tc_cbq_lssopt
309{
310 unsigned char change;
311 unsigned char flags;
312#define TCF_CBQ_LSS_BOUNDED 1
313#define TCF_CBQ_LSS_ISOLATED 2
314 unsigned char ewma_log;
315 unsigned char level;
316#define TCF_CBQ_LSS_FLAGS 1
317#define TCF_CBQ_LSS_EWMA 2
318#define TCF_CBQ_LSS_MAXIDLE 4
319#define TCF_CBQ_LSS_MINIDLE 8
320#define TCF_CBQ_LSS_OFFTIME 0x10
321#define TCF_CBQ_LSS_AVPKT 0x20
322 __u32 maxidle;
323 __u32 minidle;
324 __u32 offtime;
325 __u32 avpkt;
326};
327
328struct tc_cbq_wrropt
329{
330 unsigned char flags;
331 unsigned char priority;
332 unsigned char cpriority;
333 unsigned char __reserved;
334 __u32 allot;
335 __u32 weight;
336};
337
338struct tc_cbq_ovl
339{
340 unsigned char strategy;
341#define TC_CBQ_OVL_CLASSIC 0
342#define TC_CBQ_OVL_DELAY 1
343#define TC_CBQ_OVL_LOWPRIO 2
344#define TC_CBQ_OVL_DROP 3
345#define TC_CBQ_OVL_RCLASSIC 4
346 unsigned char priority2;
347 __u32 penalty;
348};
349
350struct tc_cbq_police
351{
352 unsigned char police;
353 unsigned char __res1;
354 unsigned short __res2;
355};
356
357struct tc_cbq_fopt
358{
359 __u32 split;
360 __u32 defmap;
361 __u32 defchange;
362};
363
364struct tc_cbq_xstats
365{
366 __u32 borrows;
367 __u32 overactions;
368 __s32 avgidle;
369 __s32 undertime;
370};
371
372enum
373{
374 TCA_CBQ_UNSPEC,
375 TCA_CBQ_LSSOPT,
376 TCA_CBQ_WRROPT,
377 TCA_CBQ_FOPT,
378 TCA_CBQ_OVL_STRATEGY,
379 TCA_CBQ_RATE,
380 TCA_CBQ_RTAB,
381 TCA_CBQ_POLICE,
382};
383
384#define TCA_CBQ_MAX TCA_CBQ_POLICE
385
386/* dsmark section */
387
388enum {
389 TCA_DSMARK_UNSPEC,
390 TCA_DSMARK_INDICES,
391 TCA_DSMARK_DEFAULT_INDEX,
392 TCA_DSMARK_SET_TC_INDEX,
393 TCA_DSMARK_MASK,
394 TCA_DSMARK_VALUE
395};
396
397#define TCA_DSMARK_MAX TCA_DSMARK_VALUE
398
399/* ATM section */
400
401enum {
402 TCA_ATM_UNSPEC,
403 TCA_ATM_FD, /* file/socket descriptor */
404 TCA_ATM_PTR, /* pointer to descriptor - later */
405 TCA_ATM_HDR, /* LL header */
406 TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */
407 TCA_ATM_ADDR, /* PVC address (for output only) */
408 TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */
409};
410
411#define TCA_ATM_MAX TCA_ATM_STATE
412
413#endif
diff --git a/networking/libiproute/ll_addr.c b/networking/libiproute/ll_addr.c
new file mode 100644
index 000000000..51ff13b8c
--- /dev/null
+++ b/networking/libiproute/ll_addr.c
@@ -0,0 +1,92 @@
1/*
2 * ll_addr.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <sys/ioctl.h>
18#include <sys/socket.h>
19#include <sys/ioctl.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <string.h>
23
24#include <linux/netdevice.h>
25#include <linux/if_arp.h>
26#include <linux/sockios.h>
27
28#include "utils.h"
29
30
31const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
32{
33 int i;
34 int l;
35
36 if (alen == 4 &&
37 (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) {
38 return inet_ntop(AF_INET, addr, buf, blen);
39 }
40 l = 0;
41 for (i=0; i<alen; i++) {
42 if (i==0) {
43 snprintf(buf+l, blen, "%02x", addr[i]);
44 blen -= 2;
45 l += 2;
46 } else {
47 snprintf(buf+l, blen, ":%02x", addr[i]);
48 blen -= 3;
49 l += 3;
50 }
51 }
52 return buf;
53}
54
55int ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
56{
57 if (strchr(arg, '.')) {
58 inet_prefix pfx;
59 if (get_addr_1(&pfx, arg, AF_INET)) {
60 fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
61 return -1;
62 }
63 if (len < 4)
64 return -1;
65 memcpy(lladdr, pfx.data, 4);
66 return 4;
67 } else {
68 int i;
69
70 for (i=0; i<len; i++) {
71 int temp;
72 char *cp = strchr(arg, ':');
73 if (cp) {
74 *cp = 0;
75 cp++;
76 }
77 if (sscanf(arg, "%x", &temp) != 1) {
78 fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
79 return -1;
80 }
81 if (temp < 0 || temp > 255) {
82 fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg);
83 return -1;
84 }
85 lladdr[i] = temp;
86 if (!cp)
87 break;
88 arg = cp;
89 }
90 return i+1;
91 }
92}
diff --git a/networking/libiproute/ll_map.c b/networking/libiproute/ll_map.c
new file mode 100644
index 000000000..e5a95e6a4
--- /dev/null
+++ b/networking/libiproute/ll_map.c
@@ -0,0 +1,169 @@
1/*
2 * ll_map.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <string.h>
21
22#include "libnetlink.h"
23#include "ll_map.h"
24
25struct idxmap
26{
27 struct idxmap * next;
28 int index;
29 int type;
30 int alen;
31 unsigned flags;
32 unsigned char addr[8];
33 char name[16];
34};
35
36static struct idxmap *idxmap[16];
37
38int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
39{
40 int h;
41 struct ifinfomsg *ifi = NLMSG_DATA(n);
42 struct idxmap *im, **imp;
43 struct rtattr *tb[IFLA_MAX+1];
44
45 if (n->nlmsg_type != RTM_NEWLINK)
46 return 0;
47
48 if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
49 return -1;
50
51
52 memset(tb, 0, sizeof(tb));
53 parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
54 if (tb[IFLA_IFNAME] == NULL)
55 return 0;
56
57 h = ifi->ifi_index&0xF;
58
59 for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next)
60 if (im->index == ifi->ifi_index)
61 break;
62
63 if (im == NULL) {
64 im = malloc(sizeof(*im));
65 if (im == NULL)
66 return 0;
67 im->next = *imp;
68 im->index = ifi->ifi_index;
69 *imp = im;
70 }
71
72 im->type = ifi->ifi_type;
73 im->flags = ifi->ifi_flags;
74 if (tb[IFLA_ADDRESS]) {
75 int alen;
76 im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
77 if (alen > sizeof(im->addr))
78 alen = sizeof(im->addr);
79 memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
80 } else {
81 im->alen = 0;
82 memset(im->addr, 0, sizeof(im->addr));
83 }
84 strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
85 return 0;
86}
87
88const char *ll_idx_n2a(int idx, char *buf)
89{
90 struct idxmap *im;
91
92 if (idx == 0)
93 return "*";
94 for (im = idxmap[idx&0xF]; im; im = im->next)
95 if (im->index == idx)
96 return im->name;
97 snprintf(buf, 16, "if%d", idx);
98 return buf;
99}
100
101
102const char *ll_index_to_name(int idx)
103{
104 static char nbuf[16];
105
106 return ll_idx_n2a(idx, nbuf);
107}
108
109int ll_index_to_type(int idx)
110{
111 struct idxmap *im;
112
113 if (idx == 0)
114 return -1;
115 for (im = idxmap[idx&0xF]; im; im = im->next)
116 if (im->index == idx)
117 return im->type;
118 return -1;
119}
120
121unsigned ll_index_to_flags(int idx)
122{
123 struct idxmap *im;
124
125 if (idx == 0)
126 return 0;
127
128 for (im = idxmap[idx&0xF]; im; im = im->next)
129 if (im->index == idx)
130 return im->flags;
131 return 0;
132}
133
134int ll_name_to_index(char *name)
135{
136 static char ncache[16];
137 static int icache;
138 struct idxmap *im;
139 int i;
140
141 if (name == NULL)
142 return 0;
143 if (icache && strcmp(name, ncache) == 0)
144 return icache;
145 for (i=0; i<16; i++) {
146 for (im = idxmap[i]; im; im = im->next) {
147 if (strcmp(im->name, name) == 0) {
148 icache = im->index;
149 strcpy(ncache, name);
150 return im->index;
151 }
152 }
153 }
154 return 0;
155}
156
157int ll_init_map(struct rtnl_handle *rth)
158{
159 if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) {
160 perror("Cannot send dump request");
161 exit(1);
162 }
163
164 if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) {
165 fprintf(stderr, "Dump terminated\n");
166 exit(1);
167 }
168 return 0;
169}
diff --git a/networking/libiproute/ll_map.h b/networking/libiproute/ll_map.h
new file mode 100644
index 000000000..739f157e7
--- /dev/null
+++ b/networking/libiproute/ll_map.h
@@ -0,0 +1,12 @@
1#ifndef __LL_MAP_H__
2#define __LL_MAP_H__ 1
3
4extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg);
5extern int ll_init_map(struct rtnl_handle *rth);
6extern int ll_name_to_index(char *name);
7extern const char *ll_index_to_name(int idx);
8extern const char *ll_idx_n2a(int idx, char *buf);
9extern int ll_index_to_type(int idx);
10extern unsigned ll_index_to_flags(int idx);
11
12#endif /* __LL_MAP_H__ */
diff --git a/networking/libiproute/ll_proto.c b/networking/libiproute/ll_proto.c
new file mode 100644
index 000000000..394338aea
--- /dev/null
+++ b/networking/libiproute/ll_proto.c
@@ -0,0 +1,118 @@
1/*
2 * ll_proto.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <sys/ioctl.h>
18#include <sys/socket.h>
19#include <sys/ioctl.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <string.h>
23
24#include <linux/netdevice.h>
25#include <linux/if_arp.h>
26#include <linux/sockios.h>
27
28#include "utils.h"
29
30
31#define __PF(f,n) { ETH_P_##f, #n },
32static struct {
33 int id;
34 char *name;
35} llproto_names[] = {
36__PF(LOOP,loop)
37__PF(PUP,pup)
38#ifdef ETH_P_PUPAT
39__PF(PUPAT,pupat)
40#endif
41__PF(IP,ip)
42__PF(X25,x25)
43__PF(ARP,arp)
44__PF(BPQ,bpq)
45#ifdef ETH_P_IEEEPUP
46__PF(IEEEPUP,ieeepup)
47#endif
48#ifdef ETH_P_IEEEPUPAT
49__PF(IEEEPUPAT,ieeepupat)
50#endif
51__PF(DEC,dec)
52__PF(DNA_DL,dna_dl)
53__PF(DNA_RC,dna_rc)
54__PF(DNA_RT,dna_rt)
55__PF(LAT,lat)
56__PF(DIAG,diag)
57__PF(CUST,cust)
58__PF(SCA,sca)
59__PF(RARP,rarp)
60__PF(ATALK,atalk)
61__PF(AARP,aarp)
62__PF(IPX,ipx)
63__PF(IPV6,ipv6)
64__PF(PPP_DISC,ppp_disc)
65__PF(PPP_SES,ppp_ses)
66__PF(ATMMPOA,atmmpoa)
67__PF(ATMFATE,atmfate)
68
69__PF(802_3,802_3)
70__PF(AX25,ax25)
71__PF(ALL,all)
72__PF(802_2,802_2)
73__PF(SNAP,snap)
74__PF(DDCMP,ddcmp)
75__PF(WAN_PPP,wan_ppp)
76__PF(PPP_MP,ppp_mp)
77__PF(LOCALTALK,localtalk)
78__PF(PPPTALK,ppptalk)
79__PF(TR_802_2,tr_802_2)
80__PF(MOBITEX,mobitex)
81__PF(CONTROL,control)
82__PF(IRDA,irda)
83__PF(ECONET,econet)
84
85{ 0x8100, "802.1Q" },
86{ ETH_P_IP, "ipv4" },
87};
88#undef __PF
89
90
91char * ll_proto_n2a(unsigned short id, char *buf, int len)
92{
93 int i;
94
95 id = ntohs(id);
96
97 for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
98 if (llproto_names[i].id == id)
99 return llproto_names[i].name;
100 }
101 snprintf(buf, len, "[%d]", id);
102 return buf;
103}
104
105int ll_proto_a2n(unsigned short *id, char *buf)
106{
107 int i;
108 for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) {
109 if (strcasecmp(llproto_names[i].name, buf) == 0) {
110 *id = htons(llproto_names[i].id);
111 return 0;
112 }
113 }
114 if (get_u16(id, buf, 0))
115 return -1;
116 *id = htons(*id);
117 return 0;
118}
diff --git a/networking/libiproute/ll_types.c b/networking/libiproute/ll_types.c
new file mode 100644
index 000000000..f14583634
--- /dev/null
+++ b/networking/libiproute/ll_types.c
@@ -0,0 +1,121 @@
1/*
2 * ll_types.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <sys/ioctl.h>
18#include <sys/socket.h>
19#include <sys/ioctl.h>
20#include <netinet/in.h>
21#include <arpa/inet.h>
22#include <string.h>
23
24#include <linux/netdevice.h>
25#include <linux/if_arp.h>
26#include <linux/sockios.h>
27
28char * ll_type_n2a(int type, char *buf, int len)
29{
30#define __PF(f,n) { ARPHRD_##f, #n },
31static struct {
32 int type;
33 char *name;
34} arphrd_names[] = {
35{ 0, "generic" },
36__PF(ETHER,ether)
37__PF(EETHER,eether)
38__PF(AX25,ax25)
39__PF(PRONET,pronet)
40__PF(CHAOS,chaos)
41#ifdef ARPHRD_IEEE802_TR
42__PF(IEEE802,ieee802)
43#else
44__PF(IEEE802,tr)
45#endif
46__PF(ARCNET,arcnet)
47__PF(APPLETLK,atalk)
48__PF(DLCI,dlci)
49__PF(ATM,atm)
50__PF(METRICOM,metricom)
51#ifdef ARPHRD_IEEE1394
52__PF(IEEE1394,ieee1394)
53#endif
54
55__PF(SLIP,slip)
56__PF(CSLIP,cslip)
57__PF(SLIP6,slip6)
58__PF(CSLIP6,cslip6)
59__PF(RSRVD,rsrvd)
60__PF(ADAPT,adapt)
61__PF(ROSE,rose)
62__PF(X25,x25)
63__PF(HWX25,hwx25)
64__PF(PPP,ppp)
65__PF(HDLC,hdlc)
66__PF(LAPB,lapb)
67__PF(DDCMP,ddcmp)
68__PF(RAWHDLC,rawhdlc)
69
70__PF(TUNNEL,ipip)
71__PF(TUNNEL6,tunnel6)
72__PF(FRAD,frad)
73__PF(SKIP,skip)
74__PF(LOOPBACK,loopback)
75__PF(LOCALTLK,ltalk)
76__PF(FDDI,fddi)
77__PF(BIF,bif)
78__PF(SIT,sit)
79__PF(IPDDP,ip/ddp)
80__PF(IPGRE,gre)
81__PF(PIMREG,pimreg)
82__PF(HIPPI,hippi)
83__PF(ASH,ash)
84__PF(ECONET,econet)
85__PF(IRDA,irda)
86__PF(FCPP,fcpp)
87__PF(FCAL,fcal)
88__PF(FCPL,fcpl)
89__PF(FCFABRIC,fcfb0)
90__PF(FCFABRIC+1,fcfb1)
91__PF(FCFABRIC+2,fcfb2)
92__PF(FCFABRIC+3,fcfb3)
93__PF(FCFABRIC+4,fcfb4)
94__PF(FCFABRIC+5,fcfb5)
95__PF(FCFABRIC+6,fcfb6)
96__PF(FCFABRIC+7,fcfb7)
97__PF(FCFABRIC+8,fcfb8)
98__PF(FCFABRIC+9,fcfb9)
99__PF(FCFABRIC+10,fcfb10)
100__PF(FCFABRIC+11,fcfb11)
101__PF(FCFABRIC+12,fcfb12)
102#ifdef ARPHRD_IEEE802_TR
103__PF(IEEE802_TR,tr)
104#endif
105#ifdef ARPHRD_IEEE80211
106__PF(IEEE80211,ieee802.11)
107#endif
108#ifdef ARPHRD_VOID
109__PF(VOID,void)
110#endif
111};
112#undef __PF
113
114 int i;
115 for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) {
116 if (arphrd_names[i].type == type)
117 return arphrd_names[i].name;
118 }
119 snprintf(buf, len, "[%d]", type);
120 return buf;
121}
diff --git a/networking/libiproute/rt_names.c b/networking/libiproute/rt_names.c
new file mode 100644
index 000000000..2a7d85cdb
--- /dev/null
+++ b/networking/libiproute/rt_names.c
@@ -0,0 +1,389 @@
1/*
2 * rt_names.c rtnetlink names DB.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <string.h>
18#include <sys/time.h>
19#include <stdint.h>
20
21static void rtnl_tab_initialize(char *file, char **tab, int size)
22{
23 char buf[512];
24 FILE *fp;
25
26 fp = fopen(file, "r");
27 if (!fp)
28 return;
29 while (fgets(buf, sizeof(buf), fp)) {
30 char *p = buf;
31 int id;
32 char namebuf[512];
33
34 while (*p == ' ' || *p == '\t')
35 p++;
36 if (*p == '#' || *p == '\n' || *p == 0)
37 continue;
38 if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 &&
39 sscanf(p, "0x%x %s #", &id, namebuf) != 2 &&
40 sscanf(p, "%d %s\n", &id, namebuf) != 2 &&
41 sscanf(p, "%d %s #", &id, namebuf) != 2) {
42 fprintf(stderr, "Database %s is corrupted at %s\n",
43 file, p);
44 return;
45 }
46
47 if (id<0 || id>size)
48 continue;
49
50 tab[id] = strdup(namebuf);
51 }
52 fclose(fp);
53}
54
55
56static char * rtnl_rtprot_tab[256] = {
57 "none",
58 "redirect",
59 "kernel",
60 "boot",
61 "static",
62 NULL,
63 NULL,
64 NULL,
65 "gated",
66 "ra",
67 "mrt",
68 "zebra",
69 "bird",
70};
71
72
73
74static int rtnl_rtprot_init;
75
76static void rtnl_rtprot_initialize(void)
77{
78 rtnl_rtprot_init = 1;
79 rtnl_tab_initialize("/etc/iproute2/rt_protos",
80 rtnl_rtprot_tab, 256);
81}
82
83char * rtnl_rtprot_n2a(int id, char *buf, int len)
84{
85 if (id<0 || id>=256) {
86 snprintf(buf, len, "%d", id);
87 return buf;
88 }
89 if (!rtnl_rtprot_tab[id]) {
90 if (!rtnl_rtprot_init)
91 rtnl_rtprot_initialize();
92 }
93 if (rtnl_rtprot_tab[id])
94 return rtnl_rtprot_tab[id];
95 snprintf(buf, len, "%d", id);
96 return buf;
97}
98
99int rtnl_rtprot_a2n(uint32_t *id, char *arg)
100{
101 static char *cache = NULL;
102 static unsigned long res;
103 char *end;
104 int i;
105
106 if (cache && strcmp(cache, arg) == 0) {
107 *id = res;
108 return 0;
109 }
110
111 if (!rtnl_rtprot_init)
112 rtnl_rtprot_initialize();
113
114 for (i=0; i<256; i++) {
115 if (rtnl_rtprot_tab[i] &&
116 strcmp(rtnl_rtprot_tab[i], arg) == 0) {
117 cache = rtnl_rtprot_tab[i];
118 res = i;
119 *id = res;
120 return 0;
121 }
122 }
123
124 res = strtoul(arg, &end, 0);
125 if (!end || end == arg || *end || res > 255)
126 return -1;
127 *id = res;
128 return 0;
129}
130
131
132
133static char * rtnl_rtscope_tab[256] = {
134 "global",
135};
136
137static int rtnl_rtscope_init;
138
139static void rtnl_rtscope_initialize(void)
140{
141 rtnl_rtscope_init = 1;
142 rtnl_rtscope_tab[255] = "nowhere";
143 rtnl_rtscope_tab[254] = "host";
144 rtnl_rtscope_tab[253] = "link";
145 rtnl_rtscope_tab[200] = "site";
146 rtnl_tab_initialize("/etc/iproute2/rt_scopes",
147 rtnl_rtscope_tab, 256);
148}
149
150char * rtnl_rtscope_n2a(int id, char *buf, int len)
151{
152 if (id<0 || id>=256) {
153 snprintf(buf, len, "%d", id);
154 return buf;
155 }
156 if (!rtnl_rtscope_tab[id]) {
157 if (!rtnl_rtscope_init)
158 rtnl_rtscope_initialize();
159 }
160 if (rtnl_rtscope_tab[id])
161 return rtnl_rtscope_tab[id];
162 snprintf(buf, len, "%d", id);
163 return buf;
164}
165
166int rtnl_rtscope_a2n(uint32_t *id, char *arg)
167{
168 static char *cache = NULL;
169 static unsigned long res;
170 char *end;
171 int i;
172
173 if (cache && strcmp(cache, arg) == 0) {
174 *id = res;
175 return 0;
176 }
177
178 if (!rtnl_rtscope_init)
179 rtnl_rtscope_initialize();
180
181 for (i=0; i<256; i++) {
182 if (rtnl_rtscope_tab[i] &&
183 strcmp(rtnl_rtscope_tab[i], arg) == 0) {
184 cache = rtnl_rtscope_tab[i];
185 res = i;
186 *id = res;
187 return 0;
188 }
189 }
190
191 res = strtoul(arg, &end, 0);
192 if (!end || end == arg || *end || res > 255)
193 return -1;
194 *id = res;
195 return 0;
196}
197
198
199
200static char * rtnl_rtrealm_tab[256] = {
201 "unknown",
202};
203
204static int rtnl_rtrealm_init;
205
206static void rtnl_rtrealm_initialize(void)
207{
208 rtnl_rtrealm_init = 1;
209 rtnl_tab_initialize("/etc/iproute2/rt_realms",
210 rtnl_rtrealm_tab, 256);
211}
212
213char * rtnl_rtrealm_n2a(int id, char *buf, int len)
214{
215 if (id<0 || id>=256) {
216 snprintf(buf, len, "%d", id);
217 return buf;
218 }
219 if (!rtnl_rtrealm_tab[id]) {
220 if (!rtnl_rtrealm_init)
221 rtnl_rtrealm_initialize();
222 }
223 if (rtnl_rtrealm_tab[id])
224 return rtnl_rtrealm_tab[id];
225 snprintf(buf, len, "%d", id);
226 return buf;
227}
228
229
230int rtnl_rtrealm_a2n(uint32_t *id, char *arg)
231{
232 static char *cache = NULL;
233 static unsigned long res;
234 char *end;
235 int i;
236
237 if (cache && strcmp(cache, arg) == 0) {
238 *id = res;
239 return 0;
240 }
241
242 if (!rtnl_rtrealm_init)
243 rtnl_rtrealm_initialize();
244
245 for (i=0; i<256; i++) {
246 if (rtnl_rtrealm_tab[i] &&
247 strcmp(rtnl_rtrealm_tab[i], arg) == 0) {
248 cache = rtnl_rtrealm_tab[i];
249 res = i;
250 *id = res;
251 return 0;
252 }
253 }
254
255 res = strtoul(arg, &end, 0);
256 if (!end || end == arg || *end || res > 255)
257 return -1;
258 *id = res;
259 return 0;
260}
261
262
263
264static char * rtnl_rttable_tab[256] = {
265 "unspec",
266};
267
268static int rtnl_rttable_init;
269
270static void rtnl_rttable_initialize(void)
271{
272 rtnl_rttable_init = 1;
273 rtnl_rttable_tab[255] = "local";
274 rtnl_rttable_tab[254] = "main";
275 rtnl_tab_initialize("/etc/iproute2/rt_tables",
276 rtnl_rttable_tab, 256);
277}
278
279char * rtnl_rttable_n2a(int id, char *buf, int len)
280{
281 if (id<0 || id>=256) {
282 snprintf(buf, len, "%d", id);
283 return buf;
284 }
285 if (!rtnl_rttable_tab[id]) {
286 if (!rtnl_rttable_init)
287 rtnl_rttable_initialize();
288 }
289 if (rtnl_rttable_tab[id])
290 return rtnl_rttable_tab[id];
291 snprintf(buf, len, "%d", id);
292 return buf;
293}
294
295int rtnl_rttable_a2n(uint32_t *id, char *arg)
296{
297 static char *cache = NULL;
298 static unsigned long res;
299 char *end;
300 int i;
301
302 if (cache && strcmp(cache, arg) == 0) {
303 *id = res;
304 return 0;
305 }
306
307 if (!rtnl_rttable_init)
308 rtnl_rttable_initialize();
309
310 for (i=0; i<256; i++) {
311 if (rtnl_rttable_tab[i] &&
312 strcmp(rtnl_rttable_tab[i], arg) == 0) {
313 cache = rtnl_rttable_tab[i];
314 res = i;
315 *id = res;
316 return 0;
317 }
318 }
319
320 i = strtoul(arg, &end, 0);
321 if (!end || end == arg || *end || i > 255)
322 return -1;
323 *id = i;
324 return 0;
325}
326
327
328static char * rtnl_rtdsfield_tab[256] = {
329 "0",
330};
331
332static int rtnl_rtdsfield_init;
333
334static void rtnl_rtdsfield_initialize(void)
335{
336 rtnl_rtdsfield_init = 1;
337 rtnl_tab_initialize("/etc/iproute2/rt_dsfield",
338 rtnl_rtdsfield_tab, 256);
339}
340
341char * rtnl_dsfield_n2a(int id, char *buf, int len)
342{
343 if (id<0 || id>=256) {
344 snprintf(buf, len, "%d", id);
345 return buf;
346 }
347 if (!rtnl_rtdsfield_tab[id]) {
348 if (!rtnl_rtdsfield_init)
349 rtnl_rtdsfield_initialize();
350 }
351 if (rtnl_rtdsfield_tab[id])
352 return rtnl_rtdsfield_tab[id];
353 snprintf(buf, len, "0x%02x", id);
354 return buf;
355}
356
357
358int rtnl_dsfield_a2n(uint32_t *id, char *arg)
359{
360 static char *cache = NULL;
361 static unsigned long res;
362 char *end;
363 int i;
364
365 if (cache && strcmp(cache, arg) == 0) {
366 *id = res;
367 return 0;
368 }
369
370 if (!rtnl_rtdsfield_init)
371 rtnl_rtdsfield_initialize();
372
373 for (i=0; i<256; i++) {
374 if (rtnl_rtdsfield_tab[i] &&
375 strcmp(rtnl_rtdsfield_tab[i], arg) == 0) {
376 cache = rtnl_rtdsfield_tab[i];
377 res = i;
378 *id = res;
379 return 0;
380 }
381 }
382
383 res = strtoul(arg, &end, 16);
384 if (!end || end == arg || *end || res > 255)
385 return -1;
386 *id = res;
387 return 0;
388}
389
diff --git a/networking/libiproute/rt_names.h b/networking/libiproute/rt_names.h
new file mode 100644
index 000000000..97bc6169f
--- /dev/null
+++ b/networking/libiproute/rt_names.h
@@ -0,0 +1,30 @@
1#ifndef RT_NAMES_H_
2#define RT_NAMES_H_ 1
3
4#include <stdint.h>
5
6const char* rtnl_rtprot_n2a(int id, char *buf, int len);
7const char* rtnl_rtscope_n2a(int id, char *buf, int len);
8const char* rtnl_rttable_n2a(int id, char *buf, int len);
9const char* rtnl_rtrealm_n2a(int id, char *buf, int len);
10const char* rtnl_dsfield_n2a(int id, char *buf, int len);
11int rtnl_rtprot_a2n(int *id, char *arg);
12int rtnl_rtscope_a2n(int *id, char *arg);
13int rtnl_rttable_a2n(int *id, char *arg);
14int rtnl_rtrealm_a2n(uint32_t *id, char *arg);
15int rtnl_dsfield_a2n(uint32_t *id, char *arg);
16
17const char *inet_proto_n2a(int proto, char *buf, int len);
18int inet_proto_a2n(char *buf);
19
20
21const char * ll_type_n2a(int type, char *buf, int len);
22
23const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen);
24int ll_addr_a2n(unsigned char *lladdr, int len, char *arg);
25
26const char * ll_proto_n2a(unsigned short id, char *buf, int len);
27int ll_proto_a2n(unsigned short *id, char *buf);
28
29
30#endif
diff --git a/networking/libiproute/rtm_map.c b/networking/libiproute/rtm_map.c
new file mode 100644
index 000000000..21e818b4a
--- /dev/null
+++ b/networking/libiproute/rtm_map.c
@@ -0,0 +1,116 @@
1/*
2 * rtm_map.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <string.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21
22#include "rt_names.h"
23#include "utils.h"
24
25char *rtnl_rtntype_n2a(int id, char *buf, int len)
26{
27 switch (id) {
28 case RTN_UNSPEC:
29 return "none";
30 case RTN_UNICAST:
31 return "unicast";
32 case RTN_LOCAL:
33 return "local";
34 case RTN_BROADCAST:
35 return "broadcast";
36 case RTN_ANYCAST:
37 return "anycast";
38 case RTN_MULTICAST:
39 return "multicast";
40 case RTN_BLACKHOLE:
41 return "blackhole";
42 case RTN_UNREACHABLE:
43 return "unreachable";
44 case RTN_PROHIBIT:
45 return "prohibit";
46 case RTN_THROW:
47 return "throw";
48 case RTN_NAT:
49 return "nat";
50 case RTN_XRESOLVE:
51 return "xresolve";
52 default:
53 snprintf(buf, len, "%d", id);
54 return buf;
55 }
56}
57
58
59int rtnl_rtntype_a2n(int *id, char *arg)
60{
61 char *end;
62 unsigned long res;
63
64 if (strcmp(arg, "local") == 0)
65 res = RTN_LOCAL;
66 else if (strcmp(arg, "nat") == 0)
67 res = RTN_NAT;
68 else if (matches(arg, "broadcast") == 0 ||
69 strcmp(arg, "brd") == 0)
70 res = RTN_BROADCAST;
71 else if (matches(arg, "anycast") == 0)
72 res = RTN_ANYCAST;
73 else if (matches(arg, "multicast") == 0)
74 res = RTN_MULTICAST;
75 else if (matches(arg, "prohibit") == 0)
76 res = RTN_PROHIBIT;
77 else if (matches(arg, "unreachable") == 0)
78 res = RTN_UNREACHABLE;
79 else if (matches(arg, "blackhole") == 0)
80 res = RTN_BLACKHOLE;
81 else if (matches(arg, "xresolve") == 0)
82 res = RTN_XRESOLVE;
83 else if (matches(arg, "unicast") == 0)
84 res = RTN_UNICAST;
85 else if (strcmp(arg, "throw") == 0)
86 res = RTN_THROW;
87 else {
88 res = strtoul(arg, &end, 0);
89 if (!end || end == arg || *end || res > 255)
90 return -1;
91 }
92 *id = res;
93 return 0;
94}
95
96int get_rt_realms(__u32 *realms, char *arg)
97{
98 __u32 realm = 0;
99 char *p = strchr(arg, '/');
100
101 *realms = 0;
102 if (p) {
103 *p = 0;
104 if (rtnl_rtrealm_a2n(realms, arg)) {
105 *p = '/';
106 return -1;
107 }
108 *realms <<= 16;
109 *p = '/';
110 arg = p+1;
111 }
112 if (*arg && rtnl_rtrealm_a2n(&realm, arg))
113 return -1;
114 *realms |= realm;
115 return 0;
116}
diff --git a/networking/libiproute/rtm_map.h b/networking/libiproute/rtm_map.h
new file mode 100644
index 000000000..70bda7d05
--- /dev/null
+++ b/networking/libiproute/rtm_map.h
@@ -0,0 +1,10 @@
1#ifndef __RTM_MAP_H__
2#define __RTM_MAP_H__ 1
3
4char *rtnl_rtntype_n2a(int id, char *buf, int len);
5int rtnl_rtntype_a2n(int *id, char *arg);
6
7int get_rt_realms(__u32 *realms, char *arg);
8
9
10#endif /* __RTM_MAP_H__ */
diff --git a/networking/libiproute/utils.c b/networking/libiproute/utils.c
new file mode 100644
index 000000000..afc02d9d9
--- /dev/null
+++ b/networking/libiproute/utils.c
@@ -0,0 +1,368 @@
1/*
2 * utils.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 *
12 * Changes:
13 *
14 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <syslog.h>
21#include <fcntl.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <string.h>
25#include <netdb.h>
26#include <arpa/inet.h>
27#include <resolv.h>
28#include "./linux/pkt_sched.h"
29
30#include "utils.h"
31
32int get_integer(int *val, char *arg, int base)
33{
34 long res;
35 char *ptr;
36
37 if (!arg || !*arg)
38 return -1;
39 res = strtol(arg, &ptr, base);
40 if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN)
41 return -1;
42 *val = res;
43 return 0;
44}
45
46int get_unsigned(unsigned *val, char *arg, int base)
47{
48 unsigned long res;
49 char *ptr;
50
51 if (!arg || !*arg)
52 return -1;
53 res = strtoul(arg, &ptr, base);
54 if (!ptr || ptr == arg || *ptr || res > UINT_MAX)
55 return -1;
56 *val = res;
57 return 0;
58}
59
60int get_u32(__u32 *val, char *arg, int base)
61{
62 unsigned long res;
63 char *ptr;
64
65 if (!arg || !*arg)
66 return -1;
67 res = strtoul(arg, &ptr, base);
68 if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
69 return -1;
70 *val = res;
71 return 0;
72}
73
74int get_u16(__u16 *val, char *arg, int base)
75{
76 unsigned long res;
77 char *ptr;
78
79 if (!arg || !*arg)
80 return -1;
81 res = strtoul(arg, &ptr, base);
82 if (!ptr || ptr == arg || *ptr || res > 0xFFFF)
83 return -1;
84 *val = res;
85 return 0;
86}
87
88int get_u8(__u8 *val, char *arg, int base)
89{
90 unsigned long res;
91 char *ptr;
92
93 if (!arg || !*arg)
94 return -1;
95 res = strtoul(arg, &ptr, base);
96 if (!ptr || ptr == arg || *ptr || res > 0xFF)
97 return -1;
98 *val = res;
99 return 0;
100}
101
102int get_s16(__s16 *val, char *arg, int base)
103{
104 long res;
105 char *ptr;
106
107 if (!arg || !*arg)
108 return -1;
109 res = strtol(arg, &ptr, base);
110 if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000)
111 return -1;
112 *val = res;
113 return 0;
114}
115
116int get_s8(__s8 *val, char *arg, int base)
117{
118 long res;
119 char *ptr;
120
121 if (!arg || !*arg)
122 return -1;
123 res = strtol(arg, &ptr, base);
124 if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80)
125 return -1;
126 *val = res;
127 return 0;
128}
129
130int get_addr_1(inet_prefix *addr, char *name, int family)
131{
132 char *cp;
133 unsigned char *ap = (unsigned char*)addr->data;
134 int i;
135
136 memset(addr, 0, sizeof(*addr));
137
138 if (strcmp(name, "default") == 0 ||
139 strcmp(name, "all") == 0 ||
140 strcmp(name, "any") == 0) {
141 addr->family = family;
142 addr->bytelen = (family == AF_INET6 ? 16 : 4);
143 addr->bitlen = -1;
144 return 0;
145 }
146
147 if (strchr(name, ':')) {
148 addr->family = AF_INET6;
149 if (family != AF_UNSPEC && family != AF_INET6)
150 return -1;
151 if (inet_pton(AF_INET6, name, addr->data) <= 0)
152 return -1;
153 addr->bytelen = 16;
154 addr->bitlen = -1;
155 return 0;
156 }
157
158 addr->family = AF_INET;
159 if (family != AF_UNSPEC && family != AF_INET)
160 return -1;
161 addr->bytelen = 4;
162 addr->bitlen = -1;
163 for (cp=name, i=0; *cp; cp++) {
164 if (*cp <= '9' && *cp >= '0') {
165 ap[i] = 10*ap[i] + (*cp-'0');
166 continue;
167 }
168 if (*cp == '.' && ++i <= 3)
169 continue;
170 return -1;
171 }
172 return 0;
173}
174
175int get_prefix_1(inet_prefix *dst, char *arg, int family)
176{
177 int err;
178 unsigned plen;
179 char *slash;
180
181 memset(dst, 0, sizeof(*dst));
182
183 if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) {
184 dst->family = family;
185 dst->bytelen = 0;
186 dst->bitlen = 0;
187 return 0;
188 }
189
190 slash = strchr(arg, '/');
191 if (slash)
192 *slash = 0;
193 err = get_addr_1(dst, arg, family);
194 if (err == 0) {
195 switch(dst->family) {
196 case AF_INET6:
197 dst->bitlen = 128;
198 break;
199 default:
200 case AF_INET:
201 dst->bitlen = 32;
202 }
203 if (slash) {
204 if (get_integer(&plen, slash+1, 0) || plen > dst->bitlen) {
205 err = -1;
206 goto done;
207 }
208 dst->bitlen = plen;
209 }
210 }
211done:
212 if (slash)
213 *slash = '/';
214 return err;
215}
216
217int get_addr(inet_prefix *dst, char *arg, int family)
218{
219 if (family == AF_PACKET) {
220 fprintf(stderr, "Error: \"%s\" may be inet address, but it is not allowed in this context.\n", arg);
221 exit(1);
222 }
223 if (get_addr_1(dst, arg, family)) {
224 fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", arg);
225 exit(1);
226 }
227 return 0;
228}
229
230int get_prefix(inet_prefix *dst, char *arg, int family)
231{
232 if (family == AF_PACKET) {
233 fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg);
234 exit(1);
235 }
236 if (get_prefix_1(dst, arg, family)) {
237 fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", arg);
238 exit(1);
239 }
240 return 0;
241}
242
243__u32 get_addr32(char *name)
244{
245 inet_prefix addr;
246 if (get_addr_1(&addr, name, AF_INET)) {
247 fprintf(stderr, "Error: an IP address is expected rather than \"%s\"\n", name);
248 exit(1);
249 }
250 return addr.data[0];
251}
252
253void incomplete_command()
254{
255 fprintf(stderr, "Command line is not complete. Try option \"help\"\n");
256 exit(-1);
257}
258
259void invarg(char *msg, char *arg)
260{
261 fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg);
262 exit(-1);
263}
264
265void duparg(char *key, char *arg)
266{
267 fprintf(stderr, "Error: duplicate \"%s\": \"%s\" is the second value.\n", key, arg);
268 exit(-1);
269}
270
271void duparg2(char *key, char *arg)
272{
273 fprintf(stderr, "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", key, arg);
274 exit(-1);
275}
276
277int matches(char *cmd, char *pattern)
278{
279 int len = strlen(cmd);
280 if (len > strlen(pattern))
281 return -1;
282 return memcmp(pattern, cmd, len);
283}
284
285int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits)
286{
287 __u32 *a1 = a->data;
288 __u32 *a2 = b->data;
289 int words = bits >> 0x05;
290
291 bits &= 0x1f;
292
293 if (words)
294 if (memcmp(a1, a2, words << 2))
295 return -1;
296
297 if (bits) {
298 __u32 w1, w2;
299 __u32 mask;
300
301 w1 = a1[words];
302 w2 = a2[words];
303
304 mask = htonl((0xffffffff) << (0x20 - bits));
305
306 if ((w1 ^ w2) & mask)
307 return 1;
308 }
309
310 return 0;
311}
312
313int __iproute2_hz_internal;
314
315int __get_hz(void)
316{
317 int hz = 0;
318 FILE *fp = fopen("/proc/net/psched", "r");
319
320 if (fp) {
321 unsigned nom, denom;
322 if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
323 if (nom == 1000000)
324 hz = denom;
325 fclose(fp);
326 }
327 if (hz)
328 return hz;
329 return HZ;
330}
331
332const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen)
333{
334 switch (af) {
335 case AF_INET:
336 case AF_INET6:
337 return inet_ntop(af, addr, buf, buflen);
338 default:
339 return "???";
340 }
341}
342
343
344const char *format_host(int af, int len, void *addr, char *buf, int buflen)
345{
346#ifdef RESOLVE_HOSTNAMES
347 if (resolve_hosts) {
348 struct hostent *h_ent;
349 if (len <= 0) {
350 switch (af) {
351 case AF_INET:
352 len = 4;
353 break;
354 case AF_INET6:
355 len = 16;
356 break;
357 default: ;
358 }
359 }
360 if (len > 0 &&
361 (h_ent = gethostbyaddr(addr, len, af)) != NULL) {
362 snprintf(buf, buflen-1, "%s", h_ent->h_name);
363 return buf;
364 }
365 }
366#endif
367 return rt_addr_n2a(af, len, addr, buf, buflen);
368}
diff --git a/networking/libiproute/utils.h b/networking/libiproute/utils.h
new file mode 100644
index 000000000..dc28c1b6a
--- /dev/null
+++ b/networking/libiproute/utils.h
@@ -0,0 +1,101 @@
1#ifndef __UTILS_H__
2#define __UTILS_H__ 1
3
4#include <asm/types.h>
5#include <resolv.h>
6
7#include "libnetlink.h"
8#include "ll_map.h"
9#include "rtm_map.h"
10
11extern int preferred_family;
12extern int show_stats;
13extern int show_details;
14extern int show_raw;
15extern int resolve_hosts;
16extern int oneline;
17extern char * _SL_;
18
19#ifndef IPPROTO_ESP
20#define IPPROTO_ESP 50
21#endif
22#ifndef IPPROTO_AH
23#define IPPROTO_AH 51
24#endif
25
26#define SPRINT_BSIZE 64
27#define SPRINT_BUF(x) char x[SPRINT_BSIZE]
28
29extern void incomplete_command(void) __attribute__((noreturn));
30
31#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
32
33typedef struct
34{
35 __u8 family;
36 __u8 bytelen;
37 __s16 bitlen;
38 __u32 data[4];
39} inet_prefix;
40
41#define DN_MAXADDL 20
42#ifndef AF_DECnet
43#define AF_DECnet 12
44#endif
45
46struct dn_naddr
47{
48 unsigned short a_len;
49 unsigned char a_addr[DN_MAXADDL];
50};
51
52#define IPX_NODE_LEN 6
53
54struct ipx_addr {
55 u_int32_t ipx_net;
56 u_int8_t ipx_node[IPX_NODE_LEN];
57};
58
59extern __u32 get_addr32(char *name);
60extern int get_addr_1(inet_prefix *dst, char *arg, int family);
61extern int get_prefix_1(inet_prefix *dst, char *arg, int family);
62extern int get_addr(inet_prefix *dst, char *arg, int family);
63extern int get_prefix(inet_prefix *dst, char *arg, int family);
64
65extern int get_integer(int *val, char *arg, int base);
66extern int get_unsigned(unsigned *val, char *arg, int base);
67#define get_byte get_u8
68#define get_ushort get_u16
69#define get_short get_s16
70extern int get_u32(__u32 *val, char *arg, int base);
71extern int get_u16(__u16 *val, char *arg, int base);
72extern int get_s16(__s16 *val, char *arg, int base);
73extern int get_u8(__u8 *val, char *arg, int base);
74extern int get_s8(__s8 *val, char *arg, int base);
75
76extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
77extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen);
78
79void invarg(char *, char *) __attribute__((noreturn));
80void duparg(char *, char *) __attribute__((noreturn));
81void duparg2(char *, char *) __attribute__((noreturn));
82int matches(char *arg, char *pattern);
83extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits);
84
85const char *dnet_ntop(int af, const void *addr, char *str, size_t len);
86int dnet_pton(int af, const char *src, void *addr);
87
88const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
89int ipx_pton(int af, const char *src, void *addr);
90
91extern int __iproute2_hz_internal;
92extern int __get_hz(void);
93
94static __inline__ int get_hz(void)
95{
96 if (__iproute2_hz_internal == 0)
97 __iproute2_hz_internal = __get_hz();
98 return __iproute2_hz_internal;
99}
100
101#endif /* __UTILS_H__ */