diff --git a/include/libnetlink.h b/include/libnetlink.h index 0e02468..58b4da9 100644 --- a/include/libnetlink.h +++ b/include/libnetlink.h @@ -97,5 +97,15 @@ extern int rtnl_from_file(FILE *, rtnl_filter_t handler, #define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg)) #endif +#ifndef MPA_RTA +#define MPA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct mpmsg)))) +#endif + +#ifndef MPLA_RTA +#define MPLA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct mplmsg)))) +#endif + #endif /* __LIBNETLINK_H__ */ diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index 3b90a97..caf91e7 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -107,6 +107,20 @@ enum { RTM_GETADDRLABEL, #define RTM_GETADDRLABEL RTM_GETADDRLABEL + RTM_NEWMESHPATH = 76, +#define RTM_NEWMESHPATH RTM_NEWMESHPATH + RTM_DELMESHPATH, +#define RTM_DELMESHPATH RTM_DELMESHPATH + RTM_GETMESHPATH, +#define RTM_GETMESHPATH RTM_GETMESHPATH + + RTM_NEWMESHPEERLINK = 80, +#define RTM_NEWMESHPEERLINK RTM_NEWMESHPEERLINK + RTM_DELMESHPEERLINK, +#define RTM_DELMESHPEERLINK RTM_DELMESHPEERLINK + RTM_GETMESHPEERLINK, +#define RTM_GETMESHPEERLINK RTM_GETMESHPEERLINK + __RTM_MAX, #define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) }; @@ -141,8 +155,60 @@ struct rtattr #define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) #define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) +/****************************************************************************** + * Definitions used for mesh path table administration. + ****/ + +struct mpmsg +{ + unsigned int mpm_flags; + int ifa_index; +}; + +enum mpattr_type_t +{ + MPA_UNSPEC, + MPA_DST, + MPA_NEXT_HOP, + MPA_FRAME_QLEN, + MPA_DSN, + MPA_METRIC, + MPA_LIFETIME, + MPA_FLAGS, + MPA_DISCOVERY_TIMEOUT, + MPA_DISCOVERY_RETRIES, + __MPA_MAX, +}; + +#define MPA_MAX (__MPA_MAX - 1) +/****************************************************************************** + * Definitions used for mesh peer link table administration. + ****/ + +/* mpl_flags */ +#define MPL_F_CREATE 0x100 +#define MPL_F_OPEN 0x200 /* Start the mesh peer link establishment */ +#define MPL_F_BLOCK 0x400 /* Block all traffic from this peer */ + +struct mplmsg +{ + unsigned int mplm_flags; + int ifa_index; +}; + +enum mplattr_type_t +{ + MPLA_UNSPEC, + MPLA_PEER_ADDR, + MPLA_STATE, + MPLA_LLID, + MPLA_PLID, + __MPLA_MAX, +}; + +#define MPLA_MAX (__MPLA_MAX - 1) /****************************************************************************** * Definitions used in routing table administration. diff --git a/ip/Makefile b/ip/Makefile index b427d58..e1b0a3b 100644 --- a/ip/Makefile +++ b/ip/Makefile @@ -2,7 +2,7 @@ IPOBJ=ip.o ipaddress.o iproute.o iprule.o \ rtm_map.o iptunnel.o ip6tunnel.o tunnel.o ipneigh.o ipntable.o iplink.o \ ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o \ ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \ - iplink_vlan.o link_veth.o + iplink_vlan.o link_veth.o ipmshpath.o ipmshplink.o RTMONOBJ=rtmon.o diff --git a/ip/ip.c b/ip/ip.c index aeb8c68..349b714 100644 --- a/ip/ip.c +++ b/ip/ip.c @@ -47,7 +47,7 @@ static void usage(void) "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n" " ip [ -force ] [-batch filename\n" "where OBJECT := { link | addr | route | rule | neigh | ntable | tunnel |\n" -" maddr | mroute | monitor | xfrm }\n" +" maddr | mroute | monitor | xfrm | mshpath | mshplink }\n" " OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n" " -f[amily] { inet | inet6 | ipx | dnet | link } |\n" " -o[neline] | -t[imestamp] }\n"); @@ -77,6 +77,8 @@ static const struct cmd { { "monitor", do_ipmonitor }, { "xfrm", do_xfrm }, { "mroute", do_multiroute }, + { "mshpath", do_ipmshpath }, + { "mshplink", do_ipmshpl }, { "help", do_help }, { 0 } }; diff --git a/ip/ip_common.h b/ip/ip_common.h index 39f2507..3c1588d 100644 --- a/ip/ip_common.h +++ b/ip/ip_common.h @@ -33,6 +33,8 @@ 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); +extern int do_ipmshpath(int argc, char **argv); +extern int do_ipmshpl(int argc, char **argv); extern int do_xfrm(int argc, char **argv); static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb) diff --git a/ip/ipmshpath.c b/ip/ipmshpath.c new file mode 100644 index 0000000..41f7bce --- /dev/null +++ b/ip/ipmshpath.c @@ -0,0 +1,262 @@ +/* + * ipmshpath.c "ip mshpath". + * + * 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: Luis Carlos Cobo, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip mshpath add dev DEV dst HW_ADDR next_hop HWADDR\n"); + fprintf(stderr, " ip mshpath show\n"); + fprintf(stderr, " ip mshpath delete dev DEV dst HW_ADDR\n"); + exit(-1); +} + +static int ipmshpath_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct mpmsg mpm; + char buf[256]; + } req; + char *dst = NULL; + char *nh = NULL; + char *dev = NULL; + char addrbuf[20]; + int addrlen; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct mpmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|flags; + req.n.nlmsg_type = cmd; + + while (argc > 0) { + if (matches(*argv, "dst") == 0) { + NEXT_ARG(); + if (dst) + duparg("dst", *argv); + dst = *argv; + } else if (matches(*argv, "next_hop") == 0) { + NEXT_ARG(); + if (nh) + duparg("next_hop", *argv); + nh = *argv; + } else if (matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (dev) + duparg("dev", *argv); + dev = *argv; + } else { + if (matches(*argv, "help") == 0) + NEXT_ARG(); + } + argc--; argv++; + } + + if (cmd == RTM_NEWMESHPATH) { + if (dev == NULL || nh == NULL || dst == NULL) { + fprintf(stderr, "Device, destination and next hop are required arguments.\n"); + exit(-1); + } + + + addrlen = ll_addr_a2n(addrbuf, sizeof(addrbuf), dst); + if (addrlen != 6) { + fprintf(stderr, "Incorrect destination address length\n"); + return -1; + } + addattr_l(&req.n, sizeof(req), MPA_DST, addrbuf, addrlen); + + addrlen = ll_addr_a2n(addrbuf, sizeof(addrbuf), nh); + if (addrlen != 6) { + fprintf(stderr, "Incorrect destination address length\n"); + return -1; + } + addattr_l(&req.n, sizeof(req), MPA_NEXT_HOP, addrbuf, addrlen); + + ll_init_map(&rth); + + if ((req.mpm.ifa_index = ll_name_to_index(dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", dev); + return -1; + } + } + + else if (cmd == RTM_DELMESHPATH) { + if (dev == NULL || dst == NULL) { + fprintf(stderr, "Device and destination address are required arguments.\n"); + exit(-1); + } + + addrlen = ll_addr_a2n(addrbuf, sizeof(addrbuf), dst); + if (addrlen != 6) { + fprintf(stderr, "Incorrect destination address length\n"); + return -1; + } + addattr_l(&req.n, sizeof(req), MPA_DST, addrbuf, addrlen); + + ll_init_map(&rth); + + if ((req.mpm.ifa_index = ll_name_to_index(dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", dev); + return -1; + } + } + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +int print_mshpath(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct mpmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + struct rtattr *tb[MPA_MAX+1]; + SPRINT_BUF(b1); + __u8 flags, discovery_retries; + __u32 dsn, metric, lifetime, discovery_timeout, frame_queue_len; + + if (n->nlmsg_type != RTM_NEWMESHPATH && n->nlmsg_type != RTM_DELMESHPATH) { + fprintf(stderr, "Not RTM_NEWMESHPATH: %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; + } + + parse_rtattr(tb, MPA_MAX, MPA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (!tb[MPA_DST]) { + perror("Received path without destination"); + return(1); + } + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[MPA_DST]), + RTA_PAYLOAD(tb[MPA_DST]), + ll_index_to_type(r->ifa_index), + b1, sizeof(b1))); + if (!tb[MPA_NEXT_HOP]) { + perror("Received path without next hop"); + return(1); + } + if (!tb[MPA_DSN]) { + perror("Received path without dsn"); + return(1); + } + dsn = *(__u32 *) RTA_DATA(tb[MPA_DSN]); + if (!tb[MPA_METRIC]) { + perror("Received path without metric"); + return(1); + } + metric = *(__u32 *) RTA_DATA(tb[MPA_METRIC]); + if (!tb[MPA_FLAGS]) { + perror("Received path without flags"); + return(1); + } + flags = *(__u8 *) RTA_DATA(tb[MPA_FLAGS]); + if (!tb[MPA_LIFETIME]) { + perror("Received path without lifetime"); + return(1); + } + lifetime = *(__u32 *) RTA_DATA(tb[MPA_LIFETIME]); + if (!tb[MPA_DISCOVERY_RETRIES]) { + perror("Received path without discovery_retries"); + return(1); + } + discovery_retries = *(__u32 *) RTA_DATA(tb[MPA_DISCOVERY_RETRIES]); + if (!tb[MPA_DISCOVERY_TIMEOUT]) { + perror("Received path without discovery_timeout"); + return(1); + } + discovery_timeout = *(__u8 *) RTA_DATA(tb[MPA_DISCOVERY_TIMEOUT]); + if (!tb[MPA_FRAME_QLEN]) { + perror("Received path without frame_queue_len"); + return(1); + } + frame_queue_len = *(__u32 *) RTA_DATA(tb[MPA_FRAME_QLEN]); + + fprintf(fp, " %s", ll_addr_n2a(RTA_DATA(tb[MPA_NEXT_HOP]), + RTA_PAYLOAD(tb[MPA_NEXT_HOP]), + ll_index_to_type(r->ifa_index), + b1, sizeof(b1))); + + fprintf(fp, " %s", ll_index_to_name(r->ifa_index)); + fprintf(fp, " %u", dsn); + fprintf(fp, " %u", metric); + fprintf(fp, " 0x%X", flags); + fprintf(fp, " %u", lifetime); + fprintf(fp, " %u", discovery_retries); + fprintf(fp, " %u", discovery_timeout); + fprintf(fp, " %u", frame_queue_len); + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +int do_show_path(int argc, char **argv) +{ + ll_init_map(&rth); + + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETMESHPATH) < 0) { + perror("Cannot send request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_mshpath, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +int do_ipmshpath(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return ipmshpath_modify(RTM_NEWMESHPATH, NLM_F_CREATE, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipmshpath_modify(RTM_DELMESHPATH, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) { + fprintf(stderr, "Sorry, \"mshpath get\" is not implemented :-(\n"); + return -1; + } + if (matches(*argv, "show") == 0) + return do_show_path(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show_path(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mshpath help\".\n", *argv); + exit(-1); +} diff --git a/ip/ipmshplink.c b/ip/ipmshplink.c new file mode 100644 index 0000000..ed3b02e --- /dev/null +++ b/ip/ipmshplink.c @@ -0,0 +1,240 @@ +/* + * ipmshpl.c "ip mshpl". + * + * 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: Luis Carlos Cobo, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt_names.h" +#include "utils.h" +#include "ip_common.h" + +enum plink_state { + LISTEN, + OPN_SNT, + OPN_RCVD, + CNF_RCVD, + ESTAB, + HOLDING, + BLOCKED +}; + +static void usage(void) __attribute__((noreturn)); + +static void usage(void) +{ + fprintf(stderr, "Usage: ip mshpl add dev DEV addr HW_ADDR\n"); + fprintf(stderr, " ip mshpl open dev DEV addr HW_ADDR\n"); + fprintf(stderr, " ip mshpl block dev DEV addr HW_ADDR\n"); + fprintf(stderr, " ip mshpl show\n"); + fprintf(stderr, " ip mshpl delete dev DEV addr HW_ADDR\n"); + exit(-1); +} + +static int ipmshpl_modify(int cmd, int flags, int argc, char **argv) +{ + struct { + struct nlmsghdr n; + struct mplmsg mplm; + char buf[256]; + } req; + char *addr = NULL; + char *dev = NULL; + char addrbuf[20]; + int addrlen; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct mpmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + + req.mplm.mplm_flags = flags; + + while (argc > 0) { + if (matches(*argv, "addr") == 0) { + NEXT_ARG(); + if (addr) + duparg("addr", *argv); + addr = *argv; + } else if (matches(*argv, "dev") == 0) { + NEXT_ARG(); + if (dev) + duparg("dev", *argv); + dev = *argv; + } else { + if (matches(*argv, "help") == 0) + NEXT_ARG(); + } + argc--; argv++; + } + + if (cmd == RTM_NEWMESHPEERLINK || cmd == RTM_DELMESHPEERLINK + || cmd == RTM_GETMESHPEERLINK) { + if (dev == NULL || addr == NULL) { + fprintf(stderr, "Device and hardware address are required arguments.\n"); + exit(-1); + } + + + addrlen = ll_addr_a2n(addrbuf, sizeof(addrbuf), addr); + if (addrlen != 6) { + fprintf(stderr, "Incorrect hardware address length\n"); + return -1; + } + addattr_l(&req.n, sizeof(req), MPLA_PEER_ADDR, addrbuf, addrlen); + + ll_init_map(&rth); + + if ((req.mplm.ifa_index = ll_name_to_index(dev)) == 0) { + fprintf(stderr, "Cannot find device \"%s\"\n", dev); + return -1; + } + } + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + exit(2); + + return 0; +} + +int print_mshpl(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) +{ + FILE *fp = (FILE *)arg; + struct mplmsg *r = NLMSG_DATA(n); + int len = n->nlmsg_len; + int state, llid, plid; + struct rtattr *tb[MPA_MAX+1]; + SPRINT_BUF(b1); + char state_name[10]; + + if (n->nlmsg_type != RTM_NEWMESHPEERLINK) { + fprintf(stderr, "Not RTM_NEWMESHPEERLINK: %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; + } + + parse_rtattr(tb, MPLA_MAX, MPLA_RTA(r), n->nlmsg_len - NLMSG_LENGTH(sizeof(*r))); + + if (!tb[MPLA_PEER_ADDR]) { + perror("Received link without peer address"); + return(1); + } + if (!tb[MPLA_STATE]) { + perror("Received link without state"); + return(1); + } + state = *(__u8 *) RTA_DATA(tb[MPLA_STATE]); + if (!tb[MPLA_LLID]) { + perror("Received link without local link id"); + return(1); + } + llid = *(__u16 *) RTA_DATA(tb[MPLA_LLID]); + if (!tb[MPLA_PLID]) { + perror("Received link without peer link id"); + return(1); + } + plid = *(__u16 *) RTA_DATA(tb[MPLA_PLID]); + + switch (state) { + case LISTEN: + strcpy(state_name, "LISTEN"); + break; + case OPN_SNT: + strcpy(state_name, "OPN_SNT"); + break; + case OPN_RCVD: + strcpy(state_name, "OPN_RCVD"); + break; + case CNF_RCVD: + strcpy(state_name, "CNF_RCVD"); + break; + case ESTAB: + strcpy(state_name, "ESTAB"); + break; + case HOLDING: + strcpy(state_name, "HOLDING"); + break; + case BLOCKED: + strcpy(state_name, "BLOCKED"); + break; + default: + strcpy(state_name, "UNKNOWN"); + break; + } + + + fprintf(fp, "%s", ll_addr_n2a(RTA_DATA(tb[MPLA_PEER_ADDR]), + RTA_PAYLOAD(tb[MPLA_PEER_ADDR]), + ll_index_to_type(r->ifa_index), + b1, sizeof(b1))); + fprintf(fp, " %s", state_name); + fprintf(fp, " %u", llid); + fprintf(fp, " %u", plid); + fprintf(fp, " %s", ll_index_to_name(r->ifa_index)); + fprintf(fp, "\n"); + fflush(fp); + return 0; +} + +int do_show_pl(int argc, char **argv) +{ + ll_init_map(&rth); + + if (rtnl_wilddump_request(&rth, preferred_family, RTM_GETMESHPEERLINK) < 0) { + perror("Cannot send request"); + exit(1); + } + + if (rtnl_dump_filter(&rth, print_mshpl, stdout, NULL, NULL) < 0) { + fprintf(stderr, "Dump terminated\n"); + exit(1); + } + + return 0; +} + +int do_ipmshpl(int argc, char **argv) +{ + if (argc > 0) { + if (matches(*argv, "add") == 0) + return ipmshpl_modify(RTM_NEWMESHPEERLINK, MPL_F_CREATE, argc-1, argv+1); + if (matches(*argv, "open") == 0) + return ipmshpl_modify(RTM_NEWMESHPEERLINK, MPL_F_OPEN, argc-1, argv+1); + if (matches(*argv, "block") == 0) + return ipmshpl_modify(RTM_NEWMESHPEERLINK, MPL_F_BLOCK, argc-1, argv+1); + if (matches(*argv, "delete") == 0) + return ipmshpl_modify(RTM_DELMESHPEERLINK, 0, argc-1, argv+1); + if (matches(*argv, "get") == 0) + return ipmshpl_modify(RTM_GETMESHPEERLINK, 0, argc-1, argv+1); + if (matches(*argv, "show") == 0) + return do_show_pl(argc-1, argv+1); + if (matches(*argv, "help") == 0) + usage(); + } else + return do_show_pl(0, NULL); + + fprintf(stderr, "Command \"%s\" is unknown, try \"ip mshpl help\".\n", *argv); + exit(-1); +}