kern/161908: ng_vlan update for QinQ support

Ivan rozhuk.im at gmail.com
Sat Oct 22 20:00:28 UTC 2011


>Number:         161908
>Category:       kern
>Synopsis:       ng_vlan update for QinQ support
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          update
>Submitter-Id:   current-users
>Arrival-Date:   Sat Oct 22 20:00:28 UTC 2011
>Closed-Date:
>Last-Modified:
>Originator:     Ivan
>Release:        FreeBSD 8.2-STABLE
>Organization:
>Environment:
FreeBSD firewall 8.2-STABLE FreeBSD 8.2-STABLE #16: Sat Oct 22 07:46:34 IRKST 2011     root at firewall:/usr/obj/usr/src/sys/RIMx64  amd64
>Description:
+ ethernet_type for VLAN encapsulation is tunable, default is: 0x8100 (33024)
+ PCP (Priority Code Point) and CFI (Canonical Format Indicator) for VLAN encapsulation is tunable per VID
+ VLAN filter can be deleted by VID
+ tunable encapsulation: on - do 802.1Q encapsulation, off - set M_VLANTAG and ether_vtag
* improved encapsulation/decapsulation code
* "vlan" changed to "vid" in "addfilter" and "gettable" messages
* many other canges


kldunload ng_vlan
kldload ng_ether

ngctl msg re0: setpromisc   1
ngctl msg re0: setautosrc   0

ngctl mkpeer re0: vlan lower downstream
ngctl connect re0: re0:lower upper nomatch

ngctl mkpeer re0:lower eiface vlan1001 ether
ngctl mkpeer re0:lower eiface vlan1002 ether

ngctl name re0:lower:vlan1001 ngeth0
ifconfig ngeth0 link 00:1a:4d:55:9a:43
ifconfig ngeth0 inet 192.168.0.36 netmask 255.255.255.0

ngctl name re0:lower:vlan1002 ngeth1
ifconfig ngeth1 link 00:1a:4d:55:9a:44
ifconfig ngeth1 inet 192.168.254.36 netmask 255.255.255.0

ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" }'
ngctl msg re0:lower addfilter '{ vid=1002 pcp=6 cfi=1 hook="vlan1002" }'



# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   { n=2 filter=[ { hook="vlan1001" vid=1001 } { hook="vlan1002" vid=1002 pcp=6 cfi=1 } ] }

# ngctl msg re0:lower getencap
Rec'd response "getencap" (5) from "[156]:":
Args:   1

# ngctl msg re0:lower getencapproto
Rec'd response "getencapproto" (7) from "[156]:":
Args:   33024

# ngctl msg re0:lower delvidflt 1001

# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   { n=1 filter=[ { hook="vlan1002" vid=1002 pcp=6 cfi=1 } ] }

# ngctl msg re0:lower delfilter '"vlan1002"'

# ngctl msg re0:lower gettable
Rec'd response "gettable" (4) from "[156]:":
Args:   {}



NOTE:
1.1 Use "setencap" = 0 with care: node connected to "downstream" must handle M_VLANTAG + ether_vtag. If it ng_ether, then IFCAP_VLAN_HWTAGGING must be enabled on attached network adapter.

1.2 Then "setencap" = 0, "setencapproto" value is ignored and assumed that 0x8100 (M_VLANTAG + ether_vtag - allways encapsulated with tag 0x8100)


2. "addfilter" syntax changed!!!
was:
ngctl msg re0:lower addfilter '{ vlan=1001 hook="vlan1001" }'

now:
ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" }'
ngctl msg re0:lower addfilter '{ vid=1001 hook="vlan1001" pcp=0 cfi=0 }'
(equivalent)


3. Trick:
kern.ipc.max_linkhdr should be increased via sysctl for best perfomance:
20 - 1 VLAN tag (.Q)
24 - 2 VLAN tags (QinQ)
28 - 3 VLAN tags (QinQinQ)
32 - 4 VLAN tags (...)

>How-To-Repeat:

>Fix:


Patch attached with submission follows:

