From owner-freebsd-bugs@FreeBSD.ORG Sat Oct 22 20:00:28 2011 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BE65D1065673 for ; Sat, 22 Oct 2011 20:00:28 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id 9B4A78FC15 for ; Sat, 22 Oct 2011 20:00:28 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.4/8.14.4) with ESMTP id p9MK0SA5008012 for ; Sat, 22 Oct 2011 20:00:28 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.4/8.14.4/Submit) id p9MK0SW1008011; Sat, 22 Oct 2011 20:00:28 GMT (envelope-from gnats) Resent-Date: Sat, 22 Oct 2011 20:00:28 GMT Resent-Message-Id: <201110222000.p9MK0SW1008011@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Ivan Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B2085106566B for ; Sat, 22 Oct 2011 19:54:18 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22]) by mx1.freebsd.org (Postfix) with ESMTP id A1A508FC0C for ; Sat, 22 Oct 2011 19:54:18 +0000 (UTC) Received: from red.freebsd.org (localhost [127.0.0.1]) by red.freebsd.org (8.14.4/8.14.4) with ESMTP id p9MJsI55026338 for ; Sat, 22 Oct 2011 19:54:18 GMT (envelope-from nobody@red.freebsd.org) Received: (from nobody@localhost) by red.freebsd.org (8.14.4/8.14.4/Submit) id p9MJsIUj026300; Sat, 22 Oct 2011 19:54:18 GMT (envelope-from nobody) Message-Id: <201110221954.p9MJsIUj026300@red.freebsd.org> Date: Sat, 22 Oct 2011 19:54:18 GMT From: Ivan To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/161908: ng_vlan update for QinQ support X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 22 Oct 2011 20:00:28 -0000 >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@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 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,6 +47,22 @@ #include #include + + +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 * 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: