7b5541
commit 8ca81014a352a125aab38fce2b1302d92c0f5ef2
7b5541
Author: Hangbin Liu <liuhangbin@gmail.com>
7b5541
Date:   Fri Mar 1 15:08:48 2019 +0800
7b5541
7b5541
    rtnl: add team activebackup support
7b5541
    
7b5541
    This patch add team interface activebackup mode support. As linux team use
7b5541
    genl netlink message, when we get a rtnl link change notify, we have to setup
7b5541
    a new genl socket and request the current active port.
7b5541
    
7b5541
    v2: check nlmsg_len before copy rta_data
7b5541
    v3: a) Do not make rtnl_buf global as it may be freed by calling rtnl_close()
7b5541
           while we are using it in rtnl_link_status()
7b5541
        b) Reorder declarations of variables as reversed Christmas tree for
7b5541
           function rtnl_link_status()
7b5541
        c) remove rtnl_len
7b5541
    v4: Remove the first !rtnl_buf check in rtnl_link_status as it's alway true
7b5541
    v5: a) Re-order {nl, rtnl}_open and add function nl_close()
7b5541
        b) revert the v3_{a,c}, v4 changes, use nl_close to close genl fd
7b5541
        c) do not use len in get_team_active_iface() as it may mislead reader
7b5541
    
7b5541
    Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
7b5541
7b5541
diff --git a/missing.h b/missing.h
7b5541
index 2f7adb9..8f92079 100644
7b5541
--- a/missing.h
7b5541
+++ b/missing.h
7b5541
@@ -118,6 +118,22 @@ enum {
7b5541
 #define IFLA_BOND_MAX   (__IFLA_BOND_MAX - 1)
7b5541
 #endif	/*IFLA_BOND_MAX*/
7b5541
 
7b5541
+#ifndef NLA_TYPE_MAX
7b5541
+enum {
7b5541
+        NLA_UNSPEC,
7b5541
+        NLA_U8,
7b5541
+        NLA_U16,
7b5541
+        NLA_U32,
7b5541
+        NLA_U64,
7b5541
+        NLA_STRING,
7b5541
+        NLA_FLAG,
7b5541
+        NLA_MSECS,
7b5541
+        NLA_NESTED,
7b5541
+        __NLA_TYPE_MAX,
7b5541
+};
7b5541
+#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
7b5541
+#endif /*NLA_TYPE_MAX*/
7b5541
+
7b5541
 #ifdef __UCLIBC__
7b5541
 
7b5541
 #if (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L) && \
7b5541
diff --git a/phc2sys.8 b/phc2sys.8
7b5541
index 45cb0e3..b3a3de3 100644
7b5541
--- a/phc2sys.8
7b5541
+++ b/phc2sys.8
7b5541
@@ -108,9 +108,9 @@ together with the
7b5541
 option, the master clock is used only to correct the offset by whole number of
7b5541
 seconds, which cannot be fixed with PPS alone. Not compatible with the
7b5541
 .B \-a
7b5541
-option. This option does not support bonded interface (e.g. bond0). If
7b5541
+option. This option does not support bonded interface (e.g. bond0, team0). If
7b5541
 .B ptp4l
7b5541
-has a port on an active-backup bond interface, the
7b5541
+has a port on an active-backup bond or team interface, the
7b5541
 .B \-a
7b5541
 option can be used to track the active interface.
7b5541
 .TP
7b5541
diff --git a/rtnl.c b/rtnl.c
7b5541
index f9a572b..e3758b3 100644
7b5541
--- a/rtnl.c
7b5541
+++ b/rtnl.c
7b5541
@@ -20,6 +20,8 @@
7b5541
 #include <sys/socket.h> /* Must come before linux/netlink.h on some systems. */
7b5541
 #include <linux/netlink.h>
7b5541
 #include <linux/rtnetlink.h>
7b5541
+#include <linux/genetlink.h>
7b5541
+#include <linux/if_team.h>
7b5541
 #include <net/if.h>
7b5541
 #include <stdio.h>
7b5541
 #include <stdlib.h>
7b5541
@@ -30,8 +32,39 @@
7b5541
 #include "print.h"
7b5541
 #include "rtnl.h"
7b5541
 
7b5541
+#define BUF_SIZE 4096
7b5541
+#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
7b5541
+
7b5541
 static int rtnl_len;
7b5541
 static char *rtnl_buf;
7b5541
+static int get_team_active_iface(int master_index);
7b5541
+
7b5541
+static int nl_close(int fd)
7b5541
+{
7b5541
+	return close(fd);
7b5541
+}
7b5541
+
7b5541
+static int nl_open(int family)
7b5541
+{
7b5541
+	int fd;
7b5541
+	struct sockaddr_nl sa;
7b5541
+
7b5541
+	memset(&sa, 0, sizeof(sa));
7b5541
+	sa.nl_family = AF_NETLINK;
7b5541
+	sa.nl_groups = RTNLGRP_LINK;
7b5541
+
7b5541
+	fd = socket(AF_NETLINK, SOCK_RAW, family);
7b5541
+	if (fd < 0) {
7b5541
+		pr_err("failed to open netlink socket: %m");
7b5541
+		return -1;
7b5541
+	}
7b5541
+	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa))) {
7b5541
+		pr_err("failed to bind netlink socket: %m");
7b5541
+		close(fd);
7b5541
+		return -1;
7b5541
+	}
7b5541
+	return fd;
7b5541
+}
7b5541
 
7b5541
 int rtnl_close(int fd)
7b5541
 {
7b5541
@@ -40,7 +73,12 @@ int rtnl_close(int fd)
7b5541
 		rtnl_buf = NULL;
7b5541
 		rtnl_len = 0;
7b5541
 	}
7b5541
-	return close(fd);
7b5541
+	return nl_close(fd);
7b5541
+}
7b5541
+
7b5541
+int rtnl_open(void)
7b5541
+{
7b5541
+	return nl_open(NETLINK_ROUTE);
7b5541
 }
7b5541
 
7b5541
 static void rtnl_get_ts_device_callback(void *ctx, int linkup, int ts_index)
7b5541
@@ -116,14 +154,24 @@ int rtnl_link_query(int fd, char *device)
7b5541
 	return 0;
7b5541
 }
7b5541
 
7b5541
-static inline __u32 rta_getattr_u32(const struct rtattr *rta)
7b5541
+static inline __u8 rta_getattr_u8(struct rtattr *rta)
7b5541
+{
7b5541
+	return *(__u8 *)RTA_DATA(rta);
7b5541
+}
7b5541
+
7b5541
+static inline __u16 rta_getattr_u16(struct rtattr *rta)
7b5541
+{
7b5541
+	return *(__u16 *)RTA_DATA(rta);
7b5541
+}
7b5541
+
7b5541
+static inline __u32 rta_getattr_u32(struct rtattr *rta)
7b5541
 {
7b5541
 	return *(__u32 *)RTA_DATA(rta);
7b5541
 }
7b5541
 
7b5541
-static inline const char *rta_getattr_str(const struct rtattr *rta)
7b5541
+static inline char *rta_getattr_str(struct rtattr *rta)
7b5541
 {
7b5541
-	return (const char *)RTA_DATA(rta);
7b5541
+	return (char *)RTA_DATA(rta);
7b5541
 }
7b5541
 
7b5541
 static int rtnl_rtattr_parse(struct rtattr *tb[], int max, struct rtattr *rta, int len)