--- /usr/src/sys/netgraph/ng_vlan.c.orig	2009-08-03 17:13:06.000000000 +0900
+++ /usr/src/sys/netgraph/ng_vlan.c	2011-10-23 03:41:00.000000000 +0900
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003 IPNET Internet Communication Company
+ * Copyright (c) 2011 Rozhuk Ivan <rozhuk.im at gmail.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -46,6 +47,22 @@
 #include <netgraph/ng_vlan.h>
 #include <netgraph/netgraph.h>
 
+
+
+struct ng_vlan_private {
+	hook_p		downstream_hook;
+	hook_p		nomatch_hook;
+	int		encap_enable;
+	u_int16_t	encap_proto;
+	hook_p		vlan_hook[(EVL_VLID_MASK + 1)];
+};
+typedef struct ng_vlan_private *priv_p;
+
+#define VLAN_TAG_MASK	0xFFFF
+#define HOOK_VLAN_TAG_SET_MASK ((uintptr_t)((~0) & ~(VLAN_TAG_MASK)))
+#define IS_HOOK_VLAN_SET(hook_data) ((((uintptr_t)hook_data) & HOOK_VLAN_TAG_SET_MASK) == HOOK_VLAN_TAG_SET_MASK)
+
+
 static ng_constructor_t	ng_vlan_constructor;
 static ng_rcvmsg_t	ng_vlan_rcvmsg;
 static ng_shutdown_t	ng_vlan_shutdown;
@@ -105,11 +122,46 @@
 	},
 	{
 	  NGM_VLAN_COOKIE,
+	  NGM_VLAN_DEL_VID_FLT,
+	  "delvidflt",
+	  &ng_parse_int16_type,
+	  NULL
+	},
+	{
+	  NGM_VLAN_COOKIE,
 	  NGM_VLAN_GET_TABLE,
 	  "gettable",
 	  NULL,
 	  &ng_vlan_table_type
 	},
+	{
+	  NGM_VLAN_COOKIE,
+	  NGM_VLAN_GET_ENCAP,
+	  "getencap",
+	  NULL,
+	  &ng_parse_int32_type
+	},
+	{
+	  NGM_VLAN_COOKIE,
+	  NGM_VLAN_SET_ENCAP,
+	  "setencap",
+	  &ng_parse_int32_type,
+	  NULL
+	},
+	{
+	  NGM_VLAN_COOKIE,
+	  NGM_VLAN_GET_ENCAP_PROTO,
+	  "getencapproto",
+	  NULL,
+	  &ng_parse_int32_type
+	},
+	{
+	  NGM_VLAN_COOKIE,
+	  NGM_VLAN_SET_ENCAP_PROTO,
+	  "setencapproto",
+	  &ng_parse_int32_type,
+	  NULL
+	},
 	{ 0 }
 };
 
@@ -126,47 +178,43 @@
 };
 NETGRAPH_INIT(vlan, &ng_vlan_typestruct);
 
-struct filter {
-	LIST_ENTRY(filter) next;
-	u_int16_t	vlan;
-	hook_p		hook;
-};
 
-#define	HASHSIZE	16
-#define	HASH(id)	((((id) >> 8) ^ ((id) >> 4) ^ (id)) & 0x0f)
-LIST_HEAD(filterhead, filter);
 
-typedef struct {
-	hook_p		downstream_hook;
-	hook_p		nomatch_hook;
-	struct filterhead hashtable[HASHSIZE];
-	u_int32_t	nent;
-} *priv_p;
+//************************************************************************
+//			HELPER STUFF
+//************************************************************************
 
-static struct filter *
-ng_vlan_findentry(priv_p priv, u_int16_t vlan)
+static __inline int
+m_chk(struct mbuf **mp, int len)
 {
-	struct filterhead *chain = &priv->hashtable[HASH(vlan)];
-	struct filter *f;
+	if ((*mp)->m_pkthdr.len < len) {
+		m_freem((*mp));
+		(*mp) = NULL;
+		return (EINVAL);
+	}
+	if ((*mp)->m_len < len && ((*mp) = m_pullup((*mp), len)) == NULL)
+		return (ENOBUFS);
 
-	LIST_FOREACH(f, chain, next)
-		if (f->vlan == vlan)
-			return (f);
-	return (NULL);
+return (0);
 }
 
+//************************************************************************
+//			NETGRAPH NODE STUFF
+//************************************************************************
+
 static int
 ng_vlan_constructor(node_p node)
 {
 	priv_p priv;
-	int i;
 
 	priv = malloc(sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
 	if (priv == NULL)
 		return (ENOMEM);
-	for (i = 0; i < HASHSIZE; i++)
-		LIST_INIT(&priv->hashtable[i]);
+	priv->encap_enable = 1;
+	priv->encap_proto = htons(ETHERTYPE_VLAN);
+	
 	NG_NODE_SET_PRIVATE(node, priv);
+
 	return (0);
 }
 
@@ -193,13 +241,14 @@
 ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook)
 {
 	const priv_p priv = NG_NODE_PRIVATE(node);
-	int error = 0;
 	struct ng_mesg *msg, *resp = NULL;
 	struct ng_vlan_filter *vf;
-	struct filter *f;
 	hook_p hook;
 	struct ng_vlan_table *t;
-	int i;
+	uintptr_t hook_data;
+	int i, vlan_count;
+	u_int16_t vid;
+	int error = 0;
 
 	NGI_GET_MSG(item, msg);
 	/* Deal with message according to cookie and command. */
@@ -214,12 +263,12 @@
 			}
 			vf = (struct ng_vlan_filter *)msg->data;
 			/* Sanity check the VLAN ID value. */
-			if (vf->vlan & ~EVL_VLID_MASK) {
+			if (vf->vid & ~EVL_VLID_MASK || vf->pcp & ~7 || vf->cfi & ~1) {
 				error = EINVAL;
 				break;
 			}
 			/* Check that a referenced hook exists. */
-			hook = ng_findhook(node, vf->hook);
+			hook = ng_findhook(node, vf->hook_name);
 			if (hook == NULL) {
 				error = ENOENT;
 				break;
@@ -231,30 +280,18 @@
 				break;
 			}
 			/* And is not already in service. */
-			if (NG_HOOK_PRIVATE(hook) != NULL) {
+			if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) {
 				error = EEXIST;
 				break;
 			}
 			/* Check we don't already trap this VLAN. */
-			if (ng_vlan_findentry(priv, vf->vlan)) {
+			if (priv->vlan_hook[vf->vid] != NULL) {
 				error = EEXIST;
 				break;
 			}
-			/* Create filter. */
-			f = malloc(sizeof(*f),
-			    M_NETGRAPH, M_NOWAIT | M_ZERO);
-			if (f == NULL) {
-				error = ENOMEM;
-				break;
-			}
-			/* Link filter and hook together. */
-			f->hook = hook;
-			f->vlan = vf->vlan;
-			NG_HOOK_SET_PRIVATE(hook, f);
-			/* Register filter in a hash table. */
-			LIST_INSERT_HEAD(
-			    &priv->hashtable[HASH(f->vlan)], f, next);
-			priv->nent++;
+			/* Link vlan and hook together. */
+			NG_HOOK_SET_PRIVATE(hook, (void *)(HOOK_VLAN_TAG_SET_MASK | EVL_MAKETAG(vf->vid, vf->pcp, vf->cfi)));
+			priv->vlan_hook[vf->vid] = hook;
 			break;
 		case NGM_VLAN_DEL_FILTER:
 			/* Check that message is long enough. */
@@ -264,35 +301,125 @@
 			}
 			/* Check that hook exists and is active. */
 			hook = ng_findhook(node, (char *)msg->data);
-			if (hook == NULL ||
-			    (f = NG_HOOK_PRIVATE(hook)) == NULL) {
+			if (hook == NULL) {
+				error = ENOENT;
+				break;
+			}
+			hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+			if (IS_HOOK_VLAN_SET(hook_data) == 0) {
+				error = ENOENT;
+				break;
+			}
+#ifdef NETGRAPH_DEBUG
+			if (priv->vlan_hook[EVL_VLANOFTAG(hook_data)] != hook)
+				printf("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n", __func__, (char *)msg->data);
+#endif
+			/* Purge a rule that refers to this hook. */
+			priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
+			NG_HOOK_SET_PRIVATE(hook, NULL);
+			break;
+		case NGM_VLAN_DEL_VID_FLT:
+			/* Check that message is long enough. */
+			if (msg->header.arglen != sizeof(u_int16_t)) {
+				error = EINVAL;
+				break;
+			}
+			vid = (*((u_int16_t *)msg->data));
+			/* Sanity check the VLAN ID value. */
+			if (vid & ~EVL_VLID_MASK) {
+				error = EINVAL;
+				break;
+			}
+			/* Check that hook exists and is active. */
+			hook = priv->vlan_hook[vid];
+			if (hook == NULL) {
+				error = ENOENT;
+				break;
+			}
+			hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+			if (IS_HOOK_VLAN_SET(hook_data) == 0) {
 				error = ENOENT;
 				break;
 			}
+#ifdef NETGRAPH_DEBUG
+			if (EVL_VLANOFTAG(hook_data) != vid)
+				printf("%s: NGM_VLAN_DEL_VID_FLT: Invalid VID Hook = %us, must be: %us\n", __func__, (u_int16_t)EVL_VLANOFTAG(hook_data), vid);
+#endif
 			/* Purge a rule that refers to this hook. */
+			priv->vlan_hook[vid] = NULL;
 			NG_HOOK_SET_PRIVATE(hook, NULL);
-			LIST_REMOVE(f, next);
-			priv->nent--;
-			free(f, M_NETGRAPH);
 			break;
 		case NGM_VLAN_GET_TABLE:
+			/* calculate vlans */
+			vlan_count = 0;
+			for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+				if (priv->vlan_hook[i] != NULL
+				   && NG_HOOK_IS_VALID(priv->vlan_hook[i]))
+					vlan_count ++;
+			}
+
+			/* allocate memory for responce */
 			NG_MKRESPONSE(resp, msg, sizeof(*t) +
-			    priv->nent * sizeof(*t->filter), M_NOWAIT);
+			    vlan_count * sizeof(*t->filter), M_NOWAIT);
 			if (resp == NULL) {
 				error = ENOMEM;
 				break;
 			}
