" ip mptcp endpoint flush\n"
" ip mptcp limits set [ subflows NR ] [ add_addr_accepted NR ]\n"
" ip mptcp limits show\n"
+ " ip mptcp monitor\n"
"FLAG-LIST := [ FLAG-LIST ] FLAG\n"
"FLAG := [ signal | subflow | backup ]\n");
return 0;
}
+static const char * const event_to_str[] = {
+ [MPTCP_EVENT_CREATED] = "CREATED",
+ [MPTCP_EVENT_ESTABLISHED] = "ESTABLISHED",
+ [MPTCP_EVENT_CLOSED] = "CLOSED",
+ [MPTCP_EVENT_ANNOUNCED] = "ANNOUNCED",
+ [MPTCP_EVENT_REMOVED] = "REMOVED",
+ [MPTCP_EVENT_SUB_ESTABLISHED] = "SF_ESTABLISHED",
+ [MPTCP_EVENT_SUB_CLOSED] = "SF_CLOSED",
+ [MPTCP_EVENT_SUB_PRIORITY] = "SF_PRIO",
+};
+
+static void print_addr(const char *key, int af, struct rtattr *value)
+{
+ void *data = RTA_DATA(value);
+ char str[INET6_ADDRSTRLEN];
+
+ if (inet_ntop(af, data, str, sizeof(str)))
+ printf(" %s=%s", key, str);
+}
+
+static int mptcp_monitor_msg(struct rtnl_ctrl_data *ctrl,
+ struct nlmsghdr *n, void *arg)
+{
+ const struct genlmsghdr *ghdr = NLMSG_DATA(n);
+ struct rtattr *tb[MPTCP_ATTR_MAX + 1];
+ int len = n->nlmsg_len;
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+ if (len < 0)
+ return -1;
+
+ if (n->nlmsg_type != genl_family)
+ return 0;
+
+ if (timestamp)
+ print_timestamp(stdout);
+
+ if (ghdr->cmd >= ARRAY_SIZE(event_to_str)) {
+ printf("[UNKNOWN %u]\n", ghdr->cmd);
+ goto out;
+ }
+
+ if (event_to_str[ghdr->cmd] == NULL) {
+ printf("[UNKNOWN %u]\n", ghdr->cmd);
+ goto out;
+ }
+
+ printf("[%14s]", event_to_str[ghdr->cmd]);
+
+ parse_rtattr(tb, MPTCP_ATTR_MAX, (void *) ghdr + GENL_HDRLEN, len);
+
+ printf(" token=%08x", rta_getattr_u32(tb[MPTCP_ATTR_TOKEN]));
+
+ if (tb[MPTCP_ATTR_REM_ID])
+ printf(" remid=%u", rta_getattr_u8(tb[MPTCP_ATTR_REM_ID]));
+ if (tb[MPTCP_ATTR_LOC_ID])
+ printf(" locid=%u", rta_getattr_u8(tb[MPTCP_ATTR_LOC_ID]));
+
+ if (tb[MPTCP_ATTR_SADDR4])
+ print_addr("saddr4", AF_INET, tb[MPTCP_ATTR_SADDR4]);
+ if (tb[MPTCP_ATTR_DADDR4])
+ print_addr("daddr4", AF_INET, tb[MPTCP_ATTR_DADDR4]);
+ if (tb[MPTCP_ATTR_SADDR6])
+ print_addr("saddr6", AF_INET6, tb[MPTCP_ATTR_SADDR6]);
+ if (tb[MPTCP_ATTR_DADDR6])
+ print_addr("daddr6", AF_INET6, tb[MPTCP_ATTR_DADDR6]);
+ if (tb[MPTCP_ATTR_SPORT])
+ printf(" sport=%u", rta_getattr_be16(tb[MPTCP_ATTR_SPORT]));
+ if (tb[MPTCP_ATTR_DPORT])
+ printf(" dport=%u", rta_getattr_be16(tb[MPTCP_ATTR_DPORT]));
+ if (tb[MPTCP_ATTR_BACKUP])
+ printf(" backup=%d", rta_getattr_u8(tb[MPTCP_ATTR_BACKUP]));
+ if (tb[MPTCP_ATTR_ERROR])
+ printf(" error=%d", rta_getattr_u8(tb[MPTCP_ATTR_ERROR]));
+ if (tb[MPTCP_ATTR_FLAGS])
+ printf(" flags=%x", rta_getattr_u16(tb[MPTCP_ATTR_FLAGS]));
+ if (tb[MPTCP_ATTR_TIMEOUT])
+ printf(" timeout=%u", rta_getattr_u32(tb[MPTCP_ATTR_TIMEOUT]));
+ if (tb[MPTCP_ATTR_IF_IDX])
+ printf(" ifindex=%d", rta_getattr_s32(tb[MPTCP_ATTR_IF_IDX]));
+ if (tb[MPTCP_ATTR_RESET_REASON])
+ printf(" reset_reason=%u", rta_getattr_u32(tb[MPTCP_ATTR_RESET_REASON]));
+ if (tb[MPTCP_ATTR_RESET_FLAGS])
+ printf(" reset_flags=0x%x", rta_getattr_u32(tb[MPTCP_ATTR_RESET_FLAGS]));
+
+ puts("");
+out:
+ fflush(stdout);
+ return 0;
+}
+
+static int mptcp_monitor(void)
+{
+ if (genl_add_mcast_grp(&genl_rth, genl_family, MPTCP_PM_EV_GRP_NAME) < 0) {
+ perror("can't subscribe to mptcp events");
+ return 1;
+ }
+
+ if (rtnl_listen(&genl_rth, mptcp_monitor_msg, stdout) < 0)
+ return 2;
+
+ return 0;
+}
+
int do_mptcp(int argc, char **argv)
{
if (argc == 0)
MPTCP_PM_CMD_GET_LIMITS);
}
+ if (matches(*argv, "monitor") == 0) {
+ NEXT_ARG_FWD();
+ if (argc == 0)
+ return mptcp_monitor();
+
+ goto unknown;
+ }
+
unknown:
fprintf(stderr, "Command \"%s\" is unknown, try \"ip mptcp help\".\n",
*argv);
return fnum;
}
+static int genl_parse_grps(struct rtattr *attr, const char *name, unsigned int *id)
+{
+ const struct rtattr *pos;
+
+ rtattr_for_each_nested(pos, attr) {
+ struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+
+ parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, pos);
+
+ if (tb[CTRL_ATTR_MCAST_GRP_NAME] && tb[CTRL_ATTR_MCAST_GRP_ID]) {
+ if (strcmp(name, rta_getattr_str(tb[CTRL_ATTR_MCAST_GRP_NAME])) == 0) {
+ *id = rta_getattr_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int genl_add_mcast_grp(struct rtnl_handle *grth, __u16 fnum, const char *group)
+{
+ GENL_REQUEST(req, 1024, GENL_ID_CTRL, 0, 0, CTRL_CMD_GETFAMILY,
+ NLM_F_REQUEST);
+ struct rtattr *tb[CTRL_ATTR_MAX + 1];
+ struct nlmsghdr *answer = NULL;
+ struct genlmsghdr *ghdr;
+ struct rtattr *attrs;
+ int len, ret = -1;
+ unsigned int id;
+
+ addattr16(&req.n, sizeof(req), CTRL_ATTR_FAMILY_ID, fnum);
+
+ if (rtnl_talk(grth, &req.n, &answer) < 0) {
+ fprintf(stderr, "Error talking to the kernel\n");
+ return -2;
+ }
+
+ ghdr = NLMSG_DATA(answer);
+ len = answer->nlmsg_len;
+
+ if (answer->nlmsg_type != GENL_ID_CTRL)
+ goto err_free;
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+ if (len < 0)
+ goto err_free;
+
+ attrs = (struct rtattr *) ((char *) ghdr + GENL_HDRLEN);
+ parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+ if (tb[CTRL_ATTR_MCAST_GROUPS] == NULL) {
+ fprintf(stderr, "Missing mcast groups TLV\n");
+ goto err_free;
+ }
+
+ if (genl_parse_grps(tb[CTRL_ATTR_MCAST_GROUPS], group, &id) < 0)
+ goto err_free;
+
+ ret = rtnl_add_nl_group(grth, id);
+
+err_free:
+ free(answer);
+ return ret;
+}
+
int genl_init_handle(struct rtnl_handle *grth, const char *family,
int *genl_family)
{