/* * Receive data, and do something with it. * Actually we receive a queue item which holds the data. * If we free the item it will also free the data unless we have * previously disassociated it using the NGI_GET_M() macro. * Possibly send it out on another link after processing. * Possibly do something different if it comes from different * hooks. The caller will never free m, so if we use up this data or * abort we must free it. * * If we want, we may decide to force this data to be queued and reprocessed * at the netgraph NETISR time. * We would do that by setting the HK_QUEUE flag on our hook. We would do that * in the connect() method. */ static int ng_xxx_rcvdata(hook_p hook, item_p item ) { const xxx_p xxxp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); int chan = -2; int dlci = -2; int error; struct mbuf *m; NGI_GET_M(item, m); if (NG_HOOK_PRIVATE(hook)) { dlci = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->dlci; chan = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->channel; if (dlci != -1) { /* If received on a DLCI hook process for this * channel and pass it to the downstream module. * Normally one would add a multiplexing header at * the front here */ /* M_PREPEND(....) ; */ /* mtod(m, xxxxxx)->dlci = dlci; */ NG_FWD_NEW_DATA(error, item, xxxp->downstream_hook.hook, m); xxxp->packets_out++; } else { /* data came from the multiplexed link */ dlci = 1; /* get dlci from header */ /* madjust(....) *//* chop off header */ for (chan = 0; chan < XXX_NUM_DLCIS; chan++) if (xxxp->channel[chan].dlci == dlci) break; if (chan == XXX_NUM_DLCIS) { NG_FREE_ITEM(item); NG_FREE_M(m); return (ENETUNREACH); } /* If we were called at splnet, use the following: * NG_SEND_DATA_ONLY(error, otherhook, m); if this * node is running at some SPL other than SPLNET * then you should use instead: error = * ng_queueit(otherhook, m, NULL); m = NULL; * This queues the data using the standard NETISR * system and schedules the data to be picked * up again once the system has moved to SPLNET and * the processing of the data can continue. After * these are run 'm' should be considered * as invalid and NG_SEND_DATA actually zaps them. */ NG_FWD_NEW_DATA(error, item, xxxp->channel[chan].hook, m); xxxp->packets_in++; } } else { /* It's the debug hook, throw it away.. */ if (hook == xxxp->downstream_hook.hook) { NG_FREE_ITEM(item); NG_FREE_M(m); } } return 0; }
/* * This is called once we've already connected a new hook to the other node. * It gives us a chance to balk at the last minute. */ static int ng_xxx_connect(hook_p hook) { #if 0 /* * If we were a driver running at other than splnet then * we should set the QUEUE bit on the edge so that we * will deliver by queing. */ if /*it is the upstream hook */ NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); #endif #if 0 /* * If for some reason we want incoming date to be queued * by the NETISR system and delivered later we can set the same bit on * OUR hook. (maybe to allow unwinding of the stack) */ if (NG_HOOK_PRIVATE(hook)) { int dlci; /* * If it's dlci 1023, requeue it so that it's handled * at a lower priority. This is how a node decides to * defer a data message. */ dlci = ((struct XXX_hookinfo *) NG_HOOK_PRIVATE(hook))->dlci; if (dlci == 1023) { NG_HOOK_FORCE_QUEUE(hook); } #endif /* otherwise be really amiable and just say "YUP that's OK by me! " */ return (0); } /* * Hook disconnection * * For this type, removal of the last link destroys the node */ static int ng_xxx_disconnect(hook_p hook) { if (NG_HOOK_PRIVATE(hook)) ((struct XXX_hookinfo *) (NG_HOOK_PRIVATE(hook)))->hook = NULL; if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) /* already shutting down? */ ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); }
/* * Hook disconnection */ static int ngt_disconnect(hook_p hook) { struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); KASSERT(hinfo != NULL, ("%s: null info", __func__)); hinfo->hook = NULL; if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); }
/* * Hook disconnection */ static int ng_split_disconnect(hook_p hook) { hook_p *localhook = NG_HOOK_PRIVATE(hook); KASSERT(localhook != NULL, ("%s: null info", __func__)); *localhook = NULL; if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { ng_rmnode_self(NG_HOOK_NODE(hook)); } return (0); }
/* * Hook disconnection * For this type, removal of any link except "debug" destroys the node. */ static int nglmi_disconnect(hook_p hook) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* OK to remove debug hook(s) */ if (NG_HOOK_PRIVATE(hook) == NULL) return (0); /* Stop timer if it's currently active */ if (sc->flags & SCF_CONNECTED) ng_uncallout(&sc->handle, sc->node); /* Self-destruct */ if (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); }
/* * Hook disconnection * * Invalidate the private data associated with this dlci. * For this type, removal of the last link resets tries to destroy the node. */ static int ngfrm_disconnect(hook_p hook) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct ctxinfo *const cp = NG_HOOK_PRIVATE(hook); int dlci; /* If it's a regular dlci hook, then free resources etc.. */ if (cp != NULL) { cp->hook = NULL; dlci = cp->dlci; if (dlci != -1) sc->ALT[dlci] = 0; cp->flags = 0; sc->datahooks--; } if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) ng_rmnode_self(NG_HOOK_NODE(hook)); return (0); }
static int ng_vlan_rcvmsg(node_p node, item_p item, hook_p lasthook) { const priv_p priv = NG_NODE_PRIVATE(node); struct ng_mesg *msg, *resp = NULL; struct ng_vlan_filter *vf; hook_p hook; struct ng_vlan_table *t; uintptr_t hook_data; int i, vlan_count; uint16_t vid; int error = 0; NGI_GET_MSG(item, msg); /* Deal with message according to cookie and command. */ switch (msg->header.typecookie) { case NGM_VLAN_COOKIE: switch (msg->header.cmd) { case NGM_VLAN_ADD_FILTER: /* Check that message is long enough. */ if (msg->header.arglen != sizeof(*vf)) { error = EINVAL; break; } vf = (struct ng_vlan_filter *)msg->data; /* Sanity check the VLAN ID value. */ #ifdef NG_VLAN_USE_OLD_VLAN_NAME if (vf->vid == 0 && vf->vid != vf->vlan) { vf->vid = vf->vlan; } else if (vf->vid != 0 && vf->vlan != 0 && vf->vid != vf->vlan) { error = EINVAL; break; } #endif 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_name); if (hook == NULL) { error = ENOENT; break; } /* And is not one of the special hooks. */ if (hook == priv->downstream_hook || hook == priv->nomatch_hook) { error = EINVAL; break; } /* And is not already in service. */ if (IS_HOOK_VLAN_SET(NG_HOOK_PRIVATE(hook))) { error = EEXIST; break; } /* Check we don't already trap this VLAN. */ if (priv->vlan_hook[vf->vid] != NULL) { error = EEXIST; break; } /* 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. */ if (msg->header.arglen != NG_HOOKSIZ) { error = EINVAL; break; } /* Check that hook exists and is active. */ hook = ng_findhook(node, (char *)msg->data); 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; } KASSERT(priv->vlan_hook[EVL_VLANOFTAG(hook_data)] == hook, ("%s: NGM_VLAN_DEL_FILTER: Invalid VID for Hook = %s\n", __func__, (char *)msg->data)); /* 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(uint16_t)) { error = EINVAL; break; } vid = (*((uint16_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; } KASSERT(EVL_VLANOFTAG(hook_data) == vid, ("%s: NGM_VLAN_DEL_VID_FLT:" " Invalid VID Hook = %us, must be: %us\n", __func__, (uint16_t )EVL_VLANOFTAG(hook_data), vid)); /* Purge a rule that refers to this hook. */ priv->vlan_hook[vid] = NULL; NG_HOOK_SET_PRIVATE(hook, NULL); 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) + 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 = 0; vf = &t->filter[0]; 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; KASSERT(EVL_VLANOFTAG(hook_data) == i, ("%s: NGM_VLAN_GET_TABLE:" " hook %s VID = %us, must be: %i\n", __func__, NG_HOOK_NAME(hook), (uint16_t)EVL_VLANOFTAG(hook_data), i)); #ifdef NG_VLAN_USE_OLD_VLAN_NAME vf->vlan = 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 ++; t->n ++; } break; case NGM_VLAN_GET_DECAP: NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint32_t *)resp->data)) = priv->decap_enable; break; case NGM_VLAN_SET_DECAP: if (msg->header.arglen != sizeof(uint32_t)) { error = EINVAL; break; } priv->decap_enable = (*((uint32_t *)msg->data)); break; case NGM_VLAN_GET_ENCAP: NG_MKRESPONSE(resp, msg, sizeof(uint32_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint32_t *)resp->data)) = priv->encap_enable; break; case NGM_VLAN_SET_ENCAP: if (msg->header.arglen != sizeof(uint32_t)) { error = EINVAL; break; } priv->encap_enable = (*((uint32_t *)msg->data)); break; case NGM_VLAN_GET_ENCAP_PROTO: NG_MKRESPONSE(resp, msg, sizeof(uint16_t), M_NOWAIT); if (resp == NULL) { error = ENOMEM; break; } (*((uint16_t *)resp->data)) = ntohs(priv->encap_proto); break; case NGM_VLAN_SET_ENCAP_PROTO: if (msg->header.arglen != sizeof(uint16_t)) { error = EINVAL; break; } priv->encap_proto = htons((*((uint16_t *)msg->data))); break; default: /* Unknown command. */ error = EINVAL; break; } break; case NGM_FLOW_COOKIE: { struct ng_mesg *copy; /* * Flow control messages should come only * from downstream. */ if (lasthook == NULL) break; if (lasthook != priv->downstream_hook) break; /* Broadcast the event to all uplinks. */ 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, priv->vlan_hook[i], 0); } break; } default: /* Unknown type cookie. */ error = EINVAL; break; } NG_RESPOND_MSG(error, node, item, resp); NG_FREE_MSG(msg); return (error); }
/* * Receive data on a hook * * If data comes in the right link send a copy out right2left, and then * send the original onwards out through the left link. * Do the opposite for data coming in from the left link. * Data coming in right2left or left2right is forwarded * on through the appropriate destination hook as if it had come * from the other side. */ static int ngt_rcvdata(hook_p hook, item_p item) { const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); struct hookinfo *const hinfo = NG_HOOK_PRIVATE(hook); struct hookinfo *dest; struct hookinfo *dup; int error = 0; struct mbuf *m; meta_p meta; m = NGI_M(item); meta = NGI_META(item); /* leave these owned by the item */ /* Which hook? */ if (hinfo == &sc->left) { dup = &sc->left2right; dest = &sc->right; } else if (hinfo == &sc->right) { dup = &sc->right2left; dest = &sc->left; } else if (hinfo == &sc->right2left) { dup = NULL; dest = &sc->right; } else if (hinfo == &sc->left2right) { dup = NULL; dest = &sc->left; } else { panic("%s: no hook!", __func__); #ifdef RESTARTABLE_PANICS return(EINVAL); #endif } /* Update stats on incoming hook */ hinfo->stats.inOctets += m->m_pkthdr.len; hinfo->stats.inFrames++; /* * Don't make a copy if only the dup hook exists. */ if ((dup && dup->hook) && (dest->hook == NULL)) { dest = dup; dup = NULL; } /* Duplicate packet and meta info if requried */ if (dup != NULL) { struct mbuf *m2; meta_p meta2; /* Copy packet (failure will not stop the original)*/ m2 = m_dup(m, M_NOWAIT); if (m2) { /* Copy meta info */ /* If we can't get a copy, tough.. */ if (meta != NULL) { meta2 = ng_copy_meta(meta); } else meta2 = NULL; /* Deliver duplicate */ dup->stats.outOctets += m->m_pkthdr.len; dup->stats.outFrames++; NG_SEND_DATA(error, dup->hook, m2, meta2); } } /* Deliver frame out destination hook */ dest->stats.outOctets += m->m_pkthdr.len; dest->stats.outFrames++; if (dest->hook) NG_FWD_ITEM_HOOK(error, item, dest->hook); else NG_FREE_ITEM(item); return (0); }
/* * Receive data packet */ static int ngfrm_rcvdata(hook_p hook, item_p item) { struct ctxinfo *const ctxp = NG_HOOK_PRIVATE(hook); int error = 0; int dlci; sc_p sc; int alen; char *data; struct mbuf *m; /* Data doesn't come in from just anywhere (e.g debug hook) */ if (ctxp == NULL) { error = ENETDOWN; goto bad; } /* If coming from downstream, decode it to a channel */ dlci = ctxp->dlci; if (dlci == -1) return (ngfrm_decode(NG_HOOK_NODE(hook), item)); NGI_GET_M(item, m); /* Derive the softc we will need */ sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); /* If there is no live channel, throw it away */ if ((sc->downstream.hook == NULL) || ((ctxp->flags & CHAN_ACTIVE) == 0)) { error = ENETDOWN; goto bad; } /* Store the DLCI on the front of the packet */ alen = sc->addrlen; if (alen == 0) alen = 2; /* default value for transmit */ M_PREPEND(m, alen, M_DONTWAIT); if (m == NULL) { error = ENOBUFS; goto bad; } data = mtod(m, char *); /* * Shift the lowest bits into the address field untill we are done. * First byte is MSBits of addr so work backwards. */ switch (alen) { case 2: data[0] = data[1] = '\0'; SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[1] |= BYTEX_EA; break; case 3: data[0] = data[1] = data[2] = '\0'; SHIFTOUT(makeup + 3, data[2], dlci); /* 3 and 2 is correct */ SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[2] |= BYTEX_EA; break; case 4: data[0] = data[1] = data[2] = data[3] = '\0'; SHIFTOUT(makeup + 3, data[3], dlci); SHIFTOUT(makeup + 2, data[2], dlci); SHIFTOUT(makeup + 1, data[1], dlci); SHIFTOUT(makeup + 0, data[0], dlci); data[3] |= BYTEX_EA; break; default: panic(__func__); } /* Send it */ NG_FWD_NEW_DATA(error, item, sc->downstream.hook, m); return (error); bad: NG_FREE_ITEM(item); NG_FREE_M(m); return (error); }
/* * receive data, and use it to update our status. * Anything coming in on the debug port is discarded. */ static int nglmi_rcvdata(hook_p hook, item_p item) { sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); const u_char *data; unsigned short dlci; u_short packetlen; int resptype_seen = 0; struct mbuf *m; NGI_GET_M(item, m); NG_FREE_ITEM(item); if (NG_HOOK_PRIVATE(hook) == NULL) { goto drop; } packetlen = m->m_len; /* XXX what if it's more than 1 mbuf? */ if ((packetlen > MHLEN) && !(m->m_flags & M_EXT)) { log(LOG_WARNING, "nglmi: packetlen (%d) too big\n", packetlen); goto drop; } if (m->m_len < packetlen && (m = m_pullup(m, packetlen)) == NULL) { log(LOG_WARNING, "nglmi: m_pullup failed for %d bytes\n", packetlen); return (0); } if (nglmi_checkdata(hook, m) == 0) return (0); /* pass the first 4 bytes (already checked in the nglmi_checkdata()) */ data = mtod(m, const u_char *); STEPBY(4); /* Now check if there is a 'locking shift'. This is only seen in * Annex D frames. don't bother checking, we already did that. Don't * increment immediatly as it might not be there. */ if (ANNEXD(sc)) STEPBY(1); /* If we get this far we should consider that it is a legitimate * frame and we know what it is. */ if (sc->flags & SCF_AUTO) { /* note the hook that this valid channel came from and drop * out of auto probe mode. */ if (ANNEXA(sc)) sc->protoname = NAME_ANNEXA; else if (ANNEXD(sc)) sc->protoname = NAME_ANNEXD; else if (GROUP4(sc)) sc->protoname = NAME_GROUP4; else { log(LOG_ERR, "nglmi: No known type\n"); goto drop; } sc->lmi_channel = hook; sc->flags &= ~SCF_AUTO; log(LOG_INFO, "nglmi: auto-detected %s LMI on DLCI %d\n", sc->protoname, hook == sc->lmi_channel0 ? 0 : 1023); } /* While there is more data in the status packet, keep processing * status items. First make sure there is enough data for the * segment descriptor's length field. */ while (packetlen >= 2) { u_int segtype = data[0]; u_int segsize = data[1]; /* Now that we know how long it claims to be, make sure * there is enough data for the next seg. */ if (packetlen < segsize + 2) break; switch (segtype) { case 0x01: case 0x51: if (resptype_seen) { log(LOG_WARNING, "nglmi: dup MSGTYPE\n"); goto nextIE; } resptype_seen++; /* The remote end tells us what kind of response * this is. Only expect a type 0 or 1. if we are a * full status, invalidate a few DLCIs just to see * that they are still ok. */ if (segsize != 1) goto nextIE; switch (data[2]) { case 1: /* partial status, do no extra processing */ break; case 0: { int count = 0; int idx = sc->invalidx; for (count = 0; count < 10; count++) { if (idx > MAXDLCI) idx = 0; if (sc->dlci_state[idx] == DLCI_UP) sc->dlci_state[idx] = DLCI_DOWN; idx++; } sc->invalidx = idx; /* we got and we wanted one. relax * now.. but don't reset to 0 if it * was unrequested. */ if (sc->livs > sc->liv_per_full) sc->livs = 0; break; } } break; case 0x03: case 0x53: /* The remote tells us what it thinks the sequence * numbers are. If it's not size 2, it must be a * duplicate to have gotten this far, skip it. */ if (segsize != 2) goto nextIE; sc->remote_seq = data[2]; if (sc->local_seq == data[3]) { sc->local_seq++; sc->seq_retries = 0; /* Note that all 3 Frame protocols seem to * not like 0 as a sequence number. */ if (sc->local_seq == 0) sc->local_seq = 1; } break; case 0x07: case 0x57: /* The remote tells us about a DLCI that it knows * about. There may be many of these in a single * status response */ switch (segsize) { case 6:/* only on 'group of 4' */ dlci = ((u_short) data[2] & 0xff) << 8; dlci |= (data[3] & 0xff); if ((dlci < 1024) && (dlci > 0)) { /* XXX */ } break; case 3: dlci = ((u_short) data[2] & 0x3f) << 4; dlci |= ((data[3] & 0x78) >> 3); if ((dlci < 1024) && (dlci > 0)) { /* set up the bottom half of the * support for that dlci if it's not * already been done */ /* store this information somewhere */ } break; default: goto nextIE; } if (sc->dlci_state[dlci] != DLCI_UP) { /* bring new DLCI to life */ /* may do more here some day */ if (sc->dlci_state[dlci] != DLCI_DOWN) log(LOG_INFO, "nglmi: DLCI %d became active\n", dlci); sc->dlci_state[dlci] = DLCI_UP; } break; } nextIE: STEPBY(segsize + 2); } NG_FREE_M(m); return (0); drop: NG_FREE_M(m); return (EINVAL); }