+
+			/* pack data to responce */
 			t = (struct ng_vlan_table *)resp->data;
-			t->n = priv->nent;
+			t->n = 0;
 			vf = &t->filter[0];
-			for (i = 0; i < HASHSIZE; i++) {
-				LIST_FOREACH(f, &priv->hashtable[i], next) {
-					vf->vlan = f->vlan;
-					strncpy(vf->hook, NG_HOOK_NAME(f->hook),
+			for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+				hook = priv->vlan_hook[i];
+				if (hook == NULL
+				   || NG_HOOK_NOT_VALID(hook))
+					continue;
+				hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+				if (IS_HOOK_VLAN_SET(hook_data) == 0)
+					continue;
+#ifdef NETGRAPH_DEBUG
+				if (EVL_VLANOFTAG(hook_data) != i)
+					printf("%s: NGM_VLAN_GET_TABLE: hook %s VID = %us, must be: %i\n", __func__, NG_HOOK_NAME(hook), (u_int16_t)EVL_VLANOFTAG(hook_data), i);
+#endif
+				vf->vid = i;
+				vf->pcp = EVL_PRIOFTAG(hook_data);
+				vf->cfi = EVL_CFIOFTAG(hook_data);
+				strncpy(vf->hook_name, NG_HOOK_NAME(hook),
 					    NG_HOOKSIZ);
-					vf++;
-				}
+				vf ++;
+				t->n ++;
+			}
+			break;
+		case NGM_VLAN_GET_ENCAP:
+			NG_MKRESPONSE(resp, msg, sizeof(u_int32_t), M_NOWAIT);
+			if (resp == NULL) {
+				error = ENOMEM;
+				break;
+			}
+			(*((u_int32_t *)resp->data)) = priv->encap_enable;
+			break;
+		case NGM_VLAN_SET_ENCAP:
+			if (msg->header.arglen != sizeof(u_int32_t)) {
+				error = EINVAL;
+				break;
+			}
+			priv->encap_enable = ((*((u_int32_t *)msg->data)) != 0);
+			break;
+		case NGM_VLAN_GET_ENCAP_PROTO:
+			NG_MKRESPONSE(resp, msg, sizeof(u_int16_t), M_NOWAIT);
+			if (resp == NULL) {
+				error = ENOMEM;
+				break;
+			}
+			(*((u_int16_t *)resp->data)) = ntohs(priv->encap_proto);
+			break;
+		case NGM_VLAN_SET_ENCAP_PROTO:
+			if (msg->header.arglen != sizeof(u_int16_t)) {
+				error = EINVAL;
+				break;
 			}
+			priv->encap_proto = htons((*((u_int16_t *)msg->data)));
 			break;
 		default:		/* Unknown command. */
 			error = EINVAL;
@@ -302,8 +429,6 @@
 	case NGM_FLOW_COOKIE:
 	    {
 		struct ng_mesg *copy;
-		struct filterhead *chain;
-		struct filter *f;
 
 		/*
 		 * Flow control messages should come only
@@ -314,17 +439,16 @@
 			break;
 		if (lasthook != priv->downstream_hook)
 			break;
-
 		/* Broadcast the event to all uplinks. */
-		for (i = 0, chain = priv->hashtable; i < HASHSIZE;
-		    i++, chain++)
-		LIST_FOREACH(f, chain, next) {
+		for (i = 0; i < (EVL_VLID_MASK + 1); i ++) {
+			if (priv->vlan_hook[i] == NULL)
+				continue;
+
 			NG_COPYMESSAGE(copy, msg, M_NOWAIT);
 			if (copy == NULL)
-				continue;
-			NG_SEND_MSG_HOOK(error, node, copy, f->hook, 0);
+					continue;
+			NG_SEND_MSG_HOOK(error, node, copy, priv->vlan_hook[i], 0);
 		}
-
 		break;
 	    }
 	default:			/* Unknown type cookie. */
@@ -343,16 +467,19 @@
 	struct ether_header *eh;
 	struct ether_vlan_header *evl = NULL;
 	int error;
-	u_int16_t vlan;
+	uintptr_t hook_data;
+	u_int16_t vid;
 	struct mbuf *m;
-	struct filter *f;
+	hook_p dst_hook;
+
 
-	/* Make sure we have an entire header. */
 	NGI_GET_M(item, m);
-	if (m->m_len < sizeof(*eh) &&
-	    (m = m_pullup(m, sizeof(*eh))) == NULL) {
+
+	/* Make sure we have an entire header. */
+	error = m_chk(&m, ETHER_HDR_LEN);
+	if (error != 0) {
 		NG_FREE_ITEM(item);
-		return (EINVAL);
+		return (error);
 	}
 	eh = mtod(m, struct ether_header *);
 	if (hook == priv->downstream_hook) {
@@ -360,75 +487,104 @@
 		 * If from downstream, select between a match hook
 		 * or the nomatch hook.
 		 */
+		dst_hook = priv->nomatch_hook;
 		if (m->m_flags & M_VLANTAG ||
-		    eh->ether_type == htons(ETHERTYPE_VLAN)) {
+		    eh->ether_type == priv->encap_proto) {
 			if (m->m_flags & M_VLANTAG) {
 				/*
 				 * Packet is tagged, m contains a normal
 				 * Ethernet frame; tag is stored out-of-band.
 				 */
-				vlan = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
-			} else {
-				if (m->m_len < sizeof(*evl) &&
-				    (m = m_pullup(m, sizeof(*evl))) == NULL) {
+				vid = EVL_VLANOFTAG(m->m_pkthdr.ether_vtag);
+			} else { /* eh->ether_type == priv->encap_proto */
+				error = m_chk(&m, (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
+				if (error != 0) {
 					NG_FREE_ITEM(item);
-					return (EINVAL);
+					return (error);
 				}
 				evl = mtod(m, struct ether_vlan_header *);
-				vlan = EVL_VLANOFTAG(ntohs(evl->evl_tag));
+				vid = EVL_VLANOFTAG(ntohs(evl->evl_tag));
 			}
-			if ((f = ng_vlan_findentry(priv, vlan)) != NULL) {
+
+			if (priv->vlan_hook[vid] != NULL) {
+				dst_hook = priv->vlan_hook[vid];
 				if (m->m_flags & M_VLANTAG) {
 					m->m_pkthdr.ether_vtag = 0;
 					m->m_flags &= ~M_VLANTAG;
 				} else {
-					evl->evl_encap_proto = evl->evl_proto;
-					bcopy(mtod(m, caddr_t),
-					    mtod(m, caddr_t) +
-					    ETHER_VLAN_ENCAP_LEN,
-					    ETHER_HDR_LEN);
+					/* 
+					 * move DstMAC and SrcMAC to ETHER_TYPE
+					 * before: [dst_mac] [src_mac] [ether_type_encap(TPID)] [PCP/CFI/VID] [ether_type] [payload]
+					 *         |-----------------| >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |---------------------|
+					 * after: [free space] [dst_mac] [src_mac] [ether_type] [payload]
+					 *                     |-----------------| |---------------------|
+					 */
+					bcopy((char *)evl, ((char *)evl + ETHER_VLAN_ENCAP_LEN),
+						(ETHER_ADDR_LEN * 2));
 					m_adj(m, ETHER_VLAN_ENCAP_LEN);
 				}
 			}
-		} else
-			f = NULL;
-		if (f != NULL)
-			NG_FWD_NEW_DATA(error, item, f->hook, m);
-		else
-			NG_FWD_NEW_DATA(error, item, priv->nomatch_hook, m);
+		}
 	} else {
 		/*
 		 * It is heading towards the downstream.
 		 * If from nomatch, pass it unmodified.
 		 * Otherwise, do the VLAN encapsulation.
 		 */
-		if (hook != priv->nomatch_hook) {
-			if ((f = NG_HOOK_PRIVATE(hook)) == NULL) {
+		dst_hook = priv->downstream_hook;
+		if (dst_hook != NULL && hook != priv->nomatch_hook) {
+			hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+			if (IS_HOOK_VLAN_SET(hook_data) == 0) {
+				m_freem(m);
 				NG_FREE_ITEM(item);
-				NG_FREE_M(m);
 				return (EOPNOTSUPP);
 			}
-			M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
-			/* M_PREPEND takes care of m_len and m_pkthdr.len. */
-			if (m == NULL || (m->m_len < sizeof(*evl) &&
-			    (m = m_pullup(m, sizeof(*evl))) == NULL)) {
-				NG_FREE_ITEM(item);
-				return (ENOMEM);
+			if (priv->encap_enable == 0) {
+				/* just set packet header tag */
+				m->m_flags |= M_VLANTAG;
+				m->m_pkthdr.ether_vtag = (hook_data & VLAN_TAG_MASK);
+			} else {
+				/*
+				 * Transform the Ethernet header into an Ethernet header
+				 * with 802.1Q encapsulation.
+				 * mod of: ether_vlanencap 
+				 */
+				M_PREPEND(m, ETHER_VLAN_ENCAP_LEN, M_DONTWAIT);
+				/* M_PREPEND takes care of m_len and m_pkthdr.len. */
+				if (m == NULL)
+					error = ENOMEM;
+				else
+					error = m_chk(&m, (ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN));
+				if (error != 0) {
+					NG_FREE_ITEM(item);
+					return (error);
+				}
+				/* move DstMAC and SrcMAC from ETHER_TYPE
+				 * before: [free - prepended space] [dst_mac] [src_mac] [ether_type] [payload]
+				 *          <<<<<<<<<<<<<<<<<<<<<<  |-----------------| |--------------------|
+				 * after: [dst_mac] [src_mac] [ether_type_encap(TPID)] [PCP/CFI/VID] [ether_type] [payload]
+				 *        |-----------------| |----------- inserted tag -----------| |--------------------| 
+				 */
+				evl = mtod(m, struct ether_vlan_header *);
+				bcopy(((char *)evl + ETHER_VLAN_ENCAP_LEN),
+					(char *)evl, (ETHER_ADDR_LEN * 2));
+				evl->evl_encap_proto = priv->encap_proto;
+				evl->evl_tag = htons((hook_data & VLAN_TAG_MASK));
 			}
-			/*
-			 * Transform the Ethernet header into an Ethernet header
-			 * with 802.1Q encapsulation.
-			 */
-			bcopy(mtod(m, char *) + ETHER_VLAN_ENCAP_LEN,
-			    mtod(m, char *), ETHER_HDR_LEN);
-			evl = mtod(m, struct ether_vlan_header *);
-			evl->evl_proto = evl->evl_encap_proto;
-			evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
-			evl->evl_tag = htons(f->vlan);
 		}
-		NG_FWD_NEW_DATA(error, item, priv->downstream_hook, m);
 	}
-	return (error);
+
+	/* send packet */
+	if (dst_hook != NULL) {
+		NG_FWD_NEW_DATA(error, item, dst_hook, m);
+		return (error);
+	}
+
+	/* no hook to send */
+	m_freem(m);
+	NG_FREE_ITEM(item);
+
+	return (ENETDOWN);
 }
 
 static int
@@ -446,7 +602,7 @@
 ng_vlan_disconnect(hook_p hook)
 {
 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
-	struct filter *f;
+	uintptr_t hook_data;
 
 	if (hook == priv->downstream_hook)
 		priv->downstream_hook = NULL;
@@ -454,11 +610,9 @@
 		priv->nomatch_hook = NULL;
 	else {
 		/* Purge a rule that refers to this hook. */
-		if ((f = NG_HOOK_PRIVATE(hook)) != NULL) {
-			LIST_REMOVE(f, next);
-			priv->nent--;
-			free(f, M_NETGRAPH);
-		}
+		hook_data = (uintptr_t)NG_HOOK_PRIVATE(hook);
+		if (IS_HOOK_VLAN_SET(hook_data) == 0)
+			priv->vlan_hook[EVL_VLANOFTAG(hook_data)] = NULL;
 	}
 	NG_HOOK_SET_PRIVATE(hook, NULL);
 	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&


--- /usr/src/sys/netgraph/ng_vlan.h.orig	2009-08-03 17:13:06.000000000 +0900
+++ /usr/src/sys/netgraph/ng_vlan.h	2011-10-22 19:11:01.000000000 +0900
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003 IPNET Internet Communication Company
+ * Copyright (c) 2011 Rozhuk Ivan <rozhuk.im at gmail.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -43,19 +44,28 @@
 enum {
 	NGM_VLAN_ADD_FILTER = 1,
 	NGM_VLAN_DEL_FILTER,
-	NGM_VLAN_GET_TABLE
+	NGM_VLAN_DEL_VID_FLT,
+	NGM_VLAN_GET_TABLE,
+	NGM_VLAN_GET_ENCAP,
+	NGM_VLAN_SET_ENCAP,
+	NGM_VLAN_GET_ENCAP_PROTO,
+	NGM_VLAN_SET_ENCAP_PROTO,
 };
 
 /* For NGM_VLAN_ADD_FILTER control message. */
 struct ng_vlan_filter {
-	char		hook[NG_HOOKSIZ];
-	u_int16_t	vlan;
-};	
+	char		hook_name[NG_HOOKSIZ];
+	u_int16_t	vid;	/* VID - VLAN Identifier */
+	u_int8_t	pcp;	/* PCP - Priority Code Point */
+	u_int8_t	cfi;	/* CFI - Canonical Format Indicator */
+};
 
 /* Keep this in sync with the above structure definition.  */
 #define	NG_VLAN_FILTER_FIELDS	{				\
-	{ "hook",	&ng_parse_hookbuf_type  },		\
-	{ "vlan",	&ng_parse_uint16_type   },		\
+	{ "hook",	&ng_parse_hookbuf_type	},		\
+	{ "vid",	&ng_parse_uint16_type	},		\
+	{ "pcp",	&ng_parse_uint8_type	},		\
+	{ "cfi",	&ng_parse_uint8_type	},		\
 	{ NULL }						\
 }
 


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list