summaryrefslogtreecommitdiffstats
path: root/sys/netgraph
diff options
context:
space:
mode:
authorharti <harti@FreeBSD.org>2003-08-11 08:40:02 +0000
committerharti <harti@FreeBSD.org>2003-08-11 08:40:02 +0000
commite6ffc57441a25e3a8cab5d8757dd6c94416ae90f (patch)
tree42177e9d186d9cbd80396592804df641db1edb31 /sys/netgraph
parent69bd57b2dd4ca4d24b0364a47a8e3074a228c1c6 (diff)
downloadFreeBSD-src-e6ffc57441a25e3a8cab5d8757dd6c94416ae90f.zip
FreeBSD-src-e6ffc57441a25e3a8cab5d8757dd6c94416ae90f.tar.gz
Add ng_atmpif: a HARP physical interface emulation. This allows one
to run the HARP ATM stack without real hardware. Submitted by: Vincent Jardin <vjardin@wanadoo.fr>
Diffstat (limited to 'sys/netgraph')
-rw-r--r--sys/netgraph/atm/atmpif/ng_atmpif.c708
-rw-r--r--sys/netgraph/atm/atmpif/ng_atmpif_harp.c933
-rw-r--r--sys/netgraph/atm/atmpif/ng_atmpif_var.h147
-rw-r--r--sys/netgraph/atm/ng_atmpif.h175
4 files changed, 1963 insertions, 0 deletions
diff --git a/sys/netgraph/atm/atmpif/ng_atmpif.c b/sys/netgraph/atm/atmpif/ng_atmpif.c
new file mode 100644
index 0000000..b79117a
--- /dev/null
+++ b/sys/netgraph/atm/atmpif/ng_atmpif.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright 2003 Harti Brandt
+ * Copyright 2003 Vincent Jardin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * ATM Virtal Adapter Support
+ * --------------------------
+ *
+ * Loadable kernel module and netgraph support
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/syslog.h>
+
+#include <vm/uma.h>
+
+#include <net/if.h>
+
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_cm.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_pcb.h>
+#include <netatm/atm_stack.h>
+#include <netatm/atm_var.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/ng_parse.h>
+#include <netgraph/atm/ng_atmpif.h>
+#include <netgraph/atm/atmpif/ng_atmpif_var.h>
+
+#ifdef NG_SEPARATE_MALLOC
+MALLOC_DEFINE(M_NETGRAPH_ATMPIF, "netgraph_vatmpif",
+ "netgraph HARP virtual Physical Interface");
+#else
+#define M_NETGRAPH_ATMPIF M_NETGRAPH
+#endif
+
+/*
+ * Local definitions
+ */
+
+/*
+ * Protocol header
+ */
+struct vatmpif_header {
+ /* The cell header (minus the HEC) is contained in the least-significant
+ * 32-bits of a word.
+ */
+ uint32_t cellhdr; /* Stored in network order */
+ /* Let's use cellhdr = htonl(ATM_HDR_SET(vpi, vci, pt, clp))
+ * and vpi = ATM_HDR_GET_VPI(ntohl(cellhdr))
+ * vci = ATM_HDR_GET_VCI(ntohl(cellhdr))
+ * pt = ATM_HDR_GET_PT (ntohl(cellhdr))
+ * clp = ATM_HDR_GET_CLP(ntohl(cellhdr))
+ */
+ int32_t seq; /* sequence number in network byte order */
+ uint64_t cookie; /* optional field */
+ uint8_t aal; /* AAL */
+ uint8_t __pad[3];
+};
+
+/*
+ * Local functions
+ */
+
+/* Parse type for a MAC address */
+static ng_parse_t ng_macaddr_parse;
+static ng_unparse_t ng_macaddr_unparse;
+const struct ng_parse_type ng_mac_addr_type = {
+ parse: ng_macaddr_parse,
+ unparse: ng_macaddr_unparse,
+};
+
+
+/* Parse type for struct ng_atmpif_config */
+static const struct ng_parse_struct_field
+ ng_atmpif_config_type_fields[] = NG_ATMPIF_CONFIG_TYPE_INFO;
+static const struct ng_parse_type ng_atmpif_config_type = {
+ &ng_parse_struct_type,
+ &ng_atmpif_config_type_fields,
+};
+
+/* Parse type for struct ng_atmpif_link_status */
+static const struct ng_parse_struct_field
+ ng_atmpif_link_status_type_fields[] = NG_ATMPIF_LINK_STATUS_TYPE_INFO;
+static const struct ng_parse_type ng_atmpif_link_status_type = {
+ &ng_parse_struct_type,
+ &ng_atmpif_link_status_type_fields,
+};
+
+/* Parse type for struct ng_atmpif_stats */
+static const struct ng_parse_struct_field
+ ng_atmpif_stats_type_fields[] = NG_ATMPIF_STATS_TYPE_INFO;
+static const struct ng_parse_type ng_atmpif_stats_type = {
+ &ng_parse_struct_type,
+ &ng_atmpif_stats_type_fields,
+};
+
+static const struct ng_cmdlist ng_atmpif_cmdlist[] = {
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_SET_CONFIG,
+ "setconfig",
+ mesgType: &ng_atmpif_config_type,
+ respType: NULL
+ },
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_GET_CONFIG,
+ "getconfig",
+ mesgType: NULL,
+ respType: &ng_atmpif_config_type
+ },
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_GET_LINK_STATUS,
+ "getlinkstatus",
+ mesgType: NULL,
+ respType: &ng_atmpif_link_status_type
+ },
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_GET_STATS,
+ "getstats",
+ mesgType: NULL,
+ respType: &ng_atmpif_stats_type
+ },
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_CLR_STATS,
+ "clrstats",
+ mesgType: NULL,
+ respType: NULL
+ },
+ {
+ NGM_ATMPIF_COOKIE,
+ NGM_ATMPIF_GETCLR_STATS,
+ "getclrstats",
+ mesgType: NULL,
+ respType: &ng_atmpif_stats_type
+ },
+
+ { 0 }
+};
+
+uma_zone_t vatmpif_nif_zone;
+uma_zone_t vatmpif_vcc_zone;
+
+/*
+ * Netgraph node methods
+ */
+static ng_constructor_t ng_atmpif_constructor;
+static ng_rcvmsg_t ng_atmpif_rcvmsg;
+static ng_shutdown_t ng_atmpif_rmnode;
+static ng_newhook_t ng_atmpif_newhook;
+static ng_rcvdata_t ng_atmpif_rcvdata;
+static ng_disconnect_t ng_atmpif_disconnect;
+static int ng_atmpif_mod_event(module_t, int, void *);
+
+/*
+ * Node type descriptor
+ */
+static struct ng_type ng_atmpif_typestruct = {
+ NG_ABI_VERSION, /* version */
+ NG_ATMPIF_NODE_TYPE, /* name */
+ ng_atmpif_mod_event, /* mod_event */
+ ng_atmpif_constructor, /* constructor */
+ ng_atmpif_rcvmsg, /* rcvmsg */
+ ng_atmpif_rmnode, /* shutdown */
+ ng_atmpif_newhook, /* newhook */
+ NULL, /* findhook */
+ NULL, /* connect */
+ ng_atmpif_rcvdata, /* rcvdata */
+ ng_atmpif_disconnect, /* disconnect */
+ ng_atmpif_cmdlist, /* cmdlist */
+};
+NETGRAPH_INIT(atmpif, &ng_atmpif_typestruct);
+
+/******************************************************************
+ NETGRAPH NODE METHODS
+******************************************************************/
+
+/*
+ * Node constructor
+ *
+ * Called at splnet()
+ */
+static int
+ng_atmpif_constructor(node_p nodep)
+{
+ priv_p priv;
+
+ /*
+ * Allocate and initialize private info
+ */
+ priv = malloc(sizeof(*priv), M_NETGRAPH_ATMPIF, M_NOWAIT | M_ZERO);
+ if (priv == NULL)
+ return (ENOMEM);
+
+ priv->conf.debug = 0x00;
+ priv->conf.pcr = ATM_PCR_OC3C;
+ priv->conf.macaddr.ma_data[0] = 0x02; /* XXX : non unique bit */
+ priv->conf.macaddr.ma_data[1] = 0x09; /* XXX */
+ priv->conf.macaddr.ma_data[2] = 0xc0; /* XXX */
+ priv->conf.macaddr.ma_data[3] = (u_char)((random() & 0xff0000) >> 16);
+ priv->conf.macaddr.ma_data[4] = (u_char)((random() & 0x00ff00) >> 8);
+ priv->conf.macaddr.ma_data[5] = (u_char)((random() & 0x0000ff) >> 0);
+
+ NG_NODE_SET_PRIVATE(nodep, priv);
+ priv->node = nodep;
+
+ /* Done */
+ return (0);
+}
+
+/*
+ * Method for attaching a new hook
+ * A hook is a virtual ATM link.
+ */
+static int
+ng_atmpif_newhook(node_p node, hook_p hook, const char *name)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ /*
+ * Check for a link hook
+ */
+ if (strcmp(name, NG_ATMPIF_HOOK_LINK) == 0) {
+ int error;
+
+ /*
+ * Do not create twice a link hook
+ */
+ if (priv->link != NULL)
+ return (EEXIST);
+
+ priv->link = malloc(sizeof(*priv->link),
+ M_NETGRAPH_ATMPIF, M_NOWAIT | M_ZERO);
+ if (priv->link == NULL)
+ return (ENOMEM);
+
+ /*
+ * Register as an HARP device
+ */
+ if ((error = vatmpif_harp_attach(node))) {
+ free(priv->link, M_NETGRAPH_ATMPIF);
+ priv->link = NULL;
+ return (error);
+ }
+
+ priv->link->hook = hook;
+ return (0);
+ }
+
+ /* Unknown hook name */
+ return (EINVAL);
+}
+
+/*
+ * Receive a control message from ngctl or the netgraph's API
+ */
+static int
+ng_atmpif_rcvmsg(node_p node, item_p item, hook_p lasthook)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ struct ng_mesg *msg;
+ struct ng_mesg *resp = NULL;
+ int error = 0;
+
+ NGI_GET_MSG(item, msg);
+
+ switch (msg->header.typecookie) {
+ case NGM_ATMPIF_COOKIE:
+ switch (msg->header.cmd) {
+ case NGM_ATMPIF_GET_CONFIG:
+ {
+ struct ng_vatmpif_config *conf;
+
+ NG_MKRESPONSE(resp, msg,
+ sizeof(struct ng_vatmpif_config), M_NOWAIT);
+ if (resp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ conf = (struct ng_vatmpif_config *)resp->data;
+ *conf = priv->conf; /* no sanity checking needed */
+ break;
+ }
+ case NGM_ATMPIF_SET_CONFIG:
+ {
+ struct ng_vatmpif_config *conf;
+
+ if (msg->header.arglen != sizeof(*conf)) {
+ error = EINVAL;
+ break;
+ }
+ conf = (struct ng_vatmpif_config *)msg->data;
+ priv->conf = *conf;
+ break;
+ }
+ case NGM_ATMPIF_GET_LINK_STATUS:
+ {
+ struct ng_vatmpif_hook *link;
+ struct ng_atmpif_link_status *status;
+
+ if ((link = priv->link) == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+
+ NG_MKRESPONSE(resp, msg, sizeof(*status), M_NOWAIT);
+ if (resp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ status = (struct ng_atmpif_link_status *)resp->data;
+ status->InSeq = link->InSeq;
+ status->OutSeq = link->OutSeq;
+ status->cur_pcr = link->cur_pcr;
+ break;
+ }
+ case NGM_ATMPIF_GET_STATS:
+ case NGM_ATMPIF_CLR_STATS:
+ case NGM_ATMPIF_GETCLR_STATS:
+ {
+ struct ng_vatmpif_hook *link;
+
+ if ((link = priv->link) == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+
+ /* Get/clear stats */
+ if (msg->header.cmd != NGM_ATMPIF_CLR_STATS) {
+ NG_MKRESPONSE(resp, msg,
+ sizeof(link->stats), M_NOWAIT);
+ if (resp == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ bcopy(&link->stats,
+ resp->data, sizeof(link->stats));
+ }
+ if (msg->header.cmd != NGM_ATMPIF_GET_STATS)
+ bzero(&link->stats, sizeof(link->stats));
+ break;
+ }
+ default:
+ error = EINVAL;
+ break;
+ }
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ /* Done */
+ NG_RESPOND_MSG(error, node, item, resp);
+ NG_FREE_MSG(msg);
+ return (error);
+}
+
+/*
+ * Hook disconnection.
+ * It shutdown the virtual ATM link however the node is kept.
+ */
+static int
+ng_atmpif_disconnect(hook_p hook)
+{
+ const node_p node = NG_HOOK_NODE(hook);
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ /*
+ * Deregister from the HARP stack
+ */
+ vatmpif_harp_detach(node);
+
+ /*
+ * Free associated link information
+ */
+ KASSERT(priv->link != NULL, ("%s: no link", __func__));
+ FREE(priv->link, M_NETGRAPH_ATMPIF);
+ priv->link = NULL;
+
+ /* Shutdown the physical interface */
+ priv->vu_pif.pif_flags &= ~PIF_UP;
+
+ /* No more hooks, however I prefer to keep the node
+ * instead of going away
+ * However, if we are interested in removing it, let's
+ * call ng_rmnode(hook->node); here.
+ */
+ return (0);
+}
+
+/*
+ * Shutdown node
+ *
+ * Free the private data.
+ */
+static int
+ng_atmpif_rmnode(node_p node)
+{
+ const priv_p priv = NG_NODE_PRIVATE(node);
+
+ /* Free private data */
+ FREE(priv, M_NETGRAPH_ATMPIF);
+ NG_NODE_SET_PRIVATE(node, NULL);
+
+ /* Unref node */
+ NG_NODE_UNREF(node);
+
+ return (0);
+}
+
+/*
+ * Receive data
+ *
+ * Then vatmpif_harp_recv_drain will schedule a call into the kernel
+ * to process the atm_intrq.
+ * It means that it should be processing at splimp() if
+ * the node was a regular hw driver.
+ */
+static int
+ng_atmpif_rcvdata(hook_p hook, item_p item)
+{
+ const node_p node = NG_HOOK_NODE(hook);
+ const priv_p priv = NG_NODE_PRIVATE(node);
+ struct vatmpif_header *h;
+ struct vatmpif_header hdrbuf;
+ int error = 0;
+ struct mbuf *m;
+
+ NGI_GET_M(item, m);
+ NG_FREE_ITEM(item);
+
+ /* Is the Physical Interface UP ? */
+ if (!(priv->vu_pif.pif_flags & PIF_UP)) {
+ log(LOG_ERR, "%s%d: down while %s",
+ priv->vu_pif.pif_name, priv->vu_pif.pif_unit, __func__);
+ error = ENETDOWN;
+ goto drop;
+ }
+
+ /* Sanity check header length */
+ if (m->m_pkthdr.len < sizeof(*h)) {
+ priv->link->stats.hva_st_ng.ng_badpdu++;
+ error = EINVAL;
+ goto drop;
+ }
+
+ /* Get the Virtual ATM Physical Interface header */
+ if (m->m_len >= sizeof(*h)) { /* the common case */
+ h = mtod(m, struct vatmpif_header *);
+ } else {
+ m_copydata(m, 0, sizeof(*h), (caddr_t)&hdrbuf);
+ h = &hdrbuf; /* allocated on the stack */
+ }
+
+ /*
+ * Consume the vatmpif header
+ */
+ m_adj(m, sizeof(*h));
+
+ /*
+ * Parse the header h
+ */
+
+ /*
+ * duplication and out of order test.
+ *
+ * . let's SEQ_MAX be the highest sequence number
+ * . let's assume that h->seq = SEQ_MAX, (1)
+ */
+ if (ntohl(h->seq) < priv->link->InSeq) {
+ /* . is false due to (1) */
+ /* duplicate or out of order */
+ priv->link->stats.hva_st_ng.ng_errseq++;
+ error = EINVAL;
+ goto drop;
+ }
+ /* . then the mbuf is not dropped */
+
+ /* PDUs have been lost ?? */
+ if (priv->link->InSeq < ntohl(h->seq)) {
+ /* . it is true only if a PDU has been lost,
+ * . else due to (1) priv->link->InSeq is
+ * . already equal to SEQ_MAX.
+ */
+ priv->link->stats.hva_st_ng.ng_lostpdu++;
+ priv->link->InSeq = ntohl(h->seq);
+ }
+
+ /* Save the sequence number */
+ priv->link->InSeq = ntohl(h->seq) + 1;
+ /* . it leads to InSeq = SEQ_MAX + 1 = SEQ_MIN */
+
+ /* . it means that InSeq is always the next intended
+ * . sequence number if none is lost, doesn't it ?
+ */
+
+ /*
+ * Send the packet to the stack.
+ */
+ priv->link->stats.hva_st_ng.ng_rx_pdu++;
+ error = vatmpif_harp_recv_drain(priv, m,
+ ATM_HDR_GET_VPI(ntohl(h->cellhdr)),
+ ATM_HDR_GET_VCI(ntohl(h->cellhdr)),
+ ATM_HDR_GET_PT (ntohl(h->cellhdr)),
+ ATM_HDR_GET_CLP(ntohl(h->cellhdr)), h->aal);
+
+ return (error);
+
+drop:
+ m_freem(m);
+ return (error);
+}
+
+/*
+ * Transmit data. Called by the HARP's outpout function. You should
+ * notice that the return value is not returned upward by the HARP
+ * stack. It is only used in order to update the stats.
+ */
+int
+ng_atmpif_transmit(const priv_p priv, struct mbuf *m,
+ uint8_t vpi, uint16_t vci, uint8_t pt, uint8_t clp, Vatmpif_aal aal)
+{
+ struct vatmpif_header *h;
+ int error = 0;
+
+ /* Is the Physical Interface UP ? */
+ if (!(priv->vu_pif.pif_flags & PIF_UP)) {
+ log(LOG_ERR, "%s%d: down while %s",
+ priv->vu_pif.pif_name, priv->vu_pif.pif_unit, __func__);
+ error = ENETDOWN;
+ goto drop;
+ }
+
+ /* If the hook is not connected, free the mbuf */
+ if (priv->link == NULL) {
+ log(LOG_ERR, "%s%d: no hook while %s",
+ priv->vu_pif.pif_name, priv->vu_pif.pif_unit, __func__);
+ error = ENETDOWN;
+ goto drop;
+ }
+
+ M_PREPEND(m, sizeof(*h), M_DONTWAIT);
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+ m = m_pullup(m, sizeof(*h));
+ if (m == NULL) {
+ error = ENOBUFS;
+ goto drop;
+ }
+ h = mtod(m, struct vatmpif_header *);
+
+ /* htonl is linear */
+ h->cellhdr = htonl(ATM_HDR_SET_VPI(vpi));
+ h->cellhdr += htonl(ATM_HDR_SET_VCI(vci));
+ h->cellhdr += htonl(ATM_HDR_SET_PT (pt));
+ h->cellhdr += htonl(ATM_HDR_SET_CLP(clp));
+ h->aal = aal;
+ priv->link->OutSeq++;
+ h->seq = htonl(priv->link->OutSeq);
+ h->cookie = 0;
+
+ if (IS_VATMPIF_DEBUG_PACKET(priv))
+ atm_pdu_print(m, __func__);
+
+ /* Send it out to the "link" hook */
+ priv->link->stats.hva_st_ng.ng_tx_pdu++;
+ NG_SEND_DATA_ONLY(error, priv->link->hook, m);
+
+ return (error);
+
+drop:
+ if (m != NULL)
+ m_freem(m);
+ return (error);
+}
+
+/******************************************************************
+ MAC Address parser
+ *****************************************************************/
+static int
+ng_macaddr_parse(const struct ng_parse_type *type, const char *s,
+ int *const off, const u_char *const start, u_char *const buf,
+ int *const buflen)
+{
+ char *eptr;
+ u_long val;
+ int i;
+
+ if (*buflen < 6)
+ return (ERANGE);
+ for (i = 0; i < 6; i++) {
+ val = strtoul(s + *off, &eptr, 16);
+ if (val > 0xff || eptr == s + *off)
+ return (EINVAL);
+ buf[i] = (u_char)val;
+ *off = (eptr - s);
+ if (i < 6 - 1) {
+ if (*eptr != ':')
+ return (EINVAL);
+ (*off)++;
+ }
+ }
+ *buflen = 6;
+ return (0);
+}
+
+static int
+ng_macaddr_unparse(const struct ng_parse_type *type, const u_char *data,
+ int *off, char *cbuf, int cbuflen)
+{
+ int len;
+
+ len = snprintf(cbuf, cbuflen, "%02x:%02x:%02x:%02x:%02x:%02x",
+ data[*off], data[*off + 1], data[*off + 2],
+ data[*off + 3], data[*off + 4], data[*off + 5]);
+ if (len >= cbuflen)
+ return (ERANGE);
+ *off += 6;
+ return (0);
+}
+
+/*
+ * this holds all the stuff that should be done at load time
+ */
+static int
+ng_atmpif_mod_event(module_t mod, int event, void *data)
+{
+ int error = 0;
+
+ switch (event) {
+
+ case MOD_LOAD:
+ vatmpif_nif_zone = uma_zcreate("vatmpif nif",
+ sizeof(struct atm_nif), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+ if (vatmpif_nif_zone == NULL) {
+ error = ENOMEM;
+ break;
+ }
+
+ vatmpif_vcc_zone = uma_zcreate("vatmpif vcc",
+ sizeof(Vatmpif_vcc), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+ if (vatmpif_vcc_zone == NULL) {
+ uma_zdestroy(vatmpif_nif_zone);
+ error = ENOMEM;
+ break;
+ }
+ break;
+
+ case MOD_UNLOAD:
+ uma_zdestroy(vatmpif_nif_zone);
+ uma_zdestroy(vatmpif_vcc_zone);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
diff --git a/sys/netgraph/atm/atmpif/ng_atmpif_harp.c b/sys/netgraph/atm/atmpif/ng_atmpif_harp.c
new file mode 100644
index 0000000..261107b
--- /dev/null
+++ b/sys/netgraph/atm/atmpif/ng_atmpif_harp.c
@@ -0,0 +1,933 @@
+/*
+ * Copyright 2003 Harti Brandt
+ * Copyright 2003 Vincent Jardin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * ATM Virtal Adapter Support
+ * --------------------------
+ *
+ * API between HARP and Netgraph
+ *
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <vm/uma.h>
+
+#include <net/if.h>
+
+#include <netatm/port.h>
+#include <netatm/queue.h>
+#include <netatm/atm.h>
+#include <netatm/atm_sys.h>
+#include <netatm/atm_cm.h>
+#include <netatm/atm_vc.h>
+#include <netatm/atm_if.h>
+#include <netatm/atm_sap.h>
+#include <netatm/atm_pcb.h>
+#include <netatm/atm_stack.h>
+#include <netatm/atm_var.h>
+#include <netatm/atm_ioctl.h>
+
+#include <net/netisr.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph/netgraph.h>
+#include <netgraph/atm/ng_atmpif.h>
+#include <netgraph/atm/atmpif/ng_atmpif_var.h>
+
+/*
+ * Local definitions
+ */
+
+/*
+ * Local methods
+ */
+
+static int vatmpif_nunits = 0;
+
+/*
+ * ATM Interface services
+ *
+ * this virtual device does not use a soft SAR of the AAL5 PDU, neither
+ * of the AAL3/4 PDU.
+ */
+static struct stack_defn vatmpif_svaal5 = {
+ sd_next: NULL,
+ sd_sap: SAP_CPCS_AAL5,
+ sd_flag: SDF_TERM, /* no soft SAR */
+ sd_inst: atm_dev_inst,
+ sd_lower: atm_dev_lower,
+ sd_upper: NULL,
+ sd_toku: 0,
+};
+static struct stack_defn vatmpif_svaal4 = {
+ sd_next: &vatmpif_svaal5,
+ sd_sap: SAP_CPCS_AAL3_4,
+ sd_flag: SDF_TERM, /* no soft SAR */
+ sd_inst: atm_dev_inst,
+ sd_lower: atm_dev_lower,
+ sd_upper: NULL,
+ sd_toku: 0,
+};
+static struct stack_defn vatmpif_svaal0 = {
+ sd_next: &vatmpif_svaal4,
+ sd_sap: SAP_ATM,
+ sd_flag: SDF_TERM, /* no soft SAR */
+ sd_inst: atm_dev_inst,
+ sd_lower: atm_dev_lower,
+ sd_upper: NULL,
+ sd_toku: 0,
+};
+static struct stack_defn *vatmpif_services = &vatmpif_svaal0;
+
+/******************************************************************
+ HARP API METHODS
+******************************************************************/
+
+/*
+ * Local methods
+ */
+static int vatmpif_harp_ioctl(int code, caddr_t data, caddr_t arg);
+static int vatmpif_harp_instvcc(Cmn_unit *cup, Cmn_vcc *cvp);
+static int vatmpif_harp_openvcc(Cmn_unit *cup, Cmn_vcc *cvp);
+static int vatmpif_harp_closevcc(Cmn_unit *cup, Cmn_vcc *cvp);
+static void vatmpif_harp_output(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m);
+static atm_intr_t vatmpif_harp_recv_stack;
+
+/*
+ * Attach an virtual ATM physical inteface with the HARP stack
+ *
+ * Each virtual ATM device interface must register itself here
+ * upon completing the netgraph node constructor.
+ *
+ * Arguments:
+ * node pointer on the netgraph node
+ *
+ * Returns:
+ * 0 successful
+ * errno failed - reason indicated
+ */
+int
+vatmpif_harp_attach(node_p node)
+{
+ Vatmpif_unit *vup;
+ static int unit = 0;
+ int err;
+
+ /*
+ * Sanity check
+ */
+ if (node == NULL)
+ return (EINVAL);
+
+ /*
+ * Get the virtual unit structure
+ */
+ vup = (Vatmpif_unit *)NG_NODE_PRIVATE(node);
+ if (vup == NULL)
+ return (EINVAL);
+
+ /*
+ * Start initializing the HARP binding
+ */
+ vup->vu_unit = unit;
+ /* 9188 bytes: Default ATM network interface MTU + LLC/SNAP header */
+ vup->vu_mtu = ATM_NIF_MTU + 8;
+ vup->vu_vcc_zone = vatmpif_vcc_zone;
+ vup->vu_nif_zone = vatmpif_nif_zone;
+ vup->vu_ioctl = vatmpif_harp_ioctl;
+ vup->vu_instvcc = vatmpif_harp_instvcc;
+ vup->vu_openvcc = vatmpif_harp_openvcc;
+ vup->vu_closevcc = vatmpif_harp_closevcc;
+ vup->vu_output = vatmpif_harp_output;
+ vup->vu_softc = vup;
+
+ /*
+ * Consider this virtual unit assigned
+ */
+ unit++;
+
+ /*
+ * Get our device type and setup the adapter config info
+ * - at least as much as we can
+ */
+ vup->vu_config.ac_vendor = VENDOR_NETGRAPH;
+ vup->vu_config.ac_vendapi = VENDAPI_NETGRAPH_1;
+ vup->vu_config.ac_device = DEV_VATMPIF;
+ vup->vu_config.ac_media = MEDIA_VIRTUAL;
+ vup->vu_config.ac_serial = (u_long)node;
+ vup->vu_config.ac_bustype = BUS_VIRTUAL;
+ vup->vu_config.ac_busslot = NGM_ATMPIF_COOKIE;
+ vup->vu_config.ac_ram = (u_long)node;
+ vup->vu_config.ac_ramsize = sizeof(*node);
+ vup->vu_config.ac_macaddr = vup->conf.macaddr;
+ snprintf(vup->vu_config.ac_hard_vers,
+ sizeof(vup->vu_config.ac_hard_vers),
+ "%s", "Virt. ATM 1.0");
+ snprintf(vup->vu_config.ac_firm_vers,
+ sizeof(vup->vu_config.ac_firm_vers),
+ "%d", __FreeBSD__);
+
+ /*
+ * Set the interface capabilities
+ */
+ vup->vu_pif.pif_maxvpi = VATMPIF_MAX_VPI;
+ vup->vu_pif.pif_maxvci = VATMPIF_MAX_VCI;
+ vup->vu_pif.pif_pcr = vup->conf.pcr;
+
+ /*
+ * Register this interface with ATM core services
+ */
+ if ((err = atm_physif_register((Cmn_unit *)vup,
+ VATMPIF_DEV_NAME, vatmpif_services)) != 0 ) {
+ /*
+ * Registration failed - back everything out
+ *
+ * The netgraph node must not be created.
+ */
+ return (err);
+ }
+
+ vatmpif_nunits++;
+
+ /*
+ * Mark device initialization completed
+ */
+ vup->vu_flags |= CUF_INITED;
+
+ /* Done */
+ return (0);
+}
+
+/*
+ * Halt driver processing
+ *
+ * This will be called just prior the destruction of the Netgraph's node.
+ *
+ * Arguments:
+ * node pointer on the netgraph node
+ *
+ * Returns:
+ * 0 detach was successful
+ * errno detach failed - reason indicated
+ */
+int
+vatmpif_harp_detach(node_p node)
+{
+ Vatmpif_unit *vup = (Vatmpif_unit *)NG_NODE_PRIVATE(node);
+ int err;
+
+ /*
+ * Deregister device from kernel services
+ */
+ if ((err = atm_physif_deregister((Cmn_unit *)vup)))
+ return (err);
+
+ vatmpif_nunits--;
+
+ /*
+ * Clear device initialized
+ */
+ vup->vu_flags &= ~CUF_INITED;
+
+ /* Done */
+ return (0);
+}
+
+/*
+ * Handle netatm core service interface ioctl requests
+ *
+ * Arguments:
+ * code ioctl function (sub)code
+ * data data to/from ioctl
+ * arg optional code-specific argument
+ *
+ * Returns:
+ * 0 request processed successfully
+ * errno request failed - reason code
+ */
+static int
+vatmpif_harp_ioctl(int code, caddr_t data, caddr_t arg)
+{
+ struct atminfreq *aip = (struct atminfreq *)data;
+ struct atm_pif *pip;
+ Vatmpif_unit *vup;
+ caddr_t buf = aip->air_buf_addr;
+ struct air_vinfo_rsp *avr;
+ size_t count, len, buf_len = aip->air_buf_len;
+ int err = 0;
+ char ifname[2 * IFNAMSIZ];
+
+ ATM_DEBUG3("%s: code=%d, opcode=%d\n", __func__, code, aip->air_opcode);
+
+ switch (aip->air_opcode) {
+
+ case AIOCS_INF_VST:
+ /*
+ * Get vendor statistics
+ */
+ pip = (struct atm_pif *)arg;
+ vup = (Vatmpif_unit *)pip;
+ if (pip == NULL)
+ return (ENXIO);
+ snprintf(ifname, sizeof(ifname), "%s%d",
+ pip->pif_name, pip->pif_unit);
+
+ /*
+ * Cast response structure onto user's buffer
+ */
+ avr = (struct air_vinfo_rsp *)(void *)buf;
+
+ /*
+ * How lare is the response structure ?
+ */
+ len = sizeof(struct air_vinfo_rsp);
+
+ /*
+ * Sanity check - enough room for response structure
+ */
+ if (buf_len < len)
+ return ENOSPC;
+
+ /*
+ * Copy interface name into response structure
+ */
+ if ((err = copyout(ifname, avr->avsp_intf, IFNAMSIZ)) != 0)
+ break;
+
+ /*
+ * Advance the buffer address and decrement the size
+ */
+ buf += len;
+ buf_len -= len;
+
+ /*
+ * Get the vendor stats
+ */
+ /* vup->vu_stats */
+
+ /*
+ * Stick as much of it as we have room for
+ * into the response
+ */
+ count = MIN(sizeof(Vatmpif_stats), buf_len);
+
+ /*
+ * Copy stats into user's buffer. Return value is
+ * amount of data copied.
+ */
+ if ((err = copyout((caddr_t)&vup->vu_stats, buf,
+ buf_len)) != 0)
+ break;
+ buf += count;
+ buf_len -= count;
+ if (count < sizeof(Vatmpif_stats))
+ err = ENOSPC;
+
+ /*
+ * Record amount we are returning as vendor info...
+ */
+ if ((err = copyout(&count, &avr->avsp_len, sizeof(count))) != 0)
+ break;
+
+ /*
+ * Update the reply pointers and lengths
+ */
+ aip->air_buf_addr = buf;
+ aip->air_buf_len = buf_len;
+ break;
+
+ default:
+ err = ENOSYS;
+ break;
+ }
+
+ return (err);
+}
+
+/*
+ * Get CBR/VBR/ABR/UBR from bearer attribute
+ *
+ * Arguments:
+ * bearer T_ATM_BEARER_CAP option value structure
+ *
+ * Returns:
+ * Driver traffic class
+ */
+static Vatmpif_traffic_type
+vatmpif_bearerclass(struct attr_bearer *bearer)
+{
+ switch (bearer->v.bearer_class) {
+ case T_ATM_CLASS_A:
+ return (VATMPIF_TRAF_CBR);
+ case T_ATM_CLASS_C:
+ return (VATMPIF_TRAF_VBR);
+ case T_ATM_CLASS_X:
+ switch (bearer->v.traffic_type) {
+ case T_ATM_CBR:
+ return (VATMPIF_TRAF_CBR);
+ case T_ATM_VBR:
+ return (VATMPIF_TRAF_VBR);
+ case T_ATM_ABR:
+ return (VATMPIF_TRAF_ABR);
+ case T_ATM_NULL:
+ case T_ATM_UBR:
+ return (VATMPIF_TRAF_UBR);
+ }
+ break;
+ }
+
+ /* never reached */
+ log(LOG_ERR, "%s: could not determine the traffic type.\n", __func__);
+ return (VATMPIF_TRAF_UBR);
+}
+
+/*
+ * VCC Stack Instantiation
+ *
+ * This function is called via the common driver code during a device VCC
+ * stack instantiation. The common code has already validated some of
+ * the request so we just need to check a few more VATMPIF-specific details.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 instantiation successful
+ * errno instantiation failed - reason indicated
+ */
+static int
+vatmpif_harp_instvcc(Cmn_unit *cup, Cmn_vcc *cvp)
+{
+ Vatmpif_unit *vup = (Vatmpif_unit *)cup;
+ Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp;
+ Atm_attributes *ap = &vvp->vv_connvc->cvc_attr;
+ int32_t pcr = 0;
+ int32_t scr = 0;
+ Vatmpif_traffic_type traffic = VATMPIF_TRAF_UBR;
+
+ ATM_DEBUG3("%s: vup=%p, vvp=%p\n", __func__, vup, vvp);
+
+ if (ap->traffic.tag == T_ATM_PRESENT) {
+ pcr = ap->traffic.v.forward.PCR_all_traffic;
+ scr = ap->traffic.v.forward.SCR_all_traffic;
+ }
+ if (pcr < 0)
+ pcr = 0;
+ if (scr < 0)
+ scr = 0;
+
+ KASSERT(ap->bearer.tag == T_ATM_PRESENT, ("Bearer tag is required"));
+ traffic = vatmpif_bearerclass(&ap->bearer);
+ /* Guarantee PCR of the PVC with CBR */
+ if (traffic == VATMPIF_TRAF_CBR &&
+ vup->vu_cur_pcr + pcr > vup->vu_pif.pif_pcr) {
+ return (EINVAL);
+ }
+ /* Guarantee SCR of the PVC with VBR */
+ if (traffic == VATMPIF_TRAF_VBR &&
+ vup->vu_cur_pcr + scr > vup->vu_pif.pif_pcr) {
+ return (EINVAL);
+ }
+
+ /*
+ * Validate requested AAL
+ */
+ KASSERT(ap->aal.tag == T_ATM_PRESENT, ("AAL tag is required"));
+ switch (ap->aal.type) {
+ case ATM_AAL0:
+ break;
+
+ case ATM_AAL1:
+ break;
+
+ case ATM_AAL2:
+ return (EINVAL);
+
+ case ATM_AAL3_4:
+ if (ap->aal.v.aal4.forward_max_SDU_size > vup->vu_mtu ||
+ ap->aal.v.aal4.backward_max_SDU_size > vup->vu_mtu)
+ return (EINVAL);
+ break;
+
+ case ATM_AAL5:
+ if (ap->aal.v.aal5.forward_max_SDU_size > vup->vu_mtu ||
+ ap->aal.v.aal5.backward_max_SDU_size > vup->vu_mtu)
+ return (EINVAL);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+ /* Done */
+ return (0);
+}
+
+/*
+ * Open a VCC
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_INIT command. The common has already validated most of
+ * the request so we just need to check a few more VATMPIF-specific details.
+ * Then we just forward to the Netgraph node.
+ *
+ * Called at splimp.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 open successful
+ * errno open failed - reason indicated
+ */
+static int
+vatmpif_harp_openvcc(Cmn_unit *cup, Cmn_vcc *cvp)
+{
+ Vatmpif_unit *vup = (Vatmpif_unit *)cup;
+ Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp;
+ struct vccb *vcp = vvp->vv_connvc->cvc_vcc;
+ Atm_attributes *ap = &vvp->vv_connvc->cvc_attr;
+
+ ATM_DEBUG5("%s: vup=%p, vvp=%p, vcc=(%d,%d)\n", __func__,
+ vup, vvp, vcp->vc_vpi, vcp->vc_vci);
+
+ /*
+ * We only need to open incoming VC's so outbound VC's
+ * just get set to CVS_ACTIVE state.
+ */
+ if ((vcp->vc_type & VCC_IN) == 0) {
+ /*
+ * Set the state and return - nothing else needed
+ */
+ vvp->vv_state = CVS_ACTIVE;
+ return (0);
+ }
+
+ /*
+ * Set the AAL and traffic
+ */
+ switch (ap->aal.type) {
+ case ATM_AAL0:
+ vvp->vv_aal = VATMPIF_AAL_0;
+ break;
+ case ATM_AAL2:
+ return (EINVAL);
+ case ATM_AAL3_4:
+ vvp->vv_aal = VATMPIF_AAL_4;
+ break;
+ case ATM_AAL5:
+ vvp->vv_aal = VATMPIF_AAL_5;
+ break;
+ default:
+ return (EINVAL);
+ }
+ vvp->vv_traffic_type = vatmpif_bearerclass(&ap->bearer);
+ vvp->vv_traffic = ap->traffic.v;
+
+ switch (vvp->vv_traffic_type) {
+ case VATMPIF_TRAF_ABR:
+ /* TODO */
+ case VATMPIF_TRAF_UBR:
+ break;
+ case VATMPIF_TRAF_VBR:
+ vup->vu_cur_pcr += vvp->vv_traffic.forward.SCR_all_traffic;
+ break;
+ case VATMPIF_TRAF_CBR:
+ vup->vu_cur_pcr += vvp->vv_traffic.forward.PCR_all_traffic;
+ break;
+ }
+
+ /*
+ * Indicate VC active
+ */
+ vvp->vv_state = CVS_ACTIVE;
+
+ /* Done */
+ return (0);
+}
+
+/*
+ * Close a VCC
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_TERM command. The common code has already validated most of
+ * the request so we just need to check a few more VATMPIF-specific detail.
+ * Then we just remove the entry from the list.
+ *
+ * Arguments:
+ * cup pointer to device common unit
+ * cvp pointer to common VCC entry
+ *
+ * Returns:
+ * 0 close successful
+ * errno close failed - reason indicated
+ */
+static int
+vatmpif_harp_closevcc(Cmn_unit *cup, Cmn_vcc *cvp)
+{
+ Vatmpif_unit *vup = (Vatmpif_unit *)cup;
+ Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp;
+ struct vccb *vcp = vvp->vv_connvc->cvc_vcc;
+
+ /*
+ * If this is an outbound only VCI, then we can close
+ * immediately.
+ */
+ if ((vcp->vc_type & VCC_IN) == 0) {
+ /*
+ * The state will be set to TERM when we return
+ * to the *_TERM caller.
+ */
+ return (0);
+ }
+
+ switch (vvp->vv_traffic_type) {
+ case VATMPIF_TRAF_ABR:
+ /* TODO */
+ case VATMPIF_TRAF_UBR:
+ break;
+ case VATMPIF_TRAF_VBR:
+ vup->vu_cur_pcr -= vvp->vv_traffic.forward.SCR_all_traffic;
+ break;
+ case VATMPIF_TRAF_CBR:
+ vup->vu_cur_pcr -= vvp->vv_traffic.forward.PCR_all_traffic;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Output a PDU
+ *
+ * This function is called via the common driver code after receiving a
+ * stack *_DATA* command. The command code has already validated most of
+ * the request so we just need to check a few more VATMPIF-specific detail.
+ * Then we just forward the transmit mbuf to the Netgraph node.
+ *
+ * Arguments:
+ * cup pointer to device common
+ * cvp pointer to common VCC entry
+ * m pointer to output PDU buffer chain head
+ *
+ * Returns:
+ * none
+ */
+static void
+vatmpif_harp_output(Cmn_unit *cup, Cmn_vcc *cvp, KBuffer *m)
+{
+ Vatmpif_unit *vup = (Vatmpif_unit *)cup;
+ Vatmpif_vcc *vvp = (Vatmpif_vcc *)cvp;
+ struct vccb *vcp = vvp->vv_connvc->cvc_vcc;
+ Atm_attributes *ap = &vvp->vv_connvc->cvc_attr;
+ int err = 0;
+ u_long pdulen = 0;
+
+ if (IS_VATMPIF_DEBUG_PACKET(vup))
+ atm_dev_pdu_print(cup, cvp, m, __func__);
+
+ /*
+ * Get packet PDU length
+ */
+ KB_PLENGET (m, pdulen);
+
+ err = ng_atmpif_transmit(vup, m, vcp->vc_vpi, vcp->vc_vci,
+ 0, 0, ap->aal.type);
+
+ /*
+ * Now collect some statistics
+ */
+ if (err) {
+ vup->vu_pif.pif_oerrors++;
+ vcp->vc_oerrors++;
+ if (vcp->vc_nif)
+ vcp->vc_nif->nif_if.if_oerrors++;
+ } else {
+ /*
+ * Good transmission
+ */
+
+ switch (ap->aal.type) {
+ case VATMPIF_AAL_0:
+ vup->vu_stats.hva_st_ng.ng_tx_rawcell++;
+ break;
+ case VATMPIF_AAL_4:
+ /* TODO */
+ break;
+ case VATMPIF_AAL_5:
+ vup->vu_stats.hva_st_aal5.aal5_xmit +=
+ (pdulen + 47) / 48;
+ vup->vu_stats.hva_st_aal5.aal5_pdu_xmit++;
+ break;
+ default:
+ log(LOG_ERR, "%s%d: unknown AAL while %s",
+ vup->vu_pif.pif_name, vup->vu_pif.pif_unit,
+ __func__);
+ }
+
+ vup->vu_pif.pif_opdus++;
+ vup->vu_pif.pif_obytes += pdulen;
+ if (vvp) {
+ vcp = vvp->vv_connvc->cvc_vcc;
+ vcp->vc_opdus++;
+ vcp->vc_obytes += pdulen;
+ if (vcp->vc_nif) {
+ vcp->vc_nif->nif_obytes += pdulen;
+ vcp->vc_nif->nif_if.if_opackets++;
+ vcp->vc_nif->nif_if.if_obytes += pdulen;
+ }
+ }
+ }
+}
+
+/*
+ * Pass Incoming PDU up to the HARP stack
+ *
+ * This function is called via the core ATM interrupt queue callback
+ * set in vatmpif_harp_recv_drain(). It will pass the supplied incoming
+ * PDU up the incoming VCC's stack.
+ *
+ * Arguments:
+ * tok token to identify stack instantiation
+ * m pointer to incoming PDU buffer chain
+ *
+ * Returns:
+ * none
+ */
+static void
+vatmpif_harp_recv_stack(void *tok, KBuffer *m)
+{
+ Vatmpif_vcc *vvp = (Vatmpif_vcc *)tok;
+ int err;
+
+ /*
+ * Send the data up the stack
+ */
+ STACK_CALL(CPCS_UNITDATA_SIG, vvp->vv_upper,
+ vvp->vv_toku, vvp->vv_connvc, (intptr_t)m, 0, err);
+ if (err)
+ KB_FREEALL(m);
+}
+
+/*
+ * Drain Receive Queue
+ *
+ * The function will process all completed entries at the head of the
+ * receive queue. The received segments will be linked into a received
+ * PDU buffer cahin and it will then be passed up the PDU's VCC stack
+ * function processing by the next higher protocol layer.
+ *
+ * May be called in interrupt state.
+ * Must be called with interrupts locked out.
+ *
+ * Arguments:
+ * vup pointer to the virtual device structure
+ * m pointer to incoming PDU buffer chain
+ * vpi Virtual Path Identifier
+ * vci Virtual Channel Identifier (host order)
+ * pt Payload Type Identifier (3 bit)
+ * ATM_PT_USER_SDU0
+ * ATM_PT_USER_SDU1
+ * ATM_PT_USER_CONG_SDU0
+ * ATM_PT_USER_CONG_SDU1
+ * ATM_PT_NONUSER
+ * ATM_PT_OAMF5_SEG
+ * ATM_PT_OAMF5_E2E
+ * clp Cell Loss Priority (1 bit)
+ *
+ * Returns:
+ * 0 close successful
+ * errno close failed - reason indicated
+ */
+int
+vatmpif_harp_recv_drain(Vatmpif_unit *vup, KBuffer *m,
+ uint8_t vpi, uint16_t vci, uint8_t pt, uint8_t clp, Vatmpif_aal aal)
+{
+ Vatmpif_vcc *vvp;
+ struct vccb *vcp;
+ u_long pdulen = 0;
+ caddr_t cp;
+ int err = 0;
+
+ /*
+ * Locate incoming VCC for this PDU
+ */
+ vvp = (Vatmpif_vcc *)atm_dev_vcc_find((Cmn_unit *)vup,
+ vpi, vci, VCC_IN);
+
+ if (vvp == NULL) {
+ vup->vu_stats.hva_st_ng.ng_rx_novcc++;
+ vup->vu_pif.pif_ierrors++;
+ KB_FREEALL(m);
+ err = EIO;
+ goto failed;
+ }
+
+ switch (aal) {
+ case VATMPIF_AAL_0:
+ vup->vu_stats.hva_st_ng.ng_rx_rawcell++;
+ break;
+ case VATMPIF_AAL_4:
+ /* TODO */
+ break;
+ case VATMPIF_AAL_5:
+ vup->vu_stats.hva_st_aal5.aal5_rcvd += (pdulen + 47) / 48;
+ vup->vu_stats.hva_st_aal5.aal5_pdu_rcvd++;
+ break;
+ default:
+ vup->vu_stats.hva_st_ng.ng_badpdu++;
+ vup->vu_pif.pif_ierrors++;
+ KB_FREEALL(m);
+ err = EINVAL;
+ goto failed;
+ }
+
+ /*
+ * TODO:
+ * For now, only user data PDUs are supported
+ */
+ if (pt & ATM_HDR_SET_PT(ATM_PT_NONUSER)) {
+ vup->vu_stats.hva_st_ng.ng_badpdu++;
+ vup->vu_pif.pif_ierrors++;
+ if (aal == VATMPIF_AAL_5) {
+ vup->vu_stats.hva_st_aal5.aal5_drops +=
+ (pdulen + 47) / 48;
+ vup->vu_stats.hva_st_aal5.aal5_pdu_drops++;
+ }
+ err = EINVAL;
+ goto failed;
+ }
+
+ if (IS_VATMPIF_DEBUG_PACKET(vup))
+ atm_dev_pdu_print((Cmn_unit *)vup, (Cmn_vcc *)vvp, m,
+ __FUNCTION__);
+
+ /*
+ * Get packet PDU length
+ */
+ KB_PLENGET(m, pdulen);
+
+ /*
+ * Only try queueing this if there is data
+ * to be handed up to the next layer.
+ */
+ if (pdulen == 0) {
+ /*
+ * Free zero-length buffer
+ */
+ vup->vu_stats.hva_st_ng.ng_badpdu++;
+ vup->vu_pif.pif_ierrors++;
+ if (aal == VATMPIF_AAL_5)
+ vup->vu_stats.hva_st_aal5.aal5_pdu_errs++;
+ err = EIO;
+ KB_FREEALL(m);
+ goto failed;
+ }
+
+ /* TODO: process the AAL4 CRC, AAL5 CRC,
+ * then update aal5_crc_len, aal5_drops, aal5_pdu_crc,
+ * aal5_pdu_errs, aal5_pdu_drops ...
+ */
+
+ /*
+ * Quick count the PDU
+ */
+ vup->vu_pif.pif_ipdus++;
+ vup->vu_pif.pif_ibytes += pdulen;
+
+ vup->vu_stats.hva_st_ng.ng_rx_pdu++;
+ vup->vu_stats.hva_st_atm.atm_rcvd += (pdulen + 47) / 48;
+
+ /*
+ * Update the VCC statistics:
+ * XXX: This code should not be into the driver.
+ */
+ vcp = vvp->vv_connvc->cvc_vcc;
+ if (vcp) {
+ vcp->vc_ipdus++;
+ vcp->vc_ibytes += pdulen;
+ /*
+ * Update the NIF statistics if any
+ * XXX: beurk !
+ */
+ if (vcp->vc_nif) {
+ vcp->vc_nif->nif_ibytes += pdulen;
+ vcp->vc_nif->nif_if.if_ipackets++;
+ vcp->vc_nif->nif_if.if_ibytes += pdulen;
+ }
+ }
+
+ /*
+ * The STACK_CALL needs to happen at splnet() in order
+ * for the stack sequence processing to work. Schedule an
+ * interrupt queue callback at splnet().
+ */
+
+ /*
+ * Prepend callback function pointer and token value to buffer.
+ * We have already guaranteed that the space is available in the
+ * first buffer because the vatmpif_header structure is greater
+ * than our callback pointer.
+ * XXX
+ */
+ KB_HEADADJ(m, sizeof(atm_intr_func_t) + sizeof(void *));
+ KB_DATASTART(m, cp, caddr_t);
+ *((atm_intr_func_t *) cp) = vatmpif_harp_recv_stack;
+ cp += sizeof (atm_intr_func_t);
+ *((void **)cp) = (void *)vvp;
+
+ /*
+ * Schedule callback
+ */
+ if (!netisr_queue(NETISR_ATM, m)) {
+ /*
+ * queue is full. Unable to pass up to the HARP stack
+ * Update the stats.
+ */
+ vup->vu_stats.hva_st_ng.ng_rx_iqfull++;
+ vup->vu_pif.pif_ierrors++;
+ goto failed;
+ }
+
+ /* Done */
+ return (0);
+
+failed:
+ return (err);
+}
diff --git a/sys/netgraph/atm/atmpif/ng_atmpif_var.h b/sys/netgraph/atm/atmpif/ng_atmpif_var.h
new file mode 100644
index 0000000..f295461
--- /dev/null
+++ b/sys/netgraph/atm/atmpif/ng_atmpif_var.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2003 Harti Brandt.
+ * Copyright (c) 2003 Vincent Jardin.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Supported AALs
+ */
+enum vatmpif_aal {
+ VATMPIF_AAL_0 = 0, /* Cell Service */
+ VATMPIF_AAL_4 = 4, /* AAL 3/4 */
+ VATMPIF_AAL_5 = 5, /* AAL 5 */
+};
+typedef enum vatmpif_aal Vatmpif_aal;
+
+/*
+ * Supported traffic type
+ */
+enum vatmpif_traffic_type {
+ VATMPIF_TRAF_CBR = 0x01, /* Constant bit rate */
+ VATMPIF_TRAF_VBR = 0x02, /* Variable bit rate */
+ VATMPIF_TRAF_ABR = 0x03, /* Available Bit Rate */
+ VATMPIF_TRAF_UBR = 0x04, /* Unspecified bit rate */
+};
+typedef enum vatmpif_traffic_type Vatmpif_traffic_type;
+
+typedef struct t_atm_traffic Vatmpif_traffic;
+
+/*
+ * Host protocol control blocks
+ *
+ */
+/*
+ * Device VCC Entry
+ *
+ * Contains the common (vv_cmn) and specific information for each VCC
+ * which is opened through a ATM PIF node.
+ * It is a virtual VCC. From the Netgraph poit of view it is a
+ * per-node's hook private data.
+ *
+ * It is a polymorph object with the instances of Cmn_vcc.
+ */
+struct vatmpif_vcc {
+ Cmn_vcc vv_cmn; /* Common VCC stuff */
+ Vatmpif_aal vv_aal; /* AAL */
+ Vatmpif_traffic vv_traffic; /* forward and backward ATM traffic */
+ Vatmpif_traffic_type vv_traffic_type;/* CBR, VBR, UBR, ... */
+};
+typedef struct vatmpif_vcc Vatmpif_vcc;
+
+#define vv_next vv_cmn.cv_next
+#define vv_toku vv_cmn.cv_toku
+#define vv_upper vv_cmn.cv_upper
+#define vv_connvc vv_cmn.cv_connvc
+#define vv_state vv_cmn.cv_state
+
+/*
+ * The hook structure describes a virtual link
+ */
+struct ng_vatmpif_hook {
+ hook_p hook; /* netgraph hook */
+ Vatmpif_stats stats; /* link stats */
+ uint32_t InSeq; /* last received sequence number + 1 */
+ uint32_t OutSeq; /* last sent sequence number */
+ uint32_t cur_pcr; /* slot's reserved PCR */
+};
+
+/*
+ * Device Virtual Unit Structure
+ *
+ * Contains all the information for a single device (adapter).
+ * It is a virtual device. From the Netgraph point of view it is
+ * a per-node private data.
+ *
+ * It is a polymorph object with the instances of Cmn_unit.
+ */
+struct vatmpif_unit {
+ Cmn_unit vu_cmn; /* Common unit stuff */
+ node_p node; /* netgraph node */
+ struct ng_vatmpif_hook* link; /* virtual link hoook */
+ struct ng_vatmpif_config conf; /* node configuration */
+};
+typedef struct vatmpif_unit Vatmpif_unit;
+
+#define ng_vatmpif_private vatmpif_unit
+typedef struct ng_vatmpif_private *priv_p;
+
+#define vu_pif vu_cmn.cu_pif
+#define vu_unit vu_cmn.cu_unit
+#define vu_flags vu_cmn.cu_flags
+#define vu_mtu vu_cmn.cu_mtu
+#define vu_open_vcc vu_cmn.cu_open_vcc
+#define vu_vcc vu_cmn.cu_vcc
+#define vu_vcc_zone vu_cmn.cu_vcc_zone
+#define vu_nif_zone vu_cmn.cu_nif_zone
+#define vu_ioctl vu_cmn.cu_ioctl
+#define vu_instvcc vu_cmn.cu_instvcc
+#define vu_openvcc vu_cmn.cu_openvcc
+#define vu_closevcc vu_cmn.cu_closevcc
+#define vu_output vu_cmn.cu_output
+#define vu_config vu_cmn.cu_config
+#define vu_softc vu_cmn.cu_softc
+
+#define vu_stats link->stats
+#define vu_cur_pcr link->cur_pcr
+
+/*
+ * Netgraph to HARP API
+ */
+int vatmpif_harp_attach(node_p node);
+int vatmpif_harp_detach(node_p node);
+
+int vatmpif_harp_recv_drain(Vatmpif_unit *vup, KBuffer *m,
+ uint8_t vpi, uint16_t vci, uint8_t pt, uint8_t clp, Vatmpif_aal aal);
+
+/*
+ * HARP to Netgraph API
+ */
+int ng_atmpif_transmit(const priv_p priv, struct mbuf *m,
+ uint8_t vpi, uint16_t vci, uint8_t pt, uint8_t clp, Vatmpif_aal aal);
+
+extern uma_zone_t vatmpif_nif_zone;
+extern uma_zone_t vatmpif_vcc_zone;
diff --git a/sys/netgraph/atm/ng_atmpif.h b/sys/netgraph/atm/ng_atmpif.h
new file mode 100644
index 0000000..b45df75
--- /dev/null
+++ b/sys/netgraph/atm/ng_atmpif.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2003 Harti Brandt.
+ * Copyright (c) 2003 Vincent Jardin.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETGRAPH_ATM_NG_ATMPIF_H_
+#define _NETGRAPH_ATM_NG_ATMPIF_H_
+
+/* Node type name and magic cookie */
+#define NG_ATMPIF_NODE_TYPE "atmpif"
+#define NGM_ATMPIF_COOKIE 967239456
+
+/*
+ * Physical device name - used to configure HARP devices
+ */
+#ifndef VATMPIF_DEV_NAME
+#define VATMPIF_DEV_NAME "hva" /* HARP Virtual ATM */
+#endif
+
+#define VATMPIF_MAX_VCI 65535
+#define VATMPIF_MAX_VPI 255
+
+/* Hook name */
+#define NG_ATMPIF_HOOK_LINK "link" /* virtual link hook */
+
+/*
+ * Node configuration structure
+ */
+struct ng_vatmpif_config {
+ uint8_t debug; /* debug bit field (see below) */
+ uint32_t pcr; /* peak cell rate */
+ Mac_addr macaddr; /* Mac Address */
+};
+/* Keep this in sync with the above structure definition */
+#define NG_ATMPIF_CONFIG_TYPE_INFO { \
+ { "debug", &ng_parse_uint8_type }, \
+ { "pcr", &ng_parse_uint32_type }, \
+ { "macaddr", &ng_mac_addr_type }, \
+ { NULL } \
+}
+
+/*
+ * Debug bit-fields
+ */
+#define VATMPIF_DEBUG_NONE 0x00
+#define VATMPIF_DEBUG_PACKET 0x01 /* enable ng_atmpif debugging */
+
+#define IS_VATMPIF_DEBUG_PACKET(a) ( (a) \
+ && ((a)->conf.debug & VATMPIF_DEBUG_PACKET) )
+
+/*
+ * Statistics
+ */
+struct hva_stats_ng {
+ uint32_t ng_errseq; /* Duplicate or out of order */
+ uint32_t ng_lostpdu; /* PDU lost detected */
+ uint32_t ng_badpdu; /* Unknown PDU type */
+ uint32_t ng_rx_novcc; /* Draining PDU on closed VCC */
+ uint32_t ng_rx_iqfull; /* PDU drops, no room in atm_intrq */
+ uint32_t ng_tx_rawcell; /* PDU raw cells transmitted */
+ uint32_t ng_rx_rawcell; /* PDU raw cells received */
+ uint64_t ng_tx_pdu; /* PDU transmitted */
+ uint64_t ng_rx_pdu; /* PDU received */
+};
+typedef struct hva_stats_ng Hva_Stats_ng;
+/* Keep this in sync with the above structure definition */
+#define HVA_STATS_NG_TYPE_INFO \
+ { "errSeqOrder", &ng_parse_uint32_type }, \
+ { "errLostPDU", &ng_parse_uint32_type }, \
+ { "recvBadPDU", &ng_parse_uint32_type }, \
+ { "ErrATMVC", &ng_parse_uint32_type }, \
+ { "ErrQfull", &ng_parse_uint32_type }, \
+ { "xmitRawCell", &ng_parse_uint32_type }, \
+ { "recvRawCell", &ng_parse_uint32_type }, \
+ { "xmitPDU", &ng_parse_uint64_type }, \
+ { "recvPDU", &ng_parse_uint64_type },
+
+
+struct hva_stats_atm {
+ uint64_t atm_xmit; /* Cells transmitted */
+ uint64_t atm_rcvd; /* Cells received */
+};
+typedef struct hva_stats_atm Hva_Stats_atm;
+/* Keep this in sync with the above structure definition */
+#define HVA_STATS_ATM_NG_TYPE_INFO \
+ { "xmitATMCells", &ng_parse_uint64_type }, \
+ { "recvATMCells", &ng_parse_uint64_type },
+
+struct hva_stats_aal5 {
+ uint64_t aal5_xmit; /* Cells transmitted */
+ uint64_t aal5_rcvd; /* Cells received */
+ uint32_t aal5_crc_len; /* Cells with CRC/length errors */
+ uint32_t aal5_drops; /* Cell drops */
+ uint64_t aal5_pdu_xmit; /* CS PDUs transmitted */
+ uint64_t aal5_pdu_rcvd; /* CS PDUs received */
+ uint32_t aal5_pdu_crc; /* CS PDUs with CRC errors */
+ uint32_t aal5_pdu_errs; /* CS layer protocol errors */
+ uint32_t aal5_pdu_drops; /* CS PDUs dropped */
+};
+typedef struct hva_stats_aal5 Hva_Stats_aal5;
+/* Keep this in sync with the above structure definition */
+#define HVA_STATS_AAL5_NG_TYPE_INFO \
+ { "xmitAAL5Cells", &ng_parse_uint64_type }, \
+ { "recvAAL5Cells", &ng_parse_uint64_type }, \
+ { "AAL5ErrCRCCells", &ng_parse_uint32_type }, \
+ { "AAL5DropsCells", &ng_parse_uint32_type }, \
+ { "xmitAAL5PDU", &ng_parse_uint64_type }, \
+ { "recvAAL5PDU", &ng_parse_uint64_type }, \
+ { "AAL5CRCPDU", &ng_parse_uint32_type }, \
+ { "AAL5ErrPDU", &ng_parse_uint32_type }, \
+ { "AAL5DropsPDU", &ng_parse_uint32_type },
+
+struct vatmpif_stats {
+ Hva_Stats_ng hva_st_ng; /* Netgraph layer stats */
+ Hva_Stats_atm hva_st_atm; /* ATM layer stats */
+ Hva_Stats_aal5 hva_st_aal5; /* AAL5 layer stats */
+};
+typedef struct vatmpif_stats Vatmpif_stats;
+/* Keep this in sync with the above structure definition */
+#define NG_ATMPIF_STATS_TYPE_INFO { \
+ HVA_STATS_NG_TYPE_INFO \
+ HVA_STATS_ATM_NG_TYPE_INFO \
+ HVA_STATS_AAL5_NG_TYPE_INFO \
+ { NULL } \
+}
+
+/* Structure returned by NGM_ATMPIF_GET_LINK_STATUS */
+struct ng_atmpif_link_status {
+ uint32_t InSeq; /* last received sequence number + 1 */
+ uint32_t OutSeq; /* last sent sequence number */
+ uint32_t cur_pcr; /* slot's reserved PCR */
+};
+/* Keep this in sync with the above structure definition */
+#define NG_ATMPIF_LINK_STATUS_TYPE_INFO { \
+ { "InSeq", &ng_parse_uint32_type }, \
+ { "OutSeq", &ng_parse_uint32_type }, \
+ { "cur_pcr", &ng_parse_uint32_type }, \
+ { NULL } \
+}
+
+/* Netgraph control messages */
+enum {
+ NGM_ATMPIF_SET_CONFIG = 1, /* set node configuration */
+ NGM_ATMPIF_GET_CONFIG, /* get node configuration */
+ NGM_ATMPIF_GET_LINK_STATUS, /* get link status */
+ NGM_ATMPIF_GET_STATS, /* get link stats */
+ NGM_ATMPIF_CLR_STATS, /* clear link stats */
+ NGM_ATMPIF_GETCLR_STATS, /* atomically get & clear link stats */
+};
+
+#endif /* _NETGRAPH_NG_ATMPIF_H_ */
OpenPOWER on IntegriCloud