| // SPDX-License-Identifier: GPL-2.0 |
| |
| #include <errno.h> |
| #include <error.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <limits.h> |
| |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include <arpa/inet.h> |
| #include <net/if.h> |
| |
| #include <linux/rtnetlink.h> |
| #include <linux/genetlink.h> |
| |
| #include "linux/mptcp.h" |
| |
| #ifndef IPPROTO_MPTCP |
| #define IPPROTO_MPTCP 262 |
| #endif |
| |
| static void syntax(char *argv[]) |
| { |
| fprintf(stderr, "%s add|ann|rem|csf|dsf|get|set|del|flush|dump|events|listen|accept [<args>]\n", argv[0]); |
| fprintf(stderr, "\tadd [flags signal|subflow|backup|fullmesh] [id <nr>] [dev <name>] <ip>\n"); |
| fprintf(stderr, "\tann <local-ip> id <local-id> token <token> [port <local-port>] [dev <name>]\n"); |
| fprintf(stderr, "\trem id <local-id> token <token>\n"); |
| fprintf(stderr, "\tcsf lip <local-ip> lid <local-id> rip <remote-ip> rport <remote-port> token <token>\n"); |
| fprintf(stderr, "\tdsf lip <local-ip> lport <local-port> rip <remote-ip> rport <remote-port> token <token>\n"); |
| fprintf(stderr, "\tdel <id> [<ip>]\n"); |
| fprintf(stderr, "\tget <id>\n"); |
| fprintf(stderr, "\tset [<ip>] [id <nr>] flags [no]backup|[no]fullmesh [port <nr>] [token <token>] [rip <ip>] [rport <port>]\n"); |
| fprintf(stderr, "\tflush\n"); |
| fprintf(stderr, "\tdump\n"); |
| fprintf(stderr, "\tlimits [<rcv addr max> <subflow max>]\n"); |
| fprintf(stderr, "\tevents\n"); |
| fprintf(stderr, "\tlisten <local-ip> <local-port>\n"); |
| exit(0); |
| } |
| |
| static int init_genl_req(char *data, int family, int cmd, int version) |
| { |
| struct nlmsghdr *nh = (void *)data; |
| struct genlmsghdr *gh; |
| int off = 0; |
| |
| nh->nlmsg_type = family; |
| nh->nlmsg_flags = NLM_F_REQUEST; |
| nh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); |
| off += NLMSG_ALIGN(sizeof(*nh)); |
| |
| gh = (void *)(data + off); |
| gh->cmd = cmd; |
| gh->version = version; |
| off += NLMSG_ALIGN(sizeof(*gh)); |
| return off; |
| } |
| |
| static int nl_error(struct nlmsghdr *nh) |
| { |
| struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(nh); |
| int len = nh->nlmsg_len - sizeof(*nh); |
| uint32_t off; |
| |
| if (len < sizeof(struct nlmsgerr)) { |
| error(1, 0, "netlink error message truncated %d min %ld", len, |
| sizeof(struct nlmsgerr)); |
| return -1; |
| } |
| |
| if (err->error) { |
| /* check messages from kernel */ |
| struct rtattr *attrs = (struct rtattr *)NLMSG_DATA(nh); |
| |
| fprintf(stderr, "netlink error %d (%s)\n", |
| err->error, strerror(-err->error)); |
| |
| while (RTA_OK(attrs, len)) { |
| if (attrs->rta_type == NLMSGERR_ATTR_MSG) |
| fprintf(stderr, "netlink ext ack msg: %s\n", |
| (char *)RTA_DATA(attrs)); |
| if (attrs->rta_type == NLMSGERR_ATTR_OFFS) { |
| memcpy(&off, RTA_DATA(attrs), 4); |
| fprintf(stderr, "netlink err off %d\n", |
| (int)off); |
| } |
| attrs = RTA_NEXT(attrs, len); |
| } |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int capture_events(int fd, int event_group) |
| { |
| u_int8_t buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024]; |
| struct genlmsghdr *ghdr; |
| struct rtattr *attrs; |
| struct nlmsghdr *nh; |
| int ret = 0; |
| int res_len; |
| int msg_len; |
| fd_set rfds; |
| |
| if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, |
| &event_group, sizeof(event_group)) < 0) |
| error(1, errno, "could not join the " MPTCP_PM_EV_GRP_NAME " mcast group"); |
| |
| do { |
| FD_ZERO(&rfds); |
| FD_SET(fd, &rfds); |
| res_len = NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + 1024; |
| |
| ret = select(FD_SETSIZE, &rfds, NULL, NULL, NULL); |
| |
| if (ret < 0) |
| error(1, ret, "error in select() on NL socket"); |
| |
| res_len = recv(fd, buffer, res_len, 0); |
| if (res_len < 0) |
| error(1, res_len, "error on recv() from NL socket"); |
| |
| nh = (struct nlmsghdr *)buffer; |
| |
| for (; NLMSG_OK(nh, res_len); nh = NLMSG_NEXT(nh, res_len)) { |
| if (nh->nlmsg_type == NLMSG_ERROR) |
| error(1, NLMSG_ERROR, "received invalid NL message"); |
| |
| ghdr = (struct genlmsghdr *)NLMSG_DATA(nh); |
| |
| if (ghdr->cmd == 0) |
| continue; |
| |
| fprintf(stderr, "type:%d", ghdr->cmd); |
| |
| msg_len = nh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN); |
| |
| attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); |
| while (RTA_OK(attrs, msg_len)) { |
| if (attrs->rta_type == MPTCP_ATTR_TOKEN) |
| fprintf(stderr, ",token:%u", *(__u32 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_FAMILY) |
| fprintf(stderr, ",family:%u", *(__u16 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_LOC_ID) |
| fprintf(stderr, ",loc_id:%u", *(__u8 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_REM_ID) |
| fprintf(stderr, ",rem_id:%u", *(__u8 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_SADDR4) { |
| u_int32_t saddr4 = ntohl(*(__u32 *)RTA_DATA(attrs)); |
| |
| fprintf(stderr, ",saddr4:%u.%u.%u.%u", saddr4 >> 24, |
| (saddr4 >> 16) & 0xFF, (saddr4 >> 8) & 0xFF, |
| (saddr4 & 0xFF)); |
| } else if (attrs->rta_type == MPTCP_ATTR_SADDR6) { |
| char buf[INET6_ADDRSTRLEN]; |
| |
| if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf, |
| sizeof(buf)) != NULL) |
| fprintf(stderr, ",saddr6:%s", buf); |
| } else if (attrs->rta_type == MPTCP_ATTR_DADDR4) { |
| u_int32_t daddr4 = ntohl(*(__u32 *)RTA_DATA(attrs)); |
| |
| fprintf(stderr, ",daddr4:%u.%u.%u.%u", daddr4 >> 24, |
| (daddr4 >> 16) & 0xFF, (daddr4 >> 8) & 0xFF, |
| (daddr4 & 0xFF)); |
| } else if (attrs->rta_type == MPTCP_ATTR_DADDR6) { |
| char buf[INET6_ADDRSTRLEN]; |
| |
| if (inet_ntop(AF_INET6, RTA_DATA(attrs), buf, |
| sizeof(buf)) != NULL) |
| fprintf(stderr, ",daddr6:%s", buf); |
| } else if (attrs->rta_type == MPTCP_ATTR_SPORT) |
| fprintf(stderr, ",sport:%u", |
| ntohs(*(__u16 *)RTA_DATA(attrs))); |
| else if (attrs->rta_type == MPTCP_ATTR_DPORT) |
| fprintf(stderr, ",dport:%u", |
| ntohs(*(__u16 *)RTA_DATA(attrs))); |
| else if (attrs->rta_type == MPTCP_ATTR_BACKUP) |
| fprintf(stderr, ",backup:%u", *(__u8 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_ERROR) |
| fprintf(stderr, ",error:%u", *(__u8 *)RTA_DATA(attrs)); |
| else if (attrs->rta_type == MPTCP_ATTR_SERVER_SIDE) |
| fprintf(stderr, ",server_side:%u", *(__u8 *)RTA_DATA(attrs)); |
| |
| attrs = RTA_NEXT(attrs, msg_len); |
| } |
| } |
| fprintf(stderr, "\n"); |
| } while (1); |
| |
| return 0; |
| } |
| |
| /* do a netlink command and, if max > 0, fetch the reply ; nh's size >1024B */ |
| static int do_nl_req(int fd, struct nlmsghdr *nh, int len, int max) |
| { |
| struct sockaddr_nl nladdr = { .nl_family = AF_NETLINK }; |
| socklen_t addr_len; |
| void *data = nh; |
| int rem, ret; |
| int err = 0; |
| |
| /* If no expected answer, ask for an ACK to look for errors if any */ |
| if (max == 0) { |
| nh->nlmsg_flags |= NLM_F_ACK; |
| max = 1024; |
| } |
| |
| nh->nlmsg_len = len; |
| ret = sendto(fd, data, len, 0, (void *)&nladdr, sizeof(nladdr)); |
| if (ret != len) |
| error(1, errno, "send netlink: %uB != %uB\n", ret, len); |
| |
| addr_len = sizeof(nladdr); |
| rem = ret = recvfrom(fd, data, max, 0, (void *)&nladdr, &addr_len); |
| if (ret < 0) |
| error(1, errno, "recv netlink: %uB\n", ret); |
| |
| /* Beware: the NLMSG_NEXT macro updates the 'rem' argument */ |
| for (; NLMSG_OK(nh, rem); nh = NLMSG_NEXT(nh, rem)) { |
| if (nh->nlmsg_type == NLMSG_DONE) |
| break; |
| |
| if (nh->nlmsg_type == NLMSG_ERROR && nl_error(nh)) |
| err = 1; |
| } |
| if (err) |
| error(1, 0, "bailing out due to netlink error[s]"); |
| return ret; |
| } |
| |
| static int genl_parse_getfamily(struct nlmsghdr *nlh, int *pm_family, |
| int *events_mcast_grp) |
| { |
| struct genlmsghdr *ghdr = NLMSG_DATA(nlh); |
| int len = nlh->nlmsg_len; |
| struct rtattr *attrs; |
| struct rtattr *grps; |
| struct rtattr *grp; |
| int got_events_grp; |
| int got_family; |
| int grps_len; |
| int grp_len; |
| |
| if (nlh->nlmsg_type != GENL_ID_CTRL) |
| error(1, errno, "Not a controller message, len=%d type=0x%x\n", |
| nlh->nlmsg_len, nlh->nlmsg_type); |
| |
| len -= NLMSG_LENGTH(GENL_HDRLEN); |
| |
| if (len < 0) |
| error(1, errno, "wrong controller message len %d\n", len); |
| |
| if (ghdr->cmd != CTRL_CMD_NEWFAMILY) |
| error(1, errno, "Unknown controller command %d\n", ghdr->cmd); |
| |
| attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN); |
| got_family = 0; |
| got_events_grp = 0; |
| |
| while (RTA_OK(attrs, len)) { |
| if (attrs->rta_type == CTRL_ATTR_FAMILY_ID) { |
| *pm_family = *(__u16 *)RTA_DATA(attrs); |
| got_family = 1; |
| } else if (attrs->rta_type == CTRL_ATTR_MCAST_GROUPS) { |
| grps = RTA_DATA(attrs); |
| grps_len = RTA_PAYLOAD(attrs); |
| |
| while (RTA_OK(grps, grps_len)) { |
| grp = RTA_DATA(grps); |
| grp_len = RTA_PAYLOAD(grps); |
| got_events_grp = 0; |
| |
| while (RTA_OK(grp, grp_len)) { |
| if (grp->rta_type == CTRL_ATTR_MCAST_GRP_ID) |
| *events_mcast_grp = *(__u32 *)RTA_DATA(grp); |
| else if (grp->rta_type == CTRL_ATTR_MCAST_GRP_NAME && |
| !strcmp(RTA_DATA(grp), MPTCP_PM_EV_GRP_NAME)) |
| got_events_grp = 1; |
| |
| grp = RTA_NEXT(grp, grp_len); |
| } |
| |
| if (got_events_grp) |
| break; |
| |
| grps = RTA_NEXT(grps, grps_len); |
| } |
| } |
| |
| if (got_family && got_events_grp) |
| return 0; |
| |
| attrs = RTA_NEXT(attrs, len); |
| } |
| |
| error(1, errno, "can't find CTRL_ATTR_FAMILY_ID attr"); |
| return -1; |
| } |
| |
| static int resolve_mptcp_pm_netlink(int fd, int *pm_family, int *events_mcast_grp) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct nlmsghdr *nh; |
| struct rtattr *rta; |
| int namelen; |
| int off = 0; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0); |
| |
| rta = (void *)(data + off); |
| namelen = strlen(MPTCP_PM_NAME) + 1; |
| rta->rta_type = CTRL_ATTR_FAMILY_NAME; |
| rta->rta_len = RTA_LENGTH(namelen); |
| memcpy(RTA_DATA(rta), MPTCP_PM_NAME, namelen); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| do_nl_req(fd, nh, off, sizeof(data)); |
| return genl_parse_getfamily((void *)data, pm_family, events_mcast_grp); |
| } |
| |
| int dsf(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct rtattr *rta, *addr; |
| u_int16_t family, port; |
| struct nlmsghdr *nh; |
| u_int32_t token; |
| int addr_start; |
| int off = 0; |
| int arg; |
| |
| const char *params[5]; |
| |
| memset(params, 0, 5 * sizeof(const char *)); |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_DESTROY, |
| MPTCP_PM_VER); |
| |
| if (argc < 12) |
| syntax(argv); |
| |
| /* Params recorded in this order: |
| * <local-ip>, <local-port>, <remote-ip>, <remote-port>, <token> |
| */ |
| for (arg = 2; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "lip")) { |
| if (++arg >= argc) |
| error(1, 0, " missing local IP"); |
| |
| params[0] = argv[arg]; |
| } else if (!strcmp(argv[arg], "lport")) { |
| if (++arg >= argc) |
| error(1, 0, " missing local port"); |
| |
| params[1] = argv[arg]; |
| } else if (!strcmp(argv[arg], "rip")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote IP"); |
| |
| params[2] = argv[arg]; |
| } else if (!strcmp(argv[arg], "rport")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote port"); |
| |
| params[3] = argv[arg]; |
| } else if (!strcmp(argv[arg], "token")) { |
| if (++arg >= argc) |
| error(1, 0, " missing token"); |
| |
| params[4] = argv[arg]; |
| } else |
| error(1, 0, "unknown keyword %s", argv[arg]); |
| } |
| |
| for (arg = 0; arg < 4; arg = arg + 2) { |
| /* addr header */ |
| addr_start = off; |
| addr = (void *)(data + off); |
| addr->rta_type = NLA_F_NESTED | |
| ((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE); |
| addr->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(addr->rta_len); |
| |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else |
| error(1, errno, "can't parse ip %s", params[arg]); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* port */ |
| port = atoi(params[arg + 1]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &port, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| addr->rta_len = off - addr_start; |
| } |
| |
| /* token */ |
| token = strtoul(params[4], NULL, 10); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| do_nl_req(fd, nh, off, 0); |
| |
| return 0; |
| } |
| |
| int csf(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| u_int32_t flags = MPTCP_PM_ADDR_FLAG_SUBFLOW; |
| const char *params[5]; |
| struct nlmsghdr *nh; |
| struct rtattr *addr; |
| struct rtattr *rta; |
| u_int16_t family; |
| u_int32_t token; |
| u_int16_t port; |
| int addr_start; |
| u_int8_t id; |
| int off = 0; |
| int arg; |
| |
| memset(params, 0, 5 * sizeof(const char *)); |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SUBFLOW_CREATE, |
| MPTCP_PM_VER); |
| |
| if (argc < 12) |
| syntax(argv); |
| |
| /* Params recorded in this order: |
| * <local-ip>, <local-id>, <remote-ip>, <remote-port>, <token> |
| */ |
| for (arg = 2; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "lip")) { |
| if (++arg >= argc) |
| error(1, 0, " missing local IP"); |
| |
| params[0] = argv[arg]; |
| } else if (!strcmp(argv[arg], "lid")) { |
| if (++arg >= argc) |
| error(1, 0, " missing local id"); |
| |
| params[1] = argv[arg]; |
| } else if (!strcmp(argv[arg], "rip")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote ip"); |
| |
| params[2] = argv[arg]; |
| } else if (!strcmp(argv[arg], "rport")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote port"); |
| |
| params[3] = argv[arg]; |
| } else if (!strcmp(argv[arg], "token")) { |
| if (++arg >= argc) |
| error(1, 0, " missing token"); |
| |
| params[4] = argv[arg]; |
| } else |
| error(1, 0, "unknown param %s", argv[arg]); |
| } |
| |
| for (arg = 0; arg < 4; arg = arg + 2) { |
| /* addr header */ |
| addr_start = off; |
| addr = (void *)(data + off); |
| addr->rta_type = NLA_F_NESTED | |
| ((arg == 0) ? MPTCP_PM_ATTR_ADDR : MPTCP_PM_ATTR_ADDR_REMOTE); |
| addr->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(addr->rta_len); |
| |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, params[arg], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, params[arg], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else |
| error(1, errno, "can't parse ip %s", params[arg]); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| if (arg == 2) { |
| /* port */ |
| port = atoi(params[arg + 1]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &port, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| if (arg == 0) { |
| /* id */ |
| id = atoi(params[arg + 1]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| /* addr flags */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &flags, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| addr->rta_len = off - addr_start; |
| } |
| |
| /* token */ |
| token = strtoul(params[4], NULL, 10); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| do_nl_req(fd, nh, off, 0); |
| |
| return 0; |
| } |
| |
| int remove_addr(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct nlmsghdr *nh; |
| struct rtattr *rta; |
| u_int32_t token; |
| u_int8_t id; |
| int off = 0; |
| int arg; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_REMOVE, |
| MPTCP_PM_VER); |
| |
| if (argc < 6) |
| syntax(argv); |
| |
| for (arg = 2; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "id")) { |
| if (++arg >= argc) |
| error(1, 0, " missing id value"); |
| |
| id = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_LOC_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "token")) { |
| if (++arg >= argc) |
| error(1, 0, " missing token value"); |
| |
| token = strtoul(argv[arg], NULL, 10); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else |
| error(1, 0, "unknown keyword %s", argv[arg]); |
| } |
| |
| do_nl_req(fd, nh, off, 0); |
| return 0; |
| } |
| |
| int announce_addr(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| u_int32_t flags = MPTCP_PM_ADDR_FLAG_SIGNAL; |
| u_int32_t token = UINT_MAX; |
| struct rtattr *rta, *addr; |
| u_int32_t id = UINT_MAX; |
| struct nlmsghdr *nh; |
| u_int16_t family; |
| int addr_start; |
| int off = 0; |
| int arg; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ANNOUNCE, |
| MPTCP_PM_VER); |
| |
| if (argc < 7) |
| syntax(argv); |
| |
| /* local-ip header */ |
| addr_start = off; |
| addr = (void *)(data + off); |
| addr->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
| addr->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(addr->rta_len); |
| |
| /* local-ip data */ |
| /* record addr type */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else |
| error(1, errno, "can't parse ip %s", argv[2]); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* addr family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| for (arg = 3; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "id")) { |
| /* local-id */ |
| if (++arg >= argc) |
| error(1, 0, " missing id value"); |
| |
| id = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "dev")) { |
| /* for the if_index */ |
| int32_t ifindex; |
| |
| if (++arg >= argc) |
| error(1, 0, " missing dev name"); |
| |
| ifindex = if_nametoindex(argv[arg]); |
| if (!ifindex) |
| error(1, errno, "unknown device %s", argv[arg]); |
| |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &ifindex, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "port")) { |
| /* local-port (optional) */ |
| u_int16_t port; |
| |
| if (++arg >= argc) |
| error(1, 0, " missing port value"); |
| |
| port = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &port, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "token")) { |
| /* MPTCP connection token */ |
| if (++arg >= argc) |
| error(1, 0, " missing token value"); |
| |
| token = strtoul(argv[arg], NULL, 10); |
| } else |
| error(1, 0, "unknown keyword %s", argv[arg]); |
| } |
| |
| /* addr flags */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &flags, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| addr->rta_len = off - addr_start; |
| |
| if (id == UINT_MAX || token == UINT_MAX) |
| error(1, 0, " missing mandatory inputs"); |
| |
| /* token */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| do_nl_req(fd, nh, off, 0); |
| |
| return 0; |
| } |
| |
| int add_addr(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct rtattr *rta, *nest; |
| struct nlmsghdr *nh; |
| u_int32_t flags = 0; |
| u_int16_t family; |
| int nest_start; |
| u_int8_t id; |
| int off = 0; |
| int arg; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_ADD_ADDR, |
| MPTCP_PM_VER); |
| |
| if (argc < 3) |
| syntax(argv); |
| |
| nest_start = off; |
| nest = (void *)(data + off); |
| nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
| nest->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(nest->rta_len); |
| |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, argv[2], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, argv[2], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else |
| error(1, errno, "can't parse ip %s", argv[2]); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| for (arg = 3; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "flags")) { |
| char *tok, *str; |
| |
| /* flags */ |
| if (++arg >= argc) |
| error(1, 0, " missing flags value"); |
| |
| /* do not support flag list yet */ |
| for (str = argv[arg]; (tok = strtok(str, ",")); |
| str = NULL) { |
| if (!strcmp(tok, "subflow")) |
| flags |= MPTCP_PM_ADDR_FLAG_SUBFLOW; |
| else if (!strcmp(tok, "signal")) |
| flags |= MPTCP_PM_ADDR_FLAG_SIGNAL; |
| else if (!strcmp(tok, "backup")) |
| flags |= MPTCP_PM_ADDR_FLAG_BACKUP; |
| else if (!strcmp(tok, "fullmesh")) |
| flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; |
| else |
| error(1, errno, |
| "unknown flag %s", argv[arg]); |
| } |
| |
| if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL && |
| flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
| error(1, errno, "error flag fullmesh"); |
| } |
| |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &flags, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "id")) { |
| if (++arg >= argc) |
| error(1, 0, " missing id value"); |
| |
| id = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "dev")) { |
| int32_t ifindex; |
| |
| if (++arg >= argc) |
| error(1, 0, " missing dev name"); |
| |
| ifindex = if_nametoindex(argv[arg]); |
| if (!ifindex) |
| error(1, errno, "unknown device %s", argv[arg]); |
| |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_IF_IDX; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &ifindex, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "port")) { |
| u_int16_t port; |
| |
| if (++arg >= argc) |
| error(1, 0, " missing port value"); |
| if (!(flags & MPTCP_PM_ADDR_FLAG_SIGNAL)) |
| error(1, 0, " flags must be signal when using port"); |
| |
| port = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &port, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else |
| error(1, 0, "unknown keyword %s", argv[arg]); |
| } |
| nest->rta_len = off - nest_start; |
| |
| do_nl_req(fd, nh, off, 0); |
| return 0; |
| } |
| |
| int del_addr(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct rtattr *rta, *nest; |
| struct nlmsghdr *nh; |
| u_int16_t family; |
| int nest_start; |
| u_int8_t id; |
| int off = 0; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_DEL_ADDR, |
| MPTCP_PM_VER); |
| |
| /* the only argument is the address id (nonzero) */ |
| if (argc != 3 && argc != 4) |
| syntax(argv); |
| |
| id = atoi(argv[2]); |
| /* zero id with the IP address */ |
| if (!id && argc != 4) |
| syntax(argv); |
| |
| nest_start = off; |
| nest = (void *)(data + off); |
| nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
| nest->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(nest->rta_len); |
| |
| /* build a dummy addr with only the ID set */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| if (!id) { |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, argv[3], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, argv[3], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else { |
| error(1, errno, "can't parse ip %s", argv[3]); |
| } |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| nest->rta_len = off - nest_start; |
| |
| do_nl_req(fd, nh, off, 0); |
| return 0; |
| } |
| |
| static void print_addr(struct rtattr *attrs, int len) |
| { |
| uint16_t family = 0; |
| uint16_t port = 0; |
| char str[1024]; |
| uint32_t flags; |
| uint8_t id; |
| |
| while (RTA_OK(attrs, len)) { |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FAMILY) |
| memcpy(&family, RTA_DATA(attrs), 2); |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_PORT) |
| memcpy(&port, RTA_DATA(attrs), 2); |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR4) { |
| if (family != AF_INET) |
| error(1, errno, "wrong IP (v4) for family %d", |
| family); |
| inet_ntop(AF_INET, RTA_DATA(attrs), str, sizeof(str)); |
| printf("%s", str); |
| if (port) |
| printf(" %d", port); |
| } |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ADDR6) { |
| if (family != AF_INET6) |
| error(1, errno, "wrong IP (v6) for family %d", |
| family); |
| inet_ntop(AF_INET6, RTA_DATA(attrs), str, sizeof(str)); |
| printf("%s", str); |
| if (port) |
| printf(" %d", port); |
| } |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_ID) { |
| memcpy(&id, RTA_DATA(attrs), 1); |
| printf("id %d ", id); |
| } |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_FLAGS) { |
| memcpy(&flags, RTA_DATA(attrs), 4); |
| |
| printf("flags "); |
| if (flags & MPTCP_PM_ADDR_FLAG_SIGNAL) { |
| printf("signal"); |
| flags &= ~MPTCP_PM_ADDR_FLAG_SIGNAL; |
| if (flags) |
| printf(","); |
| } |
| |
| if (flags & MPTCP_PM_ADDR_FLAG_SUBFLOW) { |
| printf("subflow"); |
| flags &= ~MPTCP_PM_ADDR_FLAG_SUBFLOW; |
| if (flags) |
| printf(","); |
| } |
| |
| if (flags & MPTCP_PM_ADDR_FLAG_BACKUP) { |
| printf("backup"); |
| flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP; |
| if (flags) |
| printf(","); |
| } |
| |
| if (flags & MPTCP_PM_ADDR_FLAG_FULLMESH) { |
| printf("fullmesh"); |
| flags &= ~MPTCP_PM_ADDR_FLAG_FULLMESH; |
| if (flags) |
| printf(","); |
| } |
| |
| if (flags & MPTCP_PM_ADDR_FLAG_IMPLICIT) { |
| printf("implicit"); |
| flags &= ~MPTCP_PM_ADDR_FLAG_IMPLICIT; |
| if (flags) |
| printf(","); |
| } |
| |
| /* bump unknown flags, if any */ |
| if (flags) |
| printf("0x%x", flags); |
| printf(" "); |
| } |
| if (attrs->rta_type == MPTCP_PM_ADDR_ATTR_IF_IDX) { |
| char name[IF_NAMESIZE], *ret; |
| int32_t ifindex; |
| |
| memcpy(&ifindex, RTA_DATA(attrs), 4); |
| ret = if_indextoname(ifindex, name); |
| if (ret) |
| printf("dev %s ", ret); |
| else |
| printf("dev unknown/%d", ifindex); |
| } |
| |
| attrs = RTA_NEXT(attrs, len); |
| } |
| printf("\n"); |
| } |
| |
| static void print_addrs(struct nlmsghdr *nh, int pm_family, int total_len) |
| { |
| struct rtattr *attrs; |
| |
| for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { |
| int len = nh->nlmsg_len; |
| |
| if (nh->nlmsg_type == NLMSG_DONE) |
| break; |
| if (nh->nlmsg_type == NLMSG_ERROR) |
| nl_error(nh); |
| if (nh->nlmsg_type != pm_family) |
| continue; |
| |
| len -= NLMSG_LENGTH(GENL_HDRLEN); |
| attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + |
| GENL_HDRLEN); |
| while (RTA_OK(attrs, len)) { |
| if (attrs->rta_type == |
| (MPTCP_PM_ATTR_ADDR | NLA_F_NESTED)) |
| print_addr((void *)RTA_DATA(attrs), |
| attrs->rta_len); |
| attrs = RTA_NEXT(attrs, len); |
| } |
| } |
| } |
| |
| int get_addr(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct rtattr *rta, *nest; |
| struct nlmsghdr *nh; |
| u_int32_t token = 0; |
| int nest_start; |
| u_int8_t id; |
| int off = 0; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR, |
| MPTCP_PM_VER); |
| |
| /* the only argument is the address id */ |
| if (argc != 3 && argc != 5) |
| syntax(argv); |
| |
| id = atoi(argv[2]); |
| if (argc == 5 && !strcmp(argv[3], "token")) |
| token = strtoul(argv[4], NULL, 10); |
| |
| nest_start = off; |
| nest = (void *)(data + off); |
| nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
| nest->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(nest->rta_len); |
| |
| /* build a dummy addr with only the ID set */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| nest->rta_len = off - nest_start; |
| |
| /* token */ |
| if (token) { |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data))); |
| return 0; |
| } |
| |
| int dump_addrs(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| pid_t pid = getpid(); |
| struct nlmsghdr *nh; |
| u_int32_t token = 0; |
| struct rtattr *rta; |
| int off = 0; |
| |
| if (argc != 2 && argc != 4) |
| syntax(argv); |
| |
| if (argc == 4 && !strcmp(argv[2], "token")) |
| token = strtoul(argv[3], NULL, 10); |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_GET_ADDR, |
| MPTCP_PM_VER); |
| nh->nlmsg_flags |= NLM_F_DUMP; |
| nh->nlmsg_seq = 1; |
| nh->nlmsg_pid = pid; |
| nh->nlmsg_len = off; |
| |
| /* token */ |
| if (token) { |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| print_addrs(nh, pm_family, do_nl_req(fd, nh, off, sizeof(data))); |
| return 0; |
| } |
| |
| int flush_addrs(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct nlmsghdr *nh; |
| int off = 0; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_FLUSH_ADDRS, |
| MPTCP_PM_VER); |
| |
| do_nl_req(fd, nh, off, 0); |
| return 0; |
| } |
| |
| static void print_limits(struct nlmsghdr *nh, int pm_family, int total_len) |
| { |
| struct rtattr *attrs; |
| uint32_t max; |
| |
| for (; NLMSG_OK(nh, total_len); nh = NLMSG_NEXT(nh, total_len)) { |
| int len = nh->nlmsg_len; |
| |
| if (nh->nlmsg_type == NLMSG_DONE) |
| break; |
| if (nh->nlmsg_type == NLMSG_ERROR) |
| nl_error(nh); |
| if (nh->nlmsg_type != pm_family) |
| continue; |
| |
| len -= NLMSG_LENGTH(GENL_HDRLEN); |
| attrs = (struct rtattr *) ((char *) NLMSG_DATA(nh) + |
| GENL_HDRLEN); |
| while (RTA_OK(attrs, len)) { |
| int type = attrs->rta_type; |
| |
| if (type != MPTCP_PM_ATTR_RCV_ADD_ADDRS && |
| type != MPTCP_PM_ATTR_SUBFLOWS) |
| goto next; |
| |
| memcpy(&max, RTA_DATA(attrs), 4); |
| printf("%s %u\n", type == MPTCP_PM_ATTR_SUBFLOWS ? |
| "subflows" : "accept", max); |
| |
| next: |
| attrs = RTA_NEXT(attrs, len); |
| } |
| } |
| } |
| |
| int get_set_limits(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| uint32_t rcv_addr = 0, subflows = 0; |
| int cmd, len = sizeof(data); |
| struct nlmsghdr *nh; |
| int off = 0; |
| |
| /* limit */ |
| if (argc == 4) { |
| rcv_addr = atoi(argv[2]); |
| subflows = atoi(argv[3]); |
| cmd = MPTCP_PM_CMD_SET_LIMITS; |
| } else { |
| cmd = MPTCP_PM_CMD_GET_LIMITS; |
| } |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, cmd, MPTCP_PM_VER); |
| |
| /* limit */ |
| if (cmd == MPTCP_PM_CMD_SET_LIMITS) { |
| struct rtattr *rta = (void *)(data + off); |
| |
| rta->rta_type = MPTCP_PM_ATTR_RCV_ADD_ADDRS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &rcv_addr, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_SUBFLOWS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &subflows, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* do not expect a reply */ |
| len = 0; |
| } |
| |
| len = do_nl_req(fd, nh, off, len); |
| if (cmd == MPTCP_PM_CMD_GET_LIMITS) |
| print_limits(nh, pm_family, len); |
| return 0; |
| } |
| |
| int add_listener(int argc, char *argv[]) |
| { |
| struct sockaddr_storage addr; |
| struct sockaddr_in6 *a6; |
| struct sockaddr_in *a4; |
| u_int16_t family = AF_UNSPEC; |
| int enable = 1; |
| int sock; |
| int err; |
| |
| if (argc < 4) |
| syntax(argv); |
| |
| memset(&addr, 0, sizeof(struct sockaddr_storage)); |
| a4 = (struct sockaddr_in *)&addr; |
| a6 = (struct sockaddr_in6 *)&addr; |
| |
| if (inet_pton(AF_INET, argv[2], &a4->sin_addr)) { |
| family = AF_INET; |
| a4->sin_family = family; |
| a4->sin_port = htons(atoi(argv[3])); |
| } else if (inet_pton(AF_INET6, argv[2], &a6->sin6_addr)) { |
| family = AF_INET6; |
| a6->sin6_family = family; |
| a6->sin6_port = htons(atoi(argv[3])); |
| } else |
| error(1, errno, "can't parse ip %s", argv[2]); |
| |
| sock = socket(family, SOCK_STREAM, IPPROTO_MPTCP); |
| if (sock < 0) |
| error(1, errno, "can't create listener sock\n"); |
| |
| if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable))) { |
| close(sock); |
| error(1, errno, "can't set SO_REUSEADDR on listener sock\n"); |
| } |
| |
| err = bind(sock, (struct sockaddr *)&addr, |
| ((family == AF_INET) ? sizeof(struct sockaddr_in) : |
| sizeof(struct sockaddr_in6))); |
| |
| if (err == 0 && listen(sock, 30) == 0) |
| pause(); |
| |
| close(sock); |
| return 0; |
| } |
| |
| int set_flags(int fd, int pm_family, int argc, char *argv[]) |
| { |
| char data[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + |
| NLMSG_ALIGN(sizeof(struct genlmsghdr)) + |
| 1024]; |
| struct rtattr *rta, *nest; |
| struct nlmsghdr *nh; |
| u_int32_t flags = 0; |
| u_int32_t token = 0; |
| u_int16_t rport = 0; |
| u_int16_t family; |
| void *rip = NULL; |
| int nest_start; |
| int use_id = 0; |
| u_int8_t id; |
| int off = 0; |
| int arg = 2; |
| |
| memset(data, 0, sizeof(data)); |
| nh = (void *)data; |
| off = init_genl_req(data, pm_family, MPTCP_PM_CMD_SET_FLAGS, |
| MPTCP_PM_VER); |
| |
| if (argc < 3) |
| syntax(argv); |
| |
| nest_start = off; |
| nest = (void *)(data + off); |
| nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR; |
| nest->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(nest->rta_len); |
| |
| if (!strcmp(argv[arg], "id")) { |
| if (++arg >= argc) |
| error(1, 0, " missing id value"); |
| |
| use_id = 1; |
| id = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ID; |
| rta->rta_len = RTA_LENGTH(1); |
| memcpy(RTA_DATA(rta), &id, 1); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else { |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, argv[arg], RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, argv[arg], RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else { |
| error(1, errno, "can't parse ip %s", argv[arg]); |
| } |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| if (++arg >= argc) |
| error(1, 0, " missing flags keyword"); |
| |
| for (; arg < argc; arg++) { |
| if (!strcmp(argv[arg], "token")) { |
| if (++arg >= argc) |
| error(1, 0, " missing token value"); |
| |
| /* token */ |
| token = strtoul(argv[arg], NULL, 10); |
| } else if (!strcmp(argv[arg], "flags")) { |
| char *tok, *str; |
| |
| /* flags */ |
| if (++arg >= argc) |
| error(1, 0, " missing flags value"); |
| |
| for (str = argv[arg]; (tok = strtok(str, ",")); |
| str = NULL) { |
| if (!strcmp(tok, "backup")) |
| flags |= MPTCP_PM_ADDR_FLAG_BACKUP; |
| else if (!strcmp(tok, "fullmesh")) |
| flags |= MPTCP_PM_ADDR_FLAG_FULLMESH; |
| else if (strcmp(tok, "nobackup") && |
| strcmp(tok, "nofullmesh")) |
| error(1, errno, |
| "unknown flag %s", argv[arg]); |
| } |
| |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FLAGS; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &flags, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "port")) { |
| u_int16_t port; |
| |
| if (use_id) |
| error(1, 0, " port can't be used with id"); |
| |
| if (++arg >= argc) |
| error(1, 0, " missing port value"); |
| |
| port = atoi(argv[arg]); |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &port, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } else if (!strcmp(argv[arg], "rport")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote port"); |
| |
| rport = atoi(argv[arg]); |
| } else if (!strcmp(argv[arg], "rip")) { |
| if (++arg >= argc) |
| error(1, 0, " missing remote ip"); |
| |
| rip = argv[arg]; |
| } else { |
| error(1, 0, "unknown keyword %s", argv[arg]); |
| } |
| } |
| nest->rta_len = off - nest_start; |
| |
| /* token */ |
| if (token) { |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ATTR_TOKEN; |
| rta->rta_len = RTA_LENGTH(4); |
| memcpy(RTA_DATA(rta), &token, 4); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| /* remote addr/port */ |
| if (rip) { |
| nest_start = off; |
| nest = (void *)(data + off); |
| nest->rta_type = NLA_F_NESTED | MPTCP_PM_ATTR_ADDR_REMOTE; |
| nest->rta_len = RTA_LENGTH(0); |
| off += NLMSG_ALIGN(nest->rta_len); |
| |
| /* addr data */ |
| rta = (void *)(data + off); |
| if (inet_pton(AF_INET, rip, RTA_DATA(rta))) { |
| family = AF_INET; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR4; |
| rta->rta_len = RTA_LENGTH(4); |
| } else if (inet_pton(AF_INET6, rip, RTA_DATA(rta))) { |
| family = AF_INET6; |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_ADDR6; |
| rta->rta_len = RTA_LENGTH(16); |
| } else { |
| error(1, errno, "can't parse ip %s", (char *)rip); |
| } |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| /* family */ |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_FAMILY; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &family, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| |
| if (rport) { |
| rta = (void *)(data + off); |
| rta->rta_type = MPTCP_PM_ADDR_ATTR_PORT; |
| rta->rta_len = RTA_LENGTH(2); |
| memcpy(RTA_DATA(rta), &rport, 2); |
| off += NLMSG_ALIGN(rta->rta_len); |
| } |
| |
| nest->rta_len = off - nest_start; |
| } |
| |
| do_nl_req(fd, nh, off, 0); |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int events_mcast_grp; |
| int pm_family; |
| int fd; |
| |
| if (argc < 2) |
| syntax(argv); |
| |
| fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); |
| if (fd == -1) |
| error(1, errno, "socket netlink"); |
| |
| resolve_mptcp_pm_netlink(fd, &pm_family, &events_mcast_grp); |
| |
| if (!strcmp(argv[1], "add")) |
| return add_addr(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "ann")) |
| return announce_addr(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "rem")) |
| return remove_addr(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "csf")) |
| return csf(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "dsf")) |
| return dsf(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "del")) |
| return del_addr(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "flush")) |
| return flush_addrs(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "get")) |
| return get_addr(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "dump")) |
| return dump_addrs(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "limits")) |
| return get_set_limits(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "set")) |
| return set_flags(fd, pm_family, argc, argv); |
| else if (!strcmp(argv[1], "events")) |
| return capture_events(fd, events_mcast_grp); |
| else if (!strcmp(argv[1], "listen")) |
| return add_listener(argc, argv); |
| |
| fprintf(stderr, "unknown sub-command: %s", argv[1]); |
| syntax(argv); |
| return 0; |
| } |