7b5541
@@ -150,12 +198,12 @@ static inline int rtnl_nested_rtattr_parse(struct rtattr *tb[], int max, struct
7b5541
 	return rtnl_rtattr_parse(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
7b5541
 }
7b5541
 
7b5541
-static int rtnl_linkinfo_parse(struct rtattr *rta)
7b5541
+static int rtnl_linkinfo_parse(int master_index, struct rtattr *rta)
7b5541
 {
7b5541
-	int index = -1;
7b5541
-	const char *kind;
7b5541
 	struct rtattr *linkinfo[IFLA_INFO_MAX];
7b5541
 	struct rtattr *bond[IFLA_BOND_MAX];
7b5541
+	int index = -1;
7b5541
+	char *kind;
7b5541
 
7b5541
 	if (rtnl_nested_rtattr_parse(linkinfo, IFLA_INFO_MAX, rta) < 0)
7b5541
 		return -1;
7b5541
@@ -172,6 +220,8 @@ static int rtnl_linkinfo_parse(struct rtattr *rta)
7b5541
 			if (bond[IFLA_BOND_ACTIVE_SLAVE]) {
7b5541
 				index = rta_getattr_u32(bond[IFLA_BOND_ACTIVE_SLAVE]);
7b5541
 			}
7b5541
+		} else if (kind && !strncmp(kind, "team", 4)) {
7b5541
+			index = get_team_active_iface(master_index);
7b5541
 		}
7b5541
 	}
7b5541
 	return index;
7b5541
@@ -179,18 +229,18 @@ static int rtnl_linkinfo_parse(struct rtattr *rta)
7b5541
 
7b5541
 int rtnl_link_status(int fd, char *device, rtnl_callback cb, void *ctx)
