Patchwork [Open-FCoE,09/10] fcoemon: Add FIP_VN2VN mode for providing VN2VN FIP responses

login
register
mail settings
Submitter Mark Rustad
Date March 19, 2013, 11:34 p.m.
Message ID <20130319233404.3241.66575.stgit@host1-mdrustad.localdomain>
Download mbox | patch
Permalink /patch/28/
State Accepted
Headers show

Comments

Mark Rustad - March 19, 2013, 11:34 p.m.
The FIP_VN2VN option is used to indicate that the node
should respond to VN2VN FIP requests to facilitate VLAN
discovery in VN2VN environments.

Signed-off-by: Mark Rustad <mark.d.rustad@intel.com>
Tested-by: Jack Morgan <jack.morgan@intel.com>
---

 fcoemon.c     |  132 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 fipvlan.c     |    2 -
 include/fip.h |   17 +++++++
 lib/fip.c     |  137 ++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 270 insertions(+), 18 deletions(-)

Patch

diff --git a/fcoemon.c b/fcoemon.c
index 558e2c0..7f20a7b 100644
--- a/fcoemon.c
+++ b/fcoemon.c
@@ -104,6 +104,7 @@ 
 #define CFG_IF_VAR_DCBREQUIRED "DCB_REQUIRED"
 #define CFG_IF_VAR_AUTOVLAN    "AUTO_VLAN"
 #define CFG_IF_VAR_MODE        "MODE"
+#define CFG_IF_VAR_FIP_RESP    "FIP_RESP"
 
 enum fcoe_mode {
 	FCOE_MODE_FABRIC = 0,
@@ -130,6 +131,7 @@  struct fcoe_port {
 	int fcoe_enable;
 	int dcb_required;
 	enum fcoe_mode mode;
+	bool fip_resp;
 	int auto_vlan;
 	int auto_created;
 	int ready;
@@ -145,6 +147,7 @@  struct fcoe_port {
 	struct sa_timer vlan_disc_timer;
 	int vlan_disc_count;
 	int fip_socket;
+	int fip_responder_socket;
 	char fchost[FCHOSTBUFLEN];
 	char ctlr[FCHOSTBUFLEN];
 	uint32_t last_fc_event_num;
@@ -331,6 +334,7 @@  static void fcm_link_recv(void *);
 static void fcm_link_getlink(void);
 static int fcm_link_buf_check(size_t);
 static void clear_dcbd_info(struct fcm_netif *ff);
+static int fcoe_vid_from_ifname(const char *ifname);
 
 /*
  * Table for getopt_long(3).
@@ -454,6 +458,7 @@  static struct fcoe_port *alloc_fcoe_port(char *ifname)
 		 */
 		p->last_action = FCP_DESTROY_IF;
 		p->fip_socket = -1;
+		p->fip_responder_socket = -1;
 		p->fchost[0] = '\0';
 		p->last_fc_event_num = 0;
 		sa_timer_init(&p->vlan_disc_timer, fcm_vlan_disc_timeout, p);
@@ -570,6 +575,23 @@  static int fcm_read_config_files(void)
 		if (!strncasecmp(val, "yes", 3) && rc == 1)
 			next->auto_vlan = 1;
 
+		/* FIP_RESP */
+		rc = fcm_read_config_variable(file, val, sizeof(val),
+					      fp, CFG_IF_VAR_FIP_RESP);
+		if (rc < 0) {
+			FCM_LOG("Invalid format for %s variable in %s",
+				CFG_IF_VAR_FIP_RESP, file);
+			fclose(fp);
+			free(next);
+			continue;
+		}
+		/* if not found, default to "none" */
+		next->fip_resp = false;
+		if (!strcasecmp(val, "yes") && rc == 1) {
+			FCM_LOG("Starting FIP responder on %s", next->ifname);
+			next->fip_resp = true;
+		}
+
 		/* MODE */
 		rc = fcm_read_config_variable(file, val, sizeof(val),
 					      fp, CFG_IF_VAR_MODE);
@@ -958,12 +980,17 @@  int fcm_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg)
 		return -1;
 
 	if (fh->fip_subcode == FIP_VLAN_NOTE_VN2VN &&
-	    (!p->auto_vlan || p->mode != FCOE_MODE_VN2VN))
+	    (!p->auto_vlan || p->mode != FCOE_MODE_VN2VN)) {
+		FCM_LOG_DBG("%s: vn2vn vlan notif: auto_vlan=%d, mode=%d\n",
+			    __func__, p->auto_vlan, p->mode);
 		return -1;
+	}
 
 	if (fh->fip_subcode != FIP_VLAN_NOTE &&
-	    fh->fip_subcode != FIP_VLAN_NOTE_VN2VN)
+	    fh->fip_subcode != FIP_VLAN_NOTE_VN2VN) {
+		FCM_LOG_DBG("%s: fip_subcode=%d\n", __func__, fh->fip_subcode);
 		return -1;
+	}
 
 	if (fh->fip_subcode == FIP_VLAN_NOTE_VN2VN)
 		vn2vn = true;
@@ -985,7 +1012,7 @@  int fcm_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg)
 				break;
 			}
 			vid = ntohs(((struct fip_tlv_vlan *)tlv)->vlan);
-
+			FCM_LOG_DBG("%s: vid=%d\n", __func__, vid);
 			if (vid) {
 				vp = fcm_new_vlan(sa->sll_ifindex, vid, vn2vn);
 				vp->dcb_required = p->dcb_required;
@@ -1027,7 +1054,7 @@  static int fcm_vlan_disc_socket(struct fcoe_port *p)
 	int fd;
 	int origdev = 1;
 
-	fd = fip_socket(p->ifindex);
+	fd = fip_socket(p->ifindex, FIP_NONE);
 	if (fd < 0) {
 		FCM_LOG_ERR(errno, "socket error");
 		return fd;
@@ -1386,6 +1413,100 @@  static void fcm_dcbd_state_set(struct fcm_netif *ff,
 	ff->response_pending = 0;
 }
 
+static int fip_recv_vlan_req(struct fiphdr *fh, struct sockaddr_ll *ssa,
+			     struct fcoe_port *sp)
+{
+	struct fip_tlv_vlan *vlan_tlvs = NULL;
+	int vlan_count = 0;
+	struct fcoe_port *p;
+	int rc;
+
+	/* Handle all FCoE ports which are on VLANs over this ifname. */
+	p = fcm_find_fcoe_port(sp->real_ifname, FCP_REAL_IFNAME);
+	for (; p; p = fcm_find_next_fcoe_port(p, sp->real_ifname)) {
+		int vid;
+		struct fip_tlv_vlan *vtp;
+
+		if (!p->ready)
+			continue;
+		if (p->mode != FCOE_MODE_VN2VN)
+			continue;
+		vid = fcoe_vid_from_ifname(p->ifname);
+		if (vid < 0)
+			continue;
+		vtp = realloc(vlan_tlvs, sizeof(*vlan_tlvs));
+		if (!vtp)
+			break;
+		memset(&vtp[vlan_count], 0, sizeof(*vtp));
+		vtp[vlan_count].hdr.tlv_type = FIP_TLV_VLAN;
+		vtp[vlan_count].hdr.tlv_len = 1;
+		vtp[vlan_count].vlan = htons(vid);
+		++vlan_count;
+		vlan_tlvs = vtp;
+	}
+
+	FCM_LOG_DBG("%s: %d vlans found\n", __func__, vlan_count);
+	rc = fip_send_vlan_notification(sp->fip_responder_socket,
+					ssa->sll_ifindex, sp->mac,
+					ssa->sll_addr, vlan_tlvs, vlan_count);
+	if (rc < 0) {
+		FCM_LOG_ERR(-rc, "%s: fip_send_vlan_notification error\n",
+			    __func__);
+	}
+	if (vlan_tlvs)
+		free(vlan_tlvs);
+	return rc;
+}
+
+static int fip_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa,
+				 void *arg)
+{
+	struct fcoe_port *p = arg;
+	int rc = -1;
+
+	if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) {
+		FCM_LOG_DBG("ignoring FIP packet, protocol %d\n",
+			    ntohs(fh->fip_proto));
+		return -1;
+	}
+
+	switch (fh->fip_subcode) {
+	case FIP_VLAN_REQ:
+		FCM_LOG_DBG("received VLAN req, subcode=%d\n", fh->fip_subcode);
+		rc = fip_recv_vlan_req(fh, sa, p);
+		break;
+	default:
+		FCM_LOG_DBG("ignored FIP VLAN packet with subcode %d\n",
+			    fh->fip_subcode);
+		break;
+	}
+	return rc;
+}
+
+static void fip_responder(void *arg)
+{
+	struct fcoe_port *p = arg;
+
+	fip_recv(p->fip_responder_socket, fip_vlan_disc_handler, p);
+}
+
+static void init_fip_vn2vn_responder(struct fcoe_port *p)
+{
+	int s;
+
+	if (p->fip_responder_socket >= 0)
+		return;
+	
+	s = fip_socket(p->ifindex, FIP_ALL_VN2VN);
+	if (s < 0) {
+		FCM_LOG_ERR(errno, "%s: Failed to get fip socket\n", p->ifname);
+		return;
+	}
+	p->fip_responder_socket = s;
+	FCM_LOG_DBG("%s: Adding FIP responder socket\n", p->ifname);
+	sa_select_add_fd(s, fip_responder, NULL, NULL, p);
+}
+
 static void update_fcoe_port_state(struct fcoe_port *p, unsigned int type,
 				   u_int8_t operstate, enum fcoeport_ifname t)
 {
@@ -1409,6 +1530,9 @@  static void update_fcoe_port_state(struct fcoe_port *p, unsigned int type,
 		     (operstate == IF_OPER_UP)))
 			ff->ff_operstate = operstate;
 
