From owner-svn-src-head@FreeBSD.ORG Wed May 5 10:23:31 2010 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id A2D351065672; Wed, 5 May 2010 10:23:31 +0000 (UTC) (envelope-from rwatson@FreeBSD.org) Received: from cyrus.watson.org (cyrus.watson.org [65.122.17.42]) by mx1.freebsd.org (Postfix) with ESMTP id 368C58FC18; Wed, 5 May 2010 10:23:31 +0000 (UTC) Received: from fledge.watson.org (fledge.watson.org [65.122.17.41]) by cyrus.watson.org (Postfix) with ESMTPS id CA03446B89; Wed, 5 May 2010 06:23:30 -0400 (EDT) Date: Wed, 5 May 2010 11:23:30 +0100 (BST) From: Robert Watson X-X-Sender: robert@fledge.watson.org To: Navdeep Parhar In-Reply-To: <201005050041.o450fesw090589@svn.freebsd.org> Message-ID: References: <201005050041.o450fesw090589@svn.freebsd.org> User-Agent: Alpine 2.00 (BSF 1167 2008-08-23) MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org Subject: Re: svn commit: r207643 - in head: sys/dev/cxgb usr.sbin/cxgbtool X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 05 May 2010 10:23:31 -0000 On Wed, 5 May 2010, Navdeep Parhar wrote: > L2/3/4 headers and can drop or steer packets as instructed. Filtering > based on src ip, dst ip, src port, dst port, 802.1q, udp/tcp, and mac > addr is possible. Add support in cxgbtool to program these filters. > Some simple examples: Excellent news! Now to start to teach the network stack how to use these features itself for the purposes of aligning connection affinity up/down the stack! Robert > > Drop all tcp/80 traffic coming from the subnet specified. > # cxgbtool cxgb2 filter 0 sip 192.168.1.0/24 dport 80 type tcp action drop > > Steer all incoming UDP traffic to qset 0. > # cxgbtool cxgb2 filter 1 type udp queue 0 action pass > > Steer all tcp traffic from 192.168.1.1 to qset 1. > # cxgbtool cxgb2 filter 2 sip 192.168.1.1 type tcp queue 1 action pass > > Drop fragments. > # cxgbtool cxgb2 filter 3 type frag action drop > > List all filters. > # cxgbtool cxgb2 filter list > index SIP DIP sport dport VLAN PRI P/MAC type Q > 0 192.168.1.0/24 0.0.0.0 * 80 0 0/1 */* tcp - > 1 0.0.0.0/0 0.0.0.0 * * 0 0/1 */* udp 0 > 2 192.168.1.1/32 0.0.0.0 * * 0 0/1 */* tcp 1 > 3 0.0.0.0/0 0.0.0.0 * * 0 0/1 */* frag - > 16367 0.0.0.0/0 0.0.0.0 * * 0 0/1 */* * * > > MFC after: 2 weeks > > Modified: > head/sys/dev/cxgb/cxgb_ioctl.h > head/sys/dev/cxgb/cxgb_main.c > head/usr.sbin/cxgbtool/cxgbtool.c > > Modified: head/sys/dev/cxgb/cxgb_ioctl.h > ============================================================================== > --- head/sys/dev/cxgb/cxgb_ioctl.h Wed May 5 00:39:50 2010 (r207642) > +++ head/sys/dev/cxgb/cxgb_ioctl.h Wed May 5 00:41:40 2010 (r207643) > @@ -59,6 +59,9 @@ enum { > CH_CLEAR_STATS, > CH_GET_UP_LA, > CH_GET_UP_IOQS, > + CH_SET_FILTER, > + CH_DEL_FILTER, > + CH_GET_FILTER, > }; > > /* statistics categories */ > @@ -215,6 +218,29 @@ struct ch_up_ioqs { > struct t3_ioq_entry *data; > }; > > +struct ch_filter_tuple { > + uint32_t sip; > + uint32_t dip; > + uint16_t sport; > + uint16_t dport; > + uint16_t vlan:12; > + uint16_t vlan_prio:3; > +}; > + > +struct ch_filter { > + uint32_t filter_id; > + struct ch_filter_tuple val; > + struct ch_filter_tuple mask; > + uint16_t mac_addr_idx; > + uint8_t mac_hit:1; > + uint8_t proto:2; > + > + uint8_t want_filter_id:1; > + uint8_t pass:1; > + uint8_t rss:1; > + uint8_t qset; > +}; > + > #define CHELSIO_SETREG _IOW('f', CH_SETREG, struct ch_reg) > #define CHELSIO_GETREG _IOWR('f', CH_GETREG, struct ch_reg) > #define CHELSIO_GETMTUTAB _IOR('f', CH_GETMTUTAB, struct ch_mtus) > @@ -239,4 +265,7 @@ struct ch_up_ioqs { > #define CHELSIO_GET_EEPROM _IOWR('f', CH_GET_EEPROM, struct ch_eeprom) > #define CHELSIO_GET_UP_LA _IOWR('f', CH_GET_UP_LA, struct ch_up_la) > #define CHELSIO_GET_UP_IOQS _IOWR('f', CH_GET_UP_IOQS, struct ch_up_ioqs) > +#define CHELSIO_SET_FILTER _IOW('f', CH_SET_FILTER, struct ch_filter) > +#define CHELSIO_DEL_FILTER _IOW('f', CH_DEL_FILTER, struct ch_filter) > +#define CHELSIO_GET_FILTER _IOWR('f', CH_GET_FILTER, struct ch_filter) > #endif > > Modified: head/sys/dev/cxgb/cxgb_main.c > ============================================================================== > --- head/sys/dev/cxgb/cxgb_main.c Wed May 5 00:39:50 2010 (r207642) > +++ head/sys/dev/cxgb/cxgb_main.c Wed May 5 00:41:40 2010 (r207643) > @@ -99,6 +99,13 @@ static void cxgb_ext_intr_handler(void * > static void cxgb_tick_handler(void *, int); > static void cxgb_tick(void *); > static void setup_rss(adapter_t *sc); > +static int alloc_filters(struct adapter *); > +static int setup_hw_filters(struct adapter *); > +static int set_filter(struct adapter *, int, const struct filter_info *); > +static inline void mk_set_tcb_field(struct cpl_set_tcb_field *, unsigned int, > + unsigned int, u64, u64); > +static inline void set_tcb_field_ulp(struct cpl_set_tcb_field *, unsigned int, > + unsigned int, u64, u64); > > /* Attachment glue for the PCI controller end of the device. Each port of > * the device is attached separately, as defined later. > @@ -1661,6 +1668,13 @@ cxgb_up(struct adapter *sc) > if ((err = update_tpsram(sc))) > goto out; > > + if (is_offload(sc)) { > + sc->params.mc5.nservers = 0; > + sc->params.mc5.nroutes = 0; > + sc->params.mc5.nfilters = t3_mc5_size(&sc->mc5) - > + MC5_MIN_TIDS; > + } > + > err = t3_init_hw(sc, 0); > if (err) > goto out; > @@ -1672,6 +1686,7 @@ cxgb_up(struct adapter *sc) > if (err) > goto out; > > + alloc_filters(sc); > setup_rss(sc); > > t3_intr_clear(sc); > @@ -1698,6 +1713,7 @@ cxgb_up(struct adapter *sc) > > if (!(sc->flags & QUEUES_BOUND)) { > bind_qsets(sc); > + setup_hw_filters(sc); > sc->flags |= QUEUES_BOUND; > } > > @@ -3076,6 +3092,139 @@ cxgb_extension_ioctl(struct cdev *dev, u > free(buf, M_DEVBUF); > break; > } > + case CHELSIO_SET_FILTER: { > + struct ch_filter *f = (struct ch_filter *)data;; > + struct filter_info *p; > + unsigned int nfilters = sc->params.mc5.nfilters; > + > + if (!is_offload(sc)) > + return (EOPNOTSUPP); /* No TCAM */ > + if (!(sc->flags & FULL_INIT_DONE)) > + return (EAGAIN); /* mc5 not setup yet */ > + if (nfilters == 0) > + return (EBUSY); /* TOE will use TCAM */ > + > + /* sanity checks */ > + if (f->filter_id >= nfilters || > + (f->val.dip && f->mask.dip != 0xffffffff) || > + (f->val.sport && f->mask.sport != 0xffff) || > + (f->val.dport && f->mask.dport != 0xffff) || > + (f->val.vlan && f->mask.vlan != 0xfff) || > + (f->val.vlan_prio && > + f->mask.vlan_prio != FILTER_NO_VLAN_PRI) || > + (f->mac_addr_idx != 0xffff && f->mac_addr_idx > 15) || > + f->qset >= SGE_QSETS || > + sc->rrss_map[f->qset] >= RSS_TABLE_SIZE) > + return (EINVAL); > + > + /* Was allocated with M_WAITOK */ > + KASSERT(sc->filters, ("filter table NULL\n")); > + > + p = &sc->filters[f->filter_id]; > + if (p->locked) > + return (EPERM); > + > + bzero(p, sizeof(*p)); > + p->sip = f->val.sip; > + p->sip_mask = f->mask.sip; > + p->dip = f->val.dip; > + p->sport = f->val.sport; > + p->dport = f->val.dport; > + p->vlan = f->mask.vlan ? f->val.vlan : 0xfff; > + p->vlan_prio = f->mask.vlan_prio ? (f->val.vlan_prio & 6) : > + FILTER_NO_VLAN_PRI; > + p->mac_hit = f->mac_hit; > + p->mac_vld = f->mac_addr_idx != 0xffff; > + p->mac_idx = f->mac_addr_idx; > + p->pkt_type = f->proto; > + p->report_filter_id = f->want_filter_id; > + p->pass = f->pass; > + p->rss = f->rss; > + p->qset = f->qset; > + > + error = set_filter(sc, f->filter_id, p); > + if (error == 0) > + p->valid = 1; > + break; > + } > + case CHELSIO_DEL_FILTER: { > + struct ch_filter *f = (struct ch_filter *)data; > + struct filter_info *p; > + unsigned int nfilters = sc->params.mc5.nfilters; > + > + if (!is_offload(sc)) > + return (EOPNOTSUPP); > + if (!(sc->flags & FULL_INIT_DONE)) > + return (EAGAIN); > + if (nfilters == 0 || sc->filters == NULL) > + return (EINVAL); > + if (f->filter_id >= nfilters) > + return (EINVAL); > + > + p = &sc->filters[f->filter_id]; > + if (p->locked) > + return (EPERM); > + if (!p->valid) > + return (EFAULT); /* Read "Bad address" as "Bad index" */ > + > + bzero(p, sizeof(*p)); > + p->sip = p->sip_mask = 0xffffffff; > + p->vlan = 0xfff; > + p->vlan_prio = FILTER_NO_VLAN_PRI; > + p->pkt_type = 1; > + error = set_filter(sc, f->filter_id, p); > + break; > + } > + case CHELSIO_GET_FILTER: { > + struct ch_filter *f = (struct ch_filter *)data; > + struct filter_info *p; > + unsigned int i, nfilters = sc->params.mc5.nfilters; > + > + if (!is_offload(sc)) > + return (EOPNOTSUPP); > + if (!(sc->flags & FULL_INIT_DONE)) > + return (EAGAIN); > + if (nfilters == 0 || sc->filters == NULL) > + return (EINVAL); > + > + i = f->filter_id == 0xffffffff ? 0 : f->filter_id + 1; > + for (; i < nfilters; i++) { > + p = &sc->filters[i]; > + if (!p->valid) > + continue; > + > + bzero(f, sizeof(*f)); > + > + f->filter_id = i; > + f->val.sip = p->sip; > + f->mask.sip = p->sip_mask; > + f->val.dip = p->dip; > + f->mask.dip = p->dip ? 0xffffffff : 0; > + f->val.sport = p->sport; > + f->mask.sport = p->sport ? 0xffff : 0; > + f->val.dport = p->dport; > + f->mask.dport = p->dport ? 0xffff : 0; > + f->val.vlan = p->vlan == 0xfff ? 0 : p->vlan; > + f->mask.vlan = p->vlan == 0xfff ? 0 : 0xfff; > + f->val.vlan_prio = p->vlan_prio == FILTER_NO_VLAN_PRI ? > + 0 : p->vlan_prio; > + f->mask.vlan_prio = p->vlan_prio == FILTER_NO_VLAN_PRI ? > + 0 : FILTER_NO_VLAN_PRI; > + f->mac_hit = p->mac_hit; > + f->mac_addr_idx = p->mac_vld ? p->mac_idx : 0xffff; > + f->proto = p->pkt_type; > + f->want_filter_id = p->report_filter_id; > + f->pass = p->pass; > + f->rss = p->rss; > + f->qset = p->qset; > + > + break; > + } > + > + if (i == nfilters) > + f->filter_id = 0xffffffff; > + break; > + } > default: > return (EOPNOTSUPP); > break; > @@ -3130,5 +3279,129 @@ cxgb_get_regs(adapter_t *sc, struct ch_i > XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1)); > } > > +static int > +alloc_filters(struct adapter *sc) > +{ > + struct filter_info *p; > + unsigned int nfilters = sc->params.mc5.nfilters; > + > + if (nfilters == 0) > + return (0); > + > + p = malloc(sizeof(*p) * nfilters, M_DEVBUF, M_WAITOK | M_ZERO); > + sc->filters = p; > + > + p = &sc->filters[nfilters - 1]; > + p->vlan = 0xfff; > + p->vlan_prio = FILTER_NO_VLAN_PRI; > + p->pass = p->rss = p->valid = p->locked = 1; > + > + return (0); > +} > + > +static int > +setup_hw_filters(struct adapter *sc) > +{ > + int i, rc; > + unsigned int nfilters = sc->params.mc5.nfilters; > + > + if (!sc->filters) > + return (0); > + > + t3_enable_filters(sc); > + > + for (i = rc = 0; i < nfilters && !rc; i++) { > + if (sc->filters[i].locked) > + rc = set_filter(sc, i, &sc->filters[i]); > + } > + > + return (rc); > +} > + > +static int > +set_filter(struct adapter *sc, int id, const struct filter_info *f) > +{ > + int len; > + struct mbuf *m; > + struct ulp_txpkt *txpkt; > + struct work_request_hdr *wr; > + struct cpl_pass_open_req *oreq; > + struct cpl_set_tcb_field *sreq; > + > + len = sizeof(*wr) + sizeof(*oreq) + 2 * sizeof(*sreq); > + KASSERT(len <= MHLEN, ("filter request too big for an mbuf")); > + > + id += t3_mc5_size(&sc->mc5) - sc->params.mc5.nroutes - > + sc->params.mc5.nfilters; > + > + m = m_gethdr(M_WAITOK, MT_DATA); > + m->m_len = m->m_pkthdr.len = len; > + bzero(mtod(m, char *), len); > + > + wr = mtod(m, struct work_request_hdr *); > + wr->wrh_hi = htonl(V_WR_OP(FW_WROPCODE_BYPASS) | F_WR_ATOMIC); > + > + oreq = (struct cpl_pass_open_req *)(wr + 1); > + txpkt = (struct ulp_txpkt *)oreq; > + txpkt->cmd_dest = htonl(V_ULPTX_CMD(ULP_TXPKT)); > + txpkt->len = htonl(V_ULPTX_NFLITS(sizeof(*oreq) / 8)); > + OPCODE_TID(oreq) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, id)); > + oreq->local_port = htons(f->dport); > + oreq->peer_port = htons(f->sport); > + oreq->local_ip = htonl(f->dip); > + oreq->peer_ip = htonl(f->sip); > + oreq->peer_netmask = htonl(f->sip_mask); > + oreq->opt0h = 0; > + oreq->opt0l = htonl(F_NO_OFFLOAD); > + oreq->opt1 = htonl(V_MAC_MATCH_VALID(f->mac_vld) | > + V_CONN_POLICY(CPL_CONN_POLICY_FILTER) | > + V_VLAN_PRI(f->vlan_prio >> 1) | > + V_VLAN_PRI_VALID(f->vlan_prio != FILTER_NO_VLAN_PRI) | > + V_PKT_TYPE(f->pkt_type) | V_OPT1_VLAN(f->vlan) | > + V_MAC_MATCH(f->mac_idx | (f->mac_hit << 4))); > + > + sreq = (struct cpl_set_tcb_field *)(oreq + 1); > + set_tcb_field_ulp(sreq, id, 1, 0x1800808000ULL, > + (f->report_filter_id << 15) | (1 << 23) | > + ((u64)f->pass << 35) | ((u64)!f->rss << 36)); > + set_tcb_field_ulp(sreq + 1, id, 0, 0xffffffff, (2 << 19) | 1); > + t3_mgmt_tx(sc, m); > + > + if (f->pass && !f->rss) { > + len = sizeof(*sreq); > + m = m_gethdr(M_WAITOK, MT_DATA); > + m->m_len = m->m_pkthdr.len = len; > + bzero(mtod(m, char *), len); > + sreq = mtod(m, struct cpl_set_tcb_field *); > + sreq->wr.wrh_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); > + mk_set_tcb_field(sreq, id, 25, 0x3f80000, > + (u64)sc->rrss_map[f->qset] << 19); > + t3_mgmt_tx(sc, m); > + } > + return 0; > +} > + > +static inline void > +mk_set_tcb_field(struct cpl_set_tcb_field *req, unsigned int tid, > + unsigned int word, u64 mask, u64 val) > +{ > + OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); > + req->reply = V_NO_REPLY(1); > + req->cpu_idx = 0; > + req->word = htons(word); > + req->mask = htobe64(mask); > + req->val = htobe64(val); > +} > + > +static inline void > +set_tcb_field_ulp(struct cpl_set_tcb_field *req, unsigned int tid, > + unsigned int word, u64 mask, u64 val) > +{ > + struct ulp_txpkt *txpkt = (struct ulp_txpkt *)req; > + > + txpkt->cmd_dest = htonl(V_ULPTX_CMD(ULP_TXPKT)); > + txpkt->len = htonl(V_ULPTX_NFLITS(sizeof(*req) / 8)); > + mk_set_tcb_field(req, tid, word, mask, val); > +} > > MODULE_DEPEND(if_cxgb, cxgb_t3fw, 1, 1, 1); > > Modified: head/usr.sbin/cxgbtool/cxgbtool.c > ============================================================================== > --- head/usr.sbin/cxgbtool/cxgbtool.c Wed May 5 00:39:50 2010 (r207642) > +++ head/usr.sbin/cxgbtool/cxgbtool.c Wed May 5 00:41:40 2010 (r207643) > @@ -1,6 +1,6 @@ > /************************************************************************** > > -Copyright (c) 2007-2009, Chelsio Inc. > +Copyright (c) 2007-2010, Chelsio Inc. > All rights reserved. > > Redistribution and use in source and binary forms, with or without > @@ -93,6 +93,9 @@ usage(FILE *fp) > "\tclearstats clear MAC statistics\n" > "\tcontext show an SGE context\n" > "\tdesc [] dump SGE descriptors\n" > + "\tfilter [ ] ... set a filter\n" > + "\tfilter delete|clear delete a filter\n" > + "\tfilter list list all filters\n" > "\tioqs dump uP IOQs\n" > "\tla dump uP logic analyzer info\n" > "\tloadboot download boot image\n" > @@ -1177,24 +1180,25 @@ parse_ipaddr(const char *s, uint32_t *ad > * Parse a string containing a value and an optional colon separated mask. > */ > static int > -parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask) > +parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask, > + uint32_t default_mask) > { > char *p; > > - *mask = 0xffffffffU; > + *mask = default_mask; > *val = strtoul(s, &p, 0); > - if (p == s) > + if (p == s || *val > default_mask) > return -1; > if (*p == ':' && p[1]) > *mask = strtoul(p + 1, &p, 0); > - return *p ? -1 : 0; > + return *p || *mask > default_mask ? -1 : 0; > } > > static int > parse_trace_param(const char *s, uint32_t *val, uint32_t *mask) > { > return strchr(s, '.') ? parse_ipaddr(s, val, mask) : > - parse_val_mask_param(s, val, mask); > + parse_val_mask_param(s, val, mask, 0xffffffffU); > } > > static int > @@ -1273,6 +1277,174 @@ trace_config(int argc, char *argv[], int > return 0; > } > > +static void > +show_filters(const char *iff_name) > +{ > + static const char *pkt_type[] = { "*", "tcp", "udp", "frag" }; > + struct ch_filter op; > + union { > + uint32_t nip; > + uint8_t octet[4]; > + } nsip, ndip; > + char sip[20], dip[20]; > + int header = 0; > + > + bzero(&op, sizeof(op)); > + op.filter_id = 0xffffffff; > + > + do { > + if (doit(iff_name, CHELSIO_GET_FILTER, &op) < 0) > + err(1, "list filters"); > + > + if (op.filter_id == 0xffffffff) > + break; > + > + if (!header) { > + printf("index SIP DIP sport " > + "dport VLAN PRI P/MAC type Q\n"); > + header = 1; > + } > + > + nsip.nip = htonl(op.val.sip); > + ndip.nip = htonl(op.val.dip); > + > + sprintf(sip, "%u.%u.%u.%u/%-2u", nsip.octet[0], nsip.octet[1], > + nsip.octet[2], nsip.octet[3], > + op.mask.sip ? 33 - ffs(op.mask.sip) : 0); > + sprintf(dip, "%u.%u.%u.%u", ndip.octet[0], ndip.octet[1], > + ndip.octet[2], ndip.octet[3]); > + printf("%5zu %18s %15s ", (size_t)op.filter_id, sip, dip); > + printf(op.val.sport ? "%5u " : " * ", op.val.sport); > + printf(op.val.dport ? "%5u " : " * ", op.val.dport); > + printf(op.val.vlan != 0xfff ? "%4u " : " * ", op.val.vlan); > + printf(op.val.vlan_prio == 7 ? " * " : > + "%1u/%1u ", op.val.vlan_prio, op.val.vlan_prio | 1); > + if (op.mac_addr_idx == 0xffff) > + printf("*/* "); > + else if (op.mac_hit) > + printf("%1u/%3u ", (op.mac_addr_idx >> 3) & 0x1, > + (op.mac_addr_idx) & 0x7); > + else > + printf("%1u/ * ", (op.mac_addr_idx >> 3) & 0x1); > + printf("%4s ", pkt_type[op.proto]); > + if (!op.pass) > + printf("-\n"); > + else if (op.rss) > + printf("*\n"); > + else > + printf("%1u\n", op.qset); > + } while (1); > +} > + > +static int > +filter_config(int argc, char *argv[], int start_arg, const char *iff_name) > +{ > + int ret = 0; > + uint32_t val, mask; > + struct ch_filter op; > + > + if (argc < start_arg + 1) > + return -1; > + > + memset(&op, 0, sizeof(op)); > + op.mac_addr_idx = 0xffff; > + op.rss = 1; > + > + if (argc == start_arg + 1 && !strcmp(argv[start_arg], "list")) { > + show_filters(iff_name); > + return 0; > + } > + > + if (get_int_arg(argv[start_arg++], &op.filter_id)) > + return -1; > + if (argc == start_arg + 1 && (!strcmp(argv[start_arg], "delete") || > + !strcmp(argv[start_arg], "clear"))) { > + if (doit(iff_name, CHELSIO_DEL_FILTER, &op) < 0) { > + if (errno == EBUSY) > + err(1, "no filter support when offload in use"); > + err(1, "delete filter"); > + } > + return 0; > + } > + > + while (start_arg + 2 <= argc) { > + if (!strcmp(argv[start_arg], "sip")) { > + ret = parse_ipaddr(argv[start_arg + 1], &op.val.sip, > + &op.mask.sip); > + } else if (!strcmp(argv[start_arg], "dip")) { > + ret = parse_ipaddr(argv[start_arg + 1], &op.val.dip, > + &op.mask.dip); > + } else if (!strcmp(argv[start_arg], "sport")) { > + ret = parse_val_mask_param(argv[start_arg + 1], > + &val, &mask, 0xffff); > + op.val.sport = val; > + op.mask.sport = mask; > + } else if (!strcmp(argv[start_arg], "dport")) { > + ret = parse_val_mask_param(argv[start_arg + 1], > + &val, &mask, 0xffff); > + op.val.dport = val; > + op.mask.dport = mask; > + } else if (!strcmp(argv[start_arg], "vlan")) { > + ret = parse_val_mask_param(argv[start_arg + 1], > + &val, &mask, 0xfff); > + op.val.vlan = val; > + op.mask.vlan = mask; > + } else if (!strcmp(argv[start_arg], "prio")) { > + ret = parse_val_mask_param(argv[start_arg + 1], > + &val, &mask, 7); > + op.val.vlan_prio = val; > + op.mask.vlan_prio = mask; > + } else if (!strcmp(argv[start_arg], "mac")) { > + if (!strcmp(argv[start_arg + 1], "none")) > + val = -1; > + else > + ret = get_int_arg(argv[start_arg + 1], &val); > + op.mac_hit = val != (uint32_t)-1; > + op.mac_addr_idx = op.mac_hit ? val : 0; > + } else if (!strcmp(argv[start_arg], "type")) { > + if (!strcmp(argv[start_arg + 1], "tcp")) > + op.proto = 1; > + else if (!strcmp(argv[start_arg + 1], "udp")) > + op.proto = 2; > + else if (!strcmp(argv[start_arg + 1], "frag")) > + op.proto = 3; > + else > + errx(1, "unknown type \"%s\"; must be one of " > + "\"tcp\", \"udp\", or \"frag\"", > + argv[start_arg + 1]); > + } else if (!strcmp(argv[start_arg], "queue")) { > + ret = get_int_arg(argv[start_arg + 1], &val); > + op.qset = val; > + op.rss = 0; > + } else if (!strcmp(argv[start_arg], "action")) { > + if (!strcmp(argv[start_arg + 1], "pass")) > + op.pass = 1; > + else if (strcmp(argv[start_arg + 1], "drop")) > + errx(1, "unknown action \"%s\"; must be one of " > + "\"pass\" or \"drop\"", > + argv[start_arg + 1]); > + } else > + errx(1, "unknown filter parameter \"%s\"\n" > + "known parameters are \"mac\", \"sip\", " > + "\"dip\", \"sport\", \"dport\", \"vlan\", " > + "\"prio\", \"type\", \"queue\", and \"action\"", > + argv[start_arg]); > + if (ret < 0) > + errx(1, "bad value \"%s\" for parameter \"%s\"", > + argv[start_arg + 1], argv[start_arg]); > + start_arg += 2; > + } > + if (start_arg != argc) > + errx(1, "no value for \"%s\"", argv[start_arg]); > + > + if (doit(iff_name, CHELSIO_SET_FILTER, &op) < 0) { > + if (errno == EBUSY) > + err(1, "no filter support when offload in use"); > + err(1, "set filter"); > + } > + > + return 0; > +} > static int > get_sched_param(int argc, char *argv[], int pos, unsigned int *valp) > { > @@ -1501,6 +1673,8 @@ run_cmd(int argc, char *argv[], const ch > r = pktsched(argc, argv, 3, iff_name); > else if (!strcmp(argv[2], "tcb")) > r = get_tcb2(argc, argv, 3, iff_name); > + else if (!strcmp(argv[2], "filter")) > + r = filter_config(argc, argv, 3, iff_name); > else if (!strcmp(argv[2], "clearstats")) > r = clear_stats(argc, argv, 3, iff_name); > else if (!strcmp(argv[2], "la")) >