IP applet by Bastian Blank
No binary (build failed)
—
(log yok)
| @@ -22,8 +22,8 @@ include $(TOPDIR).config | |
| include $(TOPDIR)Rules.mak | |
| SUBDIRS:=applets archival archival/libunarchive console-tools \ | |
| editors fileutils findutils init miscutils modutils networking \ | |
| - | networking/udhcp procps loginutils shell shellutils sysklogd \ |
| - | textutils util-linux libbb libpwdgrp |
| + | networking/libiproute networking/udhcp procps loginutils shell \ |
| + | shellutils sysklogd textutils util-linux libbb libpwdgrp |
| all: do-it-all | |
| @@ -251,6 +251,9 @@ | |
| #ifdef CONFIG_INSMOD | |
| APPLET(insmod, insmod_main, _BB_DIR_SBIN, _BB_SUID_NEVER) | |
| #endif | |
| + | #ifdef CONFIG_IP |
| + | APPLET(ip, ip_main, _BB_DIR_BIN, _BB_SUID_NEVER) |
| + | #endif |
| #ifdef CONFIG_KILL | |
| APPLET(kill, kill_main, _BB_DIR_BIN, _BB_SUID_NEVER) | |
| #endif | |
| @@ -977,6 +977,11 @@ | |
| "\t-L\tLock to prevent simultaneous loads of a module\n" \ | |
| "\t-x\tdo not export externs" | |
| + | #define ip_trivial_usage \ |
| + | "not written yet" |
| + | #define ip_full_usage \ |
| + | "not written yet" |
| + | |
| #define kill_trivial_usage \ | |
| "[-signal] process-id [process-id ...]" | |
| #define kill_full_usage \ | |
| @@ -26,7 +26,8 @@ NETWORKING-y:= | |
| NETWORKING-$(CONFIG_HOSTNAME) += hostname.o | |
| NETWORKING-$(CONFIG_IFCONFIG) += ifconfig.o | |
| NETWORKING-$(CONFIG_IFUPDOWN) += ifupdown.o | |
| - | NETWORKING-$(CONFIG_NC) += nc.o |
| + | NETWORKING-$(CONFIG_IP) += ip.o |
| + | NETWORKING-$(CONFIG_NC) += nc.o |
| NETWORKING-$(CONFIG_NETSTAT) += netstat.o | |
| NETWORKING-$(CONFIG_NSLOOKUP) += nslookup.o | |
| NETWORKING-$(CONFIG_PING) += ping.o | |
| @@ -22,6 +22,13 @@ if [ "$CONFIG_IFUPDOWN" = "y" ]; then | |
| bool ' Enable support for IPv6 (requires ip command)' CONFIG_FEATURE_IFUPDOWN_IPV6 | |
| bool ' Enable support for IPX (requires ipx_interface command)' CONFIG_FEATURE_IFUPDOWN_IPX | |
| fi | |
| + | bool 'ip' CONFIG_IP |
| + | if [ "$CONFIG_IP" = "y" ]; then |
| + | bool ' address' CONFIG_FEATURE_IP_ADDRESS |
| + | bool ' link' CONFIG_FEATURE_IP_LINK |
| + | bool ' route' CONFIG_FEATURE_IP_ROUTE |
| + | bool ' tunnel' CONFIG_FEATURE_IP_TUNNEL |
| + | fi |
| bool 'nc' CONFIG_NC | |
| bool 'netstat' CONFIG_NETSTAT | |
| bool 'nslookup' CONFIG_NSLOOKUP | |
| @@ -0,0 +1,104 @@ | |
| + | /* |
| + | * ip.c "ip" utility frontend. |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | * |
| + | * Changes: |
| + | * |
| + | * Rani Assaf <[email protected]> 980929: resolve addresses |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <string.h> |
| + | |
| + | #include "./libiproute/utils.h" |
| + | #include "./libiproute/ip_common.h" |
| + | |
| + | #include "busybox.h" |
| + | |
| + | int preferred_family = AF_UNSPEC; |
| + | int oneline = 0; |
| + | char * _SL_ = NULL; |
| + | |
| + | int ip_main(int argc, char **argv) |
| + | { |
| + | char *basename; |
| + | |
| + | basename = strrchr(argv[0], '/'); |
| + | if (basename == NULL) |
| + | basename = argv[0]; |
| + | else |
| + | basename++; |
| + | |
| + | while (argc > 1) { |
| + | char *opt = argv[1]; |
| + | if (strcmp(opt,"--") == 0) { |
| + | argc--; argv++; |
| + | break; |
| + | } |
| + | if (opt[0] != '-') |
| + | break; |
| + | if (opt[1] == '-') |
| + | opt++; |
| + | if (matches(opt, "-family") == 0) { |
| + | argc--; |
| + | argv++; |
| + | if (strcmp(argv[1], "inet") == 0) |
| + | preferred_family = AF_INET; |
| + | else if (strcmp(argv[1], "inet6") == 0) |
| + | preferred_family = AF_INET6; |
| + | else if (strcmp(argv[1], "link") == 0) |
| + | preferred_family = AF_PACKET; |
| + | else |
| + | invarg(argv[1], "invalid protocol family"); |
| + | } else if (strcmp(opt, "-4") == 0) { |
| + | preferred_family = AF_INET; |
| + | } else if (strcmp(opt, "-6") == 0) { |
| + | preferred_family = AF_INET6; |
| + | } else if (strcmp(opt, "-0") == 0) { |
| + | preferred_family = AF_PACKET; |
| + | } else if (matches(opt, "-oneline") == 0) { |
| + | ++oneline; |
| + | } else { |
| + | fprintf(stderr, "Option \"%s\" is unknown, try \"ip -help\".\n", opt); |
| + | exit(-1); |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | |
| + | _SL_ = oneline ? "\\" : "\n" ; |
| + | |
| + | if (argc > 1) { |
| + | #ifdef CONFIG_FEATURE_IP_ADDRESS |
| + | if (matches(argv[1], "address") == 0) |
| + | return do_ipaddr(argc-2, argv+2); |
| + | #endif |
| + | #ifdef CONFIG_FEATURE_IP_ROUTE |
| + | if (matches(argv[1], "route") == 0) |
| + | return do_iproute(argc-2, argv+2); |
| + | #endif |
| + | #ifdef CONFIG_FEATURE_IP_LINK |
| + | if (matches(argv[1], "link") == 0) |
| + | return do_iplink(argc-2, argv+2); |
| + | #endif |
| + | #ifdef CONFIG_FEATURE_IP_TUNNEL |
| + | if (matches(argv[1], "tunnel") == 0 || |
| + | strcmp(argv[1], "tunl") == 0) |
| + | return do_iptunnel(argc-2, argv+2); |
| + | #endif |
| + | fprintf(stderr, "Object \"%s\" is unknown, try \"ip help\".\n", argv[1]); |
| + | exit(-1); |
| + | } |
| + | } |
| @@ -0,0 +1,30 @@ | |
| + | # Makefile for busybox |
| + | # |
| + | # Copyright (C) 1999-2002 Erik Andersen <[email protected]> |
| + | # |
| + | # This program is free software; you can redistribute it and/or modify |
| + | # it under the terms of the GNU General Public License as published by |
| + | # the Free Software Foundation; either version 2 of the License, or |
| + | # (at your option) any later version. |
| + | # |
| + | # This program is distributed in the hope that it will be useful, |
| + | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | # General Public License for more details. |
| + | # |
| + | # You should have received a copy of the GNU General Public License |
| + | # along with this program; if not, write to the Free Software |
| + | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + | # |
| + | |
| + | TOPDIR:= ../../ |
| + | LIBIPROUTE_DIR:=./ |
| + | include $(TOPDIR).config |
| + | include $(TOPDIR)Rules.mak |
| + | include Makefile.in |
| + | all: $(libraries-y) |
| + | -include $(TOPDIR).depend |
| + | |
| + | clean: |
| + | rm -f *.o *.a $(AR_TARGET) |
| + | |
| @@ -0,0 +1,43 @@ | |
| + | # Makefile for busybox |
| + | # |
| + | # Copyright (C) 1999-2002 by Erik Andersen <[email protected]> |
| + | # |
| + | # This program is free software; you can redistribute it and/or modify |
| + | # it under the terms of the GNU General Public License as published by |
| + | # the Free Software Foundation; either version 2 of the License, or |
| + | # (at your option) any later version. |
| + | # |
| + | # This program is distributed in the hope that it will be useful, |
| + | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| + | # General Public License for more details. |
| + | # |
| + | # You should have received a copy of the GNU General Public License |
| + | # along with this program; if not, write to the Free Software |
| + | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| + | # |
| + | |
| + | LIBIPROUTE_AR:=libiproute.a |
| + | ifndef $(LIBIPROUTE_DIR) |
| + | LIBIPROUTE_DIR:=$(TOPDIR)networking/libiproute/ |
| + | endif |
| + | |
| + | LIBIPROUTE-y:= \ |
| + | ipaddress.o \ |
| + | iplink.o \ |
| + | iproute.o \ |
| + | iptunnel.o \ |
| + | libnetlink.o \ |
| + | ll_addr.o \ |
| + | ll_map.o \ |
| + | ll_proto.o \ |
| + | ll_types.o \ |
| + | rt_names.o \ |
| + | rtm_map.o \ |
| + | utils.o |
| + | |
| + | libraries-y+=$(LIBIPROUTE_DIR)$(LIBIPROUTE_AR) |
| + | |
| + | $(LIBIPROUTE_DIR)$(LIBIPROUTE_AR): $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y)) |
| + | $(AR) -ro $@ $(patsubst %,$(LIBIPROUTE_DIR)%, $(LIBIPROUTE-y)) |
| + | |
| @@ -0,0 +1,20 @@ | |
| + | extern int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); |
| + | extern int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); |
| + | extern int print_neigh(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); |
| + | extern int ipaddr_list(int argc, char **argv); |
| + | extern int ipaddr_list_link(int argc, char **argv); |
| + | extern int iproute_monitor(int argc, char **argv); |
| + | extern void iplink_usage(void) __attribute__((noreturn)); |
| + | extern void iproute_reset_filter(void); |
| + | extern void ipaddr_reset_filter(int); |
| + | extern void ipneigh_reset_filter(void); |
| + | extern int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); |
| + | extern int do_ipaddr(int argc, char **argv); |
| + | extern int do_iproute(int argc, char **argv); |
| + | extern int do_iprule(int argc, char **argv); |
| + | extern int do_ipneigh(int argc, char **argv); |
| + | extern int do_iptunnel(int argc, char **argv); |
| + | extern int do_iplink(int argc, char **argv); |
| + | extern int do_ipmonitor(int argc, char **argv); |
| + | extern int do_multiaddr(int argc, char **argv); |
| + | extern int do_multiroute(int argc, char **argv); |
| @@ -0,0 +1,723 @@ | |
| + | /* |
| + | * ipaddress.c "ip address". |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | * Changes: |
| + | * Laszlo Valko <[email protected]> 990223: address label must be zero terminated |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/ioctl.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | #include <fnmatch.h> |
| + | |
| + | #include <linux/netdevice.h> |
| + | #include <linux/if_arp.h> |
| + | #include <linux/sockios.h> |
| + | |
| + | #include "rt_names.h" |
| + | #include "utils.h" |
| + | #include "ll_map.h" |
| + | #include "ip_common.h" |
| + | |
| + | #include "busybox.h" |
| + | |
| + | static struct |
| + | { |
| + | int ifindex; |
| + | int family; |
| + | int oneline; |
| + | int showqueue; |
| + | inet_prefix pfx; |
| + | int scope, scopemask; |
| + | int flags, flagmask; |
| + | int up; |
| + | char *label; |
| + | struct rtnl_handle *rth; |
| + | } filter; |
| + | |
| + | static int do_link; |
| + | |
| + | void print_link_flags(FILE *fp, unsigned flags, unsigned mdown) |
| + | { |
| + | fprintf(fp, "<"); |
| + | flags &= ~IFF_RUNNING; |
| + | #define _PF(f) if (flags&IFF_##f) { \ |
| + | flags &= ~IFF_##f ; \ |
| + | fprintf(fp, #f "%s", flags ? "," : ""); } |
| + | _PF(LOOPBACK); |
| + | _PF(BROADCAST); |
| + | _PF(POINTOPOINT); |
| + | _PF(MULTICAST); |
| + | _PF(NOARP); |
| + | #if 0 |
| + | _PF(ALLMULTI); |
| + | _PF(PROMISC); |
| + | _PF(MASTER); |
| + | _PF(SLAVE); |
| + | _PF(DEBUG); |
| + | _PF(DYNAMIC); |
| + | _PF(AUTOMEDIA); |
| + | _PF(PORTSEL); |
| + | _PF(NOTRAILERS); |
| + | #endif |
| + | _PF(UP); |
| + | #undef _PF |
| + | if (flags) |
| + | fprintf(fp, "%x", flags); |
| + | if (mdown) |
| + | fprintf(fp, ",M-DOWN"); |
| + | fprintf(fp, "> "); |
| + | } |
| + | |
| + | void print_queuelen(char *name) |
| + | { |
| + | struct ifreq ifr; |
| + | int s; |
| + | |
| + | s = socket(AF_INET, SOCK_STREAM, 0); |
| + | if (s < 0) |
| + | return; |
| + | |
| + | memset(&ifr, 0, sizeof(ifr)); |
| + | strcpy(ifr.ifr_name, name); |
| + | if (ioctl(s, SIOCGIFTXQLEN, &ifr) < 0) { |
| + | perror("SIOCGIFXQLEN"); |
| + | close(s); |
| + | return; |
| + | } |
| + | close(s); |
| + | |
| + | if (ifr.ifr_qlen) |
| + | printf("qlen %d", ifr.ifr_qlen); |
| + | } |
| + | |
| + | int print_linkinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| + | { |
| + | FILE *fp = (FILE*)arg; |
| + | struct ifinfomsg *ifi = NLMSG_DATA(n); |
| + | struct rtattr * tb[IFLA_MAX+1]; |
| + | int len = n->nlmsg_len; |
| + | unsigned m_flag = 0; |
| + | |
| + | if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) |
| + | return 0; |
| + | |
| + | len -= NLMSG_LENGTH(sizeof(*ifi)); |
| + | if (len < 0) |
| + | return -1; |
| + | |
| + | if (filter.ifindex && ifi->ifi_index != filter.ifindex) |
| + | return 0; |
| + | if (filter.up && !(ifi->ifi_flags&IFF_UP)) |
| + | return 0; |
| + | |
| + | memset(tb, 0, sizeof(tb)); |
| + | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); |
| + | if (tb[IFLA_IFNAME] == NULL) { |
| + | fprintf(stderr, "BUG: nil ifname\n"); |
| + | return -1; |
| + | } |
| + | if (filter.label && |
| + | (!filter.family || filter.family == AF_PACKET) && |
| + | fnmatch(filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)) |
| + | return 0; |
| + | |
| + | if (n->nlmsg_type == RTM_DELLINK) |
| + | fprintf(fp, "Deleted "); |
| + | |
| + | fprintf(fp, "%d: %s", ifi->ifi_index, |
| + | tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>"); |
| + | |
| + | if (tb[IFLA_LINK]) { |
| + | SPRINT_BUF(b1); |
| + | int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]); |
| + | if (iflink == 0) |
| + | fprintf(fp, "@NONE: "); |
| + | else { |
| + | fprintf(fp, "@%s: ", ll_idx_n2a(iflink, b1)); |
| + | m_flag = ll_index_to_flags(iflink); |
| + | m_flag = !(m_flag & IFF_UP); |
| + | } |
| + | } else { |
| + | fprintf(fp, ": "); |
| + | } |
| + | print_link_flags(fp, ifi->ifi_flags, m_flag); |
| + | |
| + | if (tb[IFLA_MTU]) |
| + | fprintf(fp, "mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); |
| + | if (tb[IFLA_QDISC]) |
| + | fprintf(fp, "qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC])); |
| + | #ifdef IFLA_MASTER |
| + | if (tb[IFLA_MASTER]) { |
| + | SPRINT_BUF(b1); |
| + | fprintf(fp, "master %s ", ll_idx_n2a(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); |
| + | } |
| + | #endif |
| + | if (filter.showqueue) |
| + | print_queuelen((char*)RTA_DATA(tb[IFLA_IFNAME])); |
| + | |
| + | if (!filter.family || filter.family == AF_PACKET) { |
| + | SPRINT_BUF(b1); |
| + | fprintf(fp, "%s", _SL_); |
| + | fprintf(fp, " link/%s ", ll_type_n2a(ifi->ifi_type, b1, sizeof(b1))); |
| + | |
| + | if (tb[IFLA_ADDRESS]) { |
| + | fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]), |
| + | RTA_PAYLOAD(tb[IFLA_ADDRESS]), |
| + | ifi->ifi_type, |
| + | b1, sizeof(b1))); |
| + | } |
| + | if (tb[IFLA_BROADCAST]) { |
| + | if (ifi->ifi_flags&IFF_POINTOPOINT) |
| + | fprintf(fp, " peer "); |
| + | else |
| + | fprintf(fp, " brd "); |
| + | fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]), |
| + | RTA_PAYLOAD(tb[IFLA_BROADCAST]), |
| + | ifi->ifi_type, |
| + | b1, sizeof(b1))); |
| + | } |
| + | } |
| + | fprintf(fp, "\n"); |
| + | fflush(fp); |
| + | return 0; |
| + | } |
| + | |
| + | int print_addrinfo(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| + | { |
| + | FILE *fp = (FILE*)arg; |
| + | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
| + | int len = n->nlmsg_len; |
| + | struct rtattr * rta_tb[IFA_MAX+1]; |
| + | char abuf[256]; |
| + | SPRINT_BUF(b1); |
| + | |
| + | if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR) |
| + | return 0; |
| + | len -= NLMSG_LENGTH(sizeof(*ifa)); |
| + | if (len < 0) { |
| + | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); |
| + | return -1; |
| + | } |
| + | |
| + | memset(rta_tb, 0, sizeof(rta_tb)); |
| + | parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); |
| + | |
| + | if (!rta_tb[IFA_LOCAL]) |
| + | rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS]; |
| + | if (!rta_tb[IFA_ADDRESS]) |
| + | rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL]; |
| + | |
| + | if (filter.ifindex && filter.ifindex != ifa->ifa_index) |
| + | return 0; |
| + | if ((filter.scope^ifa->ifa_scope)&filter.scopemask) |
| + | return 0; |
| + | if ((filter.flags^ifa->ifa_flags)&filter.flagmask) |
| + | return 0; |
| + | if (filter.label) { |
| + | SPRINT_BUF(b1); |
| + | const char *label; |
| + | if (rta_tb[IFA_LABEL]) |
| + | label = RTA_DATA(rta_tb[IFA_LABEL]); |
| + | else |
| + | label = ll_idx_n2a(ifa->ifa_index, b1); |
| + | if (fnmatch(filter.label, label, 0) != 0) |
| + | return 0; |
| + | } |
| + | if (filter.pfx.family) { |
| + | if (rta_tb[IFA_LOCAL]) { |
| + | inet_prefix dst; |
| + | memset(&dst, 0, sizeof(dst)); |
| + | dst.family = ifa->ifa_family; |
| + | memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL])); |
| + | if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | if (n->nlmsg_type == RTM_DELADDR) |
| + | fprintf(fp, "Deleted "); |
| + | |
| + | if (filter.oneline) |
| + | fprintf(fp, "%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index)); |
| + | if (ifa->ifa_family == AF_INET) |
| + | fprintf(fp, " inet "); |
| + | else if (ifa->ifa_family == AF_INET6) |
| + | fprintf(fp, " inet6 "); |
| + | else |
| + | fprintf(fp, " family %d ", ifa->ifa_family); |
| + | |
| + | if (rta_tb[IFA_LOCAL]) { |
| + | fprintf(fp, "%s", rt_addr_n2a(ifa->ifa_family, |
| + | RTA_PAYLOAD(rta_tb[IFA_LOCAL]), |
| + | RTA_DATA(rta_tb[IFA_LOCAL]), |
| + | abuf, sizeof(abuf))); |
| + | |
| + | if (rta_tb[IFA_ADDRESS] == NULL || |
| + | memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0) { |
| + | fprintf(fp, "/%d ", ifa->ifa_prefixlen); |
| + | } else { |
| + | fprintf(fp, " peer %s/%d ", |
| + | rt_addr_n2a(ifa->ifa_family, |
| + | RTA_PAYLOAD(rta_tb[IFA_ADDRESS]), |
| + | RTA_DATA(rta_tb[IFA_ADDRESS]), |
| + | abuf, sizeof(abuf)), |
| + | ifa->ifa_prefixlen); |
| + | } |
| + | } |
| + | |
| + | if (rta_tb[IFA_BROADCAST]) { |
| + | fprintf(fp, "brd %s ", |
| + | rt_addr_n2a(ifa->ifa_family, |
| + | RTA_PAYLOAD(rta_tb[IFA_BROADCAST]), |
| + | RTA_DATA(rta_tb[IFA_BROADCAST]), |
| + | abuf, sizeof(abuf))); |
| + | } |
| + | if (rta_tb[IFA_ANYCAST]) { |
| + | fprintf(fp, "any %s ", |
| + | rt_addr_n2a(ifa->ifa_family, |
| + | RTA_PAYLOAD(rta_tb[IFA_ANYCAST]), |
| + | RTA_DATA(rta_tb[IFA_ANYCAST]), |
| + | abuf, sizeof(abuf))); |
| + | } |
| + | fprintf(fp, "scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1, sizeof(b1))); |
| + | if (ifa->ifa_flags&IFA_F_SECONDARY) { |
| + | ifa->ifa_flags &= ~IFA_F_SECONDARY; |
| + | fprintf(fp, "secondary "); |
| + | } |
| + | if (ifa->ifa_flags&IFA_F_TENTATIVE) { |
| + | ifa->ifa_flags &= ~IFA_F_TENTATIVE; |
| + | fprintf(fp, "tentative "); |
| + | } |
| + | if (ifa->ifa_flags&IFA_F_DEPRECATED) { |
| + | ifa->ifa_flags &= ~IFA_F_DEPRECATED; |
| + | fprintf(fp, "deprecated "); |
| + | } |
| + | if (!(ifa->ifa_flags&IFA_F_PERMANENT)) { |
| + | fprintf(fp, "dynamic "); |
| + | } else |
| + | ifa->ifa_flags &= ~IFA_F_PERMANENT; |
| + | if (ifa->ifa_flags) |
| + | fprintf(fp, "flags %02x ", ifa->ifa_flags); |
| + | if (rta_tb[IFA_LABEL]) |
| + | fprintf(fp, "%s", (char*)RTA_DATA(rta_tb[IFA_LABEL])); |
| + | if (rta_tb[IFA_CACHEINFO]) { |
| + | struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]); |
| + | char buf[128]; |
| + | fprintf(fp, "%s", _SL_); |
| + | if (ci->ifa_valid == 0xFFFFFFFFU) |
| + | sprintf(buf, "valid_lft forever"); |
| + | else |
| + | sprintf(buf, "valid_lft %dsec", ci->ifa_valid); |
| + | if (ci->ifa_prefered == 0xFFFFFFFFU) |
| + | sprintf(buf+strlen(buf), " preferred_lft forever"); |
| + | else |
| + | sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered); |
| + | fprintf(fp, " %s", buf); |
| + | } |
| + | fprintf(fp, "\n"); |
| + | fflush(fp); |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | struct nlmsg_list |
| + | { |
| + | struct nlmsg_list *next; |
| + | struct nlmsghdr h; |
| + | }; |
| + | |
| + | int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo, FILE *fp) |
| + | { |
| + | for ( ;ainfo ; ainfo = ainfo->next) { |
| + | struct nlmsghdr *n = &ainfo->h; |
| + | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
| + | |
| + | if (n->nlmsg_type != RTM_NEWADDR) |
| + | continue; |
| + | |
| + | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa))) |
| + | return -1; |
| + | |
| + | if (ifa->ifa_index != ifindex || |
| + | (filter.family && filter.family != ifa->ifa_family)) |
| + | continue; |
| + | |
| + | print_addrinfo(NULL, n, fp); |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | int store_nlmsg(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| + | { |
| + | struct nlmsg_list **linfo = (struct nlmsg_list**)arg; |
| + | struct nlmsg_list *h; |
| + | struct nlmsg_list **lp; |
| + | |
| + | h = malloc(n->nlmsg_len+sizeof(void*)); |
| + | if (h == NULL) |
| + | return -1; |
| + | |
| + | memcpy(&h->h, n, n->nlmsg_len); |
| + | h->next = NULL; |
| + | |
| + | for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */; |
| + | *lp = h; |
| + | |
| + | ll_remember_index(who, n, NULL); |
| + | return 0; |
| + | } |
| + | |
| + | int ipaddr_list(int argc, char **argv) |
| + | { |
| + | struct nlmsg_list *linfo = NULL; |
| + | struct nlmsg_list *ainfo = NULL; |
| + | struct nlmsg_list *l; |
| + | struct rtnl_handle rth; |
| + | char *filter_dev = NULL; |
| + | int no_link = 0; |
| + | |
| + | ipaddr_reset_filter(oneline); |
| + | filter.showqueue = 1; |
| + | |
| + | if (filter.family == AF_UNSPEC) |
| + | filter.family = preferred_family; |
| + | |
| + | while (argc > 0) { |
| + | if (strcmp(*argv, "to") == 0) { |
| + | NEXT_ARG(); |
| + | get_prefix(&filter.pfx, *argv, filter.family); |
| + | if (filter.family == AF_UNSPEC) |
| + | filter.family = filter.pfx.family; |
| + | } else if (strcmp(*argv, "scope") == 0) { |
| + | int scope = 0; |
| + | NEXT_ARG(); |
| + | filter.scopemask = -1; |
| + | if (rtnl_rtscope_a2n(&scope, *argv)) { |
| + | if (strcmp(*argv, "all") != 0) |
| + | invarg("invalid \"scope\"\n", *argv); |
| + | scope = RT_SCOPE_NOWHERE; |
| + | filter.scopemask = 0; |
| + | } |
| + | filter.scope = scope; |
| + | } else if (strcmp(*argv, "up") == 0) { |
| + | filter.up = 1; |
| + | } else if (strcmp(*argv, "label") == 0) { |
| + | NEXT_ARG(); |
| + | filter.label = *argv; |
| + | } else { |
| + | if (strcmp(*argv, "dev") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if (filter_dev) |
| + | duparg2("dev", *argv); |
| + | filter_dev = *argv; |
| + | } |
| + | argv++; argc--; |
| + | } |
| + | |
| + | if (rtnl_open(&rth, 0) < 0) |
| + | exit(1); |
| + | |
| + | if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK) < 0) { |
| + | perror("Cannot send dump request"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) { |
| + | fprintf(stderr, "Dump terminated\n"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (filter_dev) { |
| + | filter.ifindex = ll_name_to_index(filter_dev); |
| + | if (filter.ifindex <= 0) { |
| + | fprintf(stderr, "Device \"%s\" does not exist.\n", filter_dev); |
| + | return -1; |
| + | } |
| + | } |
| + | |
| + | if (filter.family != AF_PACKET) { |
| + | if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) { |
| + | perror("Cannot send dump request"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (rtnl_dump_filter(&rth, store_nlmsg, &ainfo, NULL, NULL) < 0) { |
| + | fprintf(stderr, "Dump terminated\n"); |
| + | exit(1); |
| + | } |
| + | } |
| + | |
| + | |
| + | if (filter.family && filter.family != AF_PACKET) { |
| + | struct nlmsg_list **lp; |
| + | lp=&linfo; |
| + | |
| + | if (filter.oneline) |
| + | no_link = 1; |
| + | |
| + | while ((l=*lp)!=NULL) { |
| + | int ok = 0; |
| + | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); |
| + | struct nlmsg_list *a; |
| + | |
| + | for (a=ainfo; a; a=a->next) { |
| + | struct nlmsghdr *n = &a->h; |
| + | struct ifaddrmsg *ifa = NLMSG_DATA(n); |
| + | |
| + | if (ifa->ifa_index != ifi->ifi_index || |
| + | (filter.family && filter.family != ifa->ifa_family)) |
| + | continue; |
| + | if ((filter.scope^ifa->ifa_scope)&filter.scopemask) |
| + | continue; |
| + | if ((filter.flags^ifa->ifa_flags)&filter.flagmask) |
| + | continue; |
| + | if (filter.pfx.family || filter.label) { |
| + | struct rtattr *tb[IFA_MAX+1]; |
| + | memset(tb, 0, sizeof(tb)); |
| + | parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n)); |
| + | if (!tb[IFA_LOCAL]) |
| + | tb[IFA_LOCAL] = tb[IFA_ADDRESS]; |
| + | |
| + | if (filter.pfx.family && tb[IFA_LOCAL]) { |
| + | inet_prefix dst; |
| + | memset(&dst, 0, sizeof(dst)); |
| + | dst.family = ifa->ifa_family; |
| + | memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL])); |
| + | if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bitlen)) |
| + | continue; |
| + | } |
| + | if (filter.label) { |
| + | SPRINT_BUF(b1); |
| + | const char *label; |
| + | if (tb[IFA_LABEL]) |
| + | label = RTA_DATA(tb[IFA_LABEL]); |
| + | else |
| + | label = ll_idx_n2a(ifa->ifa_index, b1); |
| + | if (fnmatch(filter.label, label, 0) != 0) |
| + | continue; |
| + | } |
| + | } |
| + | |
| + | ok = 1; |
| + | break; |
| + | } |
| + | if (!ok) |
| + | *lp = l->next; |
| + | else |
| + | lp = &l->next; |
| + | } |
| + | } |
| + | |
| + | for (l=linfo; l; l = l->next) { |
| + | if (no_link || print_linkinfo(NULL, &l->h, stdout) == 0) { |
| + | struct ifinfomsg *ifi = NLMSG_DATA(&l->h); |
| + | if (filter.family != AF_PACKET) |
| + | print_selected_addrinfo(ifi->ifi_index, ainfo, stdout); |
| + | } |
| + | fflush(stdout); |
| + | } |
| + | |
| + | exit(0); |
| + | } |
| + | |
| + | int ipaddr_list_link(int argc, char **argv) |
| + | { |
| + | preferred_family = AF_PACKET; |
| + | do_link = 1; |
| + | return ipaddr_list(argc, argv); |
| + | } |
| + | |
| + | void ipaddr_reset_filter(int oneline) |
| + | { |
| + | memset(&filter, 0, sizeof(filter)); |
| + | filter.oneline = oneline; |
| + | } |
| + | |
| + | int default_scope(inet_prefix *lcl) |
| + | { |
| + | if (lcl->family == AF_INET) { |
| + | if (lcl->bytelen >= 1 && *(__u8*)&lcl->data == 127) |
| + | return RT_SCOPE_HOST; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | int ipaddr_modify(int cmd, int argc, char **argv) |
| + | { |
| + | struct rtnl_handle rth; |
| + | struct { |
| + | struct nlmsghdr n; |
| + | struct ifaddrmsg ifa; |
| + | char buf[256]; |
| + | } req; |
| + | char *d = NULL; |
| + | char *l = NULL; |
| + | inet_prefix lcl; |
| + | inet_prefix peer; |
| + | int local_len = 0; |
| + | int peer_len = 0; |
| + | int brd_len = 0; |
| + | int any_len = 0; |
| + | int scoped = 0; |
| + | |
| + | memset(&req, 0, sizeof(req)); |
| + | |
| + | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); |
| + | req.n.nlmsg_flags = NLM_F_REQUEST; |
| + | req.n.nlmsg_type = cmd; |
| + | req.ifa.ifa_family = preferred_family; |
| + | |
| + | while (argc > 0) { |
| + | if (strcmp(*argv, "peer") == 0 || |
| + | strcmp(*argv, "remote") == 0) { |
| + | NEXT_ARG(); |
| + | |
| + | if (peer_len) |
| + | duparg("peer", *argv); |
| + | get_prefix(&peer, *argv, req.ifa.ifa_family); |
| + | peer_len = peer.bytelen; |
| + | if (req.ifa.ifa_family == AF_UNSPEC) |
| + | req.ifa.ifa_family = peer.family; |
| + | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen); |
| + | req.ifa.ifa_prefixlen = peer.bitlen; |
| + | } else if (matches(*argv, "broadcast") == 0 || |
| + | strcmp(*argv, "brd") == 0) { |
| + | inet_prefix addr; |
| + | NEXT_ARG(); |
| + | if (brd_len) |
| + | duparg("broadcast", *argv); |
| + | if (strcmp(*argv, "+") == 0) |
| + | brd_len = -1; |
| + | else if (strcmp(*argv, "-") == 0) |
| + | brd_len = -2; |
| + | else { |
| + | get_addr(&addr, *argv, req.ifa.ifa_family); |
| + | if (req.ifa.ifa_family == AF_UNSPEC) |
| + | req.ifa.ifa_family = addr.family; |
| + | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen); |
| + | brd_len = addr.bytelen; |
| + | } |
| + | } else if (strcmp(*argv, "anycast") == 0) { |
| + | inet_prefix addr; |
| + | NEXT_ARG(); |
| + | if (any_len) |
| + | duparg("anycast", *argv); |
| + | get_addr(&addr, *argv, req.ifa.ifa_family); |
| + | if (req.ifa.ifa_family == AF_UNSPEC) |
| + | req.ifa.ifa_family = addr.family; |
| + | addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen); |
| + | any_len = addr.bytelen; |
| + | } else if (strcmp(*argv, "scope") == 0) { |
| + | int scope = 0; |
| + | NEXT_ARG(); |
| + | if (rtnl_rtscope_a2n(&scope, *argv)) |
| + | invarg(*argv, "invalid scope value."); |
| + | req.ifa.ifa_scope = scope; |
| + | scoped = 1; |
| + | } else if (strcmp(*argv, "dev") == 0) { |
| + | NEXT_ARG(); |
| + | d = *argv; |
| + | } else if (strcmp(*argv, "label") == 0) { |
| + | NEXT_ARG(); |
| + | l = *argv; |
| + | addattr_l(&req.n, sizeof(req), IFA_LABEL, l, strlen(l)+1); |
| + | } else { |
| + | if (strcmp(*argv, "local") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if (local_len) |
| + | duparg2("local", *argv); |
| + | get_prefix(&lcl, *argv, req.ifa.ifa_family); |
| + | if (req.ifa.ifa_family == AF_UNSPEC) |
| + | req.ifa.ifa_family = lcl.family; |
| + | addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen); |
| + | local_len = lcl.bytelen; |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | if (d == NULL) { |
| + | fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); |
| + | return -1; |
| + | } |
| + | if (l && matches(d, l) != 0) { |
| + | fprintf(stderr, "\"dev\" (%s) must match \"label\" (%s).\n", d, l); |
| + | exit(1); |
| + | } |
| + | |
| + | if (peer_len == 0 && local_len && cmd != RTM_DELADDR) { |
| + | peer = lcl; |
| + | addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen); |
| + | } |
| + | if (req.ifa.ifa_prefixlen == 0) |
| + | req.ifa.ifa_prefixlen = lcl.bitlen; |
| + | |
| + | if (brd_len < 0 && cmd != RTM_DELADDR) { |
| + | inet_prefix brd; |
| + | int i; |
| + | if (req.ifa.ifa_family != AF_INET) { |
| + | fprintf(stderr, "Broadcast can be set only for IPv4 addresses\n"); |
| + | return -1; |
| + | } |
| + | brd = peer; |
| + | if (brd.bitlen <= 30) { |
| + | for (i=31; i>=brd.bitlen; i--) { |
| + | if (brd_len == -1) |
| + | brd.data[0] |= htonl(1<<(31-i)); |
| + | else |
| + | brd.data[0] &= ~htonl(1<<(31-i)); |
| + | } |
| + | addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen); |
| + | brd_len = brd.bytelen; |
| + | } |
| + | } |
| + | if (!scoped && cmd != RTM_DELADDR) |
| + | req.ifa.ifa_scope = default_scope(&lcl); |
| + | |
| + | if (rtnl_open(&rth, 0) < 0) |
| + | exit(1); |
| + | |
| + | ll_init_map(&rth); |
| + | |
| + | if ((req.ifa.ifa_index = ll_name_to_index(d)) == 0) { |
| + | fprintf(stderr, "Cannot find device \"%s\"\n", d); |
| + | return -1; |
| + | } |
| + | |
| + | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) |
| + | exit(2); |
| + | |
| + | exit(0); |
| + | } |
| + | |
| + | int do_ipaddr(int argc, char **argv) |
| + | { |
| + | if (argc < 1) |
| + | return ipaddr_list(0, NULL); |
| + | if (matches(*argv, "add") == 0) |
| + | return ipaddr_modify(RTM_NEWADDR, argc-1, argv+1); |
| + | if (matches(*argv, "delete") == 0) |
| + | return ipaddr_modify(RTM_DELADDR, argc-1, argv+1); |
| + | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
| + | || matches(*argv, "lst") == 0) |
| + | return ipaddr_list(argc-1, argv+1); |
| + | fprintf(stderr, "Command \"%s\" is unknown, try \"ip address help\".\n", *argv); |
| + | exit(-1); |
| + | } |
| @@ -0,0 +1,349 @@ | |
| + | /* |
| + | * iplink.c "ip link". |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <errno.h> |
| + | #include <sys/socket.h> |
| + | #include <linux/if.h> |
| + | #include <linux/if_packet.h> |
| + | #include <linux/if_ether.h> |
| + | #include <linux/sockios.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | #include <sys/ioctl.h> |
| + | #include <linux/sockios.h> |
| + | |
| + | #include "rt_names.h" |
| + | #include "utils.h" |
| + | #include "ip_common.h" |
| + | |
| + | #include "busybox.h" |
| + | |
| + | static int on_off(char *msg) |
| + | { |
| + | fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); |
| + | return -1; |
| + | } |
| + | |
| + | static int get_ctl_fd(void) |
| + | { |
| + | int s_errno; |
| + | int fd; |
| + | |
| + | fd = socket(PF_INET, SOCK_DGRAM, 0); |
| + | if (fd >= 0) |
| + | return fd; |
| + | s_errno = errno; |
| + | fd = socket(PF_PACKET, SOCK_DGRAM, 0); |
| + | if (fd >= 0) |
| + | return fd; |
| + | fd = socket(PF_INET6, SOCK_DGRAM, 0); |
| + | if (fd >= 0) |
| + | return fd; |
| + | errno = s_errno; |
| + | perror("Cannot create control socket"); |
| + | return -1; |
| + | } |
| + | |
| + | static int do_chflags(char *dev, __u32 flags, __u32 mask) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | strcpy(ifr.ifr_name, dev); |
| + | fd = get_ctl_fd(); |
| + | if (fd < 0) |
| + | return -1; |
| + | err = ioctl(fd, SIOCGIFFLAGS, &ifr); |
| + | if (err) { |
| + | perror("SIOCGIFFLAGS"); |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | if ((ifr.ifr_flags^flags)&mask) { |
| + | ifr.ifr_flags &= ~mask; |
| + | ifr.ifr_flags |= mask&flags; |
| + | err = ioctl(fd, SIOCSIFFLAGS, &ifr); |
| + | if (err) |
| + | perror("SIOCSIFFLAGS"); |
| + | } |
| + | close(fd); |
| + | return err; |
| + | } |
| + | |
| + | static int do_changename(char *dev, char *newdev) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | strcpy(ifr.ifr_name, dev); |
| + | strcpy(ifr.ifr_newname, newdev); |
| + | fd = get_ctl_fd(); |
| + | if (fd < 0) |
| + | return -1; |
| + | err = ioctl(fd, SIOCSIFNAME, &ifr); |
| + | if (err) { |
| + | perror("SIOCSIFNAME"); |
| + | close(fd); |
| + | return -1; |
| + | } |
| + | close(fd); |
| + | return err; |
| + | } |
| + | |
| + | static int set_qlen(char *dev, int qlen) |
| + | { |
| + | struct ifreq ifr; |
| + | int s; |
| + | |
| + | s = get_ctl_fd(); |
| + | if (s < 0) |
| + | return -1; |
| + | |
| + | memset(&ifr, 0, sizeof(ifr)); |
| + | strcpy(ifr.ifr_name, dev); |
| + | ifr.ifr_qlen = qlen; |
| + | if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { |
| + | perror("SIOCSIFXQLEN"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | close(s); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static int set_mtu(char *dev, int mtu) |
| + | { |
| + | struct ifreq ifr; |
| + | int s; |
| + | |
| + | s = get_ctl_fd(); |
| + | if (s < 0) |
| + | return -1; |
| + | |
| + | memset(&ifr, 0, sizeof(ifr)); |
| + | strcpy(ifr.ifr_name, dev); |
| + | ifr.ifr_mtu = mtu; |
| + | if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { |
| + | perror("SIOCSIFMTU"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | close(s); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static int get_address(char *dev, int *htype) |
| + | { |
| + | struct ifreq ifr; |
| + | struct sockaddr_ll me; |
| + | int alen; |
| + | int s; |
| + | |
| + | s = socket(PF_PACKET, SOCK_DGRAM, 0); |
| + | if (s < 0) { |
| + | perror("socket(PF_PACKET)"); |
| + | return -1; |
| + | } |
| + | |
| + | memset(&ifr, 0, sizeof(ifr)); |
| + | strcpy(ifr.ifr_name, dev); |
| + | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { |
| + | perror("SIOCGIFINDEX"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | |
| + | memset(&me, 0, sizeof(me)); |
| + | me.sll_family = AF_PACKET; |
| + | me.sll_ifindex = ifr.ifr_ifindex; |
| + | me.sll_protocol = htons(ETH_P_LOOP); |
| + | if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { |
| + | perror("bind"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | |
| + | alen = sizeof(me); |
| + | if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { |
| + | perror("getsockname"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | close(s); |
| + | *htype = me.sll_hatype; |
| + | return me.sll_halen; |
| + | } |
| + | |
| + | static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) |
| + | { |
| + | int alen; |
| + | |
| + | memset(ifr, 0, sizeof(*ifr)); |
| + | strcpy(ifr->ifr_name, dev); |
| + | ifr->ifr_hwaddr.sa_family = hatype; |
| + | alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); |
| + | if (alen < 0) |
| + | return -1; |
| + | if (alen != halen) { |
| + | fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); |
| + | return -1; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static int set_address(struct ifreq *ifr, int brd) |
| + | { |
| + | int s; |
| + | |
| + | s = get_ctl_fd(); |
| + | if (s < 0) |
| + | return -1; |
| + | if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { |
| + | perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); |
| + | close(s); |
| + | return -1; |
| + | } |
| + | close(s); |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static int do_set(int argc, char **argv) |
| + | { |
| + | char *dev = NULL; |
| + | __u32 mask = 0; |
| + | __u32 flags = 0; |
| + | int qlen = -1; |
| + | int mtu = -1; |
| + | char *newaddr = NULL; |
| + | char *newbrd = NULL; |
| + | struct ifreq ifr0, ifr1; |
| + | char *newname = NULL; |
| + | int htype, halen; |
| + | |
| + | while (argc > 0) { |
| + | if (strcmp(*argv, "up") == 0) { |
| + | mask |= IFF_UP; |
| + | flags |= IFF_UP; |
| + | } else if (strcmp(*argv, "down") == 0) { |
| + | mask |= IFF_UP; |
| + | flags &= ~IFF_UP; |
| + | } else if (strcmp(*argv, "name") == 0) { |
| + | NEXT_ARG(); |
| + | newname = *argv; |
| + | } else if (strcmp(*argv, "mtu") == 0) { |
| + | NEXT_ARG(); |
| + | if (mtu != -1) |
| + | duparg("mtu", *argv); |
| + | if (get_integer(&mtu, *argv, 0)) |
| + | invarg("Invalid \"mtu\" value\n", *argv); |
| + | } else if (strcmp(*argv, "multicast") == 0) { |
| + | NEXT_ARG(); |
| + | mask |= IFF_MULTICAST; |
| + | if (strcmp(*argv, "on") == 0) { |
| + | flags |= IFF_MULTICAST; |
| + | } else if (strcmp(*argv, "off") == 0) { |
| + | flags &= ~IFF_MULTICAST; |
| + | } else |
| + | return on_off("multicast"); |
| + | } else if (strcmp(*argv, "arp") == 0) { |
| + | NEXT_ARG(); |
| + | mask |= IFF_NOARP; |
| + | if (strcmp(*argv, "on") == 0) { |
| + | flags &= ~IFF_NOARP; |
| + | } else if (strcmp(*argv, "off") == 0) { |
| + | flags |= IFF_NOARP; |
| + | } else |
| + | return on_off("noarp"); |
| + | } else { |
| + | if (strcmp(*argv, "dev") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if (dev) |
| + | duparg2("dev", *argv); |
| + | dev = *argv; |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | |
| + | if (!dev) { |
| + | fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); |
| + | exit(-1); |
| + | } |
| + | |
| + | if (newaddr || newbrd) { |
| + | halen = get_address(dev, &htype); |
| + | if (halen < 0) |
| + | return -1; |
| + | if (newaddr) { |
| + | if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) |
| + | return -1; |
| + | } |
| + | if (newbrd) { |
| + | if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) |
| + | return -1; |
| + | } |
| + | } |
| + | |
| + | if (newname && strcmp(dev, newname)) { |
| + | if (do_changename(dev, newname) < 0) |
| + | return -1; |
| + | dev = newname; |
| + | } |
| + | if (qlen != -1) { |
| + | if (set_qlen(dev, qlen) < 0) |
| + | return -1; |
| + | } |
| + | if (mtu != -1) { |
| + | if (set_mtu(dev, mtu) < 0) |
| + | return -1; |
| + | } |
| + | if (newaddr || newbrd) { |
| + | if (newbrd) { |
| + | if (set_address(&ifr1, 1) < 0) |
| + | return -1; |
| + | } |
| + | if (newaddr) { |
| + | if (set_address(&ifr0, 0) < 0) |
| + | return -1; |
| + | } |
| + | } |
| + | if (mask) |
| + | return do_chflags(dev, flags, mask); |
| + | return 0; |
| + | } |
| + | |
| + | int do_iplink(int argc, char **argv) |
| + | { |
| + | if (argc > 0) { |
| + | if (matches(*argv, "set") == 0) |
| + | return do_set(argc-1, argv+1); |
| + | if (matches(*argv, "show") == 0 || |
| + | matches(*argv, "lst") == 0 || |
| + | matches(*argv, "list") == 0) |
| + | return ipaddr_list_link(argc-1, argv+1); |
| + | } else |
| + | return ipaddr_list_link(0, NULL); |
| + | |
| + | fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); |
| + | exit(-1); |
| + | } |
| @@ -0,0 +1,674 @@ | |
| + | /* |
| + | * iproute.c "ip route". |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | * |
| + | * Changes: |
| + | * |
| + | * Rani Assaf <[email protected]> 980929: resolve addresses |
| + | * Kunihiro Ishiguro <[email protected]> 001102: rtnh_ifindex was not initialized |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <string.h> |
| + | #include <sys/time.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <netinet/ip.h> |
| + | #include <arpa/inet.h> |
| + | #include <linux/in_route.h> |
| + | |
| + | #include "rt_names.h" |
| + | #include "utils.h" |
| + | #include "ip_common.h" |
| + | |
| + | #include "busybox.h" |
| + | |
| + | #ifndef RTAX_RTTVAR |
| + | #define RTAX_RTTVAR RTAX_HOPS |
| + | #endif |
| + | |
| + | |
| + | static struct |
| + | { |
| + | int tb; |
| + | int flushp; |
| + | int flushe; |
| + | struct rtnl_handle *rth; |
| + | int protocol, protocolmask; |
| + | int scope, scopemask; |
| + | int type, typemask; |
| + | int tos, tosmask; |
| + | int iif, iifmask; |
| + | int oif, oifmask; |
| + | int realm, realmmask; |
| + | inet_prefix rprefsrc; |
| + | inet_prefix rvia; |
| + | inet_prefix rdst; |
| + | inet_prefix mdst; |
| + | inet_prefix rsrc; |
| + | inet_prefix msrc; |
| + | } filter; |
| + | |
| + | int print_route(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| + | { |
| + | FILE *fp = (FILE*)arg; |
| + | struct rtmsg *r = NLMSG_DATA(n); |
| + | int len = n->nlmsg_len; |
| + | struct rtattr * tb[RTA_MAX+1]; |
| + | char abuf[256]; |
| + | int host_len = -1; |
| + | SPRINT_BUF(b1); |
| + | |
| + | |
| + | if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) { |
| + | fprintf(stderr, "Not a route: %08x %08x %08x\n", |
| + | n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); |
| + | return 0; |
| + | } |
| + | len -= NLMSG_LENGTH(sizeof(*r)); |
| + | if (len < 0) { |
| + | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); |
| + | return -1; |
| + | } |
| + | |
| + | if (r->rtm_family == AF_INET6) |
| + | host_len = 128; |
| + | else if (r->rtm_family == AF_INET) |
| + | host_len = 32; |
| + | |
| + | if (r->rtm_family == AF_INET6) { |
| + | if (filter.tb) { |
| + | if (filter.tb < 0) { |
| + | if (!(r->rtm_flags&RTM_F_CLONED)) |
| + | return 0; |
| + | } else { |
| + | if (r->rtm_flags&RTM_F_CLONED) |
| + | return 0; |
| + | if (filter.tb == RT_TABLE_LOCAL) { |
| + | if (r->rtm_type != RTN_LOCAL) |
| + | return 0; |
| + | } else if (filter.tb == RT_TABLE_MAIN) { |
| + | if (r->rtm_type == RTN_LOCAL) |
| + | return 0; |
| + | } else { |
| + | return 0; |
| + | } |
| + | } |
| + | } |
| + | } else { |
| + | if (filter.tb > 0 && filter.tb != r->rtm_table) |
| + | return 0; |
| + | } |
| + | if (filter.rdst.family && |
| + | (r->rtm_family != filter.rdst.family || filter.rdst.bitlen > r->rtm_dst_len)) |
| + | return 0; |
| + | if (filter.mdst.family && |
| + | (r->rtm_family != filter.mdst.family || |
| + | (filter.mdst.bitlen >= 0 && filter.mdst.bitlen < r->rtm_dst_len))) |
| + | return 0; |
| + | if (filter.rsrc.family && |
| + | (r->rtm_family != filter.rsrc.family || filter.rsrc.bitlen > r->rtm_src_len)) |
| + | return 0; |
| + | if (filter.msrc.family && |
| + | (r->rtm_family != filter.msrc.family || |
| + | (filter.msrc.bitlen >= 0 && filter.msrc.bitlen < r->rtm_src_len))) |
| + | return 0; |
| + | |
| + | memset(tb, 0, sizeof(tb)); |
| + | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
| + | |
| + | if (n->nlmsg_type == RTM_DELROUTE) |
| + | fprintf(fp, "Deleted "); |
| + | if (r->rtm_type != RTN_UNICAST && !filter.type) |
| + | fprintf(fp, "%s ", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); |
| + | |
| + | if (tb[RTA_DST]) { |
| + | if (r->rtm_dst_len != host_len) { |
| + | fprintf(fp, "%s/%u ", rt_addr_n2a(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_DST]), |
| + | RTA_DATA(tb[RTA_DST]), |
| + | abuf, sizeof(abuf)), |
| + | r->rtm_dst_len |
| + | ); |
| + | } else { |
| + | fprintf(fp, "%s ", format_host(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_DST]), |
| + | RTA_DATA(tb[RTA_DST]), |
| + | abuf, sizeof(abuf)) |
| + | ); |
| + | } |
| + | } else if (r->rtm_dst_len) { |
| + | fprintf(fp, "0/%d ", r->rtm_dst_len); |
| + | } else { |
| + | fprintf(fp, "default "); |
| + | } |
| + | if (tb[RTA_SRC]) { |
| + | if (r->rtm_src_len != host_len) { |
| + | fprintf(fp, "from %s/%u ", rt_addr_n2a(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_SRC]), |
| + | RTA_DATA(tb[RTA_SRC]), |
| + | abuf, sizeof(abuf)), |
| + | r->rtm_src_len |
| + | ); |
| + | } else { |
| + | fprintf(fp, "from %s ", format_host(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_SRC]), |
| + | RTA_DATA(tb[RTA_SRC]), |
| + | abuf, sizeof(abuf)) |
| + | ); |
| + | } |
| + | } else if (r->rtm_src_len) { |
| + | fprintf(fp, "from 0/%u ", r->rtm_src_len); |
| + | } |
| + | if (tb[RTA_GATEWAY] && filter.rvia.bitlen != host_len) { |
| + | fprintf(fp, "via %s ", |
| + | format_host(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_GATEWAY]), |
| + | RTA_DATA(tb[RTA_GATEWAY]), |
| + | abuf, sizeof(abuf))); |
| + | } |
| + | if (tb[RTA_OIF] && filter.oifmask != -1) |
| + | fprintf(fp, "dev %s ", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_OIF]))); |
| + | |
| + | if (tb[RTA_PREFSRC] && filter.rprefsrc.bitlen != host_len) { |
| + | /* Do not use format_host(). It is our local addr |
| + | and symbolic name will not be useful. |
| + | */ |
| + | fprintf(fp, " src %s ", |
| + | rt_addr_n2a(r->rtm_family, |
| + | RTA_PAYLOAD(tb[RTA_PREFSRC]), |
| + | RTA_DATA(tb[RTA_PREFSRC]), |
| + | abuf, sizeof(abuf))); |
| + | } |
| + | if (tb[RTA_PRIORITY]) |
| + | fprintf(fp, " metric %d ", *(__u32*)RTA_DATA(tb[RTA_PRIORITY])); |
| + | if (r->rtm_family == AF_INET6) { |
| + | struct rta_cacheinfo *ci = NULL; |
| + | if (tb[RTA_CACHEINFO]) |
| + | ci = RTA_DATA(tb[RTA_CACHEINFO]); |
| + | if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) { |
| + | static int hz; |
| + | if (!hz) |
| + | hz = get_hz(); |
| + | if (r->rtm_flags & RTM_F_CLONED) |
| + | fprintf(fp, "%s cache ", _SL_); |
| + | if (ci->rta_expires) |
| + | fprintf(fp, " expires %dsec", ci->rta_expires/hz); |
| + | if (ci->rta_error != 0) |
| + | fprintf(fp, " error %d", ci->rta_error); |
| + | } else if (ci) { |
| + | if (ci->rta_error != 0) |
| + | fprintf(fp, " error %d", ci->rta_error); |
| + | } |
| + | } |
| + | if (tb[RTA_IIF] && filter.iifmask != -1) { |
| + | fprintf(fp, " iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF]))); |
| + | } |
| + | fprintf(fp, "\n"); |
| + | fflush(fp); |
| + | return 0; |
| + | } |
| + | |
| + | int iproute_modify(int cmd, unsigned flags, int argc, char **argv) |
| + | { |
| + | struct rtnl_handle rth; |
| + | struct { |
| + | struct nlmsghdr n; |
| + | struct rtmsg r; |
| + | char buf[1024]; |
| + | } req; |
| + | char mxbuf[256]; |
| + | struct rtattr * mxrta = (void*)mxbuf; |
| + | unsigned mxlock = 0; |
| + | char *d = NULL; |
| + | int gw_ok = 0; |
| + | int dst_ok = 0; |
| + | //int nhs_ok = 0; |
| + | //int scope_ok = 0; |
| + | //int table_ok = 0; |
| + | int proto_ok = 0; |
| + | int type_ok = 0; |
| + | |
| + | memset(&req, 0, sizeof(req)); |
| + | |
| + | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); |
| + | req.n.nlmsg_flags = NLM_F_REQUEST|flags; |
| + | req.n.nlmsg_type = cmd; |
| + | req.r.rtm_family = preferred_family; |
| + | req.r.rtm_table = RT_TABLE_MAIN; |
| + | req.r.rtm_scope = RT_SCOPE_NOWHERE; |
| + | |
| + | if (cmd != RTM_DELROUTE) { |
| + | req.r.rtm_protocol = RTPROT_BOOT; |
| + | req.r.rtm_scope = RT_SCOPE_UNIVERSE; |
| + | req.r.rtm_type = RTN_UNICAST; |
| + | } |
| + | |
| + | mxrta->rta_type = RTA_METRICS; |
| + | mxrta->rta_len = RTA_LENGTH(0); |
| + | |
| + | while (argc > 0) { |
| + | if (strcmp(*argv, "src") == 0) { |
| + | inet_prefix addr; |
| + | NEXT_ARG(); |
| + | get_addr(&addr, *argv, req.r.rtm_family); |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = addr.family; |
| + | addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen); |
| + | } else if (strcmp(*argv, "via") == 0) { |
| + | inet_prefix addr; |
| + | gw_ok = 1; |
| + | NEXT_ARG(); |
| + | get_addr(&addr, *argv, req.r.rtm_family); |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = addr.family; |
| + | addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen); |
| + | } else if (strcmp(*argv, "mtu") == 0) { |
| + | unsigned mtu; |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "lock") == 0) { |
| + | mxlock |= (1<<RTAX_MTU); |
| + | NEXT_ARG(); |
| + | } |
| + | if (get_unsigned(&mtu, *argv, 0)) |
| + | invarg("\"mtu\" value is invalid\n", *argv); |
| + | rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu); |
| + | } else if (matches(*argv, "protocol") == 0) { |
| + | int prot; |
| + | NEXT_ARG(); |
| + | if (rtnl_rtprot_a2n(&prot, *argv)) |
| + | invarg("\"protocol\" value is invalid\n", *argv); |
| + | req.r.rtm_protocol = prot; |
| + | proto_ok =1; |
| + | } else if (strcmp(*argv, "dev") == 0 || |
| + | strcmp(*argv, "oif") == 0) { |
| + | NEXT_ARG(); |
| + | d = *argv; |
| + | } else { |
| + | int type; |
| + | inet_prefix dst; |
| + | |
| + | if (strcmp(*argv, "to") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if ((**argv < '0' || **argv > '9') && |
| + | rtnl_rtntype_a2n(&type, *argv) == 0) { |
| + | NEXT_ARG(); |
| + | req.r.rtm_type = type; |
| + | type_ok = 1; |
| + | } |
| + | |
| + | if (dst_ok) |
| + | duparg2("to", *argv); |
| + | get_prefix(&dst, *argv, req.r.rtm_family); |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = dst.family; |
| + | req.r.rtm_dst_len = dst.bitlen; |
| + | dst_ok = 1; |
| + | if (dst.bytelen) |
| + | addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen); |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | |
| + | if (rtnl_open(&rth, 0) < 0) |
| + | exit(1); |
| + | |
| + | if (mxrta->rta_len > RTA_LENGTH(0)) { |
| + | if (mxlock) |
| + | rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock); |
| + | addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta)); |
| + | } |
| + | |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = AF_INET; |
| + | |
| + | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) |
| + | exit(2); |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | static int rtnl_rtcache_request(struct rtnl_handle *rth, int family) |
| + | { |
| + | struct { |
| + | struct nlmsghdr nlh; |
| + | struct rtmsg rtm; |
| + | } req; |
| + | struct sockaddr_nl nladdr; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | memset(&req, 0, sizeof(req)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | |
| + | req.nlh.nlmsg_len = sizeof(req); |
| + | req.nlh.nlmsg_type = RTM_GETROUTE; |
| + | req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST; |
| + | req.nlh.nlmsg_pid = 0; |
| + | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; |
| + | req.rtm.rtm_family = family; |
| + | req.rtm.rtm_flags |= RTM_F_CLONED; |
| + | |
| + | return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
| + | } |
| + | |
| + | static int iproute_list(int argc, char **argv) |
| + | { |
| + | int do_ipv6 = preferred_family; |
| + | struct rtnl_handle rth; |
| + | char *id = NULL; |
| + | char *od = NULL; |
| + | |
| + | iproute_reset_filter(); |
| + | filter.tb = RT_TABLE_MAIN; |
| + | |
| + | while (argc > 0) { |
| + | if (matches(*argv, "protocol") == 0) { |
| + | int prot = 0; |
| + | NEXT_ARG(); |
| + | filter.protocolmask = -1; |
| + | if (rtnl_rtprot_a2n(&prot, *argv)) { |
| + | if (strcmp(*argv, "all") != 0) |
| + | invarg("invalid \"protocol\"\n", *argv); |
| + | prot = 0; |
| + | filter.protocolmask = 0; |
| + | } |
| + | filter.protocol = prot; |
| + | } else if (strcmp(*argv, "dev") == 0 || |
| + | strcmp(*argv, "oif") == 0) { |
| + | NEXT_ARG(); |
| + | od = *argv; |
| + | } else if (strcmp(*argv, "iif") == 0) { |
| + | NEXT_ARG(); |
| + | id = *argv; |
| + | } else if (matches(*argv, "from") == 0) { |
| + | NEXT_ARG(); |
| + | if (matches(*argv, "root") == 0) { |
| + | NEXT_ARG(); |
| + | get_prefix(&filter.rsrc, *argv, do_ipv6); |
| + | } else if (matches(*argv, "match") == 0) { |
| + | NEXT_ARG(); |
| + | get_prefix(&filter.msrc, *argv, do_ipv6); |
| + | } else { |
| + | if (matches(*argv, "exact") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | get_prefix(&filter.msrc, *argv, do_ipv6); |
| + | filter.rsrc = filter.msrc; |
| + | } |
| + | } else { |
| + | if (matches(*argv, "to") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if (matches(*argv, "root") == 0) { |
| + | NEXT_ARG(); |
| + | get_prefix(&filter.rdst, *argv, do_ipv6); |
| + | } else if (matches(*argv, "match") == 0) { |
| + | NEXT_ARG(); |
| + | get_prefix(&filter.mdst, *argv, do_ipv6); |
| + | } else { |
| + | if (matches(*argv, "exact") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | get_prefix(&filter.mdst, *argv, do_ipv6); |
| + | filter.rdst = filter.mdst; |
| + | } |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | |
| + | if (do_ipv6 == AF_UNSPEC && filter.tb) |
| + | do_ipv6 = AF_INET; |
| + | |
| + | if (rtnl_open(&rth, 0) < 0) |
| + | exit(1); |
| + | |
| + | ll_init_map(&rth); |
| + | |
| + | if (id || od) { |
| + | int idx; |
| + | |
| + | if (id) { |
| + | if ((idx = ll_name_to_index(id)) == 0) { |
| + | fprintf(stderr, "Cannot find device \"%s\"\n", id); |
| + | return -1; |
| + | } |
| + | filter.iif = idx; |
| + | filter.iifmask = -1; |
| + | } |
| + | if (od) { |
| + | if ((idx = ll_name_to_index(od)) == 0) { |
| + | fprintf(stderr, "Cannot find device \"%s\"\n", od); |
| + | return -1; |
| + | } |
| + | filter.oif = idx; |
| + | filter.oifmask = -1; |
| + | } |
| + | } |
| + | |
| + | if (filter.tb != -1) { |
| + | if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) { |
| + | perror("Cannot send dump request"); |
| + | exit(1); |
| + | } |
| + | } else { |
| + | if (rtnl_rtcache_request(&rth, do_ipv6) < 0) { |
| + | perror("Cannot send dump request"); |
| + | exit(1); |
| + | } |
| + | } |
| + | |
| + | if (rtnl_dump_filter(&rth, print_route, stdout, NULL, NULL) < 0) { |
| + | fprintf(stderr, "Dump terminated\n"); |
| + | exit(1); |
| + | } |
| + | |
| + | exit(0); |
| + | } |
| + | |
| + | |
| + | int iproute_get(int argc, char **argv) |
| + | { |
| + | struct rtnl_handle rth; |
| + | struct { |
| + | struct nlmsghdr n; |
| + | struct rtmsg r; |
| + | char buf[1024]; |
| + | } req; |
| + | char *idev = NULL; |
| + | char *odev = NULL; |
| + | int connected = 0; |
| + | int from_ok = 0; |
| + | |
| + | memset(&req, 0, sizeof(req)); |
| + | |
| + | iproute_reset_filter(); |
| + | |
| + | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); |
| + | req.n.nlmsg_flags = NLM_F_REQUEST; |
| + | req.n.nlmsg_type = RTM_GETROUTE; |
| + | req.r.rtm_family = preferred_family; |
| + | req.r.rtm_table = 0; |
| + | req.r.rtm_protocol = 0; |
| + | req.r.rtm_scope = 0; |
| + | req.r.rtm_type = 0; |
| + | req.r.rtm_src_len = 0; |
| + | req.r.rtm_dst_len = 0; |
| + | req.r.rtm_tos = 0; |
| + | |
| + | while (argc > 0) { |
| + | if (matches(*argv, "from") == 0) { |
| + | inet_prefix addr; |
| + | NEXT_ARG(); |
| + | from_ok = 1; |
| + | get_prefix(&addr, *argv, req.r.rtm_family); |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = addr.family; |
| + | if (addr.bytelen) |
| + | addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen); |
| + | req.r.rtm_src_len = addr.bitlen; |
| + | } else if (matches(*argv, "iif") == 0) { |
| + | NEXT_ARG(); |
| + | idev = *argv; |
| + | } else if (matches(*argv, "oif") == 0 || |
| + | strcmp(*argv, "dev") == 0) { |
| + | NEXT_ARG(); |
| + | odev = *argv; |
| + | } else if (matches(*argv, "notify") == 0) { |
| + | req.r.rtm_flags |= RTM_F_NOTIFY; |
| + | } else if (matches(*argv, "connected") == 0) { |
| + | connected = 1; |
| + | } else { |
| + | inet_prefix addr; |
| + | if (strcmp(*argv, "to") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | get_prefix(&addr, *argv, req.r.rtm_family); |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = addr.family; |
| + | if (addr.bytelen) |
| + | addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen); |
| + | req.r.rtm_dst_len = addr.bitlen; |
| + | } |
| + | argc--; argv++; |
| + | } |
| + | |
| + | if (req.r.rtm_dst_len == 0) { |
| + | fprintf(stderr, "need at least destination address\n"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (rtnl_open(&rth, 0) < 0) |
| + | exit(1); |
| + | |
| + | ll_init_map(&rth); |
| + | |
| + | if (idev || odev) { |
| + | int idx; |
| + | |
| + | if (idev) { |
| + | if ((idx = ll_name_to_index(idev)) == 0) { |
| + | fprintf(stderr, "Cannot find device \"%s\"\n", idev); |
| + | return -1; |
| + | } |
| + | addattr32(&req.n, sizeof(req), RTA_IIF, idx); |
| + | } |
| + | if (odev) { |
| + | if ((idx = ll_name_to_index(odev)) == 0) { |
| + | fprintf(stderr, "Cannot find device \"%s\"\n", odev); |
| + | return -1; |
| + | } |
| + | addattr32(&req.n, sizeof(req), RTA_OIF, idx); |
| + | } |
| + | } |
| + | |
| + | if (req.r.rtm_family == AF_UNSPEC) |
| + | req.r.rtm_family = AF_INET; |
| + | |
| + | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) |
| + | exit(2); |
| + | |
| + | if (connected && !from_ok) { |
| + | struct rtmsg *r = NLMSG_DATA(&req.n); |
| + | int len = req.n.nlmsg_len; |
| + | struct rtattr * tb[RTA_MAX+1]; |
| + | |
| + | if (print_route(NULL, &req.n, (void*)stdout) < 0) { |
| + | fprintf(stderr, "An error :-)\n"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (req.n.nlmsg_type != RTM_NEWROUTE) { |
| + | fprintf(stderr, "Not a route?\n"); |
| + | return -1; |
| + | } |
| + | len -= NLMSG_LENGTH(sizeof(*r)); |
| + | if (len < 0) { |
| + | fprintf(stderr, "Wrong len %d\n", len); |
| + | return -1; |
| + | } |
| + | |
| + | memset(tb, 0, sizeof(tb)); |
| + | parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); |
| + | |
| + | if (tb[RTA_PREFSRC]) { |
| + | tb[RTA_PREFSRC]->rta_type = RTA_SRC; |
| + | r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]); |
| + | } else if (!tb[RTA_SRC]) { |
| + | fprintf(stderr, "Failed to connect the route\n"); |
| + | return -1; |
| + | } |
| + | if (!odev && tb[RTA_OIF]) |
| + | tb[RTA_OIF]->rta_type = 0; |
| + | if (tb[RTA_GATEWAY]) |
| + | tb[RTA_GATEWAY]->rta_type = 0; |
| + | if (!idev && tb[RTA_IIF]) |
| + | tb[RTA_IIF]->rta_type = 0; |
| + | req.n.nlmsg_flags = NLM_F_REQUEST; |
| + | req.n.nlmsg_type = RTM_GETROUTE; |
| + | |
| + | if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) |
| + | exit(2); |
| + | } |
| + | |
| + | if (print_route(NULL, &req.n, (void*)stdout) < 0) { |
| + | fprintf(stderr, "An error :-)\n"); |
| + | exit(1); |
| + | } |
| + | |
| + | exit(0); |
| + | } |
| + | |
| + | void iproute_reset_filter() |
| + | { |
| + | memset(&filter, 0, sizeof(filter)); |
| + | filter.mdst.bitlen = -1; |
| + | filter.msrc.bitlen = -1; |
| + | } |
| + | |
| + | int do_iproute(int argc, char **argv) |
| + | { |
| + | if (argc < 1) |
| + | return iproute_list(0, NULL); |
| + | |
| + | if (matches(*argv, "add") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_EXCL, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_REPLACE, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "replace") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_REPLACE, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "prepend") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "append") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_CREATE|NLM_F_APPEND, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "test") == 0) |
| + | return iproute_modify(RTM_NEWROUTE, NLM_F_EXCL, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "delete") == 0) |
| + | return iproute_modify(RTM_DELROUTE, 0, |
| + | argc-1, argv+1); |
| + | if (matches(*argv, "list") == 0 || matches(*argv, "show") == 0 |
| + | || matches(*argv, "lst") == 0) |
| + | return iproute_list(argc-1, argv+1); |
| + | if (matches(*argv, "get") == 0) |
| + | return iproute_get(argc-1, argv+1); |
| + | fprintf(stderr, "Command \"%s\" is unknown, try \"ip route help\".\n", *argv); |
| + | exit(-1); |
| + | } |
| + | |
| @@ -0,0 +1,548 @@ | |
| + | /* |
| + | * iptunnel.c "ip tunnel" |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | * |
| + | * Changes: |
| + | * |
| + | * Rani Assaf <[email protected]> 980929: resolve addresses |
| + | * Rani Assaf <[email protected]> 980930: do not allow key for ipip/sit |
| + | * Phil Karn <[email protected]> 990408: "pmtudisc" flag |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <string.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <netinet/ip.h> |
| + | #include <arpa/inet.h> |
| + | |
| + | #include <linux/if.h> |
| + | #include <linux/if_arp.h> |
| + | #include <linux/if_tunnel.h> |
| + | |
| + | #include "rt_names.h" |
| + | #include "utils.h" |
| + | |
| + | #include "busybox.h" |
| + | |
| + | static int do_ioctl_get_ifindex(char *dev) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | strcpy(ifr.ifr_name, dev); |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, SIOCGIFINDEX, &ifr); |
| + | if (err) { |
| + | perror("ioctl"); |
| + | return 0; |
| + | } |
| + | close(fd); |
| + | return ifr.ifr_ifindex; |
| + | } |
| + | |
| + | static int do_ioctl_get_iftype(char *dev) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | strcpy(ifr.ifr_name, dev); |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, SIOCGIFHWADDR, &ifr); |
| + | if (err) { |
| + | perror("ioctl"); |
| + | return -1; |
| + | } |
| + | close(fd); |
| + | return ifr.ifr_addr.sa_family; |
| + | } |
| + | |
| + | |
| + | static char * do_ioctl_get_ifname(int idx) |
| + | { |
| + | static struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | ifr.ifr_ifindex = idx; |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, SIOCGIFNAME, &ifr); |
| + | if (err) { |
| + | perror("ioctl"); |
| + | return NULL; |
| + | } |
| + | close(fd); |
| + | return ifr.ifr_name; |
| + | } |
| + | |
| + | |
| + | |
| + | static int do_get_ioctl(char *basedev, struct ip_tunnel_parm *p) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | strcpy(ifr.ifr_name, basedev); |
| + | ifr.ifr_ifru.ifru_data = (void*)p; |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, SIOCGETTUNNEL, &ifr); |
| + | if (err) |
| + | perror("ioctl"); |
| + | close(fd); |
| + | return err; |
| + | } |
| + | |
| + | static int do_add_ioctl(int cmd, char *basedev, struct ip_tunnel_parm *p) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | if (cmd == SIOCCHGTUNNEL && p->name[0]) |
| + | strcpy(ifr.ifr_name, p->name); |
| + | else |
| + | strcpy(ifr.ifr_name, basedev); |
| + | ifr.ifr_ifru.ifru_data = (void*)p; |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, cmd, &ifr); |
| + | if (err) |
| + | perror("ioctl"); |
| + | close(fd); |
| + | return err; |
| + | } |
| + | |
| + | static int do_del_ioctl(char *basedev, struct ip_tunnel_parm *p) |
| + | { |
| + | struct ifreq ifr; |
| + | int fd; |
| + | int err; |
| + | |
| + | if (p->name[0]) |
| + | strcpy(ifr.ifr_name, p->name); |
| + | else |
| + | strcpy(ifr.ifr_name, basedev); |
| + | ifr.ifr_ifru.ifru_data = (void*)p; |
| + | fd = socket(AF_INET, SOCK_DGRAM, 0); |
| + | err = ioctl(fd, SIOCDELTUNNEL, &ifr); |
| + | if (err) |
| + | perror("ioctl"); |
| + | close(fd); |
| + | return err; |
| + | } |
| + | |
| + | static int parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p) |
| + | { |
| + | int count = 0; |
| + | char medium[IFNAMSIZ]; |
| + | |
| + | memset(p, 0, sizeof(*p)); |
| + | memset(&medium, 0, sizeof(medium)); |
| + | |
| + | p->iph.version = 4; |
| + | p->iph.ihl = 5; |
| + | #ifndef IP_DF |
| + | #define IP_DF 0x4000 /* Flag: "Don't Fragment" */ |
| + | #endif |
| + | p->iph.frag_off = htons(IP_DF); |
| + | |
| + | while (argc > 0) { |
| + | if (strcmp(*argv, "mode") == 0) { |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "ipip") == 0 || |
| + | strcmp(*argv, "ip/ip") == 0) { |
| + | if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) { |
| + | fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); |
| + | exit(-1); |
| + | } |
| + | p->iph.protocol = IPPROTO_IPIP; |
| + | } else if (strcmp(*argv, "gre") == 0 || |
| + | strcmp(*argv, "gre/ip") == 0) { |
| + | if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) { |
| + | fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); |
| + | exit(-1); |
| + | } |
| + | p->iph.protocol = IPPROTO_GRE; |
| + | } else if (strcmp(*argv, "sit") == 0 || |
| + | strcmp(*argv, "ipv6/ip") == 0) { |
| + | if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) { |
| + | fprintf(stderr,"You managed to ask for more than one tunnel mode.\n"); |
| + | exit(-1); |
| + | } |
| + | p->iph.protocol = IPPROTO_IPV6; |
| + | } else { |
| + | fprintf(stderr,"Cannot guess tunnel mode.\n"); |
| + | exit(-1); |
| + | } |
| + | } else if (strcmp(*argv, "key") == 0) { |
| + | unsigned uval; |
| + | NEXT_ARG(); |
| + | p->i_flags |= GRE_KEY; |
| + | p->o_flags |= GRE_KEY; |
| + | if (strchr(*argv, '.')) |
| + | p->i_key = p->o_key = get_addr32(*argv); |
| + | else { |
| + | if (get_unsigned(&uval, *argv, 0)<0) { |
| + | fprintf(stderr, "invalid value of \"key\"\n"); |
| + | exit(-1); |
| + | } |
| + | p->i_key = p->o_key = htonl(uval); |
| + | } |
| + | } else if (strcmp(*argv, "ikey") == 0) { |
| + | unsigned uval; |
| + | NEXT_ARG(); |
| + | p->i_flags |= GRE_KEY; |
| + | if (strchr(*argv, '.')) |
| + | p->o_key = get_addr32(*argv); |
| + | else { |
| + | if (get_unsigned(&uval, *argv, 0)<0) { |
| + | fprintf(stderr, "invalid value of \"ikey\"\n"); |
| + | exit(-1); |
| + | } |
| + | p->i_key = htonl(uval); |
| + | } |
| + | } else if (strcmp(*argv, "okey") == 0) { |
| + | unsigned uval; |
| + | NEXT_ARG(); |
| + | p->o_flags |= GRE_KEY; |
| + | if (strchr(*argv, '.')) |
| + | p->o_key = get_addr32(*argv); |
| + | else { |
| + | if (get_unsigned(&uval, *argv, 0)<0) { |
| + | fprintf(stderr, "invalid value of \"okey\"\n"); |
| + | exit(-1); |
| + | } |
| + | p->o_key = htonl(uval); |
| + | } |
| + | } else if (strcmp(*argv, "seq") == 0) { |
| + | p->i_flags |= GRE_SEQ; |
| + | p->o_flags |= GRE_SEQ; |
| + | } else if (strcmp(*argv, "iseq") == 0) { |
| + | p->i_flags |= GRE_SEQ; |
| + | } else if (strcmp(*argv, "oseq") == 0) { |
| + | p->o_flags |= GRE_SEQ; |
| + | } else if (strcmp(*argv, "csum") == 0) { |
| + | p->i_flags |= GRE_CSUM; |
| + | p->o_flags |= GRE_CSUM; |
| + | } else if (strcmp(*argv, "icsum") == 0) { |
| + | p->i_flags |= GRE_CSUM; |
| + | } else if (strcmp(*argv, "ocsum") == 0) { |
| + | p->o_flags |= GRE_CSUM; |
| + | } else if (strcmp(*argv, "nopmtudisc") == 0) { |
| + | p->iph.frag_off = 0; |
| + | } else if (strcmp(*argv, "pmtudisc") == 0) { |
| + | p->iph.frag_off = htons(IP_DF); |
| + | } else if (strcmp(*argv, "remote") == 0) { |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "any")) |
| + | p->iph.daddr = get_addr32(*argv); |
| + | } else if (strcmp(*argv, "local") == 0) { |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "any")) |
| + | p->iph.saddr = get_addr32(*argv); |
| + | } else if (strcmp(*argv, "dev") == 0) { |
| + | NEXT_ARG(); |
| + | strncpy(medium, *argv, IFNAMSIZ-1); |
| + | } else if (strcmp(*argv, "ttl") == 0) { |
| + | unsigned uval; |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "inherit") != 0) { |
| + | if (get_unsigned(&uval, *argv, 0)) |
| + | invarg("invalid TTL\n", *argv); |
| + | if (uval > 255) |
| + | invarg("TTL must be <=255\n", *argv); |
| + | p->iph.ttl = uval; |
| + | } |
| + | } else if (strcmp(*argv, "tos") == 0 || |
| + | matches(*argv, "dsfield") == 0) { |
| + | __u32 uval; |
| + | NEXT_ARG(); |
| + | if (strcmp(*argv, "inherit") != 0) { |
| + | if (rtnl_dsfield_a2n(&uval, *argv)) |
| + | invarg("bad TOS value", *argv); |
| + | p->iph.tos = uval; |
| + | } else |
| + | p->iph.tos = 1; |
| + | } else { |
| + | if (strcmp(*argv, "name") == 0) { |
| + | NEXT_ARG(); |
| + | } |
| + | if (p->name[0]) |
| + | duparg2("name", *argv); |
| + | strncpy(p->name, *argv, IFNAMSIZ); |
| + | if (cmd == SIOCCHGTUNNEL && count == 0) { |
| + | struct ip_tunnel_parm old_p; |
| + | memset(&old_p, 0, sizeof(old_p)); |
| + | if (do_get_ioctl(*argv, &old_p)) |
| + | return -1; |
| + | *p = old_p; |
| + | } |
| + | } |
| + | count++; |
| + | argc--; argv++; |
| + | } |
| + | |
| + | |
| + | if (p->iph.protocol == 0) { |
| + | if (memcmp(p->name, "gre", 3) == 0) |
| + | p->iph.protocol = IPPROTO_GRE; |
| + | else if (memcmp(p->name, "ipip", 4) == 0) |
| + | p->iph.protocol = IPPROTO_IPIP; |
| + | else if (memcmp(p->name, "sit", 3) == 0) |
| + | p->iph.protocol = IPPROTO_IPV6; |
| + | } |
| + | |
| + | if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) { |
| + | if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) { |
| + | fprintf(stderr, "Keys are not allowed with ipip and sit.\n"); |
| + | return -1; |
| + | } |
| + | } |
| + | |
| + | if (medium[0]) { |
| + | p->link = do_ioctl_get_ifindex(medium); |
| + | if (p->link == 0) |
| + | return -1; |
| + | } |
| + | |
| + | if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { |
| + | p->i_key = p->iph.daddr; |
| + | p->i_flags |= GRE_KEY; |
| + | } |
| + | if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) { |
| + | p->o_key = p->iph.daddr; |
| + | p->o_flags |= GRE_KEY; |
| + | } |
| + | if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) { |
| + | fprintf(stderr, "Broadcast tunnel requires a source address.\n"); |
| + | return -1; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static int do_add(int cmd, int argc, char **argv) |
| + | { |
| + | struct ip_tunnel_parm p; |
| + | |
| + | if (parse_args(argc, argv, cmd, &p) < 0) |
| + | return -1; |
| + | |
| + | if (p.iph.ttl && p.iph.frag_off == 0) { |
| + | fprintf(stderr, "ttl != 0 and noptmudisc are incompatible\n"); |
| + | return -1; |
| + | } |
| + | |
| + | switch (p.iph.protocol) { |
| + | case IPPROTO_IPIP: |
| + | return do_add_ioctl(cmd, "tunl0", &p); |
| + | case IPPROTO_GRE: |
| + | return do_add_ioctl(cmd, "gre0", &p); |
| + | case IPPROTO_IPV6: |
| + | return do_add_ioctl(cmd, "sit0", &p); |
| + | default: |
| + | fprintf(stderr, "cannot determine tunnel mode (ipip, gre or sit)\n"); |
| + | return -1; |
| + | } |
| + | return -1; |
| + | } |
| + | |
| + | int do_del(int argc, char **argv) |
| + | { |
| + | struct ip_tunnel_parm p; |
| + | |
| + | if (parse_args(argc, argv, SIOCDELTUNNEL, &p) < 0) |
| + | return -1; |
| + | |
| + | switch (p.iph.protocol) { |
| + | case IPPROTO_IPIP: |
| + | return do_del_ioctl("tunl0", &p); |
| + | case IPPROTO_GRE: |
| + | return do_del_ioctl("gre0", &p); |
| + | case IPPROTO_IPV6: |
| + | return do_del_ioctl("sit0", &p); |
| + | default: |
| + | return do_del_ioctl(p.name, &p); |
| + | } |
| + | return -1; |
| + | } |
| + | |
| + | void print_tunnel(struct ip_tunnel_parm *p) |
| + | { |
| + | char s1[256]; |
| + | char s2[256]; |
| + | char s3[64]; |
| + | char s4[64]; |
| + | |
| + | format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1)); |
| + | format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2)); |
| + | inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3)); |
| + | inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4)); |
| + | |
| + | printf("%s: %s/ip remote %s local %s ", |
| + | p->name, |
| + | p->iph.protocol == IPPROTO_IPIP ? "ip" : |
| + | (p->iph.protocol == IPPROTO_GRE ? "gre" : |
| + | (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")), |
| + | p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any"); |
| + | if (p->link) { |
| + | char *n = do_ioctl_get_ifname(p->link); |
| + | if (n) |
| + | printf(" dev %s ", n); |
| + | } |
| + | if (p->iph.ttl) |
| + | printf(" ttl %d ", p->iph.ttl); |
| + | else |
| + | printf(" ttl inherit "); |
| + | if (p->iph.tos) { |
| + | SPRINT_BUF(b1); |
| + | printf(" tos"); |
| + | if (p->iph.tos&1) |
| + | printf(" inherit"); |
| + | if (p->iph.tos&~1) |
| + | printf("%c%s ", p->iph.tos&1 ? '/' : ' ', |
| + | rtnl_dsfield_n2a(p->iph.tos&~1, b1, sizeof(b1))); |
| + | } |
| + | if (!(p->iph.frag_off&htons(IP_DF))) |
| + | printf(" nopmtudisc"); |
| + | |
| + | if ((p->i_flags&GRE_KEY) && (p->o_flags&GRE_KEY) && p->o_key == p->i_key) |
| + | printf(" key %s", s3); |
| + | else if ((p->i_flags|p->o_flags)&GRE_KEY) { |
| + | if (p->i_flags&GRE_KEY) |
| + | printf(" ikey %s ", s3); |
| + | if (p->o_flags&GRE_KEY) |
| + | printf(" okey %s ", s4); |
| + | } |
| + | |
| + | if (p->i_flags&GRE_SEQ) |
| + | printf("%s Drop packets out of sequence.\n", _SL_); |
| + | if (p->i_flags&GRE_CSUM) |
| + | printf("%s Checksum in received packet is required.", _SL_); |
| + | if (p->o_flags&GRE_SEQ) |
| + | printf("%s Sequence packets on output.", _SL_); |
| + | if (p->o_flags&GRE_CSUM) |
| + | printf("%s Checksum output packets.", _SL_); |
| + | } |
| + | |
| + | static int do_tunnels_list(struct ip_tunnel_parm *p) |
| + | { |
| + | char name[IFNAMSIZ]; |
| + | unsigned long rx_bytes, rx_packets, rx_errs, rx_drops, |
| + | rx_fifo, rx_frame, |
| + | tx_bytes, tx_packets, tx_errs, tx_drops, |
| + | tx_fifo, tx_colls, tx_carrier, rx_multi; |
| + | int type; |
| + | struct ip_tunnel_parm p1; |
| + | |
| + | char buf[512]; |
| + | FILE *fp = fopen("/proc/net/dev", "r"); |
| + | if (fp == NULL) { |
| + | perror("fopen"); |
| + | return -1; |
| + | } |
| + | |
| + | fgets(buf, sizeof(buf), fp); |
| + | fgets(buf, sizeof(buf), fp); |
| + | |
| + | while (fgets(buf, sizeof(buf), fp) != NULL) { |
| + | char *ptr; |
| + | buf[sizeof(buf) - 1] = 0; |
| + | if ((ptr = strchr(buf, ':')) == NULL || |
| + | (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) { |
| + | fprintf(stderr, "Wrong format of /proc/net/dev. Sorry.\n"); |
| + | return -1; |
| + | } |
| + | if (sscanf(ptr, "%ld%ld%ld%ld%ld%ld%ld%*d%ld%ld%ld%ld%ld%ld%ld", |
| + | &rx_bytes, &rx_packets, &rx_errs, &rx_drops, |
| + | &rx_fifo, &rx_frame, &rx_multi, |
| + | &tx_bytes, &tx_packets, &tx_errs, &tx_drops, |
| + | &tx_fifo, &tx_colls, &tx_carrier) != 14) |
| + | continue; |
| + | if (p->name[0] && strcmp(p->name, name)) |
| + | continue; |
| + | type = do_ioctl_get_iftype(name); |
| + | if (type == -1) { |
| + | fprintf(stderr, "Failed to get type of [%s]\n", name); |
| + | continue; |
| + | } |
| + | if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT) |
| + | continue; |
| + | memset(&p1, 0, sizeof(p1)); |
| + | if (do_get_ioctl(name, &p1)) |
| + | continue; |
| + | if ((p->link && p1.link != p->link) || |
| + | (p->name[0] && strcmp(p1.name, p->name)) || |
| + | (p->iph.daddr && p1.iph.daddr != p->iph.daddr) || |
| + | (p->iph.saddr && p1.iph.saddr != p->iph.saddr) || |
| + | (p->i_key && p1.i_key != p->i_key)) |
| + | continue; |
| + | print_tunnel(&p1); |
| + | printf("\n"); |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | static int do_show(int argc, char **argv) |
| + | { |
| + | int err; |
| + | struct ip_tunnel_parm p; |
| + | |
| + | if (parse_args(argc, argv, SIOCGETTUNNEL, &p) < 0) |
| + | return -1; |
| + | |
| + | switch (p.iph.protocol) { |
| + | case IPPROTO_IPIP: |
| + | err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p); |
| + | break; |
| + | case IPPROTO_GRE: |
| + | err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p); |
| + | break; |
| + | case IPPROTO_IPV6: |
| + | err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p); |
| + | break; |
| + | default: |
| + | do_tunnels_list(&p); |
| + | return 0; |
| + | } |
| + | if (err) |
| + | return -1; |
| + | |
| + | print_tunnel(&p); |
| + | printf("\n"); |
| + | return 0; |
| + | } |
| + | |
| + | int do_iptunnel(int argc, char **argv) |
| + | { |
| + | if (argc > 0) { |
| + | if (matches(*argv, "add") == 0) |
| + | return do_add(SIOCADDTUNNEL, argc-1, argv+1); |
| + | if (matches(*argv, "change") == 0) |
| + | return do_add(SIOCCHGTUNNEL, argc-1, argv+1); |
| + | if (matches(*argv, "del") == 0) |
| + | return do_del(argc-1, argv+1); |
| + | if (matches(*argv, "show") == 0 || |
| + | matches(*argv, "lst") == 0 || |
| + | matches(*argv, "list") == 0) |
| + | return do_show(argc-1, argv+1); |
| + | } else |
| + | return do_show(0, NULL); |
| + | |
| + | fprintf(stderr, "Command \"%s\" is unknown, try \"ip tunnel help\".\n", *argv); |
| + | exit(-1); |
| + | } |
| @@ -0,0 +1,521 @@ | |
| + | /* |
| + | * libnetlink.c RTnetlink service routines. |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <net/if_arp.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <string.h> |
| + | #include <errno.h> |
| + | #include <time.h> |
| + | #include <sys/uio.h> |
| + | |
| + | #include "libnetlink.h" |
| + | |
| + | void rtnl_close(struct rtnl_handle *rth) |
| + | { |
| + | close(rth->fd); |
| + | } |
| + | |
| + | int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) |
| + | { |
| + | int addr_len; |
| + | |
| + | memset(rth, 0, sizeof(rth)); |
| + | |
| + | rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
| + | if (rth->fd < 0) { |
| + | perror("Cannot open netlink socket"); |
| + | return -1; |
| + | } |
| + | |
| + | memset(&rth->local, 0, sizeof(rth->local)); |
| + | rth->local.nl_family = AF_NETLINK; |
| + | rth->local.nl_groups = subscriptions; |
| + | |
| + | if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { |
| + | perror("Cannot bind netlink socket"); |
| + | return -1; |
| + | } |
| + | addr_len = sizeof(rth->local); |
| + | if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { |
| + | perror("Cannot getsockname"); |
| + | return -1; |
| + | } |
| + | if (addr_len != sizeof(rth->local)) { |
| + | fprintf(stderr, "Wrong address length %d\n", addr_len); |
| + | return -1; |
| + | } |
| + | if (rth->local.nl_family != AF_NETLINK) { |
| + | fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family); |
| + | return -1; |
| + | } |
| + | rth->seq = time(NULL); |
| + | return 0; |
| + | } |
| + | |
| + | int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) |
| + | { |
| + | struct { |
| + | struct nlmsghdr nlh; |
| + | struct rtgenmsg g; |
| + | } req; |
| + | struct sockaddr_nl nladdr; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | |
| + | req.nlh.nlmsg_len = sizeof(req); |
| + | req.nlh.nlmsg_type = type; |
| + | req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
| + | req.nlh.nlmsg_pid = 0; |
| + | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; |
| + | req.g.rtgen_family = family; |
| + | |
| + | return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
| + | } |
| + | |
| + | int rtnl_send(struct rtnl_handle *rth, char *buf, int len) |
| + | { |
| + | struct sockaddr_nl nladdr; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | |
| + | return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
| + | } |
| + | |
| + | int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) |
| + | { |
| + | struct nlmsghdr nlh; |
| + | struct sockaddr_nl nladdr; |
| + | struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; |
| + | struct msghdr msg = { |
| + | (void*)&nladdr, sizeof(nladdr), |
| + | iov, 2, |
| + | NULL, 0, |
| + | 0 |
| + | }; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | |
| + | nlh.nlmsg_len = NLMSG_LENGTH(len); |
| + | nlh.nlmsg_type = type; |
| + | nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
| + | nlh.nlmsg_pid = 0; |
| + | nlh.nlmsg_seq = rth->dump = ++rth->seq; |
| + | |
| + | return sendmsg(rth->fd, &msg, 0); |
| + | } |
| + | |
| + | int rtnl_dump_filter(struct rtnl_handle *rth, |
| + | int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), |
| + | void *arg1, |
| + | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *arg2) |
| + | { |
| + | char buf[8192]; |
| + | struct sockaddr_nl nladdr; |
| + | struct iovec iov = { buf, sizeof(buf) }; |
| + | |
| + | while (1) { |
| + | int status; |
| + | struct nlmsghdr *h; |
| + | |
| + | struct msghdr msg = { |
| + | (void*)&nladdr, sizeof(nladdr), |
| + | &iov, 1, |
| + | NULL, 0, |
| + | 0 |
| + | }; |
| + | |
| + | status = recvmsg(rth->fd, &msg, 0); |
| + | |
| + | if (status < 0) { |
| + | if (errno == EINTR) |
| + | continue; |
| + | perror("OVERRUN"); |
| + | continue; |
| + | } |
| + | if (status == 0) { |
| + | fprintf(stderr, "EOF on netlink\n"); |
| + | return -1; |
| + | } |
| + | if (msg.msg_namelen != sizeof(nladdr)) { |
| + | fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); |
| + | exit(1); |
| + | } |
| + | |
| + | h = (struct nlmsghdr*)buf; |
| + | while (NLMSG_OK(h, status)) { |
| + | int err; |
| + | |
| + | if (h->nlmsg_pid != rth->local.nl_pid || |
| + | h->nlmsg_seq != rth->dump) { |
| + | if (junk) { |
| + | err = junk(&nladdr, h, arg2); |
| + | if (err < 0) |
| + | return err; |
| + | } |
| + | goto skip_it; |
| + | } |
| + | |
| + | if (h->nlmsg_type == NLMSG_DONE) |
| + | return 0; |
| + | if (h->nlmsg_type == NLMSG_ERROR) { |
| + | struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); |
| + | if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { |
| + | fprintf(stderr, "ERROR truncated\n"); |
| + | } else { |
| + | errno = -err->error; |
| + | perror("RTNETLINK answers"); |
| + | } |
| + | return -1; |
| + | } |
| + | err = filter(&nladdr, h, arg1); |
| + | if (err < 0) |
| + | return err; |
| + | |
| + | skip_it: |
| + | h = NLMSG_NEXT(h, status); |
| + | } |
| + | if (msg.msg_flags & MSG_TRUNC) { |
| + | fprintf(stderr, "Message truncated\n"); |
| + | continue; |
| + | } |
| + | if (status) { |
| + | fprintf(stderr, "!!!Remnant of size %d\n", status); |
| + | exit(1); |
| + | } |
| + | } |
| + | } |
| + | |
| + | int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, |
| + | unsigned groups, struct nlmsghdr *answer, |
| + | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg) |
| + | { |
| + | int status; |
| + | unsigned seq; |
| + | struct nlmsghdr *h; |
| + | struct sockaddr_nl nladdr; |
| + | struct iovec iov = { (void*)n, n->nlmsg_len }; |
| + | char buf[8192]; |
| + | struct msghdr msg = { |
| + | (void*)&nladdr, sizeof(nladdr), |
| + | &iov, 1, |
| + | NULL, 0, |
| + | 0 |
| + | }; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | nladdr.nl_pid = peer; |
| + | nladdr.nl_groups = groups; |
| + | |
| + | n->nlmsg_seq = seq = ++rtnl->seq; |
| + | if (answer == NULL) |
| + | n->nlmsg_flags |= NLM_F_ACK; |
| + | |
| + | status = sendmsg(rtnl->fd, &msg, 0); |
| + | |
| + | if (status < 0) { |
| + | perror("Cannot talk to rtnetlink"); |
| + | return -1; |
| + | } |
| + | |
| + | iov.iov_base = buf; |
| + | |
| + | while (1) { |
| + | iov.iov_len = sizeof(buf); |
| + | status = recvmsg(rtnl->fd, &msg, 0); |
| + | |
| + | if (status < 0) { |
| + | if (errno == EINTR) |
| + | continue; |
| + | perror("OVERRUN"); |
| + | continue; |
| + | } |
| + | if (status == 0) { |
| + | fprintf(stderr, "EOF on netlink\n"); |
| + | return -1; |
| + | } |
| + | if (msg.msg_namelen != sizeof(nladdr)) { |
| + | fprintf(stderr, "sender address length == %d\n", msg.msg_namelen); |
| + | exit(1); |
| + | } |
| + | for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { |
| + | int err; |
| + | int len = h->nlmsg_len; |
| + | int l = len - sizeof(*h); |
| + | |
| + | if (l<0 || len>status) { |
| + | if (msg.msg_flags & MSG_TRUNC) { |
| + | fprintf(stderr, "Truncated message\n"); |
| + | return -1; |
| + | } |
| + | fprintf(stderr, "!!!malformed message: len=%d\n", len); |
| + | exit(1); |
| + | } |
| + | |
| + | if (h->nlmsg_pid != rtnl->local.nl_pid || |
| + | h->nlmsg_seq != seq) { |
| + | if (junk) { |
| + | err = junk(&nladdr, h, jarg); |
| + | if (err < 0) |
| + | return err; |
| + | } |
| + | continue; |
| + | } |
| + | |
| + | if (h->nlmsg_type == NLMSG_ERROR) { |
| + | struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); |
| + | if (l < sizeof(struct nlmsgerr)) { |
| + | fprintf(stderr, "ERROR truncated\n"); |
| + | } else { |
| + | errno = -err->error; |
| + | if (errno == 0) { |
| + | if (answer) |
| + | memcpy(answer, h, h->nlmsg_len); |
| + | return 0; |
| + | } |
| + | perror("RTNETLINK answers"); |
| + | } |
| + | return -1; |
| + | } |
| + | if (answer) { |
| + | memcpy(answer, h, h->nlmsg_len); |
| + | return 0; |
| + | } |
| + | |
| + | fprintf(stderr, "Unexpected reply!!!\n"); |
| + | |
| + | status -= NLMSG_ALIGN(len); |
| + | h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); |
| + | } |
| + | if (msg.msg_flags & MSG_TRUNC) { |
| + | fprintf(stderr, "Message truncated\n"); |
| + | continue; |
| + | } |
| + | if (status) { |
| + | fprintf(stderr, "!!!Remnant of size %d\n", status); |
| + | exit(1); |
| + | } |
| + | } |
| + | } |
| + | |
| + | int rtnl_listen(struct rtnl_handle *rtnl, |
| + | int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg) |
| + | { |
| + | int status; |
| + | struct nlmsghdr *h; |
| + | struct sockaddr_nl nladdr; |
| + | struct iovec iov; |
| + | char buf[8192]; |
| + | struct msghdr msg = { |
| + | (void*)&nladdr, sizeof(nladdr), |
| + | &iov, 1, |
| + | NULL, 0, |
| + | 0 |
| + | }; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | nladdr.nl_pid = 0; |
| + | nladdr.nl_groups = 0; |
| + | |
| + | |
| + | iov.iov_base = buf; |
| + | |
| + | while (1) { |
| + | iov.iov_len = sizeof(buf); |
| + | status = recvmsg(rtnl->fd, &msg, 0); |
| + | |
| + | if (status < 0) { |
| + | if (errno == EINTR) |
| + | continue; |
| + | perror("OVERRUN"); |
| + | continue; |
| + | } |
| + | if (status == 0) { |
| + | fprintf(stderr, "EOF on netlink\n"); |
| + | return -1; |
| + | } |
| + | if (msg.msg_namelen != sizeof(nladdr)) { |
| + | fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen); |
| + | exit(1); |
| + | } |
| + | for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { |
| + | int err; |
| + | int len = h->nlmsg_len; |
| + | int l = len - sizeof(*h); |
| + | |
| + | if (l<0 || len>status) { |
| + | if (msg.msg_flags & MSG_TRUNC) { |
| + | fprintf(stderr, "Truncated message\n"); |
| + | return -1; |
| + | } |
| + | fprintf(stderr, "!!!malformed message: len=%d\n", len); |
| + | exit(1); |
| + | } |
| + | |
| + | err = handler(&nladdr, h, jarg); |
| + | if (err < 0) |
| + | return err; |
| + | |
| + | status -= NLMSG_ALIGN(len); |
| + | h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); |
| + | } |
| + | if (msg.msg_flags & MSG_TRUNC) { |
| + | fprintf(stderr, "Message truncated\n"); |
| + | continue; |
| + | } |
| + | if (status) { |
| + | fprintf(stderr, "!!!Remnant of size %d\n", status); |
| + | exit(1); |
| + | } |
| + | } |
| + | } |
| + | |
| + | int rtnl_from_file(FILE *rtnl, |
| + | int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg) |
| + | { |
| + | int status; |
| + | struct sockaddr_nl nladdr; |
| + | char buf[8192]; |
| + | struct nlmsghdr *h = (void*)buf; |
| + | |
| + | memset(&nladdr, 0, sizeof(nladdr)); |
| + | nladdr.nl_family = AF_NETLINK; |
| + | nladdr.nl_pid = 0; |
| + | nladdr.nl_groups = 0; |
| + | |
| + | while (1) { |
| + | int err, len, type; |
| + | int l; |
| + | |
| + | status = fread(&buf, 1, sizeof(*h), rtnl); |
| + | |
| + | if (status < 0) { |
| + | if (errno == EINTR) |
| + | continue; |
| + | perror("rtnl_from_file: fread"); |
| + | return -1; |
| + | } |
| + | if (status == 0) |
| + | return 0; |
| + | |
| + | len = h->nlmsg_len; |
| + | type= h->nlmsg_type; |
| + | l = len - sizeof(*h); |
| + | |
| + | if (l<0 || len>sizeof(buf)) { |
| + | fprintf(stderr, "!!!malformed message: len=%d @%lu\n", |
| + | len, ftell(rtnl)); |
| + | return -1; |
| + | } |
| + | |
| + | status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); |
| + | |
| + | if (status < 0) { |
| + | perror("rtnl_from_file: fread"); |
| + | return -1; |
| + | } |
| + | if (status < l) { |
| + | fprintf(stderr, "rtnl-from_file: truncated message\n"); |
| + | return -1; |
| + | } |
| + | |
| + | err = handler(&nladdr, h, jarg); |
| + | if (err < 0) |
| + | return err; |
| + | } |
| + | } |
| + | |
| + | int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) |
| + | { |
| + | int len = RTA_LENGTH(4); |
| + | struct rtattr *rta; |
| + | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) |
| + | return -1; |
| + | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
| + | rta->rta_type = type; |
| + | rta->rta_len = len; |
| + | memcpy(RTA_DATA(rta), &data, 4); |
| + | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; |
| + | return 0; |
| + | } |
| + | |
| + | int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) |
| + | { |
| + | int len = RTA_LENGTH(alen); |
| + | struct rtattr *rta; |
| + | |
| + | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) |
| + | return -1; |
| + | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
| + | rta->rta_type = type; |
| + | rta->rta_len = len; |
| + | memcpy(RTA_DATA(rta), data, alen); |
| + | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; |
| + | return 0; |
| + | } |
| + | |
| + | int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) |
| + | { |
| + | int len = RTA_LENGTH(4); |
| + | struct rtattr *subrta; |
| + | |
| + | if (RTA_ALIGN(rta->rta_len) + len > maxlen) |
| + | return -1; |
| + | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
| + | subrta->rta_type = type; |
| + | subrta->rta_len = len; |
| + | memcpy(RTA_DATA(subrta), &data, 4); |
| + | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; |
| + | return 0; |
| + | } |
| + | |
| + | int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) |
| + | { |
| + | struct rtattr *subrta; |
| + | int len = RTA_LENGTH(alen); |
| + | |
| + | if (RTA_ALIGN(rta->rta_len) + len > maxlen) |
| + | return -1; |
| + | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
| + | subrta->rta_type = type; |
| + | subrta->rta_len = len; |
| + | memcpy(RTA_DATA(subrta), data, alen); |
| + | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) |
| + | { |
| + | while (RTA_OK(rta, len)) { |
| + | if (rta->rta_type <= max) |
| + | tb[rta->rta_type] = rta; |
| + | rta = RTA_NEXT(rta,len); |
| + | } |
| + | if (len) |
| + | fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); |
| + | return 0; |
| + | } |
| @@ -0,0 +1,46 @@ | |
| + | #ifndef __LIBNETLINK_H__ |
| + | #define __LIBNETLINK_H__ 1 |
| + | |
| + | #include <asm/types.h> |
| + | #include <linux/netlink.h> |
| + | #include <linux/rtnetlink.h> |
| + | |
| + | struct rtnl_handle |
| + | { |
| + | int fd; |
| + | struct sockaddr_nl local; |
| + | struct sockaddr_nl peer; |
| + | __u32 seq; |
| + | __u32 dump; |
| + | }; |
| + | |
| + | extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); |
| + | extern void rtnl_close(struct rtnl_handle *rth); |
| + | extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); |
| + | extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); |
| + | extern int rtnl_dump_filter(struct rtnl_handle *rth, |
| + | int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), |
| + | void *arg1, |
| + | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *arg2); |
| + | extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, |
| + | unsigned groups, struct nlmsghdr *answer, |
| + | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg); |
| + | extern int rtnl_send(struct rtnl_handle *rth, char *buf, int); |
| + | |
| + | |
| + | extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); |
| + | extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen); |
| + | extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); |
| + | extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen); |
| + | |
| + | extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); |
| + | |
| + | extern int rtnl_listen(struct rtnl_handle *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg); |
| + | extern int rtnl_from_file(FILE *, int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
| + | void *jarg); |
| + | |
| + | #endif /* __LIBNETLINK_H__ */ |
| + | |
| @@ -0,0 +1,413 @@ | |
| + | #ifndef __LINUX_PKT_SCHED_H |
| + | #define __LINUX_PKT_SCHED_H |
| + | |
| + | /* Logical priority bands not depending on specific packet scheduler. |
| + | Every scheduler will map them to real traffic classes, if it has |
| + | no more precise mechanism to classify packets. |
| + | |
| + | These numbers have no special meaning, though their coincidence |
| + | with obsolete IPv6 values is not occasional :-). New IPv6 drafts |
| + | preferred full anarchy inspired by diffserv group. |
| + | |
| + | Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy |
| + | class, actually, as rule it will be handled with more care than |
| + | filler or even bulk. |
| + | */ |
| + | |
| + | #include <asm/types.h> |
| + | |
| + | #define TC_PRIO_BESTEFFORT 0 |
| + | #define TC_PRIO_FILLER 1 |
| + | #define TC_PRIO_BULK 2 |
| + | #define TC_PRIO_INTERACTIVE_BULK 4 |
| + | #define TC_PRIO_INTERACTIVE 6 |
| + | #define TC_PRIO_CONTROL 7 |
| + | |
| + | #define TC_PRIO_MAX 15 |
| + | |
| + | /* Generic queue statistics, available for all the elements. |
| + | Particular schedulers may have also their private records. |
| + | */ |
| + | |
| + | struct tc_stats |
| + | { |
| + | __u64 bytes; /* NUmber of enqueues bytes */ |
| + | __u32 packets; /* Number of enqueued packets */ |
| + | __u32 drops; /* Packets dropped because of lack of resources */ |
| + | __u32 overlimits; /* Number of throttle events when this |
| + | * flow goes out of allocated bandwidth */ |
| + | __u32 bps; /* Current flow byte rate */ |
| + | __u32 pps; /* Current flow packet rate */ |
| + | __u32 qlen; |
| + | __u32 backlog; |
| + | #ifdef __KERNEL__ |
| + | spinlock_t *lock; |
| + | #endif |
| + | }; |
| + | |
| + | struct tc_estimator |
| + | { |
| + | char interval; |
| + | unsigned char ewma_log; |
| + | }; |
| + | |
| + | /* "Handles" |
| + | --------- |
| + | |
| + | All the traffic control objects have 32bit identifiers, or "handles". |
| + | |
| + | They can be considered as opaque numbers from user API viewpoint, |
| + | but actually they always consist of two fields: major and |
| + | minor numbers, which are interpreted by kernel specially, |
| + | that may be used by applications, though not recommended. |
| + | |
| + | F.e. qdisc handles always have minor number equal to zero, |
| + | classes (or flows) have major equal to parent qdisc major, and |
| + | minor uniquely identifying class inside qdisc. |
| + | |
| + | Macros to manipulate handles: |
| + | */ |
| + | |
| + | #define TC_H_MAJ_MASK (0xFFFF0000U) |
| + | #define TC_H_MIN_MASK (0x0000FFFFU) |
| + | #define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) |
| + | #define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) |
| + | #define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) |
| + | |
| + | #define TC_H_UNSPEC (0U) |
| + | #define TC_H_ROOT (0xFFFFFFFFU) |
| + | #define TC_H_INGRESS (0xFFFFFFF1U) |
| + | |
| + | struct tc_ratespec |
| + | { |
| + | unsigned char cell_log; |
| + | unsigned char __reserved; |
| + | unsigned short feature; |
| + | short addend; |
| + | unsigned short mpu; |
| + | __u32 rate; |
| + | }; |
| + | |
| + | /* FIFO section */ |
| + | |
| + | struct tc_fifo_qopt |
| + | { |
| + | __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ |
| + | }; |
| + | |
| + | /* PRIO section */ |
| + | |
| + | #define TCQ_PRIO_BANDS 16 |
| + | |
| + | struct tc_prio_qopt |
| + | { |
| + | int bands; /* Number of bands */ |
| + | __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ |
| + | }; |
| + | |
| + | /* CSZ section */ |
| + | |
| + | struct tc_csz_qopt |
| + | { |
| + | int flows; /* Maximal number of guaranteed flows */ |
| + | unsigned char R_log; /* Fixed point position for round number */ |
| + | unsigned char delta_log; /* Log of maximal managed time interval */ |
| + | __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> CSZ band */ |
| + | }; |
| + | |
| + | struct tc_csz_copt |
| + | { |
| + | struct tc_ratespec slice; |
| + | struct tc_ratespec rate; |
| + | struct tc_ratespec peakrate; |
| + | __u32 limit; |
| + | __u32 buffer; |
| + | __u32 mtu; |
| + | }; |
| + | |
| + | enum |
| + | { |
| + | TCA_CSZ_UNSPEC, |
| + | TCA_CSZ_PARMS, |
| + | TCA_CSZ_RTAB, |
| + | TCA_CSZ_PTAB, |
| + | }; |
| + | |
| + | /* TBF section */ |
| + | |
| + | struct tc_tbf_qopt |
| + | { |
| + | struct tc_ratespec rate; |
| + | struct tc_ratespec peakrate; |
| + | __u32 limit; |
| + | __u32 buffer; |
| + | __u32 mtu; |
| + | }; |
| + | |
| + | enum |
| + | { |
| + | TCA_TBF_UNSPEC, |
| + | TCA_TBF_PARMS, |
| + | TCA_TBF_RTAB, |
| + | TCA_TBF_PTAB, |
| + | }; |
| + | |
| + | |
| + | /* TEQL section */ |
| + | |
| + | /* TEQL does not require any parameters */ |
| + | |
| + | /* SFQ section */ |
| + | |
| + | struct tc_sfq_qopt |
| + | { |
| + | unsigned quantum; /* Bytes per round allocated to flow */ |
| + | int perturb_period; /* Period of hash perturbation */ |
| + | __u32 limit; /* Maximal packets in queue */ |
| + | unsigned divisor; /* Hash divisor */ |
| + | unsigned flows; /* Maximal number of flows */ |
| + | }; |
| + | |
| + | /* |
| + | * NOTE: limit, divisor and flows are hardwired to code at the moment. |
| + | * |
| + | * limit=flows=128, divisor=1024; |
| + | * |
| + | * The only reason for this is efficiency, it is possible |
| + | * to change these parameters in compile time. |
| + | */ |
| + | |
| + | /* RED section */ |
| + | |
| + | enum |
| + | { |
| + | TCA_RED_UNSPEC, |
| + | TCA_RED_PARMS, |
| + | TCA_RED_STAB, |
| + | }; |
| + | |
| + | struct tc_red_qopt |
| + | { |
| + | __u32 limit; /* HARD maximal queue length (bytes) */ |
| + | __u32 qth_min; /* Min average length threshold (bytes) */ |
| + | __u32 qth_max; /* Max average length threshold (bytes) */ |
| + | unsigned char Wlog; /* log(W) */ |
| + | unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ |
| + | unsigned char Scell_log; /* cell size for idle damping */ |
| + | unsigned char flags; |
| + | #define TC_RED_ECN 1 |
| + | }; |
| + | |
| + | struct tc_red_xstats |
| + | { |
| + | __u32 early; /* Early drops */ |
| + | __u32 pdrop; /* Drops due to queue limits */ |
| + | __u32 other; /* Drops due to drop() calls */ |
| + | __u32 marked; /* Marked packets */ |
| + | }; |
| + | |
| + | /* GRED section */ |
| + | |
| + | #define MAX_DPs 16 |
| + | |
| + | enum |
| + | { |
| + | TCA_GRED_UNSPEC, |
| + | TCA_GRED_PARMS, |
| + | TCA_GRED_STAB, |
| + | TCA_GRED_DPS, |
| + | }; |
| + | |
| + | #define TCA_SET_OFF TCA_GRED_PARMS |
| + | struct tc_gred_qopt |
| + | { |
| + | __u32 limit; /* HARD maximal queue length (bytes) |
| + | */ |
| + | __u32 qth_min; /* Min average length threshold (bytes) |
| + | */ |
| + | __u32 qth_max; /* Max average length threshold (bytes) |
| + | */ |
| + | __u32 DP; /* upto 2^32 DPs */ |
| + | __u32 backlog; |
| + | __u32 qave; |
| + | __u32 forced; |
| + | __u32 early; |
| + | __u32 other; |
| + | __u32 pdrop; |
| + | |
| + | unsigned char Wlog; /* log(W) */ |
| + | unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ |
| + | unsigned char Scell_log; /* cell size for idle damping */ |
| + | __u8 prio; /* prio of this VQ */ |
| + | __u32 packets; |
| + | __u32 bytesin; |
| + | }; |
| + | /* gred setup */ |
| + | struct tc_gred_sopt |
| + | { |
| + | __u32 DPs; |
| + | __u32 def_DP; |
| + | __u8 grio; |
| + | }; |
| + | |
| + | /* HTB section */ |
| + | #define TC_HTB_NUMPRIO 4 |
| + | #define TC_HTB_MAXDEPTH 4 |
| + | |
| + | struct tc_htb_opt |
| + | { |
| + | struct tc_ratespec rate; |
| + | struct tc_ratespec ceil; |
| + | __u32 buffer; |
| + | __u32 cbuffer; |
| + | __u32 quantum; /* out only */ |
| + | __u32 level; /* out only */ |
| + | __u8 prio; |
| + | __u8 injectd; /* inject class distance */ |
| + | __u8 pad[2]; |
| + | }; |
| + | struct tc_htb_glob |
| + | { |
| + | __u32 rate2quantum; /* bps->quantum divisor */ |
| + | __u32 defcls; /* default class number */ |
| + | __u32 use_dcache; /* use dequeue cache ? */ |
| + | __u32 debug; /* debug flags */ |
| + | |
| + | |
| + | /* stats */ |
| + | __u32 deq_rate; /* dequeue rate */ |
| + | __u32 utilz; /* dequeue utilization */ |
| + | __u32 trials; /* deq_prio trials per dequeue */ |
| + | __u32 dcache_hits; |
| + | __u32 direct_pkts; /* count of non shapped packets */ |
| + | }; |
| + | enum |
| + | { |
| + | TCA_HTB_UNSPEC, |
| + | TCA_HTB_PARMS, |
| + | TCA_HTB_INIT, |
| + | TCA_HTB_CTAB, |
| + | TCA_HTB_RTAB, |
| + | }; |
| + | struct tc_htb_xstats |
| + | { |
| + | __u32 lends; |
| + | __u32 borrows; |
| + | __u32 giants; /* too big packets (rate will not be accurate) */ |
| + | __u32 injects; /* how many times leaf used injected bw */ |
| + | __u32 tokens; |
| + | __u32 ctokens; |
| + | }; |
| + | |
| + | /* CBQ section */ |
| + | |
| + | #define TC_CBQ_MAXPRIO 8 |
| + | #define TC_CBQ_MAXLEVEL 8 |
| + | #define TC_CBQ_DEF_EWMA 5 |
| + | |
| + | struct tc_cbq_lssopt |
| + | { |
| + | unsigned char change; |
| + | unsigned char flags; |
| + | #define TCF_CBQ_LSS_BOUNDED 1 |
| + | #define TCF_CBQ_LSS_ISOLATED 2 |
| + | unsigned char ewma_log; |
| + | unsigned char level; |
| + | #define TCF_CBQ_LSS_FLAGS 1 |
| + | #define TCF_CBQ_LSS_EWMA 2 |
| + | #define TCF_CBQ_LSS_MAXIDLE 4 |
| + | #define TCF_CBQ_LSS_MINIDLE 8 |
| + | #define TCF_CBQ_LSS_OFFTIME 0x10 |
| + | #define TCF_CBQ_LSS_AVPKT 0x20 |
| + | __u32 maxidle; |
| + | __u32 minidle; |
| + | __u32 offtime; |
| + | __u32 avpkt; |
| + | }; |
| + | |
| + | struct tc_cbq_wrropt |
| + | { |
| + | unsigned char flags; |
| + | unsigned char priority; |
| + | unsigned char cpriority; |
| + | unsigned char __reserved; |
| + | __u32 allot; |
| + | __u32 weight; |
| + | }; |
| + | |
| + | struct tc_cbq_ovl |
| + | { |
| + | unsigned char strategy; |
| + | #define TC_CBQ_OVL_CLASSIC 0 |
| + | #define TC_CBQ_OVL_DELAY 1 |
| + | #define TC_CBQ_OVL_LOWPRIO 2 |
| + | #define TC_CBQ_OVL_DROP 3 |
| + | #define TC_CBQ_OVL_RCLASSIC 4 |
| + | unsigned char priority2; |
| + | __u32 penalty; |
| + | }; |
| + | |
| + | struct tc_cbq_police |
| + | { |
| + | unsigned char police; |
| + | unsigned char __res1; |
| + | unsigned short __res2; |
| + | }; |
| + | |
| + | struct tc_cbq_fopt |
| + | { |
| + | __u32 split; |
| + | __u32 defmap; |
| + | __u32 defchange; |
| + | }; |
| + | |
| + | struct tc_cbq_xstats |
| + | { |
| + | __u32 borrows; |
| + | __u32 overactions; |
| + | __s32 avgidle; |
| + | __s32 undertime; |
| + | }; |
| + | |
| + | enum |
| + | { |
| + | TCA_CBQ_UNSPEC, |
| + | TCA_CBQ_LSSOPT, |
| + | TCA_CBQ_WRROPT, |
| + | TCA_CBQ_FOPT, |
| + | TCA_CBQ_OVL_STRATEGY, |
| + | TCA_CBQ_RATE, |
| + | TCA_CBQ_RTAB, |
| + | TCA_CBQ_POLICE, |
| + | }; |
| + | |
| + | #define TCA_CBQ_MAX TCA_CBQ_POLICE |
| + | |
| + | /* dsmark section */ |
| + | |
| + | enum { |
| + | TCA_DSMARK_UNSPEC, |
| + | TCA_DSMARK_INDICES, |
| + | TCA_DSMARK_DEFAULT_INDEX, |
| + | TCA_DSMARK_SET_TC_INDEX, |
| + | TCA_DSMARK_MASK, |
| + | TCA_DSMARK_VALUE |
| + | }; |
| + | |
| + | #define TCA_DSMARK_MAX TCA_DSMARK_VALUE |
| + | |
| + | /* ATM section */ |
| + | |
| + | enum { |
| + | TCA_ATM_UNSPEC, |
| + | TCA_ATM_FD, /* file/socket descriptor */ |
| + | TCA_ATM_PTR, /* pointer to descriptor - later */ |
| + | TCA_ATM_HDR, /* LL header */ |
| + | TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ |
| + | TCA_ATM_ADDR, /* PVC address (for output only) */ |
| + | TCA_ATM_STATE /* VC state (ATM_VS_*; for output only) */ |
| + | }; |
| + | |
| + | #define TCA_ATM_MAX TCA_ATM_STATE |
| + | |
| + | #endif |
| @@ -0,0 +1,92 @@ | |
| + | /* |
| + | * ll_addr.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/ioctl.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | |
| + | #include <linux/netdevice.h> |
| + | #include <linux/if_arp.h> |
| + | #include <linux/sockios.h> |
| + | |
| + | #include "utils.h" |
| + | |
| + | |
| + | const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen) |
| + | { |
| + | int i; |
| + | int l; |
| + | |
| + | if (alen == 4 && |
| + | (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)) { |
| + | return inet_ntop(AF_INET, addr, buf, blen); |
| + | } |
| + | l = 0; |
| + | for (i=0; i<alen; i++) { |
| + | if (i==0) { |
| + | snprintf(buf+l, blen, "%02x", addr[i]); |
| + | blen -= 2; |
| + | l += 2; |
| + | } else { |
| + | snprintf(buf+l, blen, ":%02x", addr[i]); |
| + | blen -= 3; |
| + | l += 3; |
| + | } |
| + | } |
| + | return buf; |
| + | } |
| + | |
| + | int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) |
| + | { |
| + | if (strchr(arg, '.')) { |
| + | inet_prefix pfx; |
| + | if (get_addr_1(&pfx, arg, AF_INET)) { |
| + | fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); |
| + | return -1; |
| + | } |
| + | if (len < 4) |
| + | return -1; |
| + | memcpy(lladdr, pfx.data, 4); |
| + | return 4; |
| + | } else { |
| + | int i; |
| + | |
| + | for (i=0; i<len; i++) { |
| + | int temp; |
| + | char *cp = strchr(arg, ':'); |
| + | if (cp) { |
| + | *cp = 0; |
| + | cp++; |
| + | } |
| + | if (sscanf(arg, "%x", &temp) != 1) { |
| + | fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); |
| + | return -1; |
| + | } |
| + | if (temp < 0 || temp > 255) { |
| + | fprintf(stderr, "\"%s\" is invalid lladdr.\n", arg); |
| + | return -1; |
| + | } |
| + | lladdr[i] = temp; |
| + | if (!cp) |
| + | break; |
| + | arg = cp; |
| + | } |
| + | return i+1; |
| + | } |
| + | } |
| @@ -0,0 +1,169 @@ | |
| + | /* |
| + | * ll_map.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <string.h> |
| + | |
| + | #include "libnetlink.h" |
| + | #include "ll_map.h" |
| + | |
| + | struct idxmap |
| + | { |
| + | struct idxmap * next; |
| + | int index; |
| + | int type; |
| + | int alen; |
| + | unsigned flags; |
| + | unsigned char addr[8]; |
| + | char name[16]; |
| + | }; |
| + | |
| + | static struct idxmap *idxmap[16]; |
| + | |
| + | int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) |
| + | { |
| + | int h; |
| + | struct ifinfomsg *ifi = NLMSG_DATA(n); |
| + | struct idxmap *im, **imp; |
| + | struct rtattr *tb[IFLA_MAX+1]; |
| + | |
| + | if (n->nlmsg_type != RTM_NEWLINK) |
| + | return 0; |
| + | |
| + | if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi))) |
| + | return -1; |
| + | |
| + | |
| + | memset(tb, 0, sizeof(tb)); |
| + | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n)); |
| + | if (tb[IFLA_IFNAME] == NULL) |
| + | return 0; |
| + | |
| + | h = ifi->ifi_index&0xF; |
| + | |
| + | for (imp=&idxmap[h]; (im=*imp)!=NULL; imp = &im->next) |
| + | if (im->index == ifi->ifi_index) |
| + | break; |
| + | |
| + | if (im == NULL) { |
| + | im = malloc(sizeof(*im)); |
| + | if (im == NULL) |
| + | return 0; |
| + | im->next = *imp; |
| + | im->index = ifi->ifi_index; |
| + | *imp = im; |
| + | } |
| + | |
| + | im->type = ifi->ifi_type; |
| + | im->flags = ifi->ifi_flags; |
| + | if (tb[IFLA_ADDRESS]) { |
| + | int alen; |
| + | im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]); |
| + | if (alen > sizeof(im->addr)) |
| + | alen = sizeof(im->addr); |
| + | memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen); |
| + | } else { |
| + | im->alen = 0; |
| + | memset(im->addr, 0, sizeof(im->addr)); |
| + | } |
| + | strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME])); |
| + | return 0; |
| + | } |
| + | |
| + | const char *ll_idx_n2a(int idx, char *buf) |
| + | { |
| + | struct idxmap *im; |
| + | |
| + | if (idx == 0) |
| + | return "*"; |
| + | for (im = idxmap[idx&0xF]; im; im = im->next) |
| + | if (im->index == idx) |
| + | return im->name; |
| + | snprintf(buf, 16, "if%d", idx); |
| + | return buf; |
| + | } |
| + | |
| + | |
| + | const char *ll_index_to_name(int idx) |
| + | { |
| + | static char nbuf[16]; |
| + | |
| + | return ll_idx_n2a(idx, nbuf); |
| + | } |
| + | |
| + | int ll_index_to_type(int idx) |
| + | { |
| + | struct idxmap *im; |
| + | |
| + | if (idx == 0) |
| + | return -1; |
| + | for (im = idxmap[idx&0xF]; im; im = im->next) |
| + | if (im->index == idx) |
| + | return im->type; |
| + | return -1; |
| + | } |
| + | |
| + | unsigned ll_index_to_flags(int idx) |
| + | { |
| + | struct idxmap *im; |
| + | |
| + | if (idx == 0) |
| + | return 0; |
| + | |
| + | for (im = idxmap[idx&0xF]; im; im = im->next) |
| + | if (im->index == idx) |
| + | return im->flags; |
| + | return 0; |
| + | } |
| + | |
| + | int ll_name_to_index(char *name) |
| + | { |
| + | static char ncache[16]; |
| + | static int icache; |
| + | struct idxmap *im; |
| + | int i; |
| + | |
| + | if (name == NULL) |
| + | return 0; |
| + | if (icache && strcmp(name, ncache) == 0) |
| + | return icache; |
| + | for (i=0; i<16; i++) { |
| + | for (im = idxmap[i]; im; im = im->next) { |
| + | if (strcmp(im->name, name) == 0) { |
| + | icache = im->index; |
| + | strcpy(ncache, name); |
| + | return im->index; |
| + | } |
| + | } |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | int ll_init_map(struct rtnl_handle *rth) |
| + | { |
| + | if (rtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK) < 0) { |
| + | perror("Cannot send dump request"); |
| + | exit(1); |
| + | } |
| + | |
| + | if (rtnl_dump_filter(rth, ll_remember_index, &idxmap, NULL, NULL) < 0) { |
| + | fprintf(stderr, "Dump terminated\n"); |
| + | exit(1); |
| + | } |
| + | return 0; |
| + | } |
| @@ -0,0 +1,12 @@ | |
| + | #ifndef __LL_MAP_H__ |
| + | #define __LL_MAP_H__ 1 |
| + | |
| + | extern int ll_remember_index(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg); |
| + | extern int ll_init_map(struct rtnl_handle *rth); |
| + | extern int ll_name_to_index(char *name); |
| + | extern const char *ll_index_to_name(int idx); |
| + | extern const char *ll_idx_n2a(int idx, char *buf); |
| + | extern int ll_index_to_type(int idx); |
| + | extern unsigned ll_index_to_flags(int idx); |
| + | |
| + | #endif /* __LL_MAP_H__ */ |
| @@ -0,0 +1,118 @@ | |
| + | /* |
| + | * ll_proto.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/ioctl.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | |
| + | #include <linux/netdevice.h> |
| + | #include <linux/if_arp.h> |
| + | #include <linux/sockios.h> |
| + | |
| + | #include "utils.h" |
| + | |
| + | |
| + | #define __PF(f,n) { ETH_P_##f, #n }, |
| + | static struct { |
| + | int id; |
| + | char *name; |
| + | } llproto_names[] = { |
| + | __PF(LOOP,loop) |
| + | __PF(PUP,pup) |
| + | #ifdef ETH_P_PUPAT |
| + | __PF(PUPAT,pupat) |
| + | #endif |
| + | __PF(IP,ip) |
| + | __PF(X25,x25) |
| + | __PF(ARP,arp) |
| + | __PF(BPQ,bpq) |
| + | #ifdef ETH_P_IEEEPUP |
| + | __PF(IEEEPUP,ieeepup) |
| + | #endif |
| + | #ifdef ETH_P_IEEEPUPAT |
| + | __PF(IEEEPUPAT,ieeepupat) |
| + | #endif |
| + | __PF(DEC,dec) |
| + | __PF(DNA_DL,dna_dl) |
| + | __PF(DNA_RC,dna_rc) |
| + | __PF(DNA_RT,dna_rt) |
| + | __PF(LAT,lat) |
| + | __PF(DIAG,diag) |
| + | __PF(CUST,cust) |
| + | __PF(SCA,sca) |
| + | __PF(RARP,rarp) |
| + | __PF(ATALK,atalk) |
| + | __PF(AARP,aarp) |
| + | __PF(IPX,ipx) |
| + | __PF(IPV6,ipv6) |
| + | __PF(PPP_DISC,ppp_disc) |
| + | __PF(PPP_SES,ppp_ses) |
| + | __PF(ATMMPOA,atmmpoa) |
| + | __PF(ATMFATE,atmfate) |
| + | |
| + | __PF(802_3,802_3) |
| + | __PF(AX25,ax25) |
| + | __PF(ALL,all) |
| + | __PF(802_2,802_2) |
| + | __PF(SNAP,snap) |
| + | __PF(DDCMP,ddcmp) |
| + | __PF(WAN_PPP,wan_ppp) |
| + | __PF(PPP_MP,ppp_mp) |
| + | __PF(LOCALTALK,localtalk) |
| + | __PF(PPPTALK,ppptalk) |
| + | __PF(TR_802_2,tr_802_2) |
| + | __PF(MOBITEX,mobitex) |
| + | __PF(CONTROL,control) |
| + | __PF(IRDA,irda) |
| + | __PF(ECONET,econet) |
| + | |
| + | { 0x8100, "802.1Q" }, |
| + | { ETH_P_IP, "ipv4" }, |
| + | }; |
| + | #undef __PF |
| + | |
| + | |
| + | char * ll_proto_n2a(unsigned short id, char *buf, int len) |
| + | { |
| + | int i; |
| + | |
| + | id = ntohs(id); |
| + | |
| + | for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { |
| + | if (llproto_names[i].id == id) |
| + | return llproto_names[i].name; |
| + | } |
| + | snprintf(buf, len, "[%d]", id); |
| + | return buf; |
| + | } |
| + | |
| + | int ll_proto_a2n(unsigned short *id, char *buf) |
| + | { |
| + | int i; |
| + | for (i=0; i<sizeof(llproto_names)/sizeof(llproto_names[0]); i++) { |
| + | if (strcasecmp(llproto_names[i].name, buf) == 0) { |
| + | *id = htons(llproto_names[i].id); |
| + | return 0; |
| + | } |
| + | } |
| + | if (get_u16(id, buf, 0)) |
| + | return -1; |
| + | *id = htons(*id); |
| + | return 0; |
| + | } |
| @@ -0,0 +1,121 @@ | |
| + | /* |
| + | * ll_types.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/ioctl.h> |
| + | #include <sys/socket.h> |
| + | #include <sys/ioctl.h> |
| + | #include <netinet/in.h> |
| + | #include <arpa/inet.h> |
| + | #include <string.h> |
| + | |
| + | #include <linux/netdevice.h> |
| + | #include <linux/if_arp.h> |
| + | #include <linux/sockios.h> |
| + | |
| + | char * ll_type_n2a(int type, char *buf, int len) |
| + | { |
| + | #define __PF(f,n) { ARPHRD_##f, #n }, |
| + | static struct { |
| + | int type; |
| + | char *name; |
| + | } arphrd_names[] = { |
| + | { 0, "generic" }, |
| + | __PF(ETHER,ether) |
| + | __PF(EETHER,eether) |
| + | __PF(AX25,ax25) |
| + | __PF(PRONET,pronet) |
| + | __PF(CHAOS,chaos) |
| + | #ifdef ARPHRD_IEEE802_TR |
| + | __PF(IEEE802,ieee802) |
| + | #else |
| + | __PF(IEEE802,tr) |
| + | #endif |
| + | __PF(ARCNET,arcnet) |
| + | __PF(APPLETLK,atalk) |
| + | __PF(DLCI,dlci) |
| + | __PF(ATM,atm) |
| + | __PF(METRICOM,metricom) |
| + | #ifdef ARPHRD_IEEE1394 |
| + | __PF(IEEE1394,ieee1394) |
| + | #endif |
| + | |
| + | __PF(SLIP,slip) |
| + | __PF(CSLIP,cslip) |
| + | __PF(SLIP6,slip6) |
| + | __PF(CSLIP6,cslip6) |
| + | __PF(RSRVD,rsrvd) |
| + | __PF(ADAPT,adapt) |
| + | __PF(ROSE,rose) |
| + | __PF(X25,x25) |
| + | __PF(HWX25,hwx25) |
| + | __PF(PPP,ppp) |
| + | __PF(HDLC,hdlc) |
| + | __PF(LAPB,lapb) |
| + | __PF(DDCMP,ddcmp) |
| + | __PF(RAWHDLC,rawhdlc) |
| + | |
| + | __PF(TUNNEL,ipip) |
| + | __PF(TUNNEL6,tunnel6) |
| + | __PF(FRAD,frad) |
| + | __PF(SKIP,skip) |
| + | __PF(LOOPBACK,loopback) |
| + | __PF(LOCALTLK,ltalk) |
| + | __PF(FDDI,fddi) |
| + | __PF(BIF,bif) |
| + | __PF(SIT,sit) |
| + | __PF(IPDDP,ip/ddp) |
| + | __PF(IPGRE,gre) |
| + | __PF(PIMREG,pimreg) |
| + | __PF(HIPPI,hippi) |
| + | __PF(ASH,ash) |
| + | __PF(ECONET,econet) |
| + | __PF(IRDA,irda) |
| + | __PF(FCPP,fcpp) |
| + | __PF(FCAL,fcal) |
| + | __PF(FCPL,fcpl) |
| + | __PF(FCFABRIC,fcfb0) |
| + | __PF(FCFABRIC+1,fcfb1) |
| + | __PF(FCFABRIC+2,fcfb2) |
| + | __PF(FCFABRIC+3,fcfb3) |
| + | __PF(FCFABRIC+4,fcfb4) |
| + | __PF(FCFABRIC+5,fcfb5) |
| + | __PF(FCFABRIC+6,fcfb6) |
| + | __PF(FCFABRIC+7,fcfb7) |
| + | __PF(FCFABRIC+8,fcfb8) |
| + | __PF(FCFABRIC+9,fcfb9) |
| + | __PF(FCFABRIC+10,fcfb10) |
| + | __PF(FCFABRIC+11,fcfb11) |
| + | __PF(FCFABRIC+12,fcfb12) |
| + | #ifdef ARPHRD_IEEE802_TR |
| + | __PF(IEEE802_TR,tr) |
| + | #endif |
| + | #ifdef ARPHRD_IEEE80211 |
| + | __PF(IEEE80211,ieee802.11) |
| + | #endif |
| + | #ifdef ARPHRD_VOID |
| + | __PF(VOID,void) |
| + | #endif |
| + | }; |
| + | #undef __PF |
| + | |
| + | int i; |
| + | for (i=0; i<sizeof(arphrd_names)/sizeof(arphrd_names[0]); i++) { |
| + | if (arphrd_names[i].type == type) |
| + | return arphrd_names[i].name; |
| + | } |
| + | snprintf(buf, len, "[%d]", type); |
| + | return buf; |
| + | } |
| @@ -0,0 +1,389 @@ | |
| + | /* |
| + | * rt_names.c rtnetlink names DB. |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <string.h> |
| + | #include <sys/time.h> |
| + | #include <stdint.h> |
| + | |
| + | static void rtnl_tab_initialize(char *file, char **tab, int size) |
| + | { |
| + | char buf[512]; |
| + | FILE *fp; |
| + | |
| + | fp = fopen(file, "r"); |
| + | if (!fp) |
| + | return; |
| + | while (fgets(buf, sizeof(buf), fp)) { |
| + | char *p = buf; |
| + | int id; |
| + | char namebuf[512]; |
| + | |
| + | while (*p == ' ' || *p == '\t') |
| + | p++; |
| + | if (*p == '#' || *p == '\n' || *p == 0) |
| + | continue; |
| + | if (sscanf(p, "0x%x %s\n", &id, namebuf) != 2 && |
| + | sscanf(p, "0x%x %s #", &id, namebuf) != 2 && |
| + | sscanf(p, "%d %s\n", &id, namebuf) != 2 && |
| + | sscanf(p, "%d %s #", &id, namebuf) != 2) { |
| + | fprintf(stderr, "Database %s is corrupted at %s\n", |
| + | file, p); |
| + | return; |
| + | } |
| + | |
| + | if (id<0 || id>size) |
| + | continue; |
| + | |
| + | tab[id] = strdup(namebuf); |
| + | } |
| + | fclose(fp); |
| + | } |
| + | |
| + | |
| + | static char * rtnl_rtprot_tab[256] = { |
| + | "none", |
| + | "redirect", |
| + | "kernel", |
| + | "boot", |
| + | "static", |
| + | NULL, |
| + | NULL, |
| + | NULL, |
| + | "gated", |
| + | "ra", |
| + | "mrt", |
| + | "zebra", |
| + | "bird", |
| + | }; |
| + | |
| + | |
| + | |
| + | static int rtnl_rtprot_init; |
| + | |
| + | static void rtnl_rtprot_initialize(void) |
| + | { |
| + | rtnl_rtprot_init = 1; |
| + | rtnl_tab_initialize("/etc/iproute2/rt_protos", |
| + | rtnl_rtprot_tab, 256); |
| + | } |
| + | |
| + | char * rtnl_rtprot_n2a(int id, char *buf, int len) |
| + | { |
| + | if (id<0 || id>=256) { |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | if (!rtnl_rtprot_tab[id]) { |
| + | if (!rtnl_rtprot_init) |
| + | rtnl_rtprot_initialize(); |
| + | } |
| + | if (rtnl_rtprot_tab[id]) |
| + | return rtnl_rtprot_tab[id]; |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | |
| + | int rtnl_rtprot_a2n(uint32_t *id, char *arg) |
| + | { |
| + | static char *cache = NULL; |
| + | static unsigned long res; |
| + | char *end; |
| + | int i; |
| + | |
| + | if (cache && strcmp(cache, arg) == 0) { |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | if (!rtnl_rtprot_init) |
| + | rtnl_rtprot_initialize(); |
| + | |
| + | for (i=0; i<256; i++) { |
| + | if (rtnl_rtprot_tab[i] && |
| + | strcmp(rtnl_rtprot_tab[i], arg) == 0) { |
| + | cache = rtnl_rtprot_tab[i]; |
| + | res = i; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | res = strtoul(arg, &end, 0); |
| + | if (!end || end == arg || *end || res > 255) |
| + | return -1; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | |
| + | static char * rtnl_rtscope_tab[256] = { |
| + | "global", |
| + | }; |
| + | |
| + | static int rtnl_rtscope_init; |
| + | |
| + | static void rtnl_rtscope_initialize(void) |
| + | { |
| + | rtnl_rtscope_init = 1; |
| + | rtnl_rtscope_tab[255] = "nowhere"; |
| + | rtnl_rtscope_tab[254] = "host"; |
| + | rtnl_rtscope_tab[253] = "link"; |
| + | rtnl_rtscope_tab[200] = "site"; |
| + | rtnl_tab_initialize("/etc/iproute2/rt_scopes", |
| + | rtnl_rtscope_tab, 256); |
| + | } |
| + | |
| + | char * rtnl_rtscope_n2a(int id, char *buf, int len) |
| + | { |
| + | if (id<0 || id>=256) { |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | if (!rtnl_rtscope_tab[id]) { |
| + | if (!rtnl_rtscope_init) |
| + | rtnl_rtscope_initialize(); |
| + | } |
| + | if (rtnl_rtscope_tab[id]) |
| + | return rtnl_rtscope_tab[id]; |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | |
| + | int rtnl_rtscope_a2n(uint32_t *id, char *arg) |
| + | { |
| + | static char *cache = NULL; |
| + | static unsigned long res; |
| + | char *end; |
| + | int i; |
| + | |
| + | if (cache && strcmp(cache, arg) == 0) { |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | if (!rtnl_rtscope_init) |
| + | rtnl_rtscope_initialize(); |
| + | |
| + | for (i=0; i<256; i++) { |
| + | if (rtnl_rtscope_tab[i] && |
| + | strcmp(rtnl_rtscope_tab[i], arg) == 0) { |
| + | cache = rtnl_rtscope_tab[i]; |
| + | res = i; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | res = strtoul(arg, &end, 0); |
| + | if (!end || end == arg || *end || res > 255) |
| + | return -1; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | |
| + | static char * rtnl_rtrealm_tab[256] = { |
| + | "unknown", |
| + | }; |
| + | |
| + | static int rtnl_rtrealm_init; |
| + | |
| + | static void rtnl_rtrealm_initialize(void) |
| + | { |
| + | rtnl_rtrealm_init = 1; |
| + | rtnl_tab_initialize("/etc/iproute2/rt_realms", |
| + | rtnl_rtrealm_tab, 256); |
| + | } |
| + | |
| + | char * rtnl_rtrealm_n2a(int id, char *buf, int len) |
| + | { |
| + | if (id<0 || id>=256) { |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | if (!rtnl_rtrealm_tab[id]) { |
| + | if (!rtnl_rtrealm_init) |
| + | rtnl_rtrealm_initialize(); |
| + | } |
| + | if (rtnl_rtrealm_tab[id]) |
| + | return rtnl_rtrealm_tab[id]; |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | |
| + | |
| + | int rtnl_rtrealm_a2n(uint32_t *id, char *arg) |
| + | { |
| + | static char *cache = NULL; |
| + | static unsigned long res; |
| + | char *end; |
| + | int i; |
| + | |
| + | if (cache && strcmp(cache, arg) == 0) { |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | if (!rtnl_rtrealm_init) |
| + | rtnl_rtrealm_initialize(); |
| + | |
| + | for (i=0; i<256; i++) { |
| + | if (rtnl_rtrealm_tab[i] && |
| + | strcmp(rtnl_rtrealm_tab[i], arg) == 0) { |
| + | cache = rtnl_rtrealm_tab[i]; |
| + | res = i; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | res = strtoul(arg, &end, 0); |
| + | if (!end || end == arg || *end || res > 255) |
| + | return -1; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | |
| + | static char * rtnl_rttable_tab[256] = { |
| + | "unspec", |
| + | }; |
| + | |
| + | static int rtnl_rttable_init; |
| + | |
| + | static void rtnl_rttable_initialize(void) |
| + | { |
| + | rtnl_rttable_init = 1; |
| + | rtnl_rttable_tab[255] = "local"; |
| + | rtnl_rttable_tab[254] = "main"; |
| + | rtnl_tab_initialize("/etc/iproute2/rt_tables", |
| + | rtnl_rttable_tab, 256); |
| + | } |
| + | |
| + | char * rtnl_rttable_n2a(int id, char *buf, int len) |
| + | { |
| + | if (id<0 || id>=256) { |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | if (!rtnl_rttable_tab[id]) { |
| + | if (!rtnl_rttable_init) |
| + | rtnl_rttable_initialize(); |
| + | } |
| + | if (rtnl_rttable_tab[id]) |
| + | return rtnl_rttable_tab[id]; |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | |
| + | int rtnl_rttable_a2n(uint32_t *id, char *arg) |
| + | { |
| + | static char *cache = NULL; |
| + | static unsigned long res; |
| + | char *end; |
| + | int i; |
| + | |
| + | if (cache && strcmp(cache, arg) == 0) { |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | if (!rtnl_rttable_init) |
| + | rtnl_rttable_initialize(); |
| + | |
| + | for (i=0; i<256; i++) { |
| + | if (rtnl_rttable_tab[i] && |
| + | strcmp(rtnl_rttable_tab[i], arg) == 0) { |
| + | cache = rtnl_rttable_tab[i]; |
| + | res = i; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | i = strtoul(arg, &end, 0); |
| + | if (!end || end == arg || *end || i > 255) |
| + | return -1; |
| + | *id = i; |
| + | return 0; |
| + | } |
| + | |
| + | |
| + | static char * rtnl_rtdsfield_tab[256] = { |
| + | "0", |
| + | }; |
| + | |
| + | static int rtnl_rtdsfield_init; |
| + | |
| + | static void rtnl_rtdsfield_initialize(void) |
| + | { |
| + | rtnl_rtdsfield_init = 1; |
| + | rtnl_tab_initialize("/etc/iproute2/rt_dsfield", |
| + | rtnl_rtdsfield_tab, 256); |
| + | } |
| + | |
| + | char * rtnl_dsfield_n2a(int id, char *buf, int len) |
| + | { |
| + | if (id<0 || id>=256) { |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | if (!rtnl_rtdsfield_tab[id]) { |
| + | if (!rtnl_rtdsfield_init) |
| + | rtnl_rtdsfield_initialize(); |
| + | } |
| + | if (rtnl_rtdsfield_tab[id]) |
| + | return rtnl_rtdsfield_tab[id]; |
| + | snprintf(buf, len, "0x%02x", id); |
| + | return buf; |
| + | } |
| + | |
| + | |
| + | int rtnl_dsfield_a2n(uint32_t *id, char *arg) |
| + | { |
| + | static char *cache = NULL; |
| + | static unsigned long res; |
| + | char *end; |
| + | int i; |
| + | |
| + | if (cache && strcmp(cache, arg) == 0) { |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | if (!rtnl_rtdsfield_init) |
| + | rtnl_rtdsfield_initialize(); |
| + | |
| + | for (i=0; i<256; i++) { |
| + | if (rtnl_rtdsfield_tab[i] && |
| + | strcmp(rtnl_rtdsfield_tab[i], arg) == 0) { |
| + | cache = rtnl_rtdsfield_tab[i]; |
| + | res = i; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | } |
| + | |
| + | res = strtoul(arg, &end, 16); |
| + | if (!end || end == arg || *end || res > 255) |
| + | return -1; |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| @@ -0,0 +1,30 @@ | |
| + | #ifndef RT_NAMES_H_ |
| + | #define RT_NAMES_H_ 1 |
| + | |
| + | #include <stdint.h> |
| + | |
| + | const char* rtnl_rtprot_n2a(int id, char *buf, int len); |
| + | const char* rtnl_rtscope_n2a(int id, char *buf, int len); |
| + | const char* rtnl_rttable_n2a(int id, char *buf, int len); |
| + | const char* rtnl_rtrealm_n2a(int id, char *buf, int len); |
| + | const char* rtnl_dsfield_n2a(int id, char *buf, int len); |
| + | int rtnl_rtprot_a2n(int *id, char *arg); |
| + | int rtnl_rtscope_a2n(int *id, char *arg); |
| + | int rtnl_rttable_a2n(int *id, char *arg); |
| + | int rtnl_rtrealm_a2n(uint32_t *id, char *arg); |
| + | int rtnl_dsfield_a2n(uint32_t *id, char *arg); |
| + | |
| + | const char *inet_proto_n2a(int proto, char *buf, int len); |
| + | int inet_proto_a2n(char *buf); |
| + | |
| + | |
| + | const char * ll_type_n2a(int type, char *buf, int len); |
| + | |
| + | const char *ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen); |
| + | int ll_addr_a2n(unsigned char *lladdr, int len, char *arg); |
| + | |
| + | const char * ll_proto_n2a(unsigned short id, char *buf, int len); |
| + | int ll_proto_a2n(unsigned short *id, char *buf); |
| + | |
| + | |
| + | #endif |
| @@ -0,0 +1,116 @@ | |
| + | /* |
| + | * rtm_map.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <string.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | |
| + | #include "rt_names.h" |
| + | #include "utils.h" |
| + | |
| + | char *rtnl_rtntype_n2a(int id, char *buf, int len) |
| + | { |
| + | switch (id) { |
| + | case RTN_UNSPEC: |
| + | return "none"; |
| + | case RTN_UNICAST: |
| + | return "unicast"; |
| + | case RTN_LOCAL: |
| + | return "local"; |
| + | case RTN_BROADCAST: |
| + | return "broadcast"; |
| + | case RTN_ANYCAST: |
| + | return "anycast"; |
| + | case RTN_MULTICAST: |
| + | return "multicast"; |
| + | case RTN_BLACKHOLE: |
| + | return "blackhole"; |
| + | case RTN_UNREACHABLE: |
| + | return "unreachable"; |
| + | case RTN_PROHIBIT: |
| + | return "prohibit"; |
| + | case RTN_THROW: |
| + | return "throw"; |
| + | case RTN_NAT: |
| + | return "nat"; |
| + | case RTN_XRESOLVE: |
| + | return "xresolve"; |
| + | default: |
| + | snprintf(buf, len, "%d", id); |
| + | return buf; |
| + | } |
| + | } |
| + | |
| + | |
| + | int rtnl_rtntype_a2n(int *id, char *arg) |
| + | { |
| + | char *end; |
| + | unsigned long res; |
| + | |
| + | if (strcmp(arg, "local") == 0) |
| + | res = RTN_LOCAL; |
| + | else if (strcmp(arg, "nat") == 0) |
| + | res = RTN_NAT; |
| + | else if (matches(arg, "broadcast") == 0 || |
| + | strcmp(arg, "brd") == 0) |
| + | res = RTN_BROADCAST; |
| + | else if (matches(arg, "anycast") == 0) |
| + | res = RTN_ANYCAST; |
| + | else if (matches(arg, "multicast") == 0) |
| + | res = RTN_MULTICAST; |
| + | else if (matches(arg, "prohibit") == 0) |
| + | res = RTN_PROHIBIT; |
| + | else if (matches(arg, "unreachable") == 0) |
| + | res = RTN_UNREACHABLE; |
| + | else if (matches(arg, "blackhole") == 0) |
| + | res = RTN_BLACKHOLE; |
| + | else if (matches(arg, "xresolve") == 0) |
| + | res = RTN_XRESOLVE; |
| + | else if (matches(arg, "unicast") == 0) |
| + | res = RTN_UNICAST; |
| + | else if (strcmp(arg, "throw") == 0) |
| + | res = RTN_THROW; |
| + | else { |
| + | res = strtoul(arg, &end, 0); |
| + | if (!end || end == arg || *end || res > 255) |
| + | return -1; |
| + | } |
| + | *id = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_rt_realms(__u32 *realms, char *arg) |
| + | { |
| + | __u32 realm = 0; |
| + | char *p = strchr(arg, '/'); |
| + | |
| + | *realms = 0; |
| + | if (p) { |
| + | *p = 0; |
| + | if (rtnl_rtrealm_a2n(realms, arg)) { |
| + | *p = '/'; |
| + | return -1; |
| + | } |
| + | *realms <<= 16; |
| + | *p = '/'; |
| + | arg = p+1; |
| + | } |
| + | if (*arg && rtnl_rtrealm_a2n(&realm, arg)) |
| + | return -1; |
| + | *realms |= realm; |
| + | return 0; |
| + | } |
| @@ -0,0 +1,10 @@ | |
| + | #ifndef __RTM_MAP_H__ |
| + | #define __RTM_MAP_H__ 1 |
| + | |
| + | char *rtnl_rtntype_n2a(int id, char *buf, int len); |
| + | int rtnl_rtntype_a2n(int *id, char *arg); |
| + | |
| + | int get_rt_realms(__u32 *realms, char *arg); |
| + | |
| + | |
| + | #endif /* __RTM_MAP_H__ */ |
| @@ -0,0 +1,368 @@ | |
| + | /* |
| + | * utils.c |
| + | * |
| + | * This program is free software; you can redistribute it and/or |
| + | * modify it under the terms of the GNU General Public License |
| + | * as published by the Free Software Foundation; either version |
| + | * 2 of the License, or (at your option) any later version. |
| + | * |
| + | * Authors: Alexey Kuznetsov, <[email protected]> |
| + | * |
| + | * |
| + | * Changes: |
| + | * |
| + | * Rani Assaf <[email protected]> 980929: resolve addresses |
| + | */ |
| + | |
| + | #include <stdio.h> |
| + | #include <stdlib.h> |
| + | #include <unistd.h> |
| + | #include <syslog.h> |
| + | #include <fcntl.h> |
| + | #include <sys/socket.h> |
| + | #include <netinet/in.h> |
| + | #include <string.h> |
| + | #include <netdb.h> |
| + | #include <arpa/inet.h> |
| + | #include <resolv.h> |
| + | #include "./linux/pkt_sched.h" |
| + | |
| + | #include "utils.h" |
| + | |
| + | int get_integer(int *val, char *arg, int base) |
| + | { |
| + | long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtol(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > INT_MAX || res < INT_MIN) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_unsigned(unsigned *val, char *arg, int base) |
| + | { |
| + | unsigned long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtoul(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > UINT_MAX) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_u32(__u32 *val, char *arg, int base) |
| + | { |
| + | unsigned long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtoul(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_u16(__u16 *val, char *arg, int base) |
| + | { |
| + | unsigned long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtoul(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > 0xFFFF) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_u8(__u8 *val, char *arg, int base) |
| + | { |
| + | unsigned long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtoul(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > 0xFF) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_s16(__s16 *val, char *arg, int base) |
| + | { |
| + | long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtol(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > 0x7FFF || res < -0x8000) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_s8(__s8 *val, char *arg, int base) |
| + | { |
| + | long res; |
| + | char *ptr; |
| + | |
| + | if (!arg || !*arg) |
| + | return -1; |
| + | res = strtol(arg, &ptr, base); |
| + | if (!ptr || ptr == arg || *ptr || res > 0x7F || res < -0x80) |
| + | return -1; |
| + | *val = res; |
| + | return 0; |
| + | } |
| + | |
| + | int get_addr_1(inet_prefix *addr, char *name, int family) |
| + | { |
| + | char *cp; |
| + | unsigned char *ap = (unsigned char*)addr->data; |
| + | int i; |
| + | |
| + | memset(addr, 0, sizeof(*addr)); |
| + | |
| + | if (strcmp(name, "default") == 0 || |
| + | strcmp(name, "all") == 0 || |
| + | strcmp(name, "any") == 0) { |
| + | addr->family = family; |
| + | addr->bytelen = (family == AF_INET6 ? 16 : 4); |
| + | addr->bitlen = -1; |
| + | return 0; |
| + | } |
| + | |
| + | if (strchr(name, ':')) { |
| + | addr->family = AF_INET6; |
| + | if (family != AF_UNSPEC && family != AF_INET6) |
| + | return -1; |
| + | if (inet_pton(AF_INET6, name, addr->data) <= 0) |
| + | return -1; |
| + | addr->bytelen = 16; |
| + | addr->bitlen = -1; |
| + | return 0; |
| + | } |
| + | |
| + | addr->family = AF_INET; |
| + | if (family != AF_UNSPEC && family != AF_INET) |
| + | return -1; |
| + | addr->bytelen = 4; |
| + | addr->bitlen = -1; |
| + | for (cp=name, i=0; *cp; cp++) { |
| + | if (*cp <= '9' && *cp >= '0') { |
| + | ap[i] = 10*ap[i] + (*cp-'0'); |
| + | continue; |
| + | } |
| + | if (*cp == '.' && ++i <= 3) |
| + | continue; |
| + | return -1; |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | int get_prefix_1(inet_prefix *dst, char *arg, int family) |
| + | { |
| + | int err; |
| + | unsigned plen; |
| + | char *slash; |
| + | |
| + | memset(dst, 0, sizeof(*dst)); |
| + | |
| + | if (strcmp(arg, "default") == 0 || strcmp(arg, "any") == 0) { |
| + | dst->family = family; |
| + | dst->bytelen = 0; |
| + | dst->bitlen = 0; |
| + | return 0; |
| + | } |
| + | |
| + | slash = strchr(arg, '/'); |
| + | if (slash) |
| + | *slash = 0; |
| + | err = get_addr_1(dst, arg, family); |
| + | if (err == 0) { |
| + | switch(dst->family) { |
| + | case AF_INET6: |
| + | dst->bitlen = 128; |
| + | break; |
| + | default: |
| + | case AF_INET: |
| + | dst->bitlen = 32; |
| + | } |
| + | if (slash) { |
| + | if (get_integer(&plen, slash+1, 0) || plen > dst->bitlen) { |
| + | err = -1; |
| + | goto done; |
| + | } |
| + | dst->bitlen = plen; |
| + | } |
| + | } |
| + | done: |
| + | if (slash) |
| + | *slash = '/'; |
| + | return err; |
| + | } |
| + | |
| + | int get_addr(inet_prefix *dst, char *arg, int family) |
| + | { |
| + | if (family == AF_PACKET) { |
| + | fprintf(stderr, "Error: \"%s\" may be inet address, but it is not allowed in this context.\n", arg); |
| + | exit(1); |
| + | } |
| + | if (get_addr_1(dst, arg, family)) { |
| + | fprintf(stderr, "Error: an inet address is expected rather than \"%s\".\n", arg); |
| + | exit(1); |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | int get_prefix(inet_prefix *dst, char *arg, int family) |
| + | { |
| + | if (family == AF_PACKET) { |
| + | fprintf(stderr, "Error: \"%s\" may be inet prefix, but it is not allowed in this context.\n", arg); |
| + | exit(1); |
| + | } |
| + | if (get_prefix_1(dst, arg, family)) { |
| + | fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", arg); |
| + | exit(1); |
| + | } |
| + | return 0; |
| + | } |
| + | |
| + | __u32 get_addr32(char *name) |
| + | { |
| + | inet_prefix addr; |
| + | if (get_addr_1(&addr, name, AF_INET)) { |
| + | fprintf(stderr, "Error: an IP address is expected rather than \"%s\"\n", name); |
| + | exit(1); |
| + | } |
| + | return addr.data[0]; |
| + | } |
| + | |
| + | void incomplete_command() |
| + | { |
| + | fprintf(stderr, "Command line is not complete. Try option \"help\"\n"); |
| + | exit(-1); |
| + | } |
| + | |
| + | void invarg(char *msg, char *arg) |
| + | { |
| + | fprintf(stderr, "Error: argument \"%s\" is wrong: %s\n", arg, msg); |
| + | exit(-1); |
| + | } |
| + | |
| + | void duparg(char *key, char *arg) |
| + | { |
| + | fprintf(stderr, "Error: duplicate \"%s\": \"%s\" is the second value.\n", key, arg); |
| + | exit(-1); |
| + | } |
| + | |
| + | void duparg2(char *key, char *arg) |
| + | { |
| + | fprintf(stderr, "Error: either \"%s\" is duplicate, or \"%s\" is a garbage.\n", key, arg); |
| + | exit(-1); |
| + | } |
| + | |
| + | int matches(char *cmd, char *pattern) |
| + | { |
| + | int len = strlen(cmd); |
| + | if (len > strlen(pattern)) |
| + | return -1; |
| + | return memcmp(pattern, cmd, len); |
| + | } |
| + | |
| + | int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits) |
| + | { |
| + | __u32 *a1 = a->data; |
| + | __u32 *a2 = b->data; |
| + | int words = bits >> 0x05; |
| + | |
| + | bits &= 0x1f; |
| + | |
| + | if (words) |
| + | if (memcmp(a1, a2, words << 2)) |
| + | return -1; |
| + | |
| + | if (bits) { |
| + | __u32 w1, w2; |
| + | __u32 mask; |
| + | |
| + | w1 = a1[words]; |
| + | w2 = a2[words]; |
| + | |
| + | mask = htonl((0xffffffff) << (0x20 - bits)); |
| + | |
| + | if ((w1 ^ w2) & mask) |
| + | return 1; |
| + | } |
| + | |
| + | return 0; |
| + | } |
| + | |
| + | int __iproute2_hz_internal; |
| + | |
| + | int __get_hz(void) |
| + | { |
| + | int hz = 0; |
| + | FILE *fp = fopen("/proc/net/psched", "r"); |
| + | |
| + | if (fp) { |
| + | unsigned nom, denom; |
| + | if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2) |
| + | if (nom == 1000000) |
| + | hz = denom; |
| + | fclose(fp); |
| + | } |
| + | if (hz) |
| + | return hz; |
| + | return HZ; |
| + | } |
| + | |
| + | const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen) |
| + | { |
| + | switch (af) { |
| + | case AF_INET: |
| + | case AF_INET6: |
| + | return inet_ntop(af, addr, buf, buflen); |
| + | default: |
| + | return "???"; |
| + | } |
| + | } |
| + | |
| + | |
| + | const char *format_host(int af, int len, void *addr, char *buf, int buflen) |
| + | { |
| + | #ifdef RESOLVE_HOSTNAMES |
| + | if (resolve_hosts) { |
| + | struct hostent *h_ent; |
| + | if (len <= 0) { |
| + | switch (af) { |
| + | case AF_INET: |
| + | len = 4; |
| + | break; |
| + | case AF_INET6: |
| + | len = 16; |
| + | break; |
| + | default: ; |
| + | } |
| + | } |
| + | if (len > 0 && |
| + | (h_ent = gethostbyaddr(addr, len, af)) != NULL) { |
| + | snprintf(buf, buflen-1, "%s", h_ent->h_name); |
| + | return buf; |
| + | } |
| + | } |
| + | #endif |
| + | return rt_addr_n2a(af, len, addr, buf, buflen); |
| + | } |
| @@ -0,0 +1,101 @@ | |
| + | #ifndef __UTILS_H__ |
| + | #define __UTILS_H__ 1 |
| + | |
| + | #include <asm/types.h> |
| + | #include <resolv.h> |
| + | |
| + | #include "libnetlink.h" |
| + | #include "ll_map.h" |
| + | #include "rtm_map.h" |
| + | |
| + | extern int preferred_family; |
| + | extern int show_stats; |
| + | extern int show_details; |
| + | extern int show_raw; |
| + | extern int resolve_hosts; |
| + | extern int oneline; |
| + | extern char * _SL_; |
| + | |
| + | #ifndef IPPROTO_ESP |
| + | #define IPPROTO_ESP 50 |
| + | #endif |
| + | #ifndef IPPROTO_AH |
| + | #define IPPROTO_AH 51 |
| + | #endif |
| + | |
| + | #define SPRINT_BSIZE 64 |
| + | #define SPRINT_BUF(x) char x[SPRINT_BSIZE] |
| + | |
| + | extern void incomplete_command(void) __attribute__((noreturn)); |
| + | |
| + | #define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0) |
| + | |
| + | typedef struct |
| + | { |
| + | __u8 family; |
| + | __u8 bytelen; |
| + | __s16 bitlen; |
| + | __u32 data[4]; |
| + | } inet_prefix; |
| + | |
| + | #define DN_MAXADDL 20 |
| + | #ifndef AF_DECnet |
| + | #define AF_DECnet 12 |
| + | #endif |
| + | |
| + | struct dn_naddr |
| + | { |
| + | unsigned short a_len; |
| + | unsigned char a_addr[DN_MAXADDL]; |
| + | }; |
| + | |
| + | #define IPX_NODE_LEN 6 |
| + | |
| + | struct ipx_addr { |
| + | u_int32_t ipx_net; |
| + | u_int8_t ipx_node[IPX_NODE_LEN]; |
| + | }; |
| + | |
| + | extern __u32 get_addr32(char *name); |
| + | extern int get_addr_1(inet_prefix *dst, char *arg, int family); |
| + | extern int get_prefix_1(inet_prefix *dst, char *arg, int family); |
| + | extern int get_addr(inet_prefix *dst, char *arg, int family); |
| + | extern int get_prefix(inet_prefix *dst, char *arg, int family); |
| + | |
| + | extern int get_integer(int *val, char *arg, int base); |
| + | extern int get_unsigned(unsigned *val, char *arg, int base); |
| + | #define get_byte get_u8 |
| + | #define get_ushort get_u16 |
| + | #define get_short get_s16 |
| + | extern int get_u32(__u32 *val, char *arg, int base); |
| + | extern int get_u16(__u16 *val, char *arg, int base); |
| + | extern int get_s16(__s16 *val, char *arg, int base); |
| + | extern int get_u8(__u8 *val, char *arg, int base); |
| + | extern int get_s8(__s8 *val, char *arg, int base); |
| + | |
| + | extern const char *format_host(int af, int len, void *addr, char *buf, int buflen); |
| + | extern const char *rt_addr_n2a(int af, int len, void *addr, char *buf, int buflen); |
| + | |
| + | void invarg(char *, char *) __attribute__((noreturn)); |
| + | void duparg(char *, char *) __attribute__((noreturn)); |
| + | void duparg2(char *, char *) __attribute__((noreturn)); |
| + | int matches(char *arg, char *pattern); |
| + | extern int inet_addr_match(inet_prefix *a, inet_prefix *b, int bits); |
| + | |
| + | const char *dnet_ntop(int af, const void *addr, char *str, size_t len); |
| + | int dnet_pton(int af, const char *src, void *addr); |
| + | |
| + | const char *ipx_ntop(int af, const void *addr, char *str, size_t len); |
| + | int ipx_pton(int af, const char *src, void *addr); |
| + | |
| + | extern int __iproute2_hz_internal; |
| + | extern int __get_hz(void); |
| + | |
| + | static __inline__ int get_hz(void) |
| + | { |
| + | if (__iproute2_hz_internal == 0) |
| + | __iproute2_hz_internal = __get_hz(); |
| + | return __iproute2_hz_internal; |
| + | } |
| + | |
| + | #endif /* __UTILS_H__ */ |