+		if (t == FCP_REAL_IFNAME && p->fip_resp)
+			init_fip_vn2vn_responder(p);
+
 		if (!p->fcoe_enable) {
 			fcp_set_next_action(p, FCP_DESTROY_IF);
 			return;
diff --git a/fipvlan.c b/fipvlan.c
index 98e7a22..edefe2f 100644
--- a/fipvlan.c
+++ b/fipvlan.c
@@ -710,7 +710,7 @@  static int probe_fip_interface(struct iff *iff)
 		return 0;
 
 	if (!iff->fip_ready) {
-		iff->ps = fip_socket(iff->ifindex);
+		iff->ps = fip_socket(iff->ifindex, FIP_NONE);
 		setsockopt(iff->ps, SOL_PACKET, PACKET_ORIGDEV,
 			   &origdev, sizeof(origdev));
 		pfd_add(iff->ps);
diff --git a/include/fip.h b/include/fip.h
index 7dc2463..443d3c4 100644
--- a/include/fip.h
+++ b/include/fip.h
@@ -37,6 +37,7 @@ 
  * the corresponding FIP multicast MAC address.
  */
 enum fip_multi {
+	FIP_NONE = -1,
 	FIP_ALL_FCOE = 0,
 	FIP_ALL_ENODE = 1,
 	FIP_ALL_FCF = 2,
@@ -143,7 +144,7 @@  struct fip_tlv_vlan {
 
 /* libutil / fip.c functionality */
 
-int fip_socket(int ifindex);
+int fip_socket(int ifindex, enum fip_multi multi);
 
 /* FIP message handler, passed into fip_recv */
 typedef int fip_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg);
@@ -166,4 +167,18 @@  int fip_recv(int s, fip_handler *fn, void *arg);
 ssize_t fip_send_vlan_request(int s, int ifindex, const unsigned char *mac,
 			      enum fip_multi);
 
+/**
+ * fip_send_vlan_notification - send a FIP VLAN notification
+ * @s: ETH_P_FIP packet socket to send on
+ * @ifindex: network interface to send on
+ * @mac: mac address of the netif
+ * @dest: destination mac address
+ * @vtlvs: pointer to vlan tlvs to send
+ * @vlan_count: Number of vlan tlvs to send
+ */
+int
+fip_send_vlan_notification(int s, int ifindex, const __u8 *mac,
+			   const __u8 *dest, struct fip_tlv_vlan *,
+			   int vlan_count);
+
 #endif /* FIP_H */
diff --git a/lib/fip.c b/lib/fip.c
index 1acc388..7a5d836 100644
--- a/lib/fip.c
+++ b/lib/fip.c
@@ -88,29 +88,62 @@  static int fip_get_sanmac(int ifindex, unsigned char *addr)
 }
 
 /**
- * fip_socket_sanmac - add SAN MAC to the unicast list for input socket
+ * fip_socket_add_addr - add a MAC address to the input socket
  * @s: ETH_P_FIP packet socket to setsockopt on
  * @ifindex: network interface index to send on
- * @add: 1 to add 0 to del
+ * @add: true to add false to del
+ * @mac: MAC address to add or delete
+ * @multi: false if unicast, true if multicast address
  */
-static void fip_socket_sanmac(int s, int ifindex, int add)
+static void
+fip_socket_add_addr(int s, int ifindex, bool add, const __u8 *mac, bool multi)
 {
 	struct packet_mreq mr;
-	unsigned char smac[ETHER_ADDR_LEN];
-
-	if (fip_get_sanmac(ifindex, smac))
-		return;
 
 	memset(&mr, 0, sizeof(mr));
 	mr.mr_ifindex = ifindex;
-	mr.mr_type = PACKET_MR_UNICAST;
+	mr.mr_type = multi ? PACKET_MR_MULTICAST : PACKET_MR_UNICAST;
 	mr.mr_alen = ETHER_ADDR_LEN;
-	memcpy(mr.mr_address, smac, ETHER_ADDR_LEN);
+	memcpy(mr.mr_address, mac, ETHER_ADDR_LEN);
 	if (setsockopt(s, SOL_PACKET,
-		       (add) ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
 		       &mr, sizeof(mr)) < 0)
 		FIP_LOG_DBG("PACKET_%s_MEMBERSHIP:failed\n",
-			    (add) ? "ADD" : "DROP");
+			    add ? "ADD" : "DROP");
+}
+
+/**
+ * fip_socket_sanmac - add SAN MAC to the unicast list for input socket
+ * @s: ETH_P_FIP packet socket to setsockopt on
+ * @ifindex: network interface index to send on
+ * @add: 1 to add 0 to del
+ */
+static void fip_socket_sanmac(int s, int ifindex, int add)
+{
+	unsigned char smac[ETHER_ADDR_LEN];
+
+	if (fip_get_sanmac(ifindex, smac)) {
+		FIP_LOG_DBG("%s: no sanmac, ifindex %d\n", __func__, ifindex);
+		return;
+	}
+
+	fip_socket_add_addr(s, ifindex, add, smac, false);
+}
+
+/**
+ * fip_socket_multi - add multicast MAC address to the input socket
+ * @s: ETH_P_FIP packet socket to setsockopt on
+ * @ifindex: network interface index to send on
+ * @add: true to add, false to del
+ * @multi: Multicast destination
+ */
+static void
+fip_socket_multi(int s, int ifindex, bool add, enum fip_multi multi)
+{
+	__u8 smac[ETHER_ADDR_LEN] = FIP_ALL_FCOE_MACS;
+
+	smac[ETHER_ADDR_LEN - 1] = multi;
+	fip_socket_add_addr(s, ifindex, add, smac, true);
 }
 
 /**
@@ -160,8 +193,10 @@  static void drain_socket(int s)
 
 /**
  * fip_socket - create and bind a packet socket for FIP
+ * @ifindex: ifindex of netdevice to bind to
+ * @multi: Indication of any multicast address to bind to
  */
-int fip_socket(int ifindex)
+int fip_socket(int ifindex, enum fip_multi multi)
 {
 	struct sockaddr_ll sa = {
 		.sll_family = AF_PACKET,
@@ -176,6 +211,8 @@  int fip_socket(int ifindex)
 		return s;
 
 	fip_socket_sanmac(s, ifindex, 1);
+	if (multi != FIP_NONE)
+		fip_socket_multi(s, ifindex, true, multi);
 
 	rc = bind(s, (struct sockaddr *) &sa, sizeof(sa));
 	if (rc < 0) {
@@ -257,6 +294,82 @@  ssize_t fip_send_vlan_request(int s, int ifindex, const unsigned char *mac,
 }
 
 /**
+ * fip_send_vlan_notification - send a FIP VLAN notification
+ * @s: ETH_P_FIP packet socket to send on
+ * @ifindex: network interface index to send on
+ * @mac: mac address of the sending network interface
+ * @dest: destination mac address
+ * @tlvs: pointer to vlan tlvs to send
+ * @tlv_count: number of vlan tlvs to send
+ */
+int
+fip_send_vlan_notification(int s, int ifindex, const __u8 *mac,
+			   const __u8 *dest, struct fip_tlv_vlan *vtlvs,
+			   int vlan_count)
+{
+	struct sockaddr_ll dsa = {
+		.sll_family = AF_PACKET,
+		.sll_protocol = htons(ETH_P_FIP),
+		.sll_ifindex = ifindex,
+		.sll_hatype = ARPHRD_ETHER,
+		.sll_pkttype = PACKET_OTHERHOST,
+		.sll_halen = ETHER_ADDR_LEN,
+	};
+	struct fiphdr sfh = {
+		.fip_version = FIP_VERSION(1),
+		.fip_proto = htons(FIP_PROTO_VLAN),
+		.fip_subcode = FIP_VLAN_NOTE_VN2VN,
+		.fip_desc_len = htons(2),
+		.fip_flags = 0,
+	};
+	struct {
+		struct fip_tlv_mac_addr mac;
+	} tlvs = {
+		.mac = {
+			.hdr.tlv_type = FIP_TLV_MAC_ADDR,
+			.hdr.tlv_len = 2,
+		},
+	};
+	struct ethhdr eh;
+	struct iovec iov[] = {
+		{ .iov_base = &eh, .iov_len = sizeof(eh), },
+		{ .iov_base = &sfh, .iov_len = sizeof(sfh), },
+		{ .iov_base = &tlvs, .iov_len = sizeof(tlvs), },
+		{ .iov_base = NULL, .iov_len = 0, },
+	};
+	struct msghdr msg = {
+		.msg_name = &dsa,
+		.msg_namelen = sizeof(dsa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov) - 1,
+	};
+	int rc;
+
+	if (vlan_count) {
+		sfh.fip_desc_len = htons(3);
+		iov[3].iov_base = vtlvs;
+		iov[3].iov_len = vlan_count * sizeof(*vtlvs);
+		msg.msg_iovlen = ARRAY_SIZE(iov);
+	}
+
+	if (fip_get_sanmac(ifindex, eh.h_source))
+		memcpy(eh.h_source, mac, ETHER_ADDR_LEN);
+	eh.h_proto = htons(ETH_P_FIP);
+	memcpy(dsa.sll_addr, dest, ETHER_ADDR_LEN);
+	memcpy(eh.h_dest, dest, ETHER_ADDR_LEN);
+	memcpy(tlvs.mac.mac_addr, eh.h_source, ETHER_ADDR_LEN);
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0) {
+		rc = -errno;
+		FIP_LOG_ERR(errno, "%s:sendmsg error\n", __func__);
+	} else {
+		FIP_LOG_DBG("%s: sent %d bytes to ifindex %d\n",
+			    __func__, rc, ifindex);
+	}
+	return rc;
+}
+
+/**
  * fip_recv - receive from a FIP packet socket
  * @s: packet socket with data ready to be received
  * @fn: FIP receive callback to process the payload