7b5541
 {
7b5541
+	struct rtattr *tb[IFLA_MAX+1];
7b5541
+	struct ifinfomsg *info = NULL;
7b5541
 	int index, len, link_up;
7b5541
-	int slave_index = -1;
7b5541
-	struct iovec iov;
7b5541
 	struct sockaddr_nl sa;
7b5541
-	struct msghdr msg;
7b5541
+	int slave_index = -1;
7b5541
 	struct nlmsghdr *nh;
7b5541
-	struct ifinfomsg *info = NULL;
7b5541
-	struct rtattr *tb[IFLA_MAX+1];
7b5541
+	struct msghdr msg;
7b5541
+	struct iovec iov;
7b5541
 
7b5541
 	index = if_nametoindex(device);
7b5541
 	if (!rtnl_buf) {
7b5541
-		rtnl_len = 4096;
7b5541
+		rtnl_len = BUF_SIZE;
7b5541
 		rtnl_buf = malloc(rtnl_len);
7b5541
 		if (!rtnl_buf) {
7b5541
 			pr_err("rtnl: low memory");
7b5541
@@ -246,7 +296,7 @@ int rtnl_link_status(int fd, char *device, rtnl_callback cb, void *ctx)
7b5541
 				  IFLA_PAYLOAD(nh));
7b5541
 
7b5541
 		if (tb[IFLA_LINKINFO])
7b5541
-			slave_index = rtnl_linkinfo_parse(tb[IFLA_LINKINFO]);
7b5541
+			slave_index = rtnl_linkinfo_parse(index, tb[IFLA_LINKINFO]);
7b5541
 
7b5541
 		if (cb)
7b5541
 			cb(ctx, link_up, slave_index);
7b5541
@@ -255,24 +305,164 @@ int rtnl_link_status(int fd, char *device, rtnl_callback cb, void *ctx)
7b5541
 	return 0;
7b5541
 }
7b5541
 
7b5541
-int rtnl_open(void)
7b5541
+static int genl_send_msg(int fd, int family_id, int genl_cmd, int genl_version,
7b5541
+		  int rta_type, void *rta_data, int rta_len)
7b5541
 {
7b5541
-	int fd;
7b5541
-	struct sockaddr_nl sa;
7b5541
+	struct sockaddr_nl daddr;
7b5541
+	struct genlmsghdr *gnlh;
7b5541
+	struct nlmsghdr *nlh;
7b5541
+	struct rtattr *attr;
7b5541
+	char msg[BUF_SIZE];
7b5541
 
7b5541
-	memset(&sa, 0, sizeof(sa));
7b5541
-	sa.nl_family = AF_NETLINK;
7b5541
-	sa.nl_groups = RTNLGRP_LINK;
7b5541
+	memset(&daddr, 0, sizeof(daddr));
7b5541
+	daddr.nl_family = AF_NETLINK;
7b5541
 
7b5541
-	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
7b5541
-	if (fd < 0) {
7b5541
-		pr_err("failed to open netlink socket: %m");
7b5541
+	memset(&msg, 0, sizeof(msg));
7b5541
+	nlh = (struct nlmsghdr *) msg;
7b5541
+	nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
7b5541
+	nlh->nlmsg_type = family_id;
7b5541
+	nlh->nlmsg_flags = NLM_F_REQUEST;
7b5541
+
7b5541
+	gnlh = (struct genlmsghdr *) NLMSG_DATA(nlh);
7b5541
+	gnlh->cmd = genl_cmd;
7b5541
+	gnlh->version = genl_version;
7b5541
+
7b5541
+	if (rta_data && rta_len > 0) {
7b5541
+		attr = (struct rtattr *) GENLMSG_DATA(msg);
7b5541
+		attr->rta_type = rta_type;
7b5541
+		attr->rta_len = RTA_LENGTH(rta_len);
7b5541
+		nlh->nlmsg_len += NLMSG_ALIGN(attr->rta_len);
7b5541
+		if (nlh->nlmsg_len < sizeof(msg))
7b5541
+			memcpy(RTA_DATA(attr), rta_data, rta_len);
7b5541
+		else
7b5541
+			return -1;
7b5541
+	}
7b5541
+
7b5541
+	return sendto(fd, &msg, nlh->nlmsg_len, 0,
7b5541
+		      (struct sockaddr *)&daddr, sizeof(daddr));
7b5541
+}
7b5541
+
7b5541
+static int genl_get_family_id(int fd, void *family_name)
7b5541
+{
7b5541
+	struct rtattr *tb[CTRL_ATTR_MAX+1];
7b5541
+	struct nlmsghdr *nlh;
7b5541
+	struct rtattr *attr;
7b5541
+	char msg[BUF_SIZE];
7b5541
+	int len, gf_id;
7b5541
+
7b5541
+	len = genl_send_msg(fd, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 1,
7b5541
+			    CTRL_ATTR_FAMILY_NAME, family_name,
7b5541
+			    strlen(family_name) + 1);
7b5541
+	if (len < 0)
7b5541
+		return len;
7b5541
+
7b5541
+	len = recv(fd, &msg, sizeof(msg), 0);
7b5541
+	if (len < 0)
7b5541
+		return len;
7b5541
+
7b5541
+	nlh = (struct nlmsghdr *) msg;
7b5541
+	if (nlh->nlmsg_type == NLMSG_ERROR || !NLMSG_OK(nlh, len))
7b5541
 		return -1;
7b5541
+
7b5541
+	attr = (struct rtattr *) GENLMSG_DATA(msg);
7b5541
+	rtnl_rtattr_parse(tb, CTRL_ATTR_MAX, attr, NLMSG_PAYLOAD(nlh, GENL_HDRLEN));
7b5541
+
7b5541
+	if (tb[CTRL_ATTR_FAMILY_ID])
7b5541
+		gf_id = rta_getattr_u16(tb[CTRL_ATTR_FAMILY_ID]);
7b5541
+	else
7b5541
+		gf_id = -1;
7b5541
+
7b5541
+	return gf_id;
7b5541
+}
7b5541
+
7b5541
+static int parase_team_list_option(struct rtattr *attr)
7b5541
+{
7b5541
+	struct rtattr *tb[TEAM_ATTR_OPTION_MAX+1];
7b5541
+	int len = RTA_PAYLOAD(attr);
7b5541
+	const char *optname = "";
7b5541
+	const char *mode = "";
7b5541
+	int active_index = -1;
7b5541
+
7b5541
+	for (attr = RTA_DATA(attr); RTA_OK(attr, len); attr = RTA_NEXT(attr, len)) {
7b5541
+		rtnl_nested_rtattr_parse(tb, TEAM_ATTR_OPTION_MAX, attr);
7b5541
+
7b5541
+		if (tb[TEAM_ATTR_OPTION_NAME])
7b5541
+			optname = rta_getattr_str(tb[TEAM_ATTR_OPTION_NAME]);
7b5541
+
7b5541
+		if (!strcmp(optname, "mode") && tb[TEAM_ATTR_OPTION_TYPE] &&
7b5541
+		    rta_getattr_u8(tb[TEAM_ATTR_OPTION_TYPE]) == NLA_STRING)
7b5541
+			mode = rta_getattr_str(tb[TEAM_ATTR_OPTION_DATA]);
7b5541
+
7b5541
+		if (!strcmp(optname, "activeport") && tb[TEAM_ATTR_OPTION_TYPE] &&
7b5541
+		    rta_getattr_u8(tb[TEAM_ATTR_OPTION_TYPE]) == NLA_U32)
7b5541
+			active_index = rta_getattr_u32(tb[TEAM_ATTR_OPTION_DATA]);
7b5541
 	}
7b5541
-	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa))) {
7b5541
-		pr_err("failed to bind netlink socket: %m");
7b5541
-		close(fd);
7b5541
+
7b5541
+	if (strcmp(mode, "activebackup")) {
7b5541
+		pr_err("team supported only in activebackup mode");
7b5541
 		return -1;
7b5541
+	} else {
7b5541
+		return active_index;
7b5541
 	}
7b5541
-	return fd;
7b5541
+}
7b5541
+
7b5541
+static int get_team_active_iface(int master_index)
7b5541
+{
7b5541
+	struct rtattr *tb[TEAM_ATTR_MAX+1];
7b5541
+	struct genlmsghdr *gnlh;
7b5541
+	struct nlmsghdr *nlh;
7b5541
+	char msg[BUF_SIZE];
7b5541
+	int fd, gf_id, len;
7b5541
+	int index = -1;
7b5541
+
7b5541
+	fd = nl_open(NETLINK_GENERIC);
7b5541
+	if (fd < 0)
7b5541
+		return fd;
7b5541
+
7b5541
+	gf_id = genl_get_family_id(fd, TEAM_GENL_NAME);
7b5541
+	if (gf_id < 0) {
7b5541
+		pr_err("get genl family failed");
7b5541
+		goto no_info;
7b5541
+	}
7b5541
+
7b5541
+	len = genl_send_msg(fd, gf_id, TEAM_CMD_OPTIONS_GET,
7b5541
+			    TEAM_GENL_VERSION, TEAM_ATTR_TEAM_IFINDEX,
7b5541
+			    &master_index, sizeof(master_index));
7b5541
+	if (len < 0) {
7b5541
+		pr_err("send team info request failed: %m");
7b5541
+		goto no_info;
7b5541
+	}
7b5541
+
7b5541
+	len = recv(fd, msg, sizeof(msg), 0);
7b5541
+	if (len < 0) {
7b5541
+		pr_err("recv team info failed: %m");
7b5541
+		goto no_info;
7b5541
+	}
7b5541
+
7b5541
+	nlh = (struct nlmsghdr *) msg;
7b5541
+	for ( ; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
7b5541
+		if (nlh->nlmsg_type != gf_id)
7b5541
+			continue;
7b5541
+
7b5541
+		gnlh = (struct genlmsghdr *) NLMSG_DATA(nlh);
7b5541
+		if (gnlh->cmd != TEAM_CMD_OPTIONS_GET)
7b5541
+			continue;
7b5541
+
7b5541
+		rtnl_rtattr_parse(tb, TEAM_ATTR_MAX, (struct rtattr *)GENLMSG_DATA(msg),
7b5541
+				  NLMSG_PAYLOAD(nlh, GENL_HDRLEN));
7b5541
+
7b5541
+		if (tb[TEAM_ATTR_TEAM_IFINDEX] &&
7b5541
+		    master_index != rta_getattr_u32(tb[TEAM_ATTR_TEAM_IFINDEX]))
7b5541
+			continue;
7b5541
+
7b5541
+		if (tb[TEAM_ATTR_LIST_OPTION]) {
7b5541
+			index = parase_team_list_option(tb[TEAM_ATTR_LIST_OPTION]);
7b5541
+			break;
7b5541
+		}
7b5541
+	}
7b5541
+
7b5541
+	return index;
7b5541
+no_info:
7b5541
+	nl_close(fd);
7b5541
+	return -1;
7b5541
 }
7b5541
commit d8eafa6a39a609597e6c1ce44d616ba6f33c27f8
7b5541
Author: Hangbin Liu <liuhangbin@gmail.com>
7b5541
Date:   Wed Mar 20 14:44:13 2019 +0800
7b5541
7b5541
    port: should check the new phc_index before switching
7b5541
    
7b5541
    In logic, when we want to switch phc, we should check if the new phc
7b5541
    index is valid instead of checking the previous one.
7b5541
    
7b5541
    In reality, if we use linux team interface with activebackup mode. As
7b5541
    teamd is a userspace tool, it sets the new slave as active port after
7b5541
    receiving link change message. If we set current active port down and
7b5541
    another slave up. There is a race that we receive the new slave's link
7b5541
    up message while active port(ts_index) is still the old one. This means
7b5541
    we may use a link down interface as ts_index and get phc_index with -1.
7b5541
    
7b5541
    If we update the p->phc_index to -1, there will be no possibility to
7b5541
    change it back to other value as we swith phc only when p->phc_index >= 0.
7b5541
    
7b5541
    With this fix, we will not switch phc_index until receiving the real
7b5541
    active port(p->iface->ts_info.phc_index >= 0) update message.
7b5541
    
7b5541
    Reported-by: Miroslav Lichvar <mlichvar@redhat.com>
7b5541
    Fixes: 536a71031d5c ("ptp4l: use ts label to get ts info")
7b5541
    Signed-off-by: Hangbin Liu <liuhangbin@gmail.com>
7b5541
7b5541
diff --git a/port.c b/port.c
7b5541
index ad9554f..49c6f76 100644
7b5541
--- a/port.c
7b5541
+++ b/port.c
7b5541
@@ -2442,7 +2442,7 @@ void port_link_status(void *ctx, int linkup, int ts_index)
7b5541
 		sk_get_ts_info(p->iface->ts_label, &p->iface->ts_info);
7b5541
 
7b5541
 		/* Only switch phc with HW time stamping mode */
7b5541
-		if (p->phc_index >= 0 && p->iface->ts_info.valid) {
7b5541
+		if (p->iface->ts_info.valid && p->iface->ts_info.phc_index >= 0) {
7b5541
 			required_modes = clock_required_modes(p->clock);
7b5541
 			if ((p->iface->ts_info.so_timestamping & required_modes) != required_modes) {
7b5541
 				pr_err("interface '%s' does not support requested "
7b5541
commit ecfbea2dac2312cb61aa5d2fe1646ee4c6b18643
7b5541
Author: Miroslav Lichvar <mlichvar@redhat.com>
7b5541
Date:   Thu Mar 21 15:44:32 2019 +0100
7b5541
7b5541
    rtnl: close file descriptor in get_team_active_iface()
7b5541
    
7b5541
    Signed-off-by: Miroslav Lichvar <mlichvar@redhat.com>
7b5541
7b5541
diff --git a/rtnl.c b/rtnl.c
7b5541
index e3758b3..59ed0ec 100644
7b5541
--- a/rtnl.c
7b5541
+++ b/rtnl.c
7b5541
@@ -461,8 +461,7 @@ static int get_team_active_iface(int master_index)
7b5541
 		}
7b5541
 	}
7b5541
 
7b5541
-	return index;
7b5541
 no_info:
7b5541
 	nl_close(fd);
7b5541
-	return -1;
7b5541
+	return index;
7b5541
 }