diff --git a/sbin/ifconfig/ifbridge.c b/sbin/ifconfig/ifbridge.c index 335ed9bf513f..3566acdcf54c 100644 --- a/sbin/ifconfig/ifbridge.c +++ b/sbin/ifconfig/ifbridge.c @@ -1,950 +1,968 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifconfig.h" static const char *stpstates[] = { STP_STATES }; static const char *stpproto[] = { STP_PROTOS }; static const char *stproles[] = { STP_ROLES }; static int get_val(const char *cp, u_long *valp) { char *endptr; u_long val; errno = 0; val = strtoul(cp, &endptr, 0); if (cp[0] == '\0' || endptr[0] != '\0' || errno == ERANGE) return (-1); *valp = val; return (0); } static int get_vlan_id(const char *cp, ether_vlanid_t *valp) { u_long val; if (get_val(cp, &val) == -1) return (-1); if (val < DOT1Q_VID_MIN || val > DOT1Q_VID_MAX) return (-1); *valp = (ether_vlanid_t)val; return (0); } static int do_cmd(if_ctx *ctx, u_long op, void *arg, size_t argsize, int set) { struct ifdrv ifd = {}; strlcpy(ifd.ifd_name, ctx->ifname, sizeof(ifd.ifd_name)); ifd.ifd_cmd = op; ifd.ifd_len = argsize; ifd.ifd_data = arg; return (ioctl_ctx(ctx, set ? SIOCSDRVSPEC : SIOCGDRVSPEC, &ifd)); } static void do_bridgeflag(if_ctx *ctx, const char *ifs, int flag, int set) { struct ifbreq req; strlcpy(req.ifbr_ifsname, ifs, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGGIFFLGS, &req, sizeof(req), 0) < 0) err(1, "unable to get bridge flags"); if (set) req.ifbr_ifsflags |= flag; else req.ifbr_ifsflags &= ~flag; if (do_cmd(ctx, BRDGSIFFLGS, &req, sizeof(req), 1) < 0) err(1, "unable to set bridge flags"); } static void bridge_addresses(if_ctx *ctx, const char *prefix) { struct ifbaconf ifbac; struct ifbareq *ifba; char *inbuf = NULL, *ninbuf; size_t len = 8192; struct ether_addr ea; for (;;) { ninbuf = realloc(inbuf, len); if (ninbuf == NULL) err(1, "unable to allocate address buffer"); ifbac.ifbac_len = len; ifbac.ifbac_buf = inbuf = ninbuf; if (do_cmd(ctx, BRDGRTS, &ifbac, sizeof(ifbac), 0) < 0) err(1, "unable to get address cache"); if ((ifbac.ifbac_len + sizeof(*ifba)) < len) break; len *= 2; } for (unsigned long i = 0; i < ifbac.ifbac_len / sizeof(*ifba); i++) { ifba = ifbac.ifbac_req + i; memcpy(ea.octet, ifba->ifba_dst, sizeof(ea.octet)); printf("%s%s Vlan%d %s %lu ", prefix, ether_ntoa(&ea), ifba->ifba_vlan, ifba->ifba_ifsname, ifba->ifba_expire); printb("flags", ifba->ifba_flags, IFBAFBITS); printf("\n"); } free(inbuf); } static void print_vlans(ifbvlan_set_t *vlans) { unsigned printed = 0; for (unsigned vlan = DOT1Q_VID_MIN; vlan <= DOT1Q_VID_MAX;) { unsigned last; if (!BRVLAN_TEST(vlans, vlan)) { ++vlan; continue; } last = vlan; while (last < DOT1Q_VID_MAX && BRVLAN_TEST(vlans, last + 1)) ++last; if (printed == 0) printf(" tagged "); else printf(","); printf("%u", vlan); if (last != vlan) printf("-%u", last); ++printed; vlan = last + 1; } } static void bridge_status(if_ctx *ctx) { struct ifconfig_bridge_status *bridge; struct ifbropreq *params; const char *pad, *prefix; uint8_t lladdr[ETHER_ADDR_LEN]; uint16_t bprio; if (ifconfig_bridge_get_bridge_status(lifh, ctx->ifname, &bridge) == -1) return; params = bridge->params; PV2ID(params->ifbop_bridgeid, bprio, lladdr); printf("\tid %s priority %u hellotime %u fwddelay %u\n", ether_ntoa((struct ether_addr *)lladdr), params->ifbop_priority, params->ifbop_hellotime, params->ifbop_fwddelay); printf("\tmaxage %u holdcnt %u proto %s maxaddr %u timeout %u\n", params->ifbop_maxage, params->ifbop_holdcount, stpproto[params->ifbop_protocol], bridge->cache_size, bridge->cache_lifetime); PV2ID(params->ifbop_designated_root, bprio, lladdr); printf("\troot id %s priority %d ifcost %u port %u\n", ether_ntoa((struct ether_addr *)lladdr), bprio, params->ifbop_root_path_cost, params->ifbop_root_port & 0xfff); printb("\tbridge flags", bridge->flags, IFBRFBITS); if (bridge->defpvid) printf(" defuntagged=%u", (unsigned) bridge->defpvid); printf("\n"); prefix = "\tmember: "; pad = "\t "; for (size_t i = 0; i < bridge->members_count; ++i) { struct ifbreq *member = &bridge->members[i]; printf("%s%s ", prefix, member->ifbr_ifsname); printb("flags", member->ifbr_ifsflags, IFBIFBITS); printf("\n%s", pad); if (member->ifbr_addrmax != 0) printf("ifmaxaddr %u ", member->ifbr_addrmax); printf("port %u priority %u path cost %u", member->ifbr_portno, member->ifbr_priority, member->ifbr_path_cost); if (member->ifbr_ifsflags & IFBIF_STP) { uint8_t proto = member->ifbr_proto; uint8_t role = member->ifbr_role; uint8_t state = member->ifbr_state; if (proto < nitems(stpproto)) printf(" proto %s", stpproto[proto]); else printf(" ", proto); printf("\n%s", pad); if (role < nitems(stproles)) printf("role %s", stproles[role]); else printf("", role); if (state < nitems(stpstates)) printf(" state %s", stpstates[state]); else printf(" ", state); } if (member->ifbr_pvid != 0) printf(" untagged %u", (unsigned)member->ifbr_pvid); print_vlans(&bridge->member_vlans[i]); printf("\n"); } ifconfig_bridge_free_bridge_status(bridge); } static void setbridge_add(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADD, &req, sizeof(req), 1) < 0) err(1, "BRDGADD %s", val); } static void setbridge_delete(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDEL, &req, sizeof(req), 1) < 0) err(1, "BRDGDEL %s", val); } static void setbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 1); } static void unsetbridge_discover(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_DISCOVER, 0); } static void setbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 1); } static void unsetbridge_learn(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_LEARNING, 0); } static void setbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 1); } static void unsetbridge_sticky(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STICKY, 0); } static void setbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGADDS, &req, sizeof(req), 1) < 0) err(1, "BRDGADDS %s", val); } static void unsetbridge_span(if_ctx *ctx, const char *val, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, val, sizeof(req.ifbr_ifsname)); if (do_cmd(ctx, BRDGDELS, &req, sizeof(req), 1) < 0) err(1, "BRDGDELS %s", val); } static void setbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 1); } static void unsetbridge_stp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_STP, 0); } static void setbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 1); } static void unsetbridge_edge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_EDGE, 0); } static void setbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 1); } static void unsetbridge_autoedge(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOEDGE, 0); } static void setbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 1); } static void unsetbridge_ptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_PTP, 0); } static void setbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 1); } static void unsetbridge_autoptp(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_BSTP_AUTOPTP, 0); } static void setbridge_flush(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHDYN; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static void setbridge_flushall(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); req.ifbr_ifsflags = IFBF_FLUSHALL; if (do_cmd(ctx, BRDGFLUSH, &req, sizeof(req), 1) < 0) err(1, "BRDGFLUSH"); } static int setbridge_static(if_ctx *ctx, int argc, const char *const *argv) { struct ifbareq req; struct ether_addr *ea; int arg; if (argc < 2) errx(1, "usage: static
[vlan ]"); arg = 0; memset(&req, 0, sizeof(req)); req.ifba_flags = IFBAF_STATIC; strlcpy(req.ifba_ifsname, argv[arg], sizeof(req.ifba_ifsname)); ++arg; ea = ether_aton(argv[arg]); if (ea == NULL) errx(1, "invalid address: %s", argv[arg]); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); ++arg; req.ifba_vlan = 0; if (argc > 2 && strcmp(argv[arg], "vlan") == 0) { if (argc < 3) errx(1, "usage: static
" "[vlan ]"); ++arg; if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0) errx(1, "invalid vlan id: %s", argv[arg]); ++arg; } if (do_cmd(ctx, BRDGSADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGSADDR"); return arg; } static int setbridge_deladdr(if_ctx *ctx, int argc, const char *const *argv) { struct ifbareq req; struct ether_addr *ea; int arg; if (argc < 1) errx(1, "usage: deladdr
[vlan ]"); arg = 0; memset(&req, 0, sizeof(req)); ea = ether_aton(argv[arg]); if (ea == NULL) errx(1, "invalid address: %s", argv[arg]); memcpy(req.ifba_dst, ea->octet, sizeof(req.ifba_dst)); ++arg; req.ifba_vlan = 0; if (argc >= 2 && strcmp(argv[arg], "vlan") == 0) { if (argc < 3) errx(1, "usage: deladdr
[vlan ]"); ++arg; if (get_vlan_id(argv[arg], &req.ifba_vlan) < 0) errx(1, "invalid vlan id: %s", argv[arg]); ++arg; } if (do_cmd(ctx, BRDGDADDR, &req, sizeof(req), 1) < 0) err(1, "BRDGDADDR"); return arg; } static void setbridge_addr(if_ctx *ctx, const char *val __unused, int dummy __unused) { bridge_addresses(ctx, ""); } static void setbridge_maxaddr(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_csize = val & 0xffffffff; if (do_cmd(ctx, BRDGSCACHE, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSCACHE %s", arg); } static void setbridge_hellotime(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_hellotime = val & 0xff; if (do_cmd(ctx, BRDGSHT, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSHT %s", arg); } static void setbridge_fwddelay(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_fwddelay = val & 0xff; if (do_cmd(ctx, BRDGSFD, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSFD %s", arg); } static void setbridge_maxage(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_maxage = val & 0xff; if (do_cmd(ctx, BRDGSMA, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSMA %s", arg); } static void setbridge_priority(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_prio = val & 0xffff; if (do_cmd(ctx, BRDGSPRI, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPRI %s", arg); } static void setbridge_protocol(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; if (strcasecmp(arg, "stp") == 0) { param.ifbrp_proto = 0; } else if (strcasecmp(arg, "rstp") == 0) { param.ifbrp_proto = 2; } else { errx(1, "unknown stp protocol"); } if (do_cmd(ctx, BRDGSPROTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSPROTO %s", arg); } static void setbridge_holdcount(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_txhc = val & 0xff; if (do_cmd(ctx, BRDGSTXHC, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTXHC %s", arg); } static void setbridge_ifpriority(if_ctx *ctx, const char *ifn, const char *pri) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(pri, &val) < 0 || (val & ~0xff) != 0) errx(1, "invalid value: %s", pri); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_priority = val & 0xff; if (do_cmd(ctx, BRDGSIFPRIO, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFPRIO %s", pri); } static void setbridge_ifpathcost(if_ctx *ctx, const char *ifn, const char *cost) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(cost, &val) < 0) errx(1, "invalid value: %s", cost); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_path_cost = val; if (do_cmd(ctx, BRDGSIFCOST, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFCOST %s", cost); } static void setbridge_untagged(if_ctx *ctx, const char *ifn, const char *vlanid) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); if (get_vlan_id(vlanid, &req.ifbr_pvid) < 0) errx(1, "invalid VLAN identifier: %s", vlanid); if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFPVID %s", vlanid); } static void unsetbridge_untagged(if_ctx *ctx, const char *ifn, int dummy __unused) { struct ifbreq req; memset(&req, 0, sizeof(req)); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_pvid = 0; if (do_cmd(ctx, BRDGSIFPVID, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFPVID"); } static void setbridge_ifmaxaddr(if_ctx *ctx, const char *ifn, const char *arg) { struct ifbreq req; u_long val; memset(&req, 0, sizeof(req)); if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); strlcpy(req.ifbr_ifsname, ifn, sizeof(req.ifbr_ifsname)); req.ifbr_addrmax = val & 0xffffffff; if (do_cmd(ctx, BRDGSIFAMAX, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFAMAX %s", arg); } static void setbridge_timeout(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam param; u_long val; if (get_val(arg, &val) < 0 || (val & ~0xffffffff) != 0) errx(1, "invalid value: %s", arg); param.ifbrp_ctime = val & 0xffffffff; if (do_cmd(ctx, BRDGSTO, ¶m, sizeof(param), 1) < 0) err(1, "BRDGSTO %s", arg); } static void setbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 1); } static void unsetbridge_private(if_ctx *ctx, const char *val, int dummy __unused) { do_bridgeflag(ctx, val, IFBIF_PRIVATE, 0); } static int parse_vlans(ifbvlan_set_t *set, const char *str) { char *s, *token; /* "none" means the empty vlan set */ if (strcmp(str, "none") == 0) { __BIT_ZERO(BRVLAN_SETSIZE, set); return (0); } /* "all" means all vlans, except for 0 and 4095 which are reserved */ if (strcmp(str, "all") == 0) { __BIT_FILL(BRVLAN_SETSIZE, set); BRVLAN_CLR(set, DOT1Q_VID_NULL); BRVLAN_CLR(set, DOT1Q_VID_RSVD_IMPL); return (0); } if ((s = strdup(str)) == NULL) return (-1); while ((token = strsep(&s, ",")) != NULL) { unsigned long first, last; char *p, *lastp; if ((lastp = strchr(token, '-')) != NULL) *lastp++ = '\0'; first = last = strtoul(token, &p, 10); if (*p != '\0') goto err; if (first < DOT1Q_VID_MIN || first > DOT1Q_VID_MAX) goto err; if (lastp) { last = strtoul(lastp, &p, 10); if (*p != '\0') goto err; if (last < DOT1Q_VID_MIN || last > DOT1Q_VID_MAX || last < first) goto err; } for (unsigned vlan = first; vlan <= last; ++vlan) BRVLAN_SET(set, vlan); } free(s); return (0); err: free(s); return (-1); } static void set_bridge_vlanset(if_ctx *ctx, const char *ifn, const char *vlans, int op) { struct ifbif_vlan_req req; memset(&req, 0, sizeof(req)); if (parse_vlans(&req.bv_set, vlans) != 0) errx(1, "invalid vlan set: %s", vlans); strlcpy(req.bv_ifname, ifn, sizeof(req.bv_ifname)); req.bv_op = op; if (do_cmd(ctx, BRDGSIFVLANSET, &req, sizeof(req), 1) < 0) err(1, "BRDGSIFVLANSET %s", vlans); } static void setbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans) { set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_SET); } static void addbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans) { set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_ADD); } static void delbridge_tagged(if_ctx *ctx, const char *ifn, const char *vlans) { set_bridge_vlanset(ctx, ifn, vlans, BRDG_VLAN_OP_DEL); } static void setbridge_flags(if_ctx *ctx, const char *val __unused, int newflags) { struct ifbrparam req; if (do_cmd(ctx, BRDGGFLAGS, &req, sizeof(req), 0) < 0) err(1, "BRDGGFLAGS"); req.ifbrp_flags |= (uint32_t)newflags; if (do_cmd(ctx, BRDGSFLAGS, &req, sizeof(req), 1) < 0) err(1, "BRDGSFLAGS"); } static void unsetbridge_flags(if_ctx *ctx, const char *val __unused, int newflags) { struct ifbrparam req; if (do_cmd(ctx, BRDGGFLAGS, &req, sizeof(req), 0) < 0) err(1, "BRDGGFLAGS"); req.ifbrp_flags &= ~(uint32_t)newflags; if (do_cmd(ctx, BRDGSFLAGS, &req, sizeof(req), 1) < 0) err(1, "BRDGSFLAGS"); } static void setbridge_defuntagged(if_ctx *ctx, const char *arg, int dummy __unused) { struct ifbrparam req; memset(&req, 0, sizeof(req)); if (get_vlan_id(arg, &req.ifbrp_defpvid) < 0) errx(1, "invalid vlan id: %s", arg); if (do_cmd(ctx, BRDGSDEFPVID, &req, sizeof(req), 1) < 0) err(1, "BRDGSDEFPVID"); } static void unsetbridge_defuntagged(if_ctx *ctx, const char *val __unused, int dummy __unused) { struct ifbrparam req; memset(&req, 0, sizeof(req)); req.ifbrp_defpvid = 0; if (do_cmd(ctx, BRDGSDEFPVID, &req, sizeof(req), 1) < 0) err(1, "BRDGSDEFPVID"); } +static void +setbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused) +{ + do_bridgeflag(ctx, val, IFBIF_QINQ, 1); +} + +static void +unsetbridge_qinq(if_ctx *ctx, const char *val, int dummy __unused) +{ + do_bridgeflag(ctx, val, IFBIF_QINQ, 0); +} + static struct cmd bridge_cmds[] = { DEF_CMD_ARG("addm", setbridge_add), DEF_CMD_ARG("deletem", setbridge_delete), DEF_CMD_ARG("discover", setbridge_discover), DEF_CMD_ARG("-discover", unsetbridge_discover), DEF_CMD_ARG("learn", setbridge_learn), DEF_CMD_ARG("-learn", unsetbridge_learn), DEF_CMD_ARG("sticky", setbridge_sticky), DEF_CMD_ARG("-sticky", unsetbridge_sticky), DEF_CMD_ARG("span", setbridge_span), DEF_CMD_ARG("-span", unsetbridge_span), DEF_CMD_ARG("stp", setbridge_stp), DEF_CMD_ARG("-stp", unsetbridge_stp), DEF_CMD_ARG("edge", setbridge_edge), DEF_CMD_ARG("-edge", unsetbridge_edge), DEF_CMD_ARG("autoedge", setbridge_autoedge), DEF_CMD_ARG("-autoedge", unsetbridge_autoedge), DEF_CMD_ARG("ptp", setbridge_ptp), DEF_CMD_ARG("-ptp", unsetbridge_ptp), DEF_CMD_ARG("autoptp", setbridge_autoptp), DEF_CMD_ARG("-autoptp", unsetbridge_autoptp), DEF_CMD("flush", 0, setbridge_flush), DEF_CMD("flushall", 0, setbridge_flushall), DEF_CMD_VARG("static", setbridge_static), DEF_CMD_VARG("deladdr", setbridge_deladdr), DEF_CMD("addr", 1, setbridge_addr), DEF_CMD_ARG("maxaddr", setbridge_maxaddr), DEF_CMD_ARG("hellotime", setbridge_hellotime), DEF_CMD_ARG("fwddelay", setbridge_fwddelay), DEF_CMD_ARG("maxage", setbridge_maxage), DEF_CMD_ARG("priority", setbridge_priority), DEF_CMD_ARG("proto", setbridge_protocol), DEF_CMD_ARG("holdcnt", setbridge_holdcount), DEF_CMD_ARG2("ifpriority", setbridge_ifpriority), DEF_CMD_ARG2("ifpathcost", setbridge_ifpathcost), DEF_CMD_ARG2("ifmaxaddr", setbridge_ifmaxaddr), DEF_CMD_ARG2("untagged", setbridge_untagged), DEF_CMD_ARG("-untagged", unsetbridge_untagged), DEF_CMD_ARG2("tagged", setbridge_tagged), DEF_CMD_ARG2("+tagged", addbridge_tagged), DEF_CMD_ARG2("-tagged", delbridge_tagged), DEF_CMD_ARG("timeout", setbridge_timeout), DEF_CMD_ARG("private", setbridge_private), DEF_CMD_ARG("-private", unsetbridge_private), DEF_CMD("vlanfilter", (int32_t)IFBRF_VLANFILTER, setbridge_flags), DEF_CMD("-vlanfilter", (int32_t)IFBRF_VLANFILTER, unsetbridge_flags), DEF_CMD_ARG("defuntagged", setbridge_defuntagged), DEF_CMD("-defuntagged", 0, unsetbridge_defuntagged), + DEF_CMD("defqinq", (int32_t)IFBRF_DEFQINQ, + setbridge_flags), + DEF_CMD("-defqinq", (int32_t)IFBRF_DEFQINQ, + unsetbridge_flags), + DEF_CMD_ARG("qinq", setbridge_qinq), + DEF_CMD_ARG("-qinq", unsetbridge_qinq), }; static struct afswtch af_bridge = { .af_name = "af_bridge", .af_af = AF_UNSPEC, .af_other_status = bridge_status, }; static __constructor void bridge_ctor(void) { for (size_t i = 0; i < nitems(bridge_cmds); i++) cmd_register(&bridge_cmds[i]); af_register(&af_bridge); } diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index 06ec62197fba..69a81f72421f 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,3400 +1,3418 @@ .\"- .\" SPDX-License-Identifier: BSD-3-Clause .\" .\" Copyright (c) 1983, 1991, 1993 .\" The Regents of the University of California. 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. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. .\" -.Dd July 30, 2025 +.Dd August 5, 2025 .Dt IFCONFIG 8 .Os .Sh NAME .Nm ifconfig .Nd configure network interface parameters .Sh SYNOPSIS .Nm .Op Fl j Ar jail .Op Fl DkLmn .Op Fl f Ar type Ns Cm \&: Ns Ar format .Ar interface .Op Cm create .Oo .Ar address_family .Oo .Ar address .Op Ar dest_address .Oc .Oc .Op Ar parameters .Nm .Op Fl j Ar jail .Ar interface .Cm destroy .Nm .Op Fl j Ar jail .Fl a .Op Fl dDkLmuv .Op Fl f Ar type Ns Cm \&: Ns Ar format .Op Fl G Ar groupname .Op Fl g Ar groupname .Op Ar address_family .Nm .Fl C .Nm .Op Fl j Ar jail .Fl g Ar groupname .Nm .Op Fl j Ar jail .Fl l .Op Fl du .Op Fl g Ar groupname .Op Ar address_family .Nm .Op Fl j Ar jail .Op Fl dkLmuv .Op Fl f Ar type Ns Cm \&: Ns Ar format .Sh DESCRIPTION The .Nm utility is used to assign an address to a network interface and/or configure network interface parameters. The .Nm utility must be used at boot time to define the network address of each interface present on a machine; it may also be used at a later time to redefine an interface's address or other operating parameters. .Pp The following options are available: .Bl -tag -width indent .It Fl a Display information about all interfaces in the system. .Pp The .Fl a flag may be used instead of the .Ar interface argument. .It Fl C List all the interface cloners available on the system, with no additional information. Use of this flag is mutually exclusive with all other flags and commands. .It Fl d Display only the interfaces that are down. .It Fl D Include the driver name and unit number of the interface in the output. This is normally the original name of the interface, even if it has been renamed; it may differ from the original name in some cases, such as .Xr epair 4 . .It Fl f Xo .Ar type Ns Cm \&: Ns Ar format Ns .Op Cm \&, Ns Ar type Ns Cm \&: Ns Ar format Ar ... .Xc Control the output format of .Nm . The format is specified as a comma-separated list of .Ar type Ns Cm \&: Ns Ar format pairs .Po see the .Sx EXAMPLES section for more information .Pc . .Pp The output format can also be specified via the .Ev IFCONFIG_FORMAT environment variable. The .Fl f flag can be supplied multiple times. .Pp The .Ar type Ns s and their associated .Ar format strings are: .Pp .Bl -tag -width default .It Cm addr Adjust the display of inet and inet6 addresses: .Pp .Bl -tag -width default -compact .It Cm default Default format, .Cm numeric .It Cm fqdn Fully qualified domain names .Pq FQDN .It Cm host Unqualified hostnames .It Cm numeric Numeric format .El .It Cm ether Adjust the display of link-level ethernet (MAC) addresses: .Pp .Bl -tag -width default -compact .It Cm colon Separate address segments with a colon .It Cm dash Separate address segments with a dash .It Cm dotted Dotted notation, for example: .Ql 5254.0015.4a3b .It Cm default Default format, .Cm colon .El .It Cm inet Adjust the display of inet address subnet masks: .Pp .Bl -tag -width default -compact .It Cm cidr CIDR notation, for example: .Ql 203.0.113.224/26 .It Cm default Default format, .Cm hex .It Cm dotted Dotted quad notation, for example: .Ql 255.255.255.192 .It Cm hex Hexadecimal format, for example: .Ql 0xffffffc0 .El .It Cm inet6 Adjust the display of inet6 address prefixes (subnet masks): .Pp .Bl -tag -width default -compact .It Cm cidr CIDR notation, for example: .Ql ::1/128 or .Ql fe80::1%lo0/64 .It Cm default Default format, .Cm numeric .It Cm numeric Integer format, for example: .Ql prefixlen 64 .El .El .Pp In addition, the following shortcuts are accepted: .Bl -tag -width default .It Cm default Resets all formats to their default values. .It Cm cidr Shortcut notation for .Cm inet:cidr,inet6:cidr . .El .Pp .It Fl G Ar groupname Exclude members of the specified .Ar groupname from the output. .Pp Only one .Fl G option should be specified as later ones override earlier ones. .Ar groupname may contain shell patterns in which case it should be quoted. .Pp Setting .Ar groupname to .Cm all selects all interfaces. .It Fl g Ar groupname Limit the output to the members of the specified .Ar groupname . .Pp If .Fl g is specified before other significant flags like, e.g., .Fl a , .Fl l , or .Fl C , then .Nm lists names of interfaces belonging to .Ar groupname . Any other flags and arguments are ignored in this case. .Pp Only one .Fl g option should be specified as later ones override earlier ones. .Ar groupname may contain shell patterns in which case it should be quoted. .Pp Setting .Ar groupname to .Cm all selects all interfaces. .It Fl j Ar jail Perform the actions inside the .Ar jail . .Pp The .Cm ifconfig will first attach to the .Ar jail (by jail id or jail name) before performing the effects. .Pp This allow network interfaces of .Ar jail to be configured even if the .Cm ifconfig binary is not available in .Ar jail . .It Fl k Print keying information for the .Ar interface , if available. .Pp For example, the values of 802.11 WEP keys and .Xr carp 4 passphrases will be printed, if accessible to the current user. .Pp This information is not printed by default, as it may be considered sensitive. .It Fl L Display address lifetime for IPv6 addresses as time offset string. .It Fl l List all available interfaces on the system, with no other additional information. .Pp If an .Ar address_family is specified, only interfaces of that type will be listed. .Pp If the .Ar address_family is set to .Cm ether , then .Fl l will exclude loopback interfaces from the list of Ethernet interfaces. This is a special case, because all the other synonyms of the .Cm link address family will include loopback interfaces in the list. .Pp Use of this flag is mutually exclusive with all other flags and commands, except for .Fl d , .Fl g , and .Fl u . .It Fl m Display the capability list and all of the supported media for the specified interface. .It Fl n Disable automatic loading of network interface drivers. .Pp By default if the network interface driver is not present in the kernel then .Nm will attempt to load it. .It Fl u Display only the interfaces that are up. .It Fl v Get more verbose status for an interface. .It Ar address For the inet family, the address is either a host name present in the host name data base, .Xr hosts 5 , or an IPv4 address expressed in the Internet standard .Dq dot notation . .Pp It is also possible to use the CIDR notation (also known as the slash notation) to include the netmask. That is, one can specify an address like .Li 192.168.0.1/16 . .Pp For the .Cm inet6 family, it is also possible to specify the prefix length using the slash notation, like .Li ::1/128 . See the .Cm prefixlen parameter below for more information. .Pp The link-level .Pq Cm link address is specified as a series of colon-separated hex digits. This can be used to, for example, set a new MAC address on an Ethernet interface, though the mechanism used is not Ethernet specific. .Pp Use the .Cm random keyword to set a randomly generated MAC address. A randomly-generated MAC address might be the same as one already in use in the network. Such duplications are extremely unlikely. .Pp If the interface is already up when the link-level address is modified, it will be briefly brought down and then brought back up again in order to ensure that the receive filter in the underlying Ethernet hardware is properly reprogrammed. .It Ar address_family Specify the address family which affects interpretation of the remaining parameters. Since an interface can receive transmissions in differing protocols with different naming schemes, specifying the address family is recommended. The address or protocol families currently supported are: .Bl -tag .It Cm ether Synonymous with .Cm link .Po with some exceptions, see .Fl l .Pc . .It Cm inet Default, if available. .It Cm inet6 .It Cm link Default, if .Cm inet is not available. .It Cm lladdr Synonymous with .Cm link . .El .It Ar dest_address Specify the address of the correspondent on the other end of a point to point link. .It Ar interface This parameter is a string of the form .Dq name unit , for example, .Dq Li em0 . .El .Pp The .Nm utility displays the current configuration for a network interface when no optional parameters are supplied. If a protocol family is specified, .Nm will report only the details specific to that protocol family. .Pp When no arguments are given, .Fl a is implied. .Pp Only the super-user may modify the configuration of a network interface. .Sh PARAMETERS The following .Ar parameter Ns s may be set with .Nm : .Bl -tag -width indent .It Cm add Another name for the .Cm alias parameter. Introduced for compatibility with .Bsx . .It Cm alias Establish an additional network address for this interface. This is sometimes useful when changing network numbers, and one wishes to accept packets addressed to the old interface. If the address is on the same subnet as the first network address for this interface, a non-conflicting netmask must be given. Usually .Li 0xffffffff is most appropriate. .It Fl alias Remove the network address specified. This would be used if you incorrectly specified an alias, or it was no longer needed. If you have incorrectly set an NS address having the side effect of specifying the host portion, removing all NS addresses will allow you to respecify the host portion. .It Cm anycast (Inet6 only.) Specify that the address configured is an anycast address, as described in RFC 4291 section 2.6. Anycast addresses will not be used as source address of any outgoing IPv6 packets unless an application explicitly binds to the address. .It Cm arp Enable the use of the Address Resolution Protocol .Pq Xr arp 4 in mapping between network level addresses and link level addresses (default). This is currently implemented for mapping between Internet Protocol addresses and IEEE 802 48-bit MAC addresses (Ethernet addresses). .It Fl arp Disable the use of the Address Resolution Protocol .Pq Xr arp 4 . .It Cm staticarp If the Address Resolution Protocol is enabled, the host will only reply to requests for its addresses, and will never send any requests. .It Fl staticarp If the Address Resolution Protocol is enabled, the host will perform normally, sending out requests and listening for replies. .It Cm stickyarp Enable the so-called sticky ARP mode for the interface. If this option is enabled on the given interface, any resolved address is marked as a static one and never expires. This may be used to increase security of the network by preventing ARP spoofing or to reduce latency for high-performance Ethernet networks where the time needed for ARP resolution is too high. Please note that a similar feature is also provided for bridges. See the sticky option in the .Sx Bridge Interface Parameters section. Enabling this option may impact techniques which rely on ARP expiration/overwriting feature such as load-balancers or high-availabity solutions such as .Xr carp 4 . .It Fl stickyarp Disable the so-called sticky ARP mode for the interface (default). Resolved addresses will expire normally respecting the kernel ARP configuration. .It Cm broadcast (Inet only.) Specify the address to use to represent broadcasts to the network. The default broadcast address is the address with a host part of all 1's. .It Cm debug Enable driver dependent debugging code; usually, this turns on extra console error logging. .It Fl debug Disable driver dependent debugging code. .It Cm allmulti Enable promiscuous mode for multicast packets. .It Fl allmulti Disable promiscuous mode for multicast packets. .It Cm promisc Put interface into permanently promiscuous mode. .It Fl promisc Disable permanently promiscuous mode. .It Cm delete Another name for the .Fl alias parameter. .It Cm description Ar value , Cm descr Ar value Specify a description of the interface. This can be used to label interfaces in situations where they may otherwise be difficult to distinguish. .It Cm -description , Cm -descr Clear the interface description. .It Cm down Mark an interface .Dq down . When an interface is marked .Dq down , the system will not attempt to transmit messages through that interface. If possible, the interface will be reset to disable reception as well. This action does not automatically disable routes using the interface. .It Cm group Ar groupname Assign the interface to a .Dq group . The .Ar groupname may not be longer than 15 characters and must not end in a digit. Any interface can be in multiple groups. .Pp Cloned interfaces are members of their interface family group by default. For example, a VLAN interface such as .Em vlan10 is a member of the VLAN interface family group, .Em vlan . .It Cm -group Ar groupname Remove the interface from the given .Dq group . .It Cm eui64 (Inet6 only.) Fill interface index (lowermost 64bit of an IPv6 address) automatically. .It Cm fib Ar fib_number Specify interface FIB. A FIB .Ar fib_number is assigned to all frames or packets received on that interface. The FIB is not inherited, e.g., vlans or other sub-interfaces will use the default FIB (0) irrespective of the parent interface's FIB. The kernel needs to be tuned to support more than the default FIB using the .Va ROUTETABLES kernel configuration option, or the .Va net.fibs tunable. .It Cm tunnelfib Ar fib_number Specify tunnel FIB. A FIB .Ar fib_number is assigned to all packets encapsulated by tunnel interface, e.g., .Xr gif 4 , .Xr gre 4 , .Xr vxlan 4 , and .Xr wg 4 . .It Cm maclabel Ar label If Mandatory Access Control support is enabled in the kernel, set the MAC label to .Ar label . .\" (see .\" .Xr maclabel 7 ) . .It Cm media Ar type If the driver supports the media selection system, set the media type of the interface to .Ar type . Some interfaces support the mutually exclusive use of one of several different physical media connectors. For example, a 10Mbit/s Ethernet interface might support the use of either AUI or twisted pair connectors. Setting the media type to .Cm 10base5/AUI would change the currently active connector to the AUI port. Setting it to .Cm 10baseT/UTP would activate twisted pair. Refer to the interfaces' driver specific documentation or man page for a complete list of the available types. .It Cm mediaopt Ar opts If the driver supports the media selection system, set the specified media options on the interface. The .Ar opts argument is a comma delimited list of options to apply to the interface. Refer to the interfaces' driver specific man page for a complete list of available options. .It Fl mediaopt Ar opts If the driver supports the media selection system, disable the specified media options on the interface. .It Cm mode Ar mode If the driver supports the media selection system, set the specified operating mode on the interface to .Ar mode . For IEEE 802.11 wireless interfaces that support multiple operating modes this directive is used to select between 802.11a .Pq Cm 11a , 802.11b .Pq Cm 11b , and 802.11g .Pq Cm 11g operating modes. .It Cm txrtlmt Set if the driver supports TX rate limiting. .It Cm inst Ar minst , Cm instance Ar minst Set the media instance to .Ar minst . This is useful for devices which have multiple physical layer interfaces .Pq PHYs . .It Cm name Ar name Set the interface name to .Ar name . .It Cm rxcsum , txcsum , rxcsum6 , txcsum6 If the driver supports user-configurable checksum offloading, enable receive (or transmit) checksum offloading on the interface. The feature can be turned on selectively per protocol family. Use .Cm rxcsum6 , txcsum6 for .Xr ip6 4 or .Cm rxcsum , txcsum otherwise. Some drivers may not be able to enable these flags independently of each other, so setting one may also set the other. The driver will offload as much checksum work as it can reliably support, the exact level of offloading varies between drivers. .It Fl rxcsum , txcsum , rxcsum6 , txcsum6 If the driver supports user-configurable checksum offloading, disable receive (or transmit) checksum offloading on the interface. The feature can be turned off selectively per protocol family. Use .Fl rxcsum6 , txcsum6 for .Xr ip6 4 or .Fl rxcsum , txcsum otherwise. These settings may not always be independent of each other. .It Cm tso If the driver supports .Xr tcp 4 segmentation offloading, enable TSO on the interface. Some drivers may not be able to support TSO for .Xr ip 4 and .Xr ip6 4 packets, so they may enable only one of them. .It Fl tso If the driver supports .Xr tcp 4 segmentation offloading, disable TSO on the interface. It will always disable TSO for .Xr ip 4 and .Xr ip6 4 . .It Cm tso6 , tso4 If the driver supports .Xr tcp 4 segmentation offloading for .Xr ip6 4 or .Xr ip 4 use one of these to selectively enabled it only for one protocol family. .It Fl tso6 , tso4 If the driver supports .Xr tcp 4 segmentation offloading for .Xr ip6 4 or .Xr ip 4 use one of these to selectively disable it only for one protocol family. .It Cm lro If the driver supports .Xr tcp 4 large receive offloading, enable LRO on the interface. .It Fl lro If the driver supports .Xr tcp 4 large receive offloading, disable LRO on the interface. .It Cm txtls Transmit TLS offload encrypts Transport Layer Security (TLS) records and segments the encrypted record into one or more .Xr tcp 4 segments over either .Xr ip 4 or .Xr ip6 4 . If the driver supports transmit TLS offload, enable transmit TLS offload on the interface. Some drivers may not be able to support transmit TLS offload for .Xr ip 4 and .Xr ip6 4 packets, so they may enable only one of them. .It Fl txtls If the driver supports transmit TLS offload, disable transmit TLS offload on the interface. It will always disable TLS for .Xr ip 4 and .Xr ip6 4 . .It Cm txtlsrtlmt Enable use of rate limiting (packet pacing) for TLS offload. .It Fl txtlsrtlmt Disable use of rate limiting for TLS offload. .It Cm mextpg If the driver supports extended multi-page .Xr mbuf 9 buffers, enable them on the interface. .It Fl mextpg If the driver supports extended multi-page .Xr mbuf 9 buffers, disable them on the interface. .It Cm wol , wol_ucast , wol_mcast , wol_magic Enable Wake On Lan (WOL) support, if available. WOL is a facility whereby a machine in a low power state may be woken in response to a received packet. There are three types of packets that may wake a system: ucast (directed solely to the machine's mac address), mcast (directed to a broadcast or multicast address), or magic .Po unicast or multicast frames with a .Dq magic contents .Pc . Not all devices support WOL, those that do indicate the mechanisms they support in their capabilities. .Cm wol is a synonym for enabling all available WOL mechanisms. To disable WOL use .Fl wol . .It Cm vlanmtu , vlanhwtag , vlanhwfilter , vlanhwcsum , vlanhwtso If the driver offers user-configurable VLAN support, enable reception of extended frames, tag processing in hardware, frame filtering in hardware, checksum offloading, or TSO on VLAN, respectively. Note that this must be configured on a physical interface associated with .Xr vlan 4 , not on a .Xr vlan 4 interface itself. .It Fl vlanmtu , vlanhwtag , vlanhwfilter , vlanhwcsum , vlanhwtso If the driver offers user-configurable VLAN support, disable reception of extended frames, tag processing in hardware, frame filtering in hardware, checksum offloading, or TSO on VLAN, respectively. .It Cm vxlanhwcsum , vxlanhwtso If the driver offers user-configurable VXLAN support, enable inner checksum offloading (receive and transmit) or TSO on VXLAN, respectively. Note that this must be configured on a physical interface associated with .Xr vxlan 4 , not on a .Xr vxlan 4 interface itself. The physical interface is either the interface specified as the vxlandev or the interface hosting the vxlanlocal address. The driver will offload as much checksum work and TSO as it can reliably support, the exact level of offloading may vary between drivers. .It Fl vxlanhwcsum , vxlanhwtso If the driver offers user-configurable VXLAN support, disable checksum offloading (receive and transmit) or TSO on VXLAN, respectively. .It Cm vnet Ar jail Move the interface to the .Xr jail 8 , specified by name or JID. If the jail has a virtual network stack, the interface will disappear from the current environment and become visible to the jail. .It Fl vnet Ar jail Reclaim the interface from the .Xr jail 8 , specified by name or JID. If the jail has a virtual network stack, the interface will disappear from the jail, and become visible to the current network environment. .It Cm polling Turn on .Xr polling 4 feature and disable interrupts on the interface, if driver supports this mode. .It Fl polling Turn off .Xr polling 4 feature and enable interrupt mode on the interface. .It Cm create Create the specified network pseudo-device. If the interface is given without a unit number, try to create a new device with an arbitrary unit number. If creation of an arbitrary device is successful, the new device name is printed to standard output unless the interface is renamed or destroyed in the same .Nm invocation. .It Cm destroy Destroy the specified network pseudo-device. .It Cm plumb Another name for the .Cm create parameter. Included for Solaris compatibility. .It Cm unplumb Another name for the .Cm destroy parameter. Included for Solaris compatibility. .It Cm metric Ar n Set the routing metric of the interface to .Ar n , default 0. The routing metric is used by the routing protocol .Pq Xr routed 8 . Higher metrics have the effect of making a route less favorable; metrics are counted as additional hops to the destination network or host. .It Cm mtu Ar n Set the maximum transmission unit of the interface to .Ar n , default is interface specific. The MTU is used to limit the size of packets that are transmitted on an interface. Not all interfaces support setting the MTU, and some interfaces have range restrictions. .It Cm netmask Ar mask .\" (Inet and ISO.) (Inet only.) Specify how much of the address to reserve for subdividing networks into sub-networks. The mask includes the network part of the local address and the subnet part, which is taken from the host field of the address. The mask can be specified as a single hexadecimal number with a leading .Ql 0x , with a dot-notation Internet address, or with a pseudo-network name listed in the network table .Xr networks 5 . The mask contains 1's for the bit positions in the 32-bit address which are to be used for the network and subnet parts, and 0's for the host part. The mask should contain at least the standard network portion, and the subnet field should be contiguous with the network portion. .Pp The netmask can also be specified in CIDR notation after the address. See the .Ar address option above for more information. .It Cm prefixlen Ar len (Inet6 only.) Specify that .Ar len bits are reserved for subdividing networks into sub-networks. The .Ar len must be integer, and for syntactical reason it must be between 0 to 128. It is almost always 64 under the current IPv6 assignment rule. If the parameter is omitted, 64 is used. .Pp The prefix can also be specified using the slash notation after the address. See the .Ar address option above for more information. .It Cm remove Another name for the .Fl alias parameter. Introduced for compatibility with .Bsx . .Sm off .It Cm link Op Cm 0 No - Cm 2 .Sm on Enable special processing of the link level of the interface. These three options are interface specific in actual effect, however, they are in general used to select special modes of operation. An example of this is to enable SLIP compression, or to select the connector type for some Ethernet cards. Refer to the man page for the specific driver for more information. .Sm off .It Fl link Op Cm 0 No - Cm 2 .Sm on Disable special processing at the link level with the specified interface. .It Cm monitor Put the interface in monitor mode. No packets are transmitted, and received packets are discarded after .Xr bpf 4 processing. .It Fl monitor Take the interface out of monitor mode. .It Cm pcp Ar priority_code_point Priority code point .Pq Dv PCP is an 3-bit field which refers to the IEEE 802.1p class of service and maps to the frame priority level. .It Fl pcp Stop tagging packets on the interface w/ the priority code point. .It Cm up Mark an interface .Dq up . This may be used to enable an interface after an .Dq Nm Cm down . It happens automatically when setting the first address on an interface. If the interface was reset when previously marked down, the hardware will be re-initialized. .El .Ss ICMPv6 Neighbor Discovery Protocol Parameters The following parameters are for ICMPv6 Neighbor Discovery Protocol. Note that the address family keyword .Dq Li inet6 is needed for them: .Bl -tag -width indent .It Cm accept_rtadv Set a flag to enable accepting ICMPv6 Router Advertisement messages. The .Xr sysctl 8 variable .Va net.inet6.ip6.accept_rtadv controls whether this flag is set by default or not. .It Cm -accept_rtadv Clear a flag .Cm accept_rtadv . .It Cm no_radr Set a flag to control whether routers from which the system accepts Router Advertisement messages will be added to the Default Router List or not. When the .Cm accept_rtadv flag is disabled, this flag has no effect. The .Xr sysctl 8 variable .Va net.inet6.ip6.no_radr controls whether this flag is set by default or not. .It Cm -no_radr Clear a flag .Cm no_radr . .It Cm auto_linklocal Set a flag to perform automatic link-local address configuration when the interface becomes available. The .Xr sysctl 8 variable .Va net.inet6.ip6.auto_linklocal controls whether this flag is set by default or not. .It Cm -auto_linklocal Clear a flag .Cm auto_linklocal . .It Cm defaultif Set the specified interface as the default route when there is no default router. .It Cm -defaultif Clear a flag .Cm defaultif . .It Cm ifdisabled Set a flag to disable all of IPv6 network communications on the specified interface. Note that if there are already configured IPv6 addresses on that interface, all of them are marked as .Dq tentative and DAD will be performed when this flag is cleared. .It Cm -ifdisabled Clear a flag .Cm ifdisabled . When this flag is cleared and .Cm auto_linklocal flag is enabled, automatic configuration of a link-local address is performed. .It Cm nud Set a flag to enable Neighbor Unreachability Detection. .It Cm -nud Clear a flag .Cm nud . .It Cm no_prefer_iface Set a flag to not honor rule 5 of source address selection in RFC 3484. In practice this means the address on the outgoing interface will not be preferred, effectively yielding the decision to the address selection policy table, configurable with .Xr ip6addrctl 8 . .It Cm -no_prefer_iface Clear a flag .Cm no_prefer_iface . .It Cm no_dad Set a flag to disable Duplicate Address Detection. .It Cm -no_dad Clear a flag .Cm no_dad . .El .Ss IPv6 Parameters The following parameters are specific for IPv6 addresses. Note that the address family keyword .Dq Li inet6 is needed for them: .Bl -tag -width indent .It Cm autoconf Set the IPv6 autoconfigured address bit. .It Fl autoconf Clear the IPv6 autoconfigured address bit. .It Cm deprecated Set the IPv6 deprecated address bit. .It Fl deprecated Clear the IPv6 deprecated address bit. .It Cm pltime Ar n Set preferred lifetime for the address. .It Cm prefer_source Set a flag to prefer address as a candidate of the source address for outgoing packets. .It Cm -prefer_source Clear a flag .Cm prefer_source . .It Cm vltime Ar n Set valid lifetime for the address. .El .Ss IEEE 802.11 Wireless Interfaces Cloning Parameters The following parameters are specific to cloning IEEE 802.11 wireless interfaces with the .Cm create request: .Bl -tag -width indent .It Cm wlandev Ar device Use .Ar device as the parent for the cloned device. .It Cm wlanmode Ar mode Specify the operating mode for this cloned device. .Ar mode is one of .Cm sta , .Cm ahdemo (or .Cm adhoc-demo ) , .Cm ibss (or .Cm adhoc ) , .Cm ap (or .Cm hostap ) , .Cm wds , .Cm tdma , .Cm mesh , and .Cm monitor . The operating mode of a cloned interface cannot be changed. The .Cm tdma mode is actually implemented as an .Cm adhoc-demo interface with special properties. .It Cm wlanbssid Ar bssid The 802.11 mac address to use for the bssid. This must be specified at create time for a legacy .Cm wds device. .It Cm wlanaddr Ar address The local mac address. If this is not specified then a mac address will automatically be assigned to the cloned device. Typically this address is the same as the address of the parent device but if the .Cm bssid parameter is specified then the driver will craft a unique address for the device (if supported). .It Cm wdslegacy Mark a .Cm wds device as operating in .Dq legacy mode . Legacy .Cm wds devices have a fixed peer relationship and do not, for example, roam if their peer stops communicating. For completeness a Dynamic WDS (DWDS) interface may be marked as .Fl wdslegacy . .It Cm bssid Request a unique local mac address for the cloned device. This is only possible if the device supports multiple mac addresses. To force use of the parent's mac address use .Fl bssid . .It Cm beacons Mark the cloned interface as depending on hardware support to track received beacons. To have beacons tracked in software use .Fl beacons . For .Cm hostap mode .Fl beacons can also be used to indicate no beacons should be transmitted; this can be useful when creating a WDS configuration but .Cm wds interfaces can only be created as companions to an access point. .El .Ss Cloned IEEE 802.11 Wireless Interface Parameters The following parameters are specific to IEEE 802.11 wireless interfaces cloned with a .Cm create operation: .Bl -tag -width indent .It Cm ampdu Enable sending and receiving AMPDU frames when using 802.11n (default). The 802.11n specification states a compliant station must be capable of receiving AMPDU frames but transmission is optional. Use .Fl ampdu to disable all use of AMPDU with 802.11n. For testing and/or to work around interoperability problems one can use .Cm ampdutx and .Cm ampdurx to control use of AMPDU in one direction. .It Cm ampdudensity Ar density Set the AMPDU density parameter used when operating with 802.11n. This parameter controls the inter-packet gap for AMPDU frames. The sending device normally controls this setting but a receiving station may request wider gaps. Legal values for .Ar density are 0, .25, .5, 1, 2, 4, 8, and 16 (microseconds). A value of .Cm - is treated the same as 0. .It Cm ampdulimit Ar limit Set the limit on packet size for receiving AMPDU frames when operating with 802.11n. Legal values for .Ar limit are 8192, 16384, 32768, and 65536 but one can also specify just the unique prefix: 8, 16, 32, 64. Note the sender may limit the size of AMPDU frames to be less than the maximum specified by the receiving station. .It Cm amsdu Enable sending and receiving AMSDU frames when using 802.11n. By default AMSDU is received but not transmitted. Use .Fl amsdu to disable all use of AMSDU with 802.11n. For testing and/or to work around interoperability problems one can use .Cm amsdutx and .Cm amsdurx to control use of AMSDU in one direction. .It Cm amsdulimit Ar limit Set the limit on packet size for sending and receiving AMSDU frames when operating with 802.11n. Legal values for .Ar limit are 7935 and 3839 (bytes). Note the sender may limit the size of AMSDU frames to be less than the maximum specified by the receiving station. Note also that devices are not required to support the 7935 limit, only 3839 is required by the specification and the larger value may require more memory to be dedicated to support functionality that is rarely used. .It Cm apbridge When operating as an access point, pass packets between wireless clients directly (default). To instead let them pass up through the system and be forwarded using some other mechanism, use .Fl apbridge . Disabling the internal bridging is useful when traffic is to be processed with packet filtering. .It Cm authmode Ar mode Set the desired authentication mode in infrastructure mode. Not all adapters support all modes. The set of valid modes is .Cm none , open , shared (shared key), .Cm 8021x (IEEE 802.1x), and .Cm wpa (IEEE WPA/WPA2/802.11i). The .Cm 8021x and .Cm wpa modes are only useful when using an authentication service (a supplicant for client operation or an authenticator when operating as an access point). Modes are case insensitive. .It Cm bgscan Enable background scanning when operating as a station. Background scanning is a technique whereby a station associated to an access point will temporarily leave the channel to scan for neighboring stations. This allows a station to maintain a cache of nearby access points so that roaming between access points can be done without a lengthy scan operation. Background scanning is done only when a station is not busy and any outbound traffic will cancel a scan operation. Background scanning should never cause packets to be lost though there may be some small latency if outbound traffic interrupts a scan operation. By default background scanning is enabled if the device is capable. To disable background scanning, use .Fl bgscan . Background scanning is controlled by the .Cm bgscanidle and .Cm bgscanintvl parameters. Background scanning must be enabled for roaming; this is an artifact of the current implementation and may not be required in the future. .It Cm bgscanidle Ar idletime Set the minimum time a station must be idle (not transmitting or receiving frames) before a background scan is initiated. The .Ar idletime parameter is specified in milliseconds. By default a station must be idle at least 250 milliseconds before a background scan is initiated. The idle time may not be set to less than 100 milliseconds. .It Cm bgscanintvl Ar interval Set the interval at which background scanning is attempted. The .Ar interval parameter is specified in seconds. By default a background scan is considered every 300 seconds (5 minutes). The .Ar interval may not be set to less than 15 seconds. .It Cm bintval Ar interval Set the interval at which beacon frames are sent when operating in ad-hoc or ap mode. The .Ar interval parameter is specified in TUs (1024 usecs). By default beacon frames are transmitted every 100 TUs. .It Cm bmissthreshold Ar count Set the number of consecutive missed beacons at which the station will attempt to roam (i.e., search for a new access point). The .Ar count parameter must be in the range 1 to 255; though the upper bound may be reduced according to device capabilities. The default threshold is 7 consecutive missed beacons; but this may be overridden by the device driver. Another name for the .Cm bmissthreshold parameter is .Cm bmiss . .It Cm bssid Ar address Specify the MAC address of the access point to use when operating as a station in a BSS network. This overrides any automatic selection done by the system. To disable a previously selected access point, supply .Cm any , none , or .Cm - for the address. This option is useful when more than one access point uses the same SSID. Another name for the .Cm bssid parameter is .Cm ap . .It Cm burst Enable packet bursting. Packet bursting is a transmission technique whereby the wireless medium is acquired once to send multiple frames and the interframe spacing is reduced. This technique can significantly increase throughput by reducing transmission overhead. Packet bursting is supported by the 802.11e QoS specification and some devices that do not support QoS may still be capable. By default packet bursting is enabled if a device is capable of doing it. To disable packet bursting, use .Fl burst . .It Cm chanlist Ar channels Set the desired channels to use when scanning for access points, neighbors in an IBSS network, or looking for unoccupied channels when operating as an access point. The set of channels is specified as a comma-separated list with each element in the list representing either a single channel number or a range of the form .Dq Li a-b . Channel numbers must be in the range 1 to 255 and be permissible according to the operating characteristics of the device. .It Cm channel Ar number Set a single desired channel. Channels range from 1 to 255, but the exact selection available depends on the region your adaptor was manufactured for. Setting the channel to .Cm any , or .Dq Cm - will clear any desired channel and, if the device is marked up, force a scan for a channel to operate on. Alternatively the frequency, in megahertz, may be specified instead of the channel number. .Pp When there are several ways to use a channel the channel number/frequency may be appended with attributes to clarify. For example, if a device is capable of operating on channel 6 with 802.11n and 802.11g then one can specify that g-only use should be used by specifying .Cm 6:g . Similarly the channel width can be specified by appending it with .Dq Cm \&/ ; e.g., .Cm 6/40 specifies a 40MHz wide channel. These attributes can be combined as in: .Cm 6:ht/40 . .Pp The full set of flags specified following a .Dq Cm \&: are: .Pp .Bl -tag -compact .It Cm a 802.11a .It Cm b 802.11b .It Cm d Atheros Dynamic Turbo mode .It Cm g 802.11g .It Cm h Same as .Cm n .It Cm n 802.11n aka HT .It Cm s Atheros Static Turbo mode .It Cm t Atheros Dynamic Turbo mode, or appended to .Cm st and .Cm dt .El .Pp The full set of channel widths following a .Cm \&/ are: .Pp .Bl -tag -compact .It Cm 5 5MHz aka quarter-rate channel .It Cm 10 10MHz aka half-rate channel .It Cm 20 20MHz mostly for use in specifying .Cm ht20 .It Cm 40 40MHz mostly for use in specifying .Cm ht40 .El .Pp In addition, a 40MHz HT channel specification may include the location of the extension channel by appending .Dq Cm \&+ or .Dq Cm \&- for above and below, respectively; e.g., .Cm 2437:ht/40+ specifies 40MHz wide HT operation with the center channel at frequency 2437 and the extension channel above. .It Cm country Ar name Set the country code to use in calculating the regulatory constraints for operation. In particular the set of available channels, how the wireless device will operation on the channels, and the maximum transmit power that can be used on a channel are defined by this setting. Country/Region codes are specified as a 2-character abbreviation defined by ISO 3166 or using a longer, but possibly ambiguous, spelling; e.g., "ES" and "Spain". The set of country codes are taken from .Pa /etc/regdomain.xml and can also be viewed with the .Cm list countries request. Note that not all devices support changing the country code from a default setting; typically stored in EEPROM. See also .Cm regdomain , .Cm indoor , .Cm outdoor , and .Cm anywhere . .It Cm dfs Enable Dynamic Frequency Selection (DFS) as specified in 802.11h. DFS embodies several facilities including detection of overlapping radar signals, dynamic transmit power control, and channel selection according to a least-congested criteria. DFS support is mandatory for some 5GHz frequencies in certain locales (e.g., ETSI). By default DFS is enabled according to the regulatory definitions specified in .Pa /etc/regdomain.xml and the current country code, regdomain, and channel. Note the underlying device (and driver) must support radar detection for full DFS support to work. To be fully compliant with the local regulatory agency frequencies that require DFS should not be used unless it is fully supported. Use .Fl dfs to disable this functionality for testing. .It Cm dotd Enable support for the 802.11d specification (default). When this support is enabled in station mode, beacon frames that advertise a country code different than the currently configured country code will cause an event to be dispatched to user applications. This event can be used by the station to adopt that country code and operate according to the associated regulatory constraints. When operating as an access point with 802.11d enabled the beacon and probe response frames transmitted will advertise the current regulatory domain settings. To disable 802.11d use .Fl dotd . .It Cm doth Enable 802.11h support including spectrum management. When 802.11h is enabled beacon and probe response frames will have the SpectrumMgt bit set in the capabilities field and country and power constraint information elements will be present. 802.11h support also includes handling Channel Switch Announcements (CSA) which are a mechanism to coordinate channel changes by an access point. By default 802.11h is enabled if the device is capable. To disable 802.11h use .Fl doth . .It Cm deftxkey Ar index Set the default key to use for transmission. Typically this is only set when using WEP encryption. Note that you must set a default transmit key for the system to know which key to use in encrypting outbound traffic. The .Cm weptxkey is an alias for this request; it is provided for backwards compatibility. .It Cm dtimperiod Ar period Set the DTIM period for transmitting buffered multicast data frames when operating in ap mode. The .Ar period specifies the number of beacon intervals between DTIM and must be in the range 1 to 15. By default DTIM is 1 (i.e., DTIM occurs at each beacon). .It Cm quiet Enable the use of quiet IE. Hostap will use this to silence other stations to reduce interference for radar detection when operating on 5GHz frequency and doth support is enabled. Use .Fl quiet to disable this functionality. .It Cm quiet_period Ar period Set the QUIET .Ar period to the number of beacon intervals between the start of regularly scheduled quiet intervals defined by Quiet element. .It Cm quiet_count Ar count Set the QUIET .Ar count to the number of TBTTs until the beacon interval during which the next quiet interval shall start. A value of 1 indicates the quiet interval will start during the beacon interval starting at the next TBTT. A value 0 is reserved. .It Cm quiet_offset Ar offset Set the QUIET .Ar offset to the offset of the start of the quiet interval from the TBTT specified by the Quiet count, expressed in TUs. The value of the .Ar offset shall be less than one beacon interval. .It Cm quiet_duration Ar dur Set the QUIET .Ar dur to the duration of the Quiet interval, expressed in TUs. The value should be less than beacon interval. .It Cm dturbo Enable the use of Atheros Dynamic Turbo mode when communicating with another Dynamic Turbo-capable station. Dynamic Turbo mode is an Atheros-specific mechanism by which stations switch between normal 802.11 operation and a .Dq boosted mode in which a 40MHz wide channel is used for communication. Stations using Dynamic Turbo mode operate boosted only when the channel is free of non-dturbo stations; when a non-dturbo station is identified on the channel all stations will automatically drop back to normal operation. By default, Dynamic Turbo mode is not enabled, even if the device is capable. Note that turbo mode (dynamic or static) is only allowed on some channels depending on the regulatory constraints; use the .Cm list chan command to identify the channels where turbo mode may be used. To disable Dynamic Turbo mode use .Fl dturbo . .It Cm dwds Enable Dynamic WDS (DWDS) support. DWDS is a facility by which 4-address traffic can be carried between stations operating in infrastructure mode. A station first associates to an access point and authenticates using normal procedures (e.g., WPA). Then 4-address frames are passed to carry traffic for stations operating on either side of the wireless link. DWDS extends the normal WDS mechanism by leveraging existing security protocols and eliminating static binding. .Pp When DWDS is enabled on an access point 4-address frames received from an authorized station will generate a .Dq DWDS discovery event to user applications. This event should be used to create a WDS interface that is bound to the remote station (and usually plumbed into a bridge). Once the WDS interface is up and running 4-address traffic then logically flows through that interface. .Pp When DWDS is enabled on a station, traffic with a destination address different from the peer station are encapsulated in a 4-address frame and transmitted to the peer. All 4-address traffic uses the security information of the stations (e.g., cryptographic keys). A station is associated using 802.11n facilities may transport 4-address traffic using these same mechanisms; this depends on available resources and capabilities of the device. The DWDS implementation guards against layer 2 routing loops of multicast traffic. .It Cm ff Enable the use of Atheros Fast Frames when communicating with another Fast Frames-capable station. Fast Frames are an encapsulation technique by which two 802.3 frames are transmitted in a single 802.11 frame. This can noticeably improve throughput but requires that the receiving station understand how to decapsulate the frame. Fast frame use is negotiated using the Atheros 802.11 vendor-specific protocol extension so enabling use is safe when communicating with non-Atheros devices. By default, use of fast frames is enabled if the device is capable. To explicitly disable fast frames, use .Fl ff . .It Cm fragthreshold Ar length Set the threshold for which transmitted frames are broken into fragments. The .Ar length argument is the frame size in bytes and must be in the range 256 to 2346. Setting .Ar length to .Li 2346 , .Cm any , or .Cm - disables transmit fragmentation. Not all adapters honor the fragmentation threshold. .It Cm hidessid When operating as an access point, do not broadcast the SSID in beacon frames or respond to probe request frames unless they are directed to the ap (i.e., they include the ap's SSID). By default, the SSID is included in beacon frames and undirected probe request frames are answered. To re-enable the broadcast of the SSID etc., use .Fl hidessid . .It Cm ht Enable use of High Throughput (HT) when using 802.11n (default). The 802.11n specification includes mechanisms for operation on 20MHz and 40MHz wide channels using different signalling mechanisms than specified in 802.11b, 802.11g, and 802.11a. Stations negotiate use of these facilities, termed HT20 and HT40, when they associate. To disable all use of 802.11n use .Fl ht . To disable use of HT20 (e.g., to force only HT40 use) use .Fl ht20 . To disable use of HT40 use .Fl ht40 . .Pp HT configuration is used to .Dq auto promote operation when several choices are available. For example, if a station associates to an 11n-capable access point it controls whether the station uses legacy operation, HT20, or HT40. When an 11n-capable device is setup as an access point and Auto Channel Selection is used to locate a channel to operate on, HT configuration controls whether legacy, HT20, or HT40 operation is setup on the selected channel. If a fixed channel is specified for a station then HT configuration can be given as part of the channel specification; e.g., 6:ht/20 to setup HT20 operation on channel 6. .It Cm htcompat Enable use of compatibility support for pre-802.11n devices (default). The 802.11n protocol specification went through several incompatible iterations. Some vendors implemented 11n support to older specifications that will not interoperate with a purely 11n-compliant station. In particular the information elements included in management frames for old devices are different. When compatibility support is enabled both standard and compatible data will be provided. Stations that associate using the compatibility mechanisms are flagged in .Cm list sta . To disable compatibility support use .Fl htcompat . .It Cm htprotmode Ar technique For interfaces operating in 802.11n, use the specified .Ar technique for protecting HT frames in a mixed legacy/HT network. The set of valid techniques is .Cm off , and .Cm rts (RTS/CTS, default). Technique names are case insensitive. .It Cm inact Enable inactivity processing for stations associated to an access point (default). When operating as an access point the 802.11 layer monitors the activity of each associated station. When a station is inactive for 5 minutes it will send several .Dq probe frames to see if the station is still present. If no response is received then the station is deauthenticated. Applications that prefer to handle this work can disable this facility by using .Fl inact . .It Cm indoor Set the location to use in calculating regulatory constraints. The location is also advertised in beacon and probe response frames when 802.11d is enabled with .Cm dotd . See also .Cm outdoor , .Cm anywhere , .Cm country , and .Cm regdomain . .It Cm list active Display the list of channels available for use taking into account any restrictions set with the .Cm chanlist directive. See the description of .Cm list chan for more information. .It Cm list caps Display the adaptor's capabilities, including the operating modes supported. .It Cm list chan Display the list of channels available for use. Channels are shown with their IEEE channel number, equivalent frequency, and usage modes. Channels identified as .Ql 11g are also usable in .Ql 11b mode. Channels identified as .Ql 11a Turbo may be used only for Atheros' Static Turbo mode (specified with . Cm mediaopt turbo ) . Channels marked with a .Ql * have a regulatory constraint that they be passively scanned. This means a station is not permitted to transmit on the channel until it identifies the channel is being used for 802.11 communication; typically by hearing a beacon frame from an access point operating on the channel. .Cm list freq is another way of requesting this information. By default a compacted list of channels is displayed; if the .Fl v option is specified then all channels are shown. .It Cm list countries Display the set of country codes and regulatory domains that can be used in regulatory configuration. .It Cm list mac Display the current MAC Access Control List state. Each address is prefixed with a character that indicates the current policy applied to it: .Ql + indicates the address is allowed access, .Ql - indicates the address is denied access, .Ql * indicates the address is present but the current policy open (so the ACL is not consulted). .It Cm list mesh Displays the mesh routing table, used for forwarding packets on a mesh network. .It Cm list regdomain Display the current regulatory settings including the available channels and transmit power caps. .It Cm list roam Display the parameters that govern roaming operation. .It Cm list txparam Display the parameters that govern transmit operation. .It Cm list txpower Display the transmit power caps for each channel. .It Cm list scan Display the access points and/or ad-hoc neighbors located in the vicinity. This information may be updated automatically by the adapter with a .Cm scan request or through background scanning. Depending on the capabilities of the stations the following flags (capability codes) can be included in the output: .Bl -tag -width 3n .It Li A Channel agility. .It Li B PBCC modulation. .It Li C Poll request capability. .It Li D DSSS/OFDM capability. .It Li E Extended Service Set (ESS). Indicates that the station is part of an infrastructure network rather than an IBSS/ad-hoc network. .It Li I Independent Basic Service Set (IBSS). Indicates that the station is part of an ad-hoc network rather than an ESS network. .It Li P Privacy capability. The station requires authentication and encryption for all data frames exchanged within the BSS using cryptographic means such as WEP, TKIP, or AES-CCMP. .It Li R Robust Secure Network (RSN). .It Li S Short Preamble. Indicates that the network is using short preambles, defined in 802.11b High Rate/DSSS PHY, and utilizes a 56 bit sync field rather than the 128 bit field used in long preamble mode. Short preambles are used to optionally improve throughput performance with 802.11g and 802.11b. .It Li c Pollable capability. .It Li s Short slot time capability. Indicates that the 802.11g network is using a short slot time because there are no legacy (802.11b) stations present. .El .Pp By default interesting information elements captured from the neighboring stations are displayed at the end of each row. Possible elements include: .Cm WME (station supports WME), .Cm WPA (station supports WPA), .Cm WPS (station supports WPS), .Cm RSN (station supports 802.11i/RSN), .Cm HTCAP (station supports 802.11n/HT communication), .Cm ATH (station supports Atheros protocol extensions), .Cm VEN (station supports unknown vendor-specific extensions). If the .Fl v flag is used all the information elements and their contents will be shown. Specifying the .Fl v flag also enables display of long SSIDs. The .Cm list ap command is another way of requesting this information. .It Cm list sta When operating as an access point display the stations that are currently associated. When operating in ad-hoc mode display stations identified as neighbors in the IBSS. When operating in mesh mode display stations identified as neighbors in the MBSS. When operating in station mode display the access point. Capabilities advertised by the stations are described under the .Cm scan request. The following flags can be included in the output: .Bl -tag -width 3n .It Li A Authorized. Indicates that the station is permitted to send/receive data frames. .It Li E Extended Rate Phy (ERP). Indicates that the station is operating in an 802.11g network using extended transmit rates. .It Li H High Throughput (HT). Indicates that the station is using HT transmit rates. If a .Sq Li + follows immediately after then the station associated using deprecated mechanisms supported only when .Cm htcompat is enabled. .It Li P Power Save. Indicates that the station is operating in power save mode. .It Li Q Quality of Service (QoS). Indicates that the station is using QoS encapsulation for data frame. QoS encapsulation is enabled only when WME mode is enabled. .It Li S Short GI in HT 40MHz mode enabled. If a .Sq Li + follows immediately after then short GI in HT 20MHz mode is enabled as well. .It Li T Transitional Security Network (TSN). Indicates that the station associated using TSN; see also .Cm tsn below. .It Li W Wi-Fi Protected Setup (WPS). Indicates that the station associated using WPS. .It Li s Short GI in HT 20MHz mode enabled. .El .Pp By default information elements received from associated stations are displayed in a short form; the .Fl v flag causes this information to be displayed symbolically. .It Cm list wme Display the current channel parameters to use when operating in WME mode. If the .Fl v option is specified then both channel and BSS parameters are displayed for each AC (first channel, then BSS). When WME mode is enabled for an adaptor this information will be displayed with the regular status; this command is mostly useful for examining parameters when WME mode is disabled. See the description of the .Cm wme directive for information on the various parameters. .It Cm maxretry Ar count Set the maximum number of tries to use in sending unicast frames. The default setting is 6 but drivers may override this with a value they choose. .It Cm mcastrate Ar rate Set the rate for transmitting multicast/broadcast frames. Rates are specified as megabits/second in decimal; e.g.,\& 5.5 for 5.5 Mb/s. This rate should be valid for the current operating conditions; if an invalid rate is specified drivers are free to chose an appropriate rate. .It Cm mgtrate Ar rate Set the rate for transmitting management and/or control frames. Rates are specified as megabits/second in decimal; e.g.,\& 5.5 for 5.5 Mb/s. .It Cm outdoor Set the location to use in calculating regulatory constraints. The location is also advertised in beacon and probe response frames when 802.11d is enabled with .Cm dotd . See also .Cm anywhere , .Cm country , .Cm indoor , and .Cm regdomain . .It Cm powersave Enable powersave operation. When operating as a client, the station will conserve power by periodically turning off the radio and listening for messages from the access point telling it there are packets waiting. The station must then retrieve the packets. Not all devices support power save operation as a client. The 802.11 specification requires that all access points support power save but some drivers do not. Use .Fl powersave to disable powersave operation when operating as a client. .It Cm powersavesleep Ar sleep Set the desired max powersave sleep time in TUs (1024 usecs). By default the max powersave sleep time is 100 TUs. .It Cm protmode Ar technique For interfaces operating in 802.11g, use the specified .Ar technique for protecting OFDM frames in a mixed 11b/11g network. The set of valid techniques is .Cm off , cts (CTS to self), and .Cm rtscts (RTS/CTS). Technique names are case insensitive. Not all devices support .Cm cts as a protection technique. .It Cm pureg When operating as an access point in 802.11g mode allow only 11g-capable stations to associate (11b-only stations are not permitted to associate). To allow both 11g and 11b-only stations to associate, use .Fl pureg . .It Cm puren When operating as an access point in 802.11n mode allow only HT-capable stations to associate (legacy stations are not permitted to associate). To allow both HT and legacy stations to associate, use .Fl puren . .It Cm regdomain Ar sku Set the regulatory domain to use in calculating the regulatory constraints for operation. In particular the set of available channels, how the wireless device will operation on the channels, and the maximum transmit power that can be used on a channel are defined by this setting. Regdomain codes (SKU's) are taken from .Pa /etc/regdomain.xml and can also be viewed with the .Cm list countries request. Note that not all devices support changing the regdomain from a default setting; typically stored in EEPROM. See also .Cm country , .Cm indoor , .Cm outdoor , and .Cm anywhere . .It Cm rifs Enable use of Reduced InterFrame Spacing (RIFS) when operating in 802.11n on an HT channel. Note that RIFS must be supported by both the station and access point for it to be used. To disable RIFS use .Fl rifs . .It Cm roam:rate Ar rate Set the threshold for controlling roaming when operating in a BSS. The .Ar rate parameter specifies the transmit rate in megabits at which roaming should be considered. If the current transmit rate drops below this setting and background scanning is enabled, then the system will check if a more desirable access point is available and switch over to it. The current scan cache contents are used if they are considered valid according to the .Cm scanvalid parameter; otherwise a background scan operation is triggered before any selection occurs. Each channel type has a separate rate threshold; the default values are: 12 Mb/s (11a), 2 Mb/s (11b), 2 Mb/s (11g), MCS 1 (11na, 11ng). .It Cm roam:rssi Ar rssi Set the threshold for controlling roaming when operating in a BSS. The .Ar rssi parameter specifies the receive signal strength in dBm units at which roaming should be considered. If the current rssi drops below this setting and background scanning is enabled, then the system will check if a more desirable access point is available and switch over to it. The current scan cache contents are used if they are considered valid according to the .Cm scanvalid parameter; otherwise a background scan operation is triggered before any selection occurs. Each channel type has a separate rssi threshold; the default values are all 7 dBm. .It Cm roaming Ar mode When operating as a station, control how the system will behave when communication with the current access point is broken. The .Ar mode argument may be one of .Cm device (leave it to the hardware device to decide), .Cm auto (handle either in the device or the operating system\[em]as appropriate), .Cm manual (do nothing until explicitly instructed). By default, the device is left to handle this if it is capable; otherwise, the operating system will automatically attempt to reestablish communication. Manual mode is used by applications such as .Xr wpa_supplicant 8 that want to control the selection of an access point. .It Cm rtsthreshold Ar length Set the threshold for which transmitted frames are preceded by transmission of an RTS control frame. The .Ar length argument is the frame size in bytes and must be in the range 1 to 2346. Setting .Ar length to .Li 2346 , .Cm any , or .Cm - disables transmission of RTS frames. Not all adapters support setting the RTS threshold. .It Cm scan Initiate a scan of neighboring stations, wait for it to complete, and display all stations found. Only the super-user can initiate a scan. See .Cm list scan for information on the display. By default a background scan is done; otherwise a foreground scan is done and the station may roam to a different access point. The .Cm list scan request can be used to show recent scan results without initiating a new scan. .It Cm scanvalid Ar threshold Set the maximum time the scan cache contents are considered valid; i.e., will be used without first triggering a scan operation to refresh the data. The .Ar threshold parameter is specified in seconds and defaults to 60 seconds. The minimum setting for .Ar threshold is 10 seconds. One should take care setting this threshold; if it is set too low then attempts to roam to another access point may trigger unnecessary background scan operations. .It Cm shortgi Enable use of Short Guard Interval when operating in 802.11n on an HT channel. NB: this currently enables Short GI on both HT40 and HT20 channels. To disable Short GI use .Fl shortgi . .It Cm smps Enable use of Static Spatial Multiplexing Power Save (SMPS) when operating in 802.11n. A station operating with Static SMPS maintains only a single receive chain active (this can significantly reduce power consumption). To disable SMPS use .Fl smps . .It Cm smpsdyn Enable use of Dynamic Spatial Multiplexing Power Save (SMPS) when operating in 802.11n. A station operating with Dynamic SMPS maintains only a single receive chain active but switches to multiple receive chains when it receives an RTS frame (this can significantly reduce power consumption). Note that stations cannot distinguish between RTS/CTS intended to enable multiple receive chains and those used for other purposes. To disable SMPS use .Fl smps . .It Cm ssid Ar ssid Set the desired Service Set Identifier (aka network name). The SSID is a string up to 32 characters in length and may be specified as either a normal string or in hexadecimal when preceded by .Ql 0x . Additionally, the SSID may be cleared by setting it to .Ql - . .It Cm tdmaslot Ar slot When operating with TDMA, use the specified .Ar slot configuration. The .Ar slot is a number between 0 and the maximum number of slots in the BSS. Note that a station configured as slot 0 is a master and will broadcast beacon frames advertising the BSS; stations configured to use other slots will always scan to locate a master before they ever transmit. By default .Cm tdmaslot is set to 1. .It Cm tdmaslotcnt Ar cnt When operating with TDMA, setup a BSS with .Ar cnt slots. The slot count may be at most 8. The current implementation is only tested with two stations (i.e., point to point applications). This setting is only meaningful when a station is configured as slot 0; other stations adopt this setting from the BSS they join. By default .Cm tdmaslotcnt is set to 2. .It Cm tdmaslotlen Ar len When operating with TDMA, setup a BSS such that each station has a slot .Ar len microseconds long. The slot length must be at least 150 microseconds (1/8 TU) and no more than 65 milliseconds. Note that setting too small a slot length may result in poor channel bandwidth utilization due to factors such as timer granularity and guard time. This setting is only meaningful when a station is configured as slot 0; other stations adopt this setting from the BSS they join. By default .Cm tdmaslotlen is set to 10 milliseconds. .It Cm tdmabintval Ar intval When operating with TDMA, setup a BSS such that beacons are transmitted every .Ar intval superframes to synchronize the TDMA slot timing. A superframe is defined as the number of slots times the slot length; e.g., a BSS with two slots of 10 milliseconds has a 20 millisecond superframe. The beacon interval may not be zero. A lower setting of .Cm tdmabintval causes the timers to be resynchronized more often; this can be help if significant timer drift is observed. By default .Cm tdmabintval is set to 5. .It Cm tsn When operating as an access point with WPA/802.11i allow legacy stations to associate using static key WEP and open authentication. To disallow legacy station use of WEP, use .Fl tsn . .It Cm txpower Ar power Set the power used to transmit frames. The .Ar power argument is specified in .5 dBm units. Out of range values are truncated. Typically only a few discrete power settings are available and the driver will use the setting closest to the specified value. Not all adapters support changing the transmit power. .It Cm ucastrate Ar rate Set a fixed rate for transmitting unicast frames. Rates are specified as megabits/second in decimal; e.g.,\& 5.5 for 5.5 Mb/s. This rate should be valid for the current operating conditions; if an invalid rate is specified drivers are free to chose an appropriate rate. .It Cm wepmode Ar mode Set the desired WEP mode. Not all adapters support all modes. The set of valid modes is .Cm off , on , and .Cm mixed . The .Cm mixed mode explicitly tells the adaptor to allow association with access points which allow both encrypted and unencrypted traffic. On these adapters, .Cm on means that the access point must only allow encrypted connections. On other adapters, .Cm on is generally another name for .Cm mixed . Modes are case insensitive. .It Cm weptxkey Ar index Set the WEP key to be used for transmission. This is the same as setting the default transmission key with .Cm deftxkey . .It Cm wepkey Ar key Ns | Ns Ar index : Ns Ar key Set the selected WEP key. If an .Ar index is not given, key 1 is set. A WEP key will be either 5 or 13 characters (40 or 104 bits) depending on the local network and the capabilities of the adaptor. It may be specified either as a plain string or as a string of hexadecimal digits preceded by .Ql 0x . For maximum portability, hex keys are recommended; the mapping of text keys to WEP encryption is usually driver-specific. In particular, the Windows drivers do this mapping differently to .Fx . A key may be cleared by setting it to .Ql - . If WEP is supported then there are at least four keys. Some adapters support more than four keys. If that is the case, then the first four keys (1-4) will be the standard temporary keys and any others will be adaptor specific keys such as permanent keys stored in NVRAM. .Pp Note that you must set a default transmit key with .Cm deftxkey for the system to know which key to use in encrypting outbound traffic. .It Cm wme Enable Wireless Multimedia Extensions (WME) support, if available, for the specified interface. WME is a subset of the IEEE 802.11e standard to support the efficient communication of realtime and multimedia data. To disable WME support, use .Fl wme . Another name for this parameter is .Cm wmm . .Pp The following parameters are meaningful only when WME support is in use. Parameters are specified per-AC (Access Category) and split into those that are used by a station when acting as an access point and those for client stations in the BSS. The latter are received from the access point and may not be changed (at the station). The following Access Categories are recognized: .Pp .Bl -tag -width ".Cm AC_BK" -compact .It Cm AC_BE (or .Cm BE ) best effort delivery, .It Cm AC_BK (or .Cm BK ) background traffic, .It Cm AC_VI (or .Cm VI ) video traffic, .It Cm AC_VO (or .Cm VO ) voice traffic. .El .Pp AC parameters are case-insensitive. Traffic classification is done in the operating system using the vlan priority associated with data frames or the ToS (Type of Service) indication in IP-encapsulated frames. If neither information is present, traffic is assigned to the Best Effort (BE) category. .Bl -tag -width indent .It Cm ack Ar ac Set the ACK policy for QoS transmissions by the local station; this controls whether or not data frames transmitted by a station require an ACK response from the receiving station. To disable waiting for an ACK use .Fl ack . This parameter is applied only to the local station. .It Cm acm Ar ac Enable the Admission Control Mandatory (ACM) mechanism for transmissions by the local station. To disable the ACM use .Fl acm . On stations in a BSS this parameter is read-only and indicates the setting received from the access point. NB: ACM is not supported right now. .It Cm aifs Ar ac Ar count Set the Arbitration Inter Frame Spacing (AIFS) channel access parameter to use for transmissions by the local station. On stations in a BSS this parameter is read-only and indicates the setting received from the access point. .It Cm cwmin Ar ac Ar count Set the CWmin channel access parameter to use for transmissions by the local station. On stations in a BSS this parameter is read-only and indicates the setting received from the access point. .It Cm cwmax Ar ac Ar count Set the CWmax channel access parameter to use for transmissions by the local station. On stations in a BSS this parameter is read-only and indicates the setting received from the access point. .It Cm txoplimit Ar ac Ar limit Set the Transmission Opportunity Limit channel access parameter to use for transmissions by the local station. This parameter defines an interval of time when a WME station has the right to initiate transmissions onto the wireless medium. On stations in a BSS this parameter is read-only and indicates the setting received from the access point. .It Cm bss:aifs Ar ac Ar count Set the AIFS channel access parameter to send to stations in a BSS. This parameter is meaningful only when operating in ap mode. .It Cm bss:cwmin Ar ac Ar count Set the CWmin channel access parameter to send to stations in a BSS. This parameter is meaningful only when operating in ap mode. .It Cm bss:cwmax Ar ac Ar count Set the CWmax channel access parameter to send to stations in a BSS. This parameter is meaningful only when operating in ap mode. .It Cm bss:txoplimit Ar ac Ar limit Set the TxOpLimit channel access parameter to send to stations in a BSS. This parameter is meaningful only when operating in ap mode. .El .It Cm wps Enable Wireless Privacy Subscriber support. Note that WPS support requires a WPS-capable supplicant. To disable this function use .Fl wps . .El .Ss MAC-Based Access Control List Parameters The following parameters support an optional access control list feature available with some adapters when operating in ap mode; see .Xr wlan_acl 4 . This facility allows an access point to accept/deny association requests based on the MAC address of the station. Note that this feature does not significantly enhance security as MAC address spoofing is easy to do. .Bl -tag -width indent .It Cm mac:add Ar address Add the specified MAC address to the database. Depending on the policy setting association requests from the specified station will be allowed or denied. .It Cm mac:allow Set the ACL policy to permit association only by stations registered in the database. .It Cm mac:del Ar address Delete the specified MAC address from the database. .It Cm mac:deny Set the ACL policy to deny association only by stations registered in the database. .It Cm mac:kick Ar address Force the specified station to be deauthenticated. This typically is done to block a station after updating the address database. .It Cm mac:open Set the ACL policy to allow all stations to associate. .It Cm mac:flush Delete all entries in the database. .It Cm mac:radius Set the ACL policy to permit association only by stations approved by a RADIUS server. Note that this feature requires the .Xr hostapd 8 program be configured to do the right thing as it handles the RADIUS processing (and marks stations as authorized). .El .Ss Mesh Mode Wireless Interface Parameters The following parameters are related to a wireless interface operating in mesh mode: .Bl -tag -width indent .It Cm meshid Ar meshid Set the desired Mesh Identifier. The Mesh ID is a string up to 32 characters in length. A mesh interface must have a Mesh Identifier specified to reach an operational state. .It Cm meshttl Ar ttl Set the desired .Dq time to live for mesh forwarded packets; this is the number of hops a packet may be forwarded before it is discarded. The default setting for .Cm meshttl is 31. .It Cm meshpeering Enable or disable peering with neighbor mesh stations. Stations must peer before any data packets can be exchanged. By default .Cm meshpeering is enabled. .It Cm meshforward Enable or disable forwarding packets by a mesh interface. By default .Cm meshforward is enabled. .It Cm meshgate This attribute specifies whether or not the mesh STA activates mesh gate announcements. By default .Cm meshgate is disabled. .It Cm meshmetric Ar protocol Set the specified .Ar protocol as the link metric protocol used on a mesh network. The default protocol is called .Ar AIRTIME . The mesh interface will restart after changing this setting. .It Cm meshpath Ar protocol Set the specified .Ar protocol as the path selection protocol used on a mesh network. The only available protocol at the moment is called .Ar HWMP (Hybrid Wireless Mesh Protocol). The mesh interface will restart after changing this setting. .It Cm hwmprootmode Ar mode Stations on a mesh network can operate as .Dq root nodes . Root nodes try to find paths to all mesh nodes and advertise themselves regularly. When there is a root mesh node on a network, other mesh nodes can setup paths between themselves faster because they can use the root node to find the destination. This path may not be the best, but on-demand routing will eventually find the best path. The following modes are recognized: .Pp .Bl -tag -width ".Cm PROACTIVE" -compact .It Cm DISABLED Disable root mode. .It Cm NORMAL Send broadcast path requests every two seconds. Nodes on the mesh without a path to this root mesh station with try to discover a path to us. .It Cm PROACTIVE Send broadcast path requests every two seconds and every node must reply with a path reply even if it already has a path to this root mesh station. .It Cm RANN Send broadcast root announcement (RANN) frames. Nodes on the mesh without a path to this root mesh station with try to discover a path to us. .El By default .Cm hwmprootmode is set to .Ar DISABLED . .It Cm hwmpmaxhops Ar cnt Set the maximum number of hops allowed in an HMWP path to .Ar cnt . The default setting for .Cm hwmpmaxhops is 31. .El .Ss Compatibility Parameters The following parameters are for compatibility with other systems: .Bl -tag -width indent .It Cm nwid Ar ssid Another name for the .Cm ssid parameter. Included for .Nx compatibility. .It Cm stationname Ar name Set the name of this station. The station name is not part of the IEEE 802.11 protocol though some interfaces support it. As such it only seems to be meaningful to identical or virtually identical equipment. Setting the station name is identical in syntax to setting the SSID. One can also use .Cm station for .Bsx compatibility. .It Cm wep Another way of saying .Cm wepmode on . Included for .Bsx compatibility. .It Fl wep Another way of saying .Cm wepmode off . Included for .Bsx compatibility. .It Cm nwkey key Another way of saying: .Dq Li "wepmode on weptxkey 1 wepkey 1:key wepkey 2:- wepkey 3:- wepkey 4:-" . Included for .Nx compatibility. .It Cm nwkey Xo .Sm off .Ar n : k1 , k2 , k3 , k4 .Sm on .Xc Another way of saying .Dq Li "wepmode on weptxkey n wepkey 1:k1 wepkey 2:k2 wepkey 3:k3 wepkey 4:k4" . Included for .Nx compatibility. .It Fl nwkey Another way of saying .Cm wepmode off . Included for .Nx compatibility. .El .Ss Bridge Interface Parameters The following parameters are specific to bridge interfaces: .Bl -tag -width indent .It Cm addm Ar interface Add the interface named by .Ar interface as a member of the bridge. The interface is put into promiscuous mode so that it can receive every packet sent on the network. .It Cm deletem Ar interface Remove the interface named by .Ar interface from the bridge. Promiscuous mode is disabled on the interface when it is removed from the bridge. .It Cm maxaddr Ar size Set the size of the bridge address cache to .Ar size . The default is 2000 entries. .It Cm timeout Ar seconds Set the timeout of address cache entries to .Ar seconds seconds. If .Ar seconds is zero, then address cache entries will not be expired. The default is 1200 seconds. .It Cm addr Display the addresses that have been learned by the bridge. .It Cm static Ar interface-name Ar address Op Cm vlan Ar vlan-id Add a static entry into the address cache for pointing to .Ar interface-name . If .Ar vlan-id is specified, the entry is added for that VLAN, otherwise it is added for VLAN 0. .Pp Static entries are never aged out of the cache or re-placed, even if the address is seen on a different interface. .It Cm deladdr Ar address Op Cm vlan Ar vlan-id Delete .Ar address from the address cache. If .Ar vlan-id is specified, the entry is deleted from that VLAN's address table, otherwise it is deleted from the VLAN 0 address table. .It Cm flush Delete all dynamically-learned addresses from the address cache. .It Cm flushall Delete all addresses, including static addresses, from the address cache. .It Cm discover Ar interface Mark an interface as a .Dq discovering interface. When the bridge has no address cache entry (either dynamic or static) for the destination address of a packet, the bridge will forward the packet to all member interfaces marked as .Dq discovering . This is the default for all interfaces added to a bridge. .It Cm -discover Ar interface Clear the .Dq discovering attribute on a member interface. For packets without the .Dq discovering attribute, the only packets forwarded on the interface are broadcast or multicast packets and packets for which the destination address is known to be on the interface's segment. .It Cm learn Ar interface Mark an interface as a .Dq learning interface. When a packet arrives on such an interface, the source address of the packet is entered into the address cache as being a destination address on the interface's segment. This is the default for all interfaces added to a bridge. .It Cm -learn Ar interface Clear the .Dq learning attribute on a member interface. .It Cm sticky Ar interface Mark an interface as a .Dq sticky interface. Dynamically learned address entries are treated at static once entered into the cache. Sticky entries are never aged out of the cache or replaced, even if the address is seen on a different interface. .It Cm -sticky Ar interface Clear the .Dq sticky attribute on a member interface. .It Cm private Ar interface Mark an interface as a .Dq private interface. A private interface does not forward any traffic to any other port that is also a private interface. .It Cm -private Ar interface Clear the .Dq private attribute on a member interface. .It Cm span Ar interface Add the interface named by .Ar interface as a span port on the bridge. Span ports transmit a copy of every frame received by the bridge. This is most useful for snooping a bridged network passively on another host connected to one of the span ports of the bridge. .It Cm -span Ar interface Delete the interface named by .Ar interface from the list of span ports of the bridge. .It Cm stp Ar interface Enable Spanning Tree protocol on .Ar interface . The .Xr if_bridge 4 driver has support for the IEEE 802.1D Spanning Tree protocol (STP). Spanning Tree is used to detect and remove loops in a network topology. .It Cm -stp Ar interface Disable Spanning Tree protocol on .Ar interface . This is the default for all interfaces added to a bridge. .It Cm edge Ar interface Set .Ar interface as an edge port. An edge port connects directly to end stations cannot create bridging loops in the network, this allows it to transition straight to forwarding. .It Cm -edge Ar interface Disable edge status on .Ar interface . .It Cm autoedge Ar interface Allow .Ar interface to automatically detect edge status. This is the default for all interfaces added to a bridge. .It Cm -autoedge Ar interface Disable automatic edge status on .Ar interface . .It Cm ptp Ar interface Set the .Ar interface as a point to point link. This is required for straight transitions to forwarding and should be enabled on a direct link to another RSTP capable switch. .It Cm -ptp Ar interface Disable point to point link status on .Ar interface . This should be disabled for a half duplex link and for an interface connected to a shared network segment, like a hub or a wireless network. .It Cm autoptp Ar interface Automatically detect the point to point status on .Ar interface by checking the full duplex link status. This is the default for interfaces added to the bridge. .It Cm -autoptp Ar interface Disable automatic point to point link detection on .Ar interface . .It Cm maxage Ar seconds Set the time that a Spanning Tree protocol configuration is valid. The default is 20 seconds. The minimum is 6 seconds and the maximum is 40 seconds. .It Cm fwddelay Ar seconds Set the time that must pass before an interface begins forwarding packets when Spanning Tree is enabled. The default is 15 seconds. The minimum is 4 seconds and the maximum is 30 seconds. .It Cm hellotime Ar seconds Set the time between broadcasting of Spanning Tree protocol configuration messages. The hello time may only be changed when operating in legacy stp mode. The default is 2 seconds. The minimum is 1 second and the maximum is 2 seconds. .It Cm priority Ar value Set the bridge priority for Spanning Tree. The default is 32768. The minimum is 0 and the maximum is 61440. .It Cm proto Ar value Set the Spanning Tree protocol. The default is rstp. The available options are stp and rstp. .It Cm holdcnt Ar value Set the transmit hold count for Spanning Tree. This is the number of packets transmitted before being rate limited. The default is 6. The minimum is 1 and the maximum is 10. .It Cm ifpriority Ar interface Ar value Set the Spanning Tree priority of .Ar interface to .Ar value . The default is 128. The minimum is 0 and the maximum is 240. .It Cm ifpathcost Ar interface Ar value Set the Spanning Tree path cost of .Ar interface to .Ar value . The default is calculated from the link speed. To change a previously selected path cost back to automatic, set the cost to 0. The minimum is 1 and the maximum is 200000000. .It Cm ifmaxaddr Ar interface Ar size Set the maximum number of hosts allowed from an interface, packets with unknown source addresses are dropped until an existing host cache entry expires or is removed. Set to 0 to disable. .El .Ss Bridge VLAN Filtering Parameters The behaviour of these options is described in the .Dq VLAN SUPPORT section of .Xr bridge 4 . .Bl -tag -width indent .It Cm vlanfilter Enable VLAN filtering on the bridge. .It Cm -vlanfilter Disable VLAN filtering on the bridge. This is the default. .It Cm untagged Ar interface Ar vlan-id Set the untagged VLAN identifier for an interface. .It Cm -untagged Ar interface Ar vlan-id Clear the untagged VLAN identifier for an interface. .It Cm defuntagged Ar vlan-id Enable the .Cm untagged option by default on newly added members. .It Cm -defuntagged Do not enable the .Cm untagged option by default on newly added members. .It Cm tagged Ar interface Ar vlan-list Set the interface's VLAN access list to the provided list of VLANs. The list should be a comma-separated list of one or more VLAN IDs or ranges formatted as .Ar first-last , the value .Dq none meaning the empty set, or the value .Dq all meaning all VLANs (1-4094). .It Cm +tagged Ar interface Ar vlan-list Add the provided list of VLAN IDs to the interface's VLAN access list. The list should be formatted as described for .Cm tagged . .It Cm -tagged Ar interface Ar vlan-list Remove the provided list of VLAN IDs from the interface's VLAN access list. The list should be formatted as described for .Cm tagged . +.It Cm qinq Ar interface +Allow this interface to send 802.1ad +.Dq Q-in-Q +frames. +.It Cm -qinq Ar interface +Do not allow this interface to send 802.1ad +.Dq Q-in-Q +frames. +This is the default behavior. +.It Cm defqinq +Enable the +.Cm qinq +option by default on newly added members. +.It Cm -defqinq +Do not enable the +.Cm qinq +option by default on newly added members. +This is the default behavior. .El .Ss Link Aggregation and Link Failover Parameters The following parameters are specific to lagg interfaces: .Bl -tag -width indent .It Cm laggtype Ar type When creating a lagg interface the type can be specified as either .Cm ethernet or .Cm infiniband . If not specified ethernet is the default lagg type. .It Cm laggport Ar interface Add the interface named by .Ar interface as a port of the aggregation interface. .It Cm -laggport Ar interface Remove the interface named by .Ar interface from the aggregation interface. .It Cm laggproto Ar proto Set the aggregation protocol. The default is .Li failover . The available options are .Li failover , .Li lacp , .Li loadbalance , .Li roundrobin , .Li broadcast and .Li none . .It Cm lagghash Ar option Ns Oo , Ns Ar option Oc Set the packet layers to hash for aggregation protocols which load balance. The default is .Dq l2,l3,l4 . The options can be combined using commas. .Pp .Bl -tag -width ".Cm l2" -compact .It Cm l2 src/dst mac address and optional vlan number. .It Cm l3 src/dst address for IPv4 or IPv6. .It Cm l4 src/dst port for TCP/UDP/SCTP. .El .It Cm -use_flowid Enable local hash computation for RSS hash on the interface. The .Li loadbalance and .Li lacp modes will use the RSS hash from the network card if available to avoid computing one, this may give poor traffic distribution if the hash is invalid or uses less of the protocol header information. .Cm -use_flowid disables use of RSS hash from the network card. The default value can be set via the .Va net.link.lagg.default_use_flowid .Xr sysctl 8 variable. .Li 0 means .Dq disabled and .Li 1 means .Dq enabled . .It Cm use_flowid Use the RSS hash from the network card if available. .It Cm flowid_shift Ar number Set a shift parameter for RSS local hash computation. Hash is calculated by using flowid bits in a packet header mbuf which are shifted by the number of this parameter. .It Cm use_numa Enable selection of egress ports based on the native .Xr numa 4 domain for the packets being transmitted. This is currently only implemented for lacp mode. This works only on .Xr numa 4 hardware, running a kernel compiled with the .Xr numa 4 option, and when interfaces from multiple .Xr numa 4 domains are ports of the aggregation interface. .It Cm -use_numa Disable selection of egress ports based on the native .Xr numa 4 domain for the packets being transmitted. .It Cm lacp_fast_timeout Enable lacp fast-timeout on the interface. .It Cm -lacp_fast_timeout Disable lacp fast-timeout on the interface. .It Cm lacp_strict Enable lacp strict compliance on the interface. The default value can be set via the .Va net.link.lagg.lacp.default_strict_mode .Xr sysctl 8 variable. .Li 0 means .Dq disabled and .Li 1 means .Dq enabled . .It Cm -lacp_strict Disable lacp strict compliance on the interface. .It Cm rr_limit Ar number Configure a stride for an interface in round-robin mode. The default stride is 1. .El .Ss Generic IP Tunnel Parameters The following parameters apply to IP tunnel interfaces, .Xr gif 4 : .Bl -tag -width indent .It Cm tunnel Ar src_addr dest_addr Configure the physical source and destination address for IP tunnel interfaces. The arguments .Ar src_addr and .Ar dest_addr are interpreted as the outer source/destination for the encapsulating IPv4/IPv6 header. .It Fl tunnel Unconfigure the physical source and destination address for IP tunnel interfaces previously configured with .Cm tunnel . .It Cm deletetunnel Another name for the .Fl tunnel parameter. .It Cm noclamp This flag prevents the MTU from being clamped to 1280 bytes, the minimum MTU for IPv6, when the outer protocol is IPv6. When the flag is set, the MTU value configured on the interface will be used instead of the fixed length of 1280 bytes. For more details, please refer to the .Ar MTU Configuration and Path MTU Discovery section in .Xr gif 4 . .It Cm -noclamp Clear the flag .Cm noclamp . .It Cm ignore_source Set a flag to accept encapsulated packets destined to this host independently from source address. This may be useful for hosts, that receive encapsulated packets from the load balancers. .It Cm -ignore_source Clear the flag .Cm ignore_source . .El .Ss GRE Tunnel Parameters The following parameters apply to GRE tunnel interfaces, .Xr gre 4 : .Bl -tag -width indent .It Cm tunnel Ar src_addr dest_addr Configure the physical source and destination address for GRE tunnel interfaces. The arguments .Ar src_addr and .Ar dest_addr are interpreted as the outer source/destination for the encapsulating IPv4/IPv6 header. .It Fl tunnel Unconfigure the physical source and destination address for GRE tunnel interfaces previously configured with .Cm tunnel . .It Cm deletetunnel Another name for the .Fl tunnel parameter. .It Cm grekey Ar key Configure the GRE key to be used for outgoing packets. Note that .Xr gre 4 will always accept GRE packets with invalid or absent keys. This command will result in a four byte MTU reduction on the interface. .El .Ss Packet Filter State Table Sychronisation Parameters The following parameters are specific to .Xr pfsync 4 interfaces: .Bl -tag -width indent .It Cm syncdev Ar iface Use the specified interface to send and receive pfsync state synchronisation messages. .It Fl syncdev Stop sending pfsync state synchronisation messages over the network. .It Cm syncpeer Ar peer_address Set the destination address for the state synchronization messages sent. The .Ar peer_address is normally the IPv4 or IPv6 address of the other host taking part in the pfsync cluster. .Pp When the .Ar peer_address is set to a unicast IP address, the pfsync link will behave as point-to-point rather than using multicast to broadcast the messages. .Pp When the .Ar peer_address is set to ff12::f0, the state synchronization messages will be broadcast using multicast over IPv6. .It Fl syncpeer Unset the syncpeer. Packets will then be broadcast using multicast over IPv4. .It Cm maxupd Ar n Set the maximum number of updates for a single state which can be collapsed into one. This is an 8-bit number; the default value is 128. .It Cm defer Defer transmission of the first packet in a state until a peer has acknowledged that the associated state has been inserted. .It Fl defer Do not defer the first packet in a state. This is the default. .It Fl version Ar n Configure message format for compatibility with older versions of FreeBSD. Refer to .Xr pfsync 4 for details. .El .Ss VLAN Parameters The following parameters are specific to .Xr vlan 4 interfaces: .Bl -tag -width indent .It Cm vlan Ar vlan_tag Set the VLAN tag value to .Ar vlan_tag . This value is a 12-bit VLAN Identifier (VID) which is used to create an 802.1Q or 802.1ad VLAN header for packets sent from the .Xr vlan 4 interface. Note that .Cm vlan and .Cm vlandev must both be set at the same time. .It Cm vlanproto Ar vlan_proto Set the VLAN encapsulation protocol to .Ar vlan_proto . Supported encapsulation protocols are currently: .Bl -tag .It Cm 802.1Q Default. .It Cm 802.1ad .It Cm QinQ Same as .Cm 802.1ad . .El .It Cm vlanpcp Ar priority_code_point Priority code point .Pq Dv PCP is an 3-bit field which refers to the IEEE 802.1p class of service and maps to the frame priority level. .Pp Values in order of priority are: .Cm 1 .Pq Dv Background (lowest) , .Cm 0 .Pq Dv Best effort (default) , .Cm 2 .Pq Dv Excellent effort , .Cm 3 .Pq Dv Critical applications , .Cm 4 .Pq Dv Video, < 100ms latency and jitter , .Cm 5 .Pq Dv Voice, < 10ms latency and jitter , .Cm 6 .Pq Dv Internetwork control , .Cm 7 .Pq Dv Network control (highest) . .It Cm vlandev Ar iface Associate the physical interface .Ar iface with a .Xr vlan 4 interface. Packets transmitted through the .Xr vlan 4 interface will be diverted to the specified physical interface .Ar iface with 802.1Q VLAN encapsulation. Packets with 802.1Q encapsulation received by the parent interface with the correct VLAN Identifier will be diverted to the associated .Xr vlan 4 pseudo-interface. The .Xr vlan 4 interface is assigned a copy of the parent interface's flags and the parent's Ethernet address. The .Cm vlandev and .Cm vlan must both be set at the same time. If the .Xr vlan 4 interface already has a physical interface associated with it, this command will fail. To change the association to another physical interface, the existing association must be cleared first. .Pp Note: if the hardware tagging capability is set on the parent interface, the .Xr vlan 4 pseudo interface's behavior changes: the .Xr vlan 4 interface recognizes that the parent interface supports insertion and extraction of VLAN tags on its own (usually in firmware) and that it should pass packets to and from the parent unaltered. .It Fl vlandev Op Ar iface If the driver is a .Xr vlan 4 pseudo device, disassociate the parent interface from it. This breaks the link between the .Xr vlan 4 interface and its parent, clears its VLAN Identifier, flags and its link address and shuts the interface down. The .Ar iface argument is useless and hence deprecated. .El .Ss Virtual eXtensible LAN Parameters The following parameters are used to configure .Xr vxlan 4 interfaces. .Bl -tag -width indent .It Cm vxlanid Ar identifier This value is a 24-bit VXLAN Network Identifier (VNI) that identifies the virtual network segment membership of the interface. .It Cm vxlanlocal Ar address The source address used in the encapsulating IPv4/IPv6 header. The address should already be assigned to an existing interface. When the interface is configured in unicast mode, the listening socket is bound to this address. .It Cm vxlanremote Ar address The interface can be configured in a unicast, or point-to-point, mode to create a tunnel between two hosts. This is the IP address of the remote end of the tunnel. .It Cm vxlangroup Ar address The interface can be configured in a multicast mode to create a virtual network of hosts. This is the IP multicast group address the interface will join. .It Cm vxlanlocalport Ar port The port number the interface will listen on. The default port number is 4789. .It Cm vxlanremoteport Ar port The destination port number used in the encapsulating IPv4/IPv6 header. The remote host should be listening on this port. The default port number is 4789. Note some other implementations, such as Linux, do not default to the IANA assigned port, but instead listen on port 8472. .It Cm vxlanportrange Ar low high The range of source ports used in the encapsulating IPv4/IPv6 header. The port selected within the range is based on a hash of the inner frame. A range is useful to provide entropy within the outer IP header for more effective load balancing. The default range is between the .Xr sysctl 8 variables .Va net.inet.ip.portrange.first and .Va net.inet.ip.portrange.last .It Cm vxlantimeout Ar timeout The maximum time, in seconds, before an entry in the forwarding table is pruned. The default is 1200 seconds (20 minutes). .It Cm vxlanmaxaddr Ar max The maximum number of entries in the forwarding table. The default is 2000. .It Cm vxlandev Ar dev When the interface is configured in multicast mode, the .Cm dev interface is used to transmit IP multicast packets. .It Cm vxlanttl Ar ttl The TTL used in the encapsulating IPv4/IPv6 header. The default is 64. .It Cm vxlanlearn The source IP address and inner source Ethernet MAC address of received packets are used to dynamically populate the forwarding table. When in multicast mode, an entry in the forwarding table allows the interface to send the frame directly to the remote host instead of broadcasting the frame to the multicast group. This is the default. .It Fl vxlanlearn The forwarding table is not populated by received packets. .It Cm vxlanflush Delete all dynamically-learned addresses from the forwarding table. .It Cm vxlanflushall Delete all addresses, including static addresses, from the forwarding table. .El .Ss CARP Parameters The following parameters are used to configure .Xr carp 4 protocol on an interface: .Bl -tag -width indent .It Cm vhid Ar n Set the virtual host ID. This is a required setting to initiate .Xr carp 4 . If the virtual host ID does not exist yet, it is created and attached to the interface, otherwise configuration of an existing vhid is adjusted. If the .Cm vhid keyword is supplied along with an .Dq inet6 or .Dq inet address, then this address is configured to be run under control of the specified vhid. Whenever a last address that refers to a particular vhid is removed from an interface, the vhid is automatically removed from interface and destroyed. Any other configuration parameters for the .Xr carp 4 protocol should be supplied along with the .Cm vhid keyword. Acceptable values for vhid are 1 to 255. .It Cm advbase Ar seconds Specifies the base of the advertisement interval in seconds. The acceptable values are 1 to 255. The default value is 1. .It Cm advskew Ar interval Specifies the skew to add to the base advertisement interval to make one host advertise slower than another host. It is specified in 1/256 of seconds. The acceptable values are 1 to 254. The default value is 0. .It Cm pass Ar phrase Set the authentication key to .Ar phrase . .It Cm state Ar state Forcibly change state of a given vhid. The following states are recognized: .Cm MASTER and .Cm BACKUP . .It Cm peer Ar address Set the address to send (IPv4) .Xr carp 4 announcements to. .It Cm mcast Restore the default destination address for (IPv4) .Xr carp 4 announcements, which is 224.0.0.18. .It Cm peer6 Ar address Set the address to send (IPv6) .Xr carp 4 announcements to. .It Cm mcast6 Restore the default destination address for (IPv4) .Xr carp 4 announcements, which is ff02::12. .It Cm carpver Set the protocol version. Valid choices are 2 (for .Xr carp 4) and 3 (for VRRPv3). This can only be set when .Xr carp 4 is initiated. .It Cm vrrpprio Set the VRRPv3 priority. Valid values are 1-255. .It Cm vrrpinterval Set the VRRPv3 Master Advertisement Interval. Values are in centiseconds. .El .Sh ENVIRONMENT The following environment variables affect the execution of .Nm : .Bl -tag -width IFCONFIG_FORMAT .It Ev IFCONFIG_FORMAT This variable can contain a specification of the output format. See the description of the .Fl f flag for more details. .El .Sh EXAMPLES Assign the IPv4 address .Li 192.0.2.10 , with a network mask of .Li 255.255.255.0 , to the interface .Li em0 : .Dl # ifconfig em0 inet 192.0.2.10 netmask 255.255.255.0 .Pp Add the IPv4 address .Li 192.0.2.45 , with the CIDR network prefix .Li /28 , to the interface .Li em0 : .Dl # ifconfig em0 inet 192.0.2.45/28 alias .Pp Remove the IPv4 address .Li 192.0.2.45 from the interface .Li em0 : .Dl # ifconfig em0 inet 192.0.2.45 -alias .Pp Enable IPv6 functionality of the interface: .Dl # ifconfig em0 inet6 -ifdisabled .Pp Add the IPv6 address .Li 2001:DB8:DBDB::123/48 to the interface .Li em0 : .Dl # ifconfig em0 inet6 2001:db8:bdbd::123 prefixlen 48 alias Note that lower case hexadecimal IPv6 addresses are acceptable. .Pp Remove the IPv6 address added in the above example, using the .Li / character as shorthand for the network prefix: .Dl # ifconfig em0 inet6 2001:db8:bdbd::123/48 -alias .Pp Configure a single CARP redundant address on igb0, and then switch it to be master: .Bd -literal -offset indent -compact # ifconfig igb0 vhid 1 10.0.0.1/24 pass foobar up # ifconfig igb0 vhid 1 state master .Ed .Pp Configure the interface .Li xl0 , to use 100baseTX, full duplex Ethernet media options: .Dl # ifconfig xl0 media 100baseTX mediaopt full-duplex .Pp Label the em0 interface as an uplink: .Dl # ifconfig em0 description \&"Uplink to Gigabit Switch 2\&" .Pp Create the software network interface .Li gif1 : .Dl # ifconfig gif1 create .Pp Destroy the software network interface .Li gif1 : .Dl # ifconfig gif1 destroy .Pp Display available wireless networks using .Li wlan0 : .Dl # ifconfig wlan0 list scan .Pp Display inet and inet6 address subnet masks in CIDR notation .Dl # ifconfig -f inet:cidr,inet6:cidr .Pp Display interfaces that are up with the exception of loopback .Dl # ifconfig -a -u -G lo .Pp Display a list of interface names beloning to the wlan group: .Bd -literal -offset indent -compact # ifconfig -g wlan wlan0 wlan1 .Ed .Pp Display details about the interfaces belonging to the wlan group: .Bd -literal -offset indent -compact # ifconfig -a -g wlan wlan0: flags=8843 metric 0 mtu 1500 ether 75:4c:61:6b:7a:73 inet6 fe80::4c75:636a:616e:ffd8%wlan0 prefixlen 64 scopeid 0x3 inet6 2001:5761:6e64:6152:6f6d:616e:fea4:ffe2 prefixlen 64 autoconf inet 192.168.10.5 netmask 0xffffff00 broadcast 192.168.10.255 groups: wlan ssid "Hotspot" channel 11 (2462 MHz 11g) bssid 12:34:ff:ff:43:21 regdomain ETSI country DE authmode WPA2/802.11i privacy ON deftxkey UNDEF AES-CCM 2:128-bit AES-CCM 3:128-bit txpower 30 bmiss 10 scanvalid 60 protmode CTS wme roaming MANUAL parent interface: iwm0 media: IEEE 802.11 Wireless Ethernet DS/2Mbps mode 11g status: associated nd6 options=23 wlan1: flags=8843 metric 0 mtu 1500 ether 00:50:69:6f:74:72 groups: wlan ssid "" channel 2 (2417 MHz 11g) regdomain FCC country US authmode OPEN privacy OFF txpower 30 bmiss 7 scanvalid 60 bgscan bgscanintvl 300 bgscanidle 250 roam:rssi 7 roam:rate 5 protmode CTS wme bintval 0 parent interface: rum0 media: IEEE 802.11 Wireless Ethernet autoselect (autoselect) status: no carrier nd6 options=29 .Ed .Pp Set a randomly-generated MAC address on tap0: .Dl # ifconfig tap0 ether random .Sh DIAGNOSTICS Messages indicating the specified interface does not exist, the requested address is unknown, or the user is not privileged and tried to alter an interface's configuration. .Sh SEE ALSO .Xr netstat 1 , .Xr carp 4 , .Xr gif 4 , .Xr netintro 4 , .Xr pfsync 4 , .Xr polling 4 , .Xr vlan 4 , .Xr vxlan 4 , .Xr devd.conf 5 , .Xr devd 8 , .Xr jail 8 , .Xr rc 8 , .Xr routed 8 , .Xr sysctl 8 .Rs .%R RFC 3484 .%D February 2003 .%T "Default Address Selection for Internet Protocol version 6 (IPv6)" .Re .Rs .%R RFC 4291 .%D February 2006 .%T "IP Version 6 Addressing Architecture" .Re .Sh HISTORY The .Nm utility appeared in .Bx 4.2 . .Sh BUGS Basic IPv6 node operation requires a link-local address on each interface configured for IPv6. Normally, such an address is automatically configured by the kernel on each interface added to the system or enabled; this behavior may be disabled by setting per-interface flag .Cm -auto_linklocal . The default value of this flag is 1 and can be disabled by using the sysctl MIB variable .Va net.inet6.ip6.auto_linklocal . .Pp Do not configure IPv6 addresses with no link-local address by using .Nm . It can result in unexpected behaviors of the kernel. diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index 46e54339a171..26ea4400e67d 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4406 +1,4430 @@ /* $NetBSD: if_bridge.c,v 1.31 2005/06/01 19:45:34 jdc Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) * 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 ``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 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. * * OpenBSD: if_bridge.c,v 1.60 2001/06/15 03:38:33 itojun Exp */ /* * Network interface bridge support. * * TODO: * * - Currently only supports Ethernet-like interfaces (Ethernet, * 802.11, VLANs on Ethernet, etc.) Figure out a nice way * to bridge other types of interfaces (maybe consider * heterogeneous bridges). */ #include "opt_inet.h" #include "opt_inet6.h" #define EXTERR_CATEGORY EXTERR_CAT_BRIDGE #include #include /* string functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for net/if.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET6 #include #include #include #endif #if defined(INET) || defined(INET6) #include #endif #include #include #include #include #include #include #include /* * At various points in the code we need to know if we're hooked into the INET * and/or INET6 pfil. Define some macros to do that based on which IP versions * are enabled in the kernel. This avoids littering the rest of the code with * #ifnet INET6 to avoid referencing V_inet6_pfil_head. */ #ifdef INET6 #define PFIL_HOOKED_IN_INET6 PFIL_HOOKED_IN(V_inet6_pfil_head) #define PFIL_HOOKED_OUT_INET6 PFIL_HOOKED_OUT(V_inet6_pfil_head) #else #define PFIL_HOOKED_IN_INET6 false #define PFIL_HOOKED_OUT_INET6 false #endif #ifdef INET #define PFIL_HOOKED_IN_INET PFIL_HOOKED_IN(V_inet_pfil_head) #define PFIL_HOOKED_OUT_INET PFIL_HOOKED_OUT(V_inet_pfil_head) #else #define PFIL_HOOKED_IN_INET false #define PFIL_HOOKED_OUT_INET false #endif #define PFIL_HOOKED_IN_46 (PFIL_HOOKED_IN_INET6 || PFIL_HOOKED_IN_INET) #define PFIL_HOOKED_OUT_46 (PFIL_HOOKED_OUT_INET6 || PFIL_HOOKED_OUT_INET) /* * Size of the route hash table. Must be a power of two. */ #ifndef BRIDGE_RTHASH_SIZE #define BRIDGE_RTHASH_SIZE 1024 #endif #define BRIDGE_RTHASH_MASK (BRIDGE_RTHASH_SIZE - 1) /* * Default maximum number of addresses to cache. */ #ifndef BRIDGE_RTABLE_MAX #define BRIDGE_RTABLE_MAX 2000 #endif /* * Timeout (in seconds) for entries learned dynamically. */ #ifndef BRIDGE_RTABLE_TIMEOUT #define BRIDGE_RTABLE_TIMEOUT (20 * 60) /* same as ARP */ #endif /* * Number of seconds between walks of the route list. */ #ifndef BRIDGE_RTABLE_PRUNE_PERIOD #define BRIDGE_RTABLE_PRUNE_PERIOD (5 * 60) #endif /* * List of capabilities to possibly mask on the member interface. */ #define BRIDGE_IFCAPS_MASK (IFCAP_TOE|IFCAP_TSO|IFCAP_TXCSUM|\ IFCAP_TXCSUM_IPV6|IFCAP_MEXTPG) /* * List of capabilities to strip */ #define BRIDGE_IFCAPS_STRIP IFCAP_LRO /* * Bridge locking * * The bridge relies heavily on the epoch(9) system to protect its data * structures. This means we can safely use CK_LISTs while in NET_EPOCH, but we * must ensure there is only one writer at a time. * * That is: for read accesses we only need to be in NET_EPOCH, but for write * accesses we must hold: * * - BRIDGE_RT_LOCK, for any change to bridge_rtnodes * - BRIDGE_LOCK, for any other change * * The BRIDGE_LOCK is a sleepable lock, because it is held across ioctl() * calls to bridge member interfaces and these ioctl()s can sleep. * The BRIDGE_RT_LOCK is a non-sleepable mutex, because it is sometimes * required while we're in NET_EPOCH and then we're not allowed to sleep. */ #define BRIDGE_LOCK_INIT(_sc) do { \ sx_init(&(_sc)->sc_sx, "if_bridge"); \ mtx_init(&(_sc)->sc_rt_mtx, "if_bridge rt", NULL, MTX_DEF); \ } while (0) #define BRIDGE_LOCK_DESTROY(_sc) do { \ sx_destroy(&(_sc)->sc_sx); \ mtx_destroy(&(_sc)->sc_rt_mtx); \ } while (0) #define BRIDGE_LOCK(_sc) sx_xlock(&(_sc)->sc_sx) #define BRIDGE_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) #define BRIDGE_LOCK_ASSERT(_sc) sx_assert(&(_sc)->sc_sx, SX_XLOCKED) #define BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(_sc) \ MPASS(in_epoch(net_epoch_preempt) || sx_xlocked(&(_sc)->sc_sx)) #define BRIDGE_UNLOCK_ASSERT(_sc) sx_assert(&(_sc)->sc_sx, SX_UNLOCKED) #define BRIDGE_RT_LOCK(_sc) mtx_lock(&(_sc)->sc_rt_mtx) #define BRIDGE_RT_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_rt_mtx) #define BRIDGE_RT_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_rt_mtx, MA_OWNED) #define BRIDGE_RT_LOCK_OR_NET_EPOCH_ASSERT(_sc) \ MPASS(in_epoch(net_epoch_preempt) || mtx_owned(&(_sc)->sc_rt_mtx)) struct bridge_softc; /* * Bridge interface list entry. */ struct bridge_iflist { CK_LIST_ENTRY(bridge_iflist) bif_next; struct ifnet *bif_ifp; /* member if */ struct bridge_softc *bif_sc; /* parent bridge */ struct bstp_port bif_stp; /* STP state */ uint32_t bif_flags; /* member if flags */ int bif_savedcaps; /* saved capabilities */ uint32_t bif_addrmax; /* max # of addresses */ uint32_t bif_addrcnt; /* cur. # of addresses */ uint32_t bif_addrexceeded;/* # of address violations */ struct epoch_context bif_epoch_ctx; ether_vlanid_t bif_pvid; /* port vlan id */ ifbvlan_set_t bif_vlan_set; /* if allowed tagged vlans */ }; /* * Bridge route node. */ struct bridge_rtnode { CK_LIST_ENTRY(bridge_rtnode) brt_hash; /* hash table linkage */ CK_LIST_ENTRY(bridge_rtnode) brt_list; /* list linkage */ struct bridge_iflist *brt_dst; /* destination if */ unsigned long brt_expire; /* expiration time */ uint8_t brt_flags; /* address flags */ uint8_t brt_addr[ETHER_ADDR_LEN]; ether_vlanid_t brt_vlan; /* vlan id */ struct vnet *brt_vnet; struct epoch_context brt_epoch_ctx; }; #define brt_ifp brt_dst->bif_ifp /* * Software state for each bridge. */ struct bridge_softc { struct ifnet *sc_ifp; /* make this an interface */ LIST_ENTRY(bridge_softc) sc_list; struct sx sc_sx; struct mtx sc_rt_mtx; uint32_t sc_brtmax; /* max # of addresses */ uint32_t sc_brtcnt; /* cur. # of addresses */ uint32_t sc_brttimeout; /* rt timeout in seconds */ struct callout sc_brcallout; /* bridge callout */ CK_LIST_HEAD(, bridge_iflist) sc_iflist; /* member interface list */ CK_LIST_HEAD(, bridge_rtnode) *sc_rthash; /* our forwarding table */ CK_LIST_HEAD(, bridge_rtnode) sc_rtlist; /* list version of above */ uint32_t sc_rthash_key; /* key for hash */ CK_LIST_HEAD(, bridge_iflist) sc_spanlist; /* span ports list */ struct bstp_state sc_stp; /* STP state */ uint32_t sc_brtexceeded; /* # of cache drops */ struct ifnet *sc_ifaddr; /* member mac copied from */ struct ether_addr sc_defaddr; /* Default MAC address */ if_input_fn_t sc_if_input; /* Saved copy of if_input */ struct epoch_context sc_epoch_ctx; ifbr_flags_t sc_flags; /* bridge flags */ ether_vlanid_t sc_defpvid; /* default PVID */ }; VNET_DEFINE_STATIC(struct sx, bridge_list_sx); #define V_bridge_list_sx VNET(bridge_list_sx) static eventhandler_tag bridge_detach_cookie; int bridge_rtable_prune_period = BRIDGE_RTABLE_PRUNE_PERIOD; VNET_DEFINE_STATIC(uma_zone_t, bridge_rtnode_zone); #define V_bridge_rtnode_zone VNET(bridge_rtnode_zone) static int bridge_clone_create(struct if_clone *, char *, size_t, struct ifc_data *, struct ifnet **); static int bridge_clone_destroy(struct if_clone *, struct ifnet *, uint32_t); static int bridge_ioctl(struct ifnet *, u_long, caddr_t); static void bridge_mutecaps(struct bridge_softc *); static void bridge_set_ifcap(struct bridge_softc *, struct bridge_iflist *, int); static void bridge_ifdetach(void *arg __unused, struct ifnet *); static void bridge_init(void *); static void bridge_dummynet(struct mbuf *, struct ifnet *); static bool bridge_same(const void *, const void *); static void *bridge_get_softc(struct ifnet *); static void bridge_stop(struct ifnet *, int); static int bridge_transmit(struct ifnet *, struct mbuf *); #ifdef ALTQ static void bridge_altq_start(if_t); static int bridge_altq_transmit(if_t, struct mbuf *); #endif static void bridge_qflush(struct ifnet *); static struct mbuf *bridge_input(struct ifnet *, struct mbuf *); static void bridge_inject(struct ifnet *, struct mbuf *); static int bridge_output(struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *); static int bridge_enqueue(struct bridge_softc *, struct ifnet *, struct mbuf *, struct bridge_iflist *); static void bridge_rtdelete(struct bridge_softc *, struct ifnet *ifp, int); static void bridge_forward(struct bridge_softc *, struct bridge_iflist *, struct mbuf *m); static bool bridge_member_ifaddrs(void); static void bridge_timer(void *); static void bridge_broadcast(struct bridge_softc *, struct ifnet *, struct mbuf *, int); static void bridge_span(struct bridge_softc *, struct mbuf *); static int bridge_rtupdate(struct bridge_softc *, const uint8_t *, ether_vlanid_t, struct bridge_iflist *, int, uint8_t); static struct ifnet *bridge_rtlookup(struct bridge_softc *, const uint8_t *, ether_vlanid_t); static void bridge_rttrim(struct bridge_softc *); static void bridge_rtage(struct bridge_softc *); static void bridge_rtflush(struct bridge_softc *, int); static int bridge_rtdaddr(struct bridge_softc *, const uint8_t *, ether_vlanid_t); static bool bridge_vfilter_in(const struct bridge_iflist *, struct mbuf *); static bool bridge_vfilter_out(const struct bridge_iflist *, const struct mbuf *); static void bridge_rtable_init(struct bridge_softc *); static void bridge_rtable_fini(struct bridge_softc *); static int bridge_rtnode_addr_cmp(const uint8_t *, const uint8_t *); static struct bridge_rtnode *bridge_rtnode_lookup(struct bridge_softc *, const uint8_t *, ether_vlanid_t); static int bridge_rtnode_insert(struct bridge_softc *, struct bridge_rtnode *); static void bridge_rtnode_destroy(struct bridge_softc *, struct bridge_rtnode *); static void bridge_rtable_expire(struct ifnet *, int); static void bridge_state_change(struct ifnet *, int); static struct bridge_iflist *bridge_lookup_member(struct bridge_softc *, const char *name); static struct bridge_iflist *bridge_lookup_member_if(struct bridge_softc *, struct ifnet *ifp); static void bridge_delete_member(struct bridge_softc *, struct bridge_iflist *, int); static void bridge_delete_span(struct bridge_softc *, struct bridge_iflist *); static int bridge_ioctl_add(struct bridge_softc *, void *); static int bridge_ioctl_del(struct bridge_softc *, void *); static int bridge_ioctl_gifflags(struct bridge_softc *, void *); static int bridge_ioctl_sifflags(struct bridge_softc *, void *); static int bridge_ioctl_scache(struct bridge_softc *, void *); static int bridge_ioctl_gcache(struct bridge_softc *, void *); static int bridge_ioctl_gifs(struct bridge_softc *, void *); static int bridge_ioctl_rts(struct bridge_softc *, void *); static int bridge_ioctl_saddr(struct bridge_softc *, void *); static int bridge_ioctl_sto(struct bridge_softc *, void *); static int bridge_ioctl_gto(struct bridge_softc *, void *); static int bridge_ioctl_daddr(struct bridge_softc *, void *); static int bridge_ioctl_flush(struct bridge_softc *, void *); static int bridge_ioctl_gpri(struct bridge_softc *, void *); static int bridge_ioctl_spri(struct bridge_softc *, void *); static int bridge_ioctl_ght(struct bridge_softc *, void *); static int bridge_ioctl_sht(struct bridge_softc *, void *); static int bridge_ioctl_gfd(struct bridge_softc *, void *); static int bridge_ioctl_sfd(struct bridge_softc *, void *); static int bridge_ioctl_gma(struct bridge_softc *, void *); static int bridge_ioctl_sma(struct bridge_softc *, void *); static int bridge_ioctl_sifprio(struct bridge_softc *, void *); static int bridge_ioctl_sifcost(struct bridge_softc *, void *); static int bridge_ioctl_sifmaxaddr(struct bridge_softc *, void *); static int bridge_ioctl_sifpvid(struct bridge_softc *, void *); static int bridge_ioctl_sifvlanset(struct bridge_softc *, void *); static int bridge_ioctl_gifvlanset(struct bridge_softc *, void *); static int bridge_ioctl_addspan(struct bridge_softc *, void *); static int bridge_ioctl_delspan(struct bridge_softc *, void *); static int bridge_ioctl_gbparam(struct bridge_softc *, void *); static int bridge_ioctl_grte(struct bridge_softc *, void *); static int bridge_ioctl_gifsstp(struct bridge_softc *, void *); static int bridge_ioctl_sproto(struct bridge_softc *, void *); static int bridge_ioctl_stxhc(struct bridge_softc *, void *); static int bridge_ioctl_gflags(struct bridge_softc *, void *); static int bridge_ioctl_sflags(struct bridge_softc *, void *); static int bridge_ioctl_gdefpvid(struct bridge_softc *, void *); static int bridge_ioctl_sdefpvid(struct bridge_softc *, void *); static int bridge_pfil(struct mbuf **, struct ifnet *, struct ifnet *, int); #ifdef INET static int bridge_ip_checkbasic(struct mbuf **mp); static int bridge_fragment(struct ifnet *, struct mbuf **mp, struct ether_header *, int, struct llc *); #endif /* INET */ #ifdef INET6 static int bridge_ip6_checkbasic(struct mbuf **mp); #endif /* INET6 */ static void bridge_linkstate(struct ifnet *ifp); static void bridge_linkcheck(struct bridge_softc *sc); /* * Use the "null" value from IEEE 802.1Q-2014 Table 9-2 * to indicate untagged frames. */ #define VLANTAGOF(_m) \ ((_m->m_flags & M_VLANTAG) ? EVL_VLANOFTAG(_m->m_pkthdr.ether_vtag) : DOT1Q_VID_NULL) static struct bstp_cb_ops bridge_ops = { .bcb_state = bridge_state_change, .bcb_rtage = bridge_rtable_expire }; SYSCTL_DECL(_net_link); static SYSCTL_NODE(_net_link, IFT_BRIDGE, bridge, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Bridge"); /* only pass IP[46] packets when pfil is enabled */ VNET_DEFINE_STATIC(int, pfil_onlyip) = 1; #define V_pfil_onlyip VNET(pfil_onlyip) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_onlyip, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_onlyip), 0, "Only pass IP packets when pfil is enabled"); /* run pfil hooks on the bridge interface */ VNET_DEFINE_STATIC(int, pfil_bridge) = 0; #define V_pfil_bridge VNET(pfil_bridge) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_bridge, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_bridge), 0, "Packet filter on the bridge interface"); /* layer2 filter with ipfw */ VNET_DEFINE_STATIC(int, pfil_ipfw); #define V_pfil_ipfw VNET(pfil_ipfw) /* layer2 ARP filter with ipfw */ VNET_DEFINE_STATIC(int, pfil_ipfw_arp); #define V_pfil_ipfw_arp VNET(pfil_ipfw_arp) SYSCTL_INT(_net_link_bridge, OID_AUTO, ipfw_arp, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_ipfw_arp), 0, "Filter ARP packets through IPFW layer2"); /* run pfil hooks on the member interface */ VNET_DEFINE_STATIC(int, pfil_member) = 0; #define V_pfil_member VNET(pfil_member) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_member, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_member), 0, "Packet filter on the member interface"); /* run pfil hooks on the physical interface for locally destined packets */ VNET_DEFINE_STATIC(int, pfil_local_phys); #define V_pfil_local_phys VNET(pfil_local_phys) SYSCTL_INT(_net_link_bridge, OID_AUTO, pfil_local_phys, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(pfil_local_phys), 0, "Packet filter on the physical interface for locally destined packets"); /* log STP state changes */ VNET_DEFINE_STATIC(int, log_stp); #define V_log_stp VNET(log_stp) SYSCTL_INT(_net_link_bridge, OID_AUTO, log_stp, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(log_stp), 0, "Log STP state changes"); /* share MAC with first bridge member */ VNET_DEFINE_STATIC(int, bridge_inherit_mac); #define V_bridge_inherit_mac VNET(bridge_inherit_mac) SYSCTL_INT(_net_link_bridge, OID_AUTO, inherit_mac, CTLFLAG_RWTUN | CTLFLAG_VNET, &VNET_NAME(bridge_inherit_mac), 0, "Inherit MAC address from the first bridge member"); VNET_DEFINE_STATIC(int, allow_llz_overlap) = 0; #define V_allow_llz_overlap VNET(allow_llz_overlap) SYSCTL_INT(_net_link_bridge, OID_AUTO, allow_llz_overlap, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(allow_llz_overlap), 0, "Allow overlap of link-local scope " "zones of a bridge interface and the member interfaces"); /* log MAC address port flapping */ VNET_DEFINE_STATIC(bool, log_mac_flap) = true; #define V_log_mac_flap VNET(log_mac_flap) SYSCTL_BOOL(_net_link_bridge, OID_AUTO, log_mac_flap, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(log_mac_flap), true, "Log MAC address port flapping"); /* allow IP addresses on bridge members */ VNET_DEFINE_STATIC(bool, member_ifaddrs) = false; #define V_member_ifaddrs VNET(member_ifaddrs) SYSCTL_BOOL(_net_link_bridge, OID_AUTO, member_ifaddrs, CTLFLAG_RW | CTLFLAG_VNET, &VNET_NAME(member_ifaddrs), false, "Allow layer 3 addresses on bridge members"); static bool bridge_member_ifaddrs(void) { return (V_member_ifaddrs); } VNET_DEFINE_STATIC(int, log_interval) = 5; VNET_DEFINE_STATIC(int, log_count) = 0; VNET_DEFINE_STATIC(struct timeval, log_last) = { 0 }; #define V_log_interval VNET(log_interval) #define V_log_count VNET(log_count) #define V_log_last VNET(log_last) struct bridge_control { int (*bc_func)(struct bridge_softc *, void *); int bc_argsize; int bc_flags; }; #define BC_F_COPYIN 0x01 /* copy arguments in */ #define BC_F_COPYOUT 0x02 /* copy arguments out */ #define BC_F_SUSER 0x04 /* do super-user check */ static const struct bridge_control bridge_control_table[] = { { bridge_ioctl_add, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_del, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_sifflags, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_scache, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gcache, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_gifs, sizeof(struct ifbifconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_rts, sizeof(struct ifbaconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_saddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sto, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gto, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_daddr, sizeof(struct ifbareq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_flush, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gpri, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_spri, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_ght, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sht, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gfd, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sfd, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gma, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sma, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifprio, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifcost, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_addspan, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_delspan, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gbparam, sizeof(struct ifbropreq), BC_F_COPYOUT }, { bridge_ioctl_grte, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_gifsstp, sizeof(struct ifbpstpconf), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_sproto, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_stxhc, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifmaxaddr, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifpvid, sizeof(struct ifbreq), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_sifvlanset, sizeof(struct ifbif_vlan_req), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gifvlanset, sizeof(struct ifbif_vlan_req), BC_F_COPYIN|BC_F_COPYOUT }, { bridge_ioctl_gflags, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sflags, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, { bridge_ioctl_gdefpvid, sizeof(struct ifbrparam), BC_F_COPYOUT }, { bridge_ioctl_sdefpvid, sizeof(struct ifbrparam), BC_F_COPYIN|BC_F_SUSER }, }; static const int bridge_control_table_size = nitems(bridge_control_table); VNET_DEFINE_STATIC(LIST_HEAD(, bridge_softc), bridge_list) = LIST_HEAD_INITIALIZER(); #define V_bridge_list VNET(bridge_list) #define BRIDGE_LIST_LOCK_INIT(x) sx_init(&V_bridge_list_sx, \ "if_bridge list") #define BRIDGE_LIST_LOCK_DESTROY(x) sx_destroy(&V_bridge_list_sx) #define BRIDGE_LIST_LOCK(x) sx_xlock(&V_bridge_list_sx) #define BRIDGE_LIST_UNLOCK(x) sx_xunlock(&V_bridge_list_sx) VNET_DEFINE_STATIC(struct if_clone *, bridge_cloner); #define V_bridge_cloner VNET(bridge_cloner) static const char bridge_name[] = "bridge"; static void vnet_bridge_init(const void *unused __unused) { V_bridge_rtnode_zone = uma_zcreate("bridge_rtnode", sizeof(struct bridge_rtnode), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); BRIDGE_LIST_LOCK_INIT(); struct if_clone_addreq req = { .create_f = bridge_clone_create, .destroy_f = bridge_clone_destroy, .flags = IFC_F_AUTOUNIT, }; V_bridge_cloner = ifc_attach_cloner(bridge_name, &req); } VNET_SYSINIT(vnet_bridge_init, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY, vnet_bridge_init, NULL); static void vnet_bridge_uninit(const void *unused __unused) { ifc_detach_cloner(V_bridge_cloner); V_bridge_cloner = NULL; BRIDGE_LIST_LOCK_DESTROY(); /* Callbacks may use the UMA zone. */ NET_EPOCH_DRAIN_CALLBACKS(); uma_zdestroy(V_bridge_rtnode_zone); } VNET_SYSUNINIT(vnet_bridge_uninit, SI_SUB_PSEUDO, SI_ORDER_ANY, vnet_bridge_uninit, NULL); static int bridge_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: bridge_dn_p = bridge_dummynet; bridge_same_p = bridge_same; bridge_get_softc_p = bridge_get_softc; bridge_member_ifaddrs_p = bridge_member_ifaddrs; bridge_detach_cookie = EVENTHANDLER_REGISTER( ifnet_departure_event, bridge_ifdetach, NULL, EVENTHANDLER_PRI_ANY); break; case MOD_UNLOAD: EVENTHANDLER_DEREGISTER(ifnet_departure_event, bridge_detach_cookie); bridge_dn_p = NULL; bridge_same_p = NULL; bridge_get_softc_p = NULL; bridge_member_ifaddrs_p = NULL; break; default: return (EOPNOTSUPP); } return (0); } static moduledata_t bridge_mod = { "if_bridge", bridge_modevent, 0 }; DECLARE_MODULE(if_bridge, bridge_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_VERSION(if_bridge, 1); MODULE_DEPEND(if_bridge, bridgestp, 1, 1, 1); /* * handler for net.link.bridge.ipfw */ static int sysctl_pfil_ipfw(SYSCTL_HANDLER_ARGS) { int enable = V_pfil_ipfw; int error; error = sysctl_handle_int(oidp, &enable, 0, req); enable &= 1; if (enable != V_pfil_ipfw) { V_pfil_ipfw = enable; /* * Disable pfil so that ipfw doesnt run twice, if the user * really wants both then they can re-enable pfil_bridge and/or * pfil_member. Also allow non-ip packets as ipfw can filter by * layer2 type. */ if (V_pfil_ipfw) { V_pfil_onlyip = 0; V_pfil_bridge = 0; V_pfil_member = 0; } } return (error); } SYSCTL_PROC(_net_link_bridge, OID_AUTO, ipfw, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_VNET | CTLFLAG_NEEDGIANT, &VNET_NAME(pfil_ipfw), 0, &sysctl_pfil_ipfw, "I", "Layer2 filter with IPFW"); #ifdef VIMAGE static void bridge_reassign(struct ifnet *ifp, struct vnet *newvnet, char *arg) { struct bridge_softc *sc = ifp->if_softc; struct bridge_iflist *bif; BRIDGE_LOCK(sc); while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif, 0); while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { bridge_delete_span(sc, bif); } BRIDGE_UNLOCK(sc); ether_reassign(ifp, newvnet, arg); } #endif /* * bridge_get_softc: * * Return the bridge softc for an ifnet. */ static void * bridge_get_softc(struct ifnet *ifp) { struct bridge_iflist *bif; NET_EPOCH_ASSERT(); bif = ifp->if_bridge; if (bif == NULL) return (NULL); return (bif->bif_sc); } /* * bridge_same: * * Return true if two interfaces are in the same bridge. This is only used by * bridgestp via bridge_same_p. */ static bool bridge_same(const void *bifap, const void *bifbp) { const struct bridge_iflist *bifa = bifap, *bifb = bifbp; NET_EPOCH_ASSERT(); if (bifa == NULL || bifb == NULL) return (false); return (bifa->bif_sc == bifb->bif_sc); } /* * bridge_clone_create: * * Create a new bridge instance. */ static int bridge_clone_create(struct if_clone *ifc, char *name, size_t len, struct ifc_data *ifd, struct ifnet **ifpp) { struct bridge_softc *sc; struct ifnet *ifp; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); ifp = sc->sc_ifp = if_alloc(IFT_ETHER); BRIDGE_LOCK_INIT(sc); sc->sc_brtmax = BRIDGE_RTABLE_MAX; sc->sc_brttimeout = BRIDGE_RTABLE_TIMEOUT; /* Initialize our routing table. */ bridge_rtable_init(sc); callout_init_mtx(&sc->sc_brcallout, &sc->sc_rt_mtx, 0); CK_LIST_INIT(&sc->sc_iflist); CK_LIST_INIT(&sc->sc_spanlist); ifp->if_softc = sc; if_initname(ifp, bridge_name, ifd->unit); ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_capabilities = ifp->if_capenable = IFCAP_VLAN_HWTAGGING; ifp->if_ioctl = bridge_ioctl; #ifdef ALTQ ifp->if_start = bridge_altq_start; ifp->if_transmit = bridge_altq_transmit; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = 0; IFQ_SET_READY(&ifp->if_snd); #else ifp->if_transmit = bridge_transmit; #endif ifp->if_qflush = bridge_qflush; ifp->if_init = bridge_init; ifp->if_type = IFT_BRIDGE; ether_gen_addr(ifp, &sc->sc_defaddr); bstp_attach(&sc->sc_stp, &bridge_ops); ether_ifattach(ifp, sc->sc_defaddr.octet); /* Now undo some of the damage... */ ifp->if_baudrate = 0; #ifdef VIMAGE ifp->if_reassign = bridge_reassign; #endif sc->sc_if_input = ifp->if_input; /* ether_input */ ifp->if_input = bridge_inject; /* * Allow BRIDGE_INPUT() to pass in packets originating from the bridge * itself via bridge_inject(). This is required for netmap but * otherwise has no effect. */ ifp->if_bridge_input = bridge_input; BRIDGE_LIST_LOCK(); LIST_INSERT_HEAD(&V_bridge_list, sc, sc_list); BRIDGE_LIST_UNLOCK(); *ifpp = ifp; return (0); } static void bridge_clone_destroy_cb(struct epoch_context *ctx) { struct bridge_softc *sc; sc = __containerof(ctx, struct bridge_softc, sc_epoch_ctx); BRIDGE_LOCK_DESTROY(sc); free(sc, M_DEVBUF); } /* * bridge_clone_destroy: * * Destroy a bridge instance. */ static int bridge_clone_destroy(struct if_clone *ifc, struct ifnet *ifp, uint32_t flags) { struct bridge_softc *sc = ifp->if_softc; struct bridge_iflist *bif; struct epoch_tracker et; BRIDGE_LOCK(sc); bridge_stop(ifp, 1); ifp->if_flags &= ~IFF_UP; while ((bif = CK_LIST_FIRST(&sc->sc_iflist)) != NULL) bridge_delete_member(sc, bif, 0); while ((bif = CK_LIST_FIRST(&sc->sc_spanlist)) != NULL) { bridge_delete_span(sc, bif); } /* Tear down the routing table. */ bridge_rtable_fini(sc); BRIDGE_UNLOCK(sc); NET_EPOCH_ENTER(et); callout_drain(&sc->sc_brcallout); BRIDGE_LIST_LOCK(); LIST_REMOVE(sc, sc_list); BRIDGE_LIST_UNLOCK(); bstp_detach(&sc->sc_stp); #ifdef ALTQ IFQ_PURGE(&ifp->if_snd); #endif NET_EPOCH_EXIT(et); ether_ifdetach(ifp); if_free(ifp); NET_EPOCH_CALL(bridge_clone_destroy_cb, &sc->sc_epoch_ctx); return (0); } /* * bridge_ioctl: * * Handle a control request from the operator. */ static int bridge_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct bridge_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct bridge_iflist *bif; struct thread *td = curthread; union { struct ifbreq ifbreq; struct ifbifconf ifbifconf; struct ifbareq ifbareq; struct ifbaconf ifbaconf; struct ifbrparam ifbrparam; struct ifbropreq ifbropreq; struct ifbif_vlan_req ifvlanreq; } args; struct ifdrv *ifd = (struct ifdrv *) data; const struct bridge_control *bc; int error = 0, oldmtu; BRIDGE_LOCK(sc); switch (cmd) { case SIOCADDMULTI: case SIOCDELMULTI: break; case SIOCGDRVSPEC: case SIOCSDRVSPEC: if (ifd->ifd_cmd >= bridge_control_table_size) { error = EXTERROR(EINVAL, "Invalid control command"); break; } bc = &bridge_control_table[ifd->ifd_cmd]; if (cmd == SIOCGDRVSPEC && (bc->bc_flags & BC_F_COPYOUT) == 0) { error = EXTERROR(EINVAL, "Inappropriate ioctl for command " "(expected SIOCSDRVSPEC)"); break; } else if (cmd == SIOCSDRVSPEC && (bc->bc_flags & BC_F_COPYOUT) != 0) { error = EXTERROR(EINVAL, "Inappropriate ioctl for command " "(expected SIOCGDRVSPEC)"); break; } if (bc->bc_flags & BC_F_SUSER) { error = priv_check(td, PRIV_NET_BRIDGE); if (error) { EXTERROR(error, "PRIV_NET_BRIDGE required"); break; } } if (ifd->ifd_len != bc->bc_argsize || ifd->ifd_len > sizeof(args)) { error = EXTERROR(EINVAL, "Invalid argument size"); break; } bzero(&args, sizeof(args)); if (bc->bc_flags & BC_F_COPYIN) { error = copyin(ifd->ifd_data, &args, ifd->ifd_len); if (error) break; } oldmtu = ifp->if_mtu; error = (*bc->bc_func)(sc, &args); if (error) break; /* * Bridge MTU may change during addition of the first port. * If it did, do network layer specific procedure. */ if (ifp->if_mtu != oldmtu) if_notifymtu(ifp); if (bc->bc_flags & BC_F_COPYOUT) error = copyout(&args, ifd->ifd_data, ifd->ifd_len); break; case SIOCSIFFLAGS: if (!(ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked down and it is running, * then stop and disable it. */ bridge_stop(ifp, 1); } else if ((ifp->if_flags & IFF_UP) && !(ifp->if_drv_flags & IFF_DRV_RUNNING)) { /* * If interface is marked up and it is stopped, then * start it. */ BRIDGE_UNLOCK(sc); (*ifp->if_init)(sc); BRIDGE_LOCK(sc); } break; case SIOCSIFMTU: oldmtu = sc->sc_ifp->if_mtu; if (ifr->ifr_mtu < IF_MINMTU) { error = EXTERROR(EINVAL, "Requested MTU is lower than IF_MINMTU"); break; } if (CK_LIST_EMPTY(&sc->sc_iflist)) { sc->sc_ifp->if_mtu = ifr->ifr_mtu; break; } CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { error = (*bif->bif_ifp->if_ioctl)(bif->bif_ifp, SIOCSIFMTU, (caddr_t)ifr); if (error != 0) { log(LOG_NOTICE, "%s: invalid MTU: %u for" " member %s\n", sc->sc_ifp->if_xname, ifr->ifr_mtu, bif->bif_ifp->if_xname); error = EINVAL; break; } } if (error) { /* Restore the previous MTU on all member interfaces. */ ifr->ifr_mtu = oldmtu; CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { (*bif->bif_ifp->if_ioctl)(bif->bif_ifp, SIOCSIFMTU, (caddr_t)ifr); } EXTERROR(error, "Failed to set MTU on member interface"); } else { sc->sc_ifp->if_mtu = ifr->ifr_mtu; } break; default: /* * drop the lock as ether_ioctl() will call bridge_start() and * cause the lock to be recursed. */ BRIDGE_UNLOCK(sc); error = ether_ioctl(ifp, cmd, data); BRIDGE_LOCK(sc); break; } BRIDGE_UNLOCK(sc); return (error); } /* * bridge_mutecaps: * * Clear or restore unwanted capabilities on the member interface */ static void bridge_mutecaps(struct bridge_softc *sc) { struct bridge_iflist *bif; int enabled, mask; BRIDGE_LOCK_ASSERT(sc); /* Initial bitmask of capabilities to test */ mask = BRIDGE_IFCAPS_MASK; CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { /* Every member must support it or it's disabled */ mask &= bif->bif_savedcaps; } CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { enabled = bif->bif_ifp->if_capenable; enabled &= ~BRIDGE_IFCAPS_STRIP; /* Strip off mask bits and enable them again if allowed */ enabled &= ~BRIDGE_IFCAPS_MASK; enabled |= mask; bridge_set_ifcap(sc, bif, enabled); } } static void bridge_set_ifcap(struct bridge_softc *sc, struct bridge_iflist *bif, int set) { struct ifnet *ifp = bif->bif_ifp; struct ifreq ifr; int error, mask, stuck; bzero(&ifr, sizeof(ifr)); ifr.ifr_reqcap = set; if (ifp->if_capenable != set) { error = (*ifp->if_ioctl)(ifp, SIOCSIFCAP, (caddr_t)&ifr); if (error) if_printf(sc->sc_ifp, "error setting capabilities on %s: %d\n", ifp->if_xname, error); mask = BRIDGE_IFCAPS_MASK | BRIDGE_IFCAPS_STRIP; stuck = ifp->if_capenable & mask & ~set; if (stuck != 0) if_printf(sc->sc_ifp, "can't disable some capabilities on %s: 0x%x\n", ifp->if_xname, stuck); } } /* * bridge_lookup_member: * * Lookup a bridge member interface. */ static struct bridge_iflist * bridge_lookup_member(struct bridge_softc *sc, const char *name) { struct bridge_iflist *bif; struct ifnet *ifp; BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { ifp = bif->bif_ifp; if (strcmp(ifp->if_xname, name) == 0) return (bif); } return (NULL); } /* * bridge_lookup_member_if: * * Lookup a bridge member interface by ifnet*. */ static struct bridge_iflist * bridge_lookup_member_if(struct bridge_softc *sc, struct ifnet *member_ifp) { BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); return (member_ifp->if_bridge); } static void bridge_delete_member_cb(struct epoch_context *ctx) { struct bridge_iflist *bif; bif = __containerof(ctx, struct bridge_iflist, bif_epoch_ctx); free(bif, M_DEVBUF); } /* * bridge_delete_member: * * Delete the specified member interface. */ static void bridge_delete_member(struct bridge_softc *sc, struct bridge_iflist *bif, int gone) { struct ifnet *ifs = bif->bif_ifp; struct ifnet *fif = NULL; struct bridge_iflist *bifl; BRIDGE_LOCK_ASSERT(sc); if (bif->bif_flags & IFBIF_STP) bstp_disable(&bif->bif_stp); ifs->if_bridge = NULL; CK_LIST_REMOVE(bif, bif_next); /* * If removing the interface that gave the bridge its mac address, set * the mac address of the bridge to the address of the next member, or * to its default address if no members are left. */ if (V_bridge_inherit_mac && sc->sc_ifaddr == ifs) { if (CK_LIST_EMPTY(&sc->sc_iflist)) { bcopy(&sc->sc_defaddr, IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = NULL; } else { bifl = CK_LIST_FIRST(&sc->sc_iflist); fif = bifl->bif_ifp; bcopy(IF_LLADDR(fif), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = fif; } EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); } bridge_linkcheck(sc); bridge_mutecaps(sc); /* recalcuate now this interface is removed */ BRIDGE_RT_LOCK(sc); bridge_rtdelete(sc, ifs, IFBF_FLUSHALL); BRIDGE_RT_UNLOCK(sc); KASSERT(bif->bif_addrcnt == 0, ("%s: %d bridge routes referenced", __func__, bif->bif_addrcnt)); ifs->if_bridge_output = NULL; ifs->if_bridge_input = NULL; ifs->if_bridge_linkstate = NULL; if (!gone) { switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: /* * Take the interface out of promiscuous mode, but only * if it was promiscuous in the first place. It might * not be if we're in the bridge_ioctl_add() error path. */ if (ifs->if_flags & IFF_PROMISC) (void) ifpromisc(ifs, 0); break; case IFT_GIF: break; default: #ifdef DIAGNOSTIC panic("bridge_delete_member: impossible"); #endif break; } /* Re-enable any interface capabilities */ bridge_set_ifcap(sc, bif, bif->bif_savedcaps); } bstp_destroy(&bif->bif_stp); /* prepare to free */ NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); } /* * bridge_delete_span: * * Delete the specified span interface. */ static void bridge_delete_span(struct bridge_softc *sc, struct bridge_iflist *bif) { BRIDGE_LOCK_ASSERT(sc); KASSERT(bif->bif_ifp->if_bridge == NULL, ("%s: not a span interface", __func__)); CK_LIST_REMOVE(bif, bif_next); NET_EPOCH_CALL(bridge_delete_member_cb, &bif->bif_epoch_ctx); } static int bridge_ioctl_add(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif = NULL; struct ifnet *ifs; int error = 0; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (EXTERROR(ENOENT, "No such interface", req->ifbr_ifsname)); if (ifs->if_ioctl == NULL) /* must be supported */ return (EXTERROR(EINVAL, "Interface must support ioctl(2)")); /* * If the new interface is a vlan(4), it could be a bridge SVI. * Don't allow such things to be added to bridges. */ if (ifs->if_type == IFT_L2VLAN) { struct ifnet *parent; struct epoch_tracker et; bool is_bridge; /* * Entering NET_EPOCH with BRIDGE_LOCK held, but this is okay * since we don't sleep here. */ NET_EPOCH_ENTER(et); parent = VLAN_TRUNKDEV(ifs); is_bridge = (parent != NULL && parent->if_type == IFT_BRIDGE); NET_EPOCH_EXIT(et); if (is_bridge) return (EXTERROR(EINVAL, "Bridge SVI cannot be added to a bridge")); } /* If it's in the span list, it can't be a member. */ CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) return (EXTERROR(EBUSY, "Span interface cannot be a member")); if (ifs->if_bridge) { struct bridge_iflist *sbif = ifs->if_bridge; if (sbif->bif_sc == sc) return (EXTERROR(EEXIST, "Interface is already a member of this bridge")); return (EXTERROR(EBUSY, "Interface is already a member of another bridge")); } switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: case IFT_GIF: /* permitted interface types */ break; default: return (EXTERROR(EINVAL, "Unsupported interface type")); } #ifdef INET6 /* * Two valid inet6 addresses with link-local scope must not be * on the parent interface and the member interfaces at the * same time. This restriction is needed to prevent violation * of link-local scope zone. Attempts to add a member * interface which has inet6 addresses when the parent has * inet6 triggers removal of all inet6 addresses on the member * interface. */ /* Check if the parent interface has a link-local scope addr. */ if (V_allow_llz_overlap == 0 && in6ifa_llaonifp(sc->sc_ifp) != NULL) { /* * If any, remove all inet6 addresses from the member * interfaces. */ CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (in6ifa_llaonifp(bif->bif_ifp)) { in6_ifdetach(bif->bif_ifp); if_printf(sc->sc_ifp, "IPv6 addresses on %s have been removed " "before adding it as a member to prevent " "IPv6 address scope violation.\n", bif->bif_ifp->if_xname); } } if (in6ifa_llaonifp(ifs)) { in6_ifdetach(ifs); if_printf(sc->sc_ifp, "IPv6 addresses on %s have been removed " "before adding it as a member to prevent " "IPv6 address scope violation.\n", ifs->if_xname); } } #endif /* * If member_ifaddrs is disabled, do not allow an interface with * assigned IP addresses to be added to a bridge. */ if (!V_member_ifaddrs) { struct ifaddr *ifa; CK_STAILQ_FOREACH(ifa, &ifs->if_addrhead, ifa_link) { #ifdef INET if (ifa->ifa_addr->sa_family == AF_INET) return (EXTERROR(EINVAL, "Member interface may not have " "an IPv4 address configured")); #endif #ifdef INET6 if (ifa->ifa_addr->sa_family == AF_INET6) return (EXTERROR(EINVAL, "Member interface may not have " "an IPv6 address configured")); #endif } } /* Allow the first Ethernet member to define the MTU */ if (CK_LIST_EMPTY(&sc->sc_iflist)) sc->sc_ifp->if_mtu = ifs->if_mtu; else if (sc->sc_ifp->if_mtu != ifs->if_mtu) { struct ifreq ifr; snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifs->if_xname); ifr.ifr_mtu = sc->sc_ifp->if_mtu; error = (*ifs->if_ioctl)(ifs, SIOCSIFMTU, (caddr_t)&ifr); if (error != 0) { log(LOG_NOTICE, "%s: invalid MTU: %u for" " new member %s\n", sc->sc_ifp->if_xname, ifr.ifr_mtu, ifs->if_xname); return (EXTERROR(EINVAL, "Failed to set MTU on new member")); } } bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); if (bif == NULL) return (ENOMEM); bif->bif_sc = sc; bif->bif_ifp = ifs; bif->bif_flags = IFBIF_LEARNING | IFBIF_DISCOVER; bif->bif_savedcaps = ifs->if_capenable; if (sc->sc_flags & IFBRF_VLANFILTER) bif->bif_pvid = sc->sc_defpvid; + if (sc->sc_flags & IFBRF_DEFQINQ) + bif->bif_flags |= IFBIF_QINQ; /* * Assign the interface's MAC address to the bridge if it's the first * member and the MAC address of the bridge has not been changed from * the default randomly generated one. */ if (V_bridge_inherit_mac && CK_LIST_EMPTY(&sc->sc_iflist) && !memcmp(IF_LLADDR(sc->sc_ifp), sc->sc_defaddr.octet, ETHER_ADDR_LEN)) { bcopy(IF_LLADDR(ifs), IF_LLADDR(sc->sc_ifp), ETHER_ADDR_LEN); sc->sc_ifaddr = ifs; EVENTHANDLER_INVOKE(iflladdr_event, sc->sc_ifp); } ifs->if_bridge = bif; ifs->if_bridge_output = bridge_output; ifs->if_bridge_input = bridge_input; ifs->if_bridge_linkstate = bridge_linkstate; bstp_create(&sc->sc_stp, &bif->bif_stp, bif->bif_ifp); /* * XXX: XLOCK HERE!?! * * NOTE: insert_***HEAD*** should be safe for the traversals. */ CK_LIST_INSERT_HEAD(&sc->sc_iflist, bif, bif_next); /* Set interface capabilities to the intersection set of all members */ bridge_mutecaps(sc); bridge_linkcheck(sc); /* Place the interface into promiscuous mode */ switch (ifs->if_type) { case IFT_ETHER: case IFT_L2VLAN: error = ifpromisc(ifs, 1); break; } if (error) bridge_delete_member(sc, bif, 0); return (error); } static int bridge_ioctl_del(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); bridge_delete_member(sc, bif, 0); return (0); } static int bridge_ioctl_gifflags(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; struct bstp_port *bp; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); bp = &bif->bif_stp; req->ifbr_ifsflags = bif->bif_flags; req->ifbr_state = bp->bp_state; req->ifbr_priority = bp->bp_priority; req->ifbr_path_cost = bp->bp_path_cost; req->ifbr_portno = bif->bif_ifp->if_index & 0xfff; req->ifbr_proto = bp->bp_protover; req->ifbr_role = bp->bp_role; req->ifbr_stpflags = bp->bp_flags; req->ifbr_addrcnt = bif->bif_addrcnt; req->ifbr_addrmax = bif->bif_addrmax; req->ifbr_addrexceeded = bif->bif_addrexceeded; req->ifbr_pvid = bif->bif_pvid; /* Copy STP state options as flags */ if (bp->bp_operedge) req->ifbr_ifsflags |= IFBIF_BSTP_EDGE; if (bp->bp_flags & BSTP_PORT_AUTOEDGE) req->ifbr_ifsflags |= IFBIF_BSTP_AUTOEDGE; if (bp->bp_ptp_link) req->ifbr_ifsflags |= IFBIF_BSTP_PTP; if (bp->bp_flags & BSTP_PORT_AUTOPTP) req->ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP; if (bp->bp_flags & BSTP_PORT_ADMEDGE) req->ifbr_ifsflags |= IFBIF_BSTP_ADMEDGE; if (bp->bp_flags & BSTP_PORT_ADMCOST) req->ifbr_ifsflags |= IFBIF_BSTP_ADMCOST; return (0); } static int bridge_ioctl_sifflags(struct bridge_softc *sc, void *arg) { struct epoch_tracker et; struct ifbreq *req = arg; struct bridge_iflist *bif; struct bstp_port *bp; int error; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); bp = &bif->bif_stp; if (req->ifbr_ifsflags & IFBIF_SPAN) /* SPAN is readonly */ return (EXTERROR(EINVAL, "Span interface cannot be modified")); NET_EPOCH_ENTER(et); if (req->ifbr_ifsflags & IFBIF_STP) { if ((bif->bif_flags & IFBIF_STP) == 0) { error = bstp_enable(&bif->bif_stp); if (error) { NET_EPOCH_EXIT(et); return (EXTERROR(error, "Failed to enable STP")); } } } else { if ((bif->bif_flags & IFBIF_STP) != 0) bstp_disable(&bif->bif_stp); } /* Pass on STP flags */ bstp_set_edge(bp, req->ifbr_ifsflags & IFBIF_BSTP_EDGE ? 1 : 0); bstp_set_autoedge(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOEDGE ? 1 : 0); bstp_set_ptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_PTP ? 1 : 0); bstp_set_autoptp(bp, req->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP ? 1 : 0); /* Save the bits relating to the bridge */ bif->bif_flags = req->ifbr_ifsflags & IFBIFMASK; NET_EPOCH_EXIT(et); return (0); } static int bridge_ioctl_scache(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; sc->sc_brtmax = param->ifbrp_csize; bridge_rttrim(sc); return (0); } static int bridge_ioctl_gcache(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_csize = sc->sc_brtmax; return (0); } static int bridge_ioctl_gifs(struct bridge_softc *sc, void *arg) { struct ifbifconf *bifc = arg; struct bridge_iflist *bif; struct ifbreq breq; char *buf, *outbuf; int count, buflen, len, error = 0; count = 0; CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) count++; CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) count++; buflen = sizeof(breq) * count; if (bifc->ifbic_len == 0) { bifc->ifbic_len = buflen; return (0); } outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); if (outbuf == NULL) return (ENOMEM); count = 0; buf = outbuf; len = min(bifc->ifbic_len, buflen); bzero(&breq, sizeof(breq)); CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (len < sizeof(breq)) break; strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, sizeof(breq.ifbr_ifsname)); /* Fill in the ifbreq structure */ error = bridge_ioctl_gifflags(sc, &breq); if (error) break; memcpy(buf, &breq, sizeof(breq)); count++; buf += sizeof(breq); len -= sizeof(breq); } CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { if (len < sizeof(breq)) break; strlcpy(breq.ifbr_ifsname, bif->bif_ifp->if_xname, sizeof(breq.ifbr_ifsname)); breq.ifbr_ifsflags = bif->bif_flags; breq.ifbr_portno = bif->bif_ifp->if_index & 0xfff; memcpy(buf, &breq, sizeof(breq)); count++; buf += sizeof(breq); len -= sizeof(breq); } bifc->ifbic_len = sizeof(breq) * count; error = copyout(outbuf, bifc->ifbic_req, bifc->ifbic_len); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_rts(struct bridge_softc *sc, void *arg) { struct ifbaconf *bac = arg; struct bridge_rtnode *brt; struct ifbareq bareq; char *buf, *outbuf; int count, buflen, len, error = 0; if (bac->ifbac_len == 0) return (0); count = 0; CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) count++; buflen = sizeof(bareq) * count; outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); if (outbuf == NULL) return (ENOMEM); count = 0; buf = outbuf; len = min(bac->ifbac_len, buflen); bzero(&bareq, sizeof(bareq)); CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { if (len < sizeof(bareq)) goto out; strlcpy(bareq.ifba_ifsname, brt->brt_ifp->if_xname, sizeof(bareq.ifba_ifsname)); memcpy(bareq.ifba_dst, brt->brt_addr, sizeof(brt->brt_addr)); bareq.ifba_vlan = brt->brt_vlan; if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && time_uptime < brt->brt_expire) bareq.ifba_expire = brt->brt_expire - time_uptime; else bareq.ifba_expire = 0; bareq.ifba_flags = brt->brt_flags; memcpy(buf, &bareq, sizeof(bareq)); count++; buf += sizeof(bareq); len -= sizeof(bareq); } out: bac->ifbac_len = sizeof(bareq) * count; error = copyout(outbuf, bac->ifbac_req, bac->ifbac_len); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_saddr(struct bridge_softc *sc, void *arg) { struct ifbareq *req = arg; struct bridge_iflist *bif; struct epoch_tracker et; int error; NET_EPOCH_ENTER(et); bif = bridge_lookup_member(sc, req->ifba_ifsname); if (bif == NULL) { NET_EPOCH_EXIT(et); return (EXTERROR(ENOENT, "Interface is not a bridge member")); } /* bridge_rtupdate() may acquire the lock. */ error = bridge_rtupdate(sc, req->ifba_dst, req->ifba_vlan, bif, 1, req->ifba_flags); NET_EPOCH_EXIT(et); return (error); } static int bridge_ioctl_sto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; sc->sc_brttimeout = param->ifbrp_ctime; return (0); } static int bridge_ioctl_gto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_ctime = sc->sc_brttimeout; return (0); } static int bridge_ioctl_daddr(struct bridge_softc *sc, void *arg) { struct ifbareq *req = arg; int vlan = req->ifba_vlan; /* Userspace uses '0' to mean 'any vlan' */ if (vlan == 0) vlan = DOT1Q_VID_RSVD_IMPL; return (bridge_rtdaddr(sc, req->ifba_dst, vlan)); } static int bridge_ioctl_flush(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; BRIDGE_RT_LOCK(sc); bridge_rtflush(sc, req->ifbr_ifsflags); BRIDGE_RT_UNLOCK(sc); return (0); } static int bridge_ioctl_gpri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_prio = bs->bs_bridge_priority; return (0); } static int bridge_ioctl_spri(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_priority(&sc->sc_stp, param->ifbrp_prio)); } static int bridge_ioctl_ght(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_hellotime = bs->bs_bridge_htime >> 8; return (0); } static int bridge_ioctl_sht(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_htime(&sc->sc_stp, param->ifbrp_hellotime)); } static int bridge_ioctl_gfd(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_fwddelay = bs->bs_bridge_fdelay >> 8; return (0); } static int bridge_ioctl_sfd(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_fdelay(&sc->sc_stp, param->ifbrp_fwddelay)); } static int bridge_ioctl_gma(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; struct bstp_state *bs = &sc->sc_stp; param->ifbrp_maxage = bs->bs_bridge_max_age >> 8; return (0); } static int bridge_ioctl_sma(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_maxage(&sc->sc_stp, param->ifbrp_maxage)); } static int bridge_ioctl_sifprio(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); return (bstp_set_port_priority(&bif->bif_stp, req->ifbr_priority)); } static int bridge_ioctl_sifcost(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); return (bstp_set_path_cost(&bif->bif_stp, req->ifbr_path_cost)); } static int bridge_ioctl_sifmaxaddr(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); bif->bif_addrmax = req->ifbr_addrmax; return (0); } static int bridge_ioctl_sifpvid(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; if ((sc->sc_flags & IFBRF_VLANFILTER) == 0) return (EXTERROR(EINVAL, "VLAN filtering not enabled")); bif = bridge_lookup_member(sc, req->ifbr_ifsname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); if (req->ifbr_pvid > DOT1Q_VID_MAX) return (EXTERROR(EINVAL, "Invalid VLAN ID")); bif->bif_pvid = req->ifbr_pvid; return (0); } static int bridge_ioctl_sifvlanset(struct bridge_softc *sc, void *arg) { struct ifbif_vlan_req *req = arg; struct bridge_iflist *bif; if ((sc->sc_flags & IFBRF_VLANFILTER) == 0) return (EXTERROR(EINVAL, "VLAN filtering not enabled")); bif = bridge_lookup_member(sc, req->bv_ifname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); /* Reject invalid VIDs. */ if (BRVLAN_TEST(&req->bv_set, DOT1Q_VID_NULL) || BRVLAN_TEST(&req->bv_set, DOT1Q_VID_RSVD_IMPL)) return (EXTERROR(EINVAL, "Invalid VLAN ID in set")); switch (req->bv_op) { /* Replace the existing vlan set with the new set */ case BRDG_VLAN_OP_SET: BIT_COPY(BRVLAN_SETSIZE, &req->bv_set, &bif->bif_vlan_set); break; /* Modify the existing vlan set to add the given vlans */ case BRDG_VLAN_OP_ADD: BIT_OR(BRVLAN_SETSIZE, &bif->bif_vlan_set, &req->bv_set); break; /* Modify the existing vlan set to remove the given vlans */ case BRDG_VLAN_OP_DEL: BIT_ANDNOT(BRVLAN_SETSIZE, &bif->bif_vlan_set, &req->bv_set); break; /* Invalid or unknown operation */ default: return (EXTERROR(EINVAL, "Unsupported BRDGSIFVLANSET operation")); } return (0); } static int bridge_ioctl_gifvlanset(struct bridge_softc *sc, void *arg) { struct ifbif_vlan_req *req = arg; struct bridge_iflist *bif; bif = bridge_lookup_member(sc, req->bv_ifname); if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a bridge member")); BIT_COPY(BRVLAN_SETSIZE, &bif->bif_vlan_set, &req->bv_set); return (0); } static int bridge_ioctl_addspan(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif = NULL; struct ifnet *ifs; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (EXTERROR(ENOENT, "No such interface")); CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) return (EXTERROR(EBUSY, "Interface is already a span port")); if (ifs->if_bridge != NULL) return (EXTERROR(EEXIST, "Interface is already a bridge member")); switch (ifs->if_type) { case IFT_ETHER: case IFT_GIF: case IFT_L2VLAN: break; default: return (EXTERROR(EINVAL, "Unsupported interface type")); } bif = malloc(sizeof(*bif), M_DEVBUF, M_NOWAIT|M_ZERO); if (bif == NULL) return (ENOMEM); bif->bif_ifp = ifs; bif->bif_flags = IFBIF_SPAN; CK_LIST_INSERT_HEAD(&sc->sc_spanlist, bif, bif_next); return (0); } static int bridge_ioctl_delspan(struct bridge_softc *sc, void *arg) { struct ifbreq *req = arg; struct bridge_iflist *bif; struct ifnet *ifs; ifs = ifunit(req->ifbr_ifsname); if (ifs == NULL) return (EXTERROR(ENOENT, "No such interface")); CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifs == bif->bif_ifp) break; if (bif == NULL) return (EXTERROR(ENOENT, "Interface is not a span port")); bridge_delete_span(sc, bif); return (0); } static int bridge_ioctl_gbparam(struct bridge_softc *sc, void *arg) { struct ifbropreq *req = arg; struct bstp_state *bs = &sc->sc_stp; struct bstp_port *root_port; req->ifbop_maxage = bs->bs_bridge_max_age >> 8; req->ifbop_hellotime = bs->bs_bridge_htime >> 8; req->ifbop_fwddelay = bs->bs_bridge_fdelay >> 8; root_port = bs->bs_root_port; if (root_port == NULL) req->ifbop_root_port = 0; else req->ifbop_root_port = root_port->bp_ifp->if_index; req->ifbop_holdcount = bs->bs_txholdcount; req->ifbop_priority = bs->bs_bridge_priority; req->ifbop_protocol = bs->bs_protover; req->ifbop_root_path_cost = bs->bs_root_pv.pv_cost; req->ifbop_bridgeid = bs->bs_bridge_pv.pv_dbridge_id; req->ifbop_designated_root = bs->bs_root_pv.pv_root_id; req->ifbop_designated_bridge = bs->bs_root_pv.pv_dbridge_id; req->ifbop_last_tc_time.tv_sec = bs->bs_last_tc_time.tv_sec; req->ifbop_last_tc_time.tv_usec = bs->bs_last_tc_time.tv_usec; return (0); } static int bridge_ioctl_grte(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_cexceeded = sc->sc_brtexceeded; return (0); } static int bridge_ioctl_gifsstp(struct bridge_softc *sc, void *arg) { struct ifbpstpconf *bifstp = arg; struct bridge_iflist *bif; struct bstp_port *bp; struct ifbpstpreq bpreq; char *buf, *outbuf; int count, buflen, len, error = 0; count = 0; CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if ((bif->bif_flags & IFBIF_STP) != 0) count++; } buflen = sizeof(bpreq) * count; if (bifstp->ifbpstp_len == 0) { bifstp->ifbpstp_len = buflen; return (0); } outbuf = malloc(buflen, M_TEMP, M_NOWAIT | M_ZERO); if (outbuf == NULL) return (ENOMEM); count = 0; buf = outbuf; len = min(bifstp->ifbpstp_len, buflen); bzero(&bpreq, sizeof(bpreq)); CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (len < sizeof(bpreq)) break; if ((bif->bif_flags & IFBIF_STP) == 0) continue; bp = &bif->bif_stp; bpreq.ifbp_portno = bif->bif_ifp->if_index & 0xfff; bpreq.ifbp_fwd_trans = bp->bp_forward_transitions; bpreq.ifbp_design_cost = bp->bp_desg_pv.pv_cost; bpreq.ifbp_design_port = bp->bp_desg_pv.pv_port_id; bpreq.ifbp_design_bridge = bp->bp_desg_pv.pv_dbridge_id; bpreq.ifbp_design_root = bp->bp_desg_pv.pv_root_id; memcpy(buf, &bpreq, sizeof(bpreq)); count++; buf += sizeof(bpreq); len -= sizeof(bpreq); } bifstp->ifbpstp_len = sizeof(bpreq) * count; error = copyout(outbuf, bifstp->ifbpstp_req, bifstp->ifbpstp_len); free(outbuf, M_TEMP); return (error); } static int bridge_ioctl_sproto(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_protocol(&sc->sc_stp, param->ifbrp_proto)); } static int bridge_ioctl_stxhc(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; return (bstp_set_holdcount(&sc->sc_stp, param->ifbrp_txhc)); } static int bridge_ioctl_gflags(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_flags = sc->sc_flags; return (0); } static int bridge_ioctl_sflags(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; sc->sc_flags = param->ifbrp_flags; return (0); } static int bridge_ioctl_gdefpvid(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; param->ifbrp_defpvid = sc->sc_defpvid; return (0); } static int bridge_ioctl_sdefpvid(struct bridge_softc *sc, void *arg) { struct ifbrparam *param = arg; /* Reject invalid VIDs, but allow 0 to mean 'none'. */ if (param->ifbrp_defpvid > DOT1Q_VID_MAX) return (EINVAL); sc->sc_defpvid = param->ifbrp_defpvid; return (0); } /* * bridge_ifdetach: * * Detach an interface from a bridge. Called when a member * interface is detaching. */ static void bridge_ifdetach(void *arg __unused, struct ifnet *ifp) { struct bridge_iflist *bif = ifp->if_bridge; struct bridge_softc *sc = NULL; if (bif) sc = bif->bif_sc; if (ifp->if_flags & IFF_RENAMING) return; if (V_bridge_cloner == NULL) { /* * This detach handler can be called after * vnet_bridge_uninit(). Just return in that case. */ return; } /* Check if the interface is a bridge member */ if (sc != NULL) { BRIDGE_LOCK(sc); bridge_delete_member(sc, bif, 1); BRIDGE_UNLOCK(sc); return; } /* Check if the interface is a span port */ BRIDGE_LIST_LOCK(); LIST_FOREACH(sc, &V_bridge_list, sc_list) { BRIDGE_LOCK(sc); CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) if (ifp == bif->bif_ifp) { bridge_delete_span(sc, bif); break; } BRIDGE_UNLOCK(sc); } BRIDGE_LIST_UNLOCK(); } /* * bridge_init: * * Initialize a bridge interface. */ static void bridge_init(void *xsc) { struct bridge_softc *sc = (struct bridge_softc *)xsc; struct ifnet *ifp = sc->sc_ifp; if (ifp->if_drv_flags & IFF_DRV_RUNNING) return; BRIDGE_LOCK(sc); callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); ifp->if_drv_flags |= IFF_DRV_RUNNING; bstp_init(&sc->sc_stp); /* Initialize Spanning Tree */ BRIDGE_UNLOCK(sc); } /* * bridge_stop: * * Stop the bridge interface. */ static void bridge_stop(struct ifnet *ifp, int disable) { struct bridge_softc *sc = ifp->if_softc; BRIDGE_LOCK_ASSERT(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; BRIDGE_RT_LOCK(sc); callout_stop(&sc->sc_brcallout); bstp_stop(&sc->sc_stp); bridge_rtflush(sc, IFBF_FLUSHDYN); BRIDGE_RT_UNLOCK(sc); ifp->if_drv_flags &= ~IFF_DRV_RUNNING; } /* * bridge_enqueue: * * Enqueue a packet on a bridge member interface. * */ static int bridge_enqueue(struct bridge_softc *sc, struct ifnet *dst_ifp, struct mbuf *m, struct bridge_iflist *bif) { int len, err = 0; short mflags; struct mbuf *m0; /* * Find the bridge member port this packet is being sent on, if the * caller didn't already provide it. */ if (bif == NULL) bif = bridge_lookup_member_if(sc, dst_ifp); if (bif == NULL) { /* Perhaps the interface was removed from the bridge */ m_freem(m); return (EINVAL); } /* We may be sending a fragment so traverse the mbuf */ for (; m; m = m0) { m0 = m->m_nextpkt; m->m_nextpkt = NULL; len = m->m_pkthdr.len; mflags = m->m_flags; /* * If VLAN filtering is enabled, and the native VLAN ID of the * outgoing interface matches the VLAN ID of the frame, remove * the VLAN header. */ if ((sc->sc_flags & IFBRF_VLANFILTER) && bif->bif_pvid != DOT1Q_VID_NULL && VLANTAGOF(m) == bif->bif_pvid) { m->m_flags &= ~M_VLANTAG; m->m_pkthdr.ether_vtag = 0; } /* * If underlying interface can not do VLAN tag insertion itself * then attach a packet tag that holds it. */ if ((m->m_flags & M_VLANTAG) && (dst_ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0) { m = ether_vlanencap(m, m->m_pkthdr.ether_vtag); if (m == NULL) { if_printf(dst_ifp, "unable to prepend VLAN header\n"); if_inc_counter(dst_ifp, IFCOUNTER_OERRORS, 1); continue; } m->m_flags &= ~M_VLANTAG; } M_ASSERTPKTHDR(m); /* We shouldn't transmit mbuf without pkthdr */ if ((err = dst_ifp->if_transmit(dst_ifp, m))) { int n; for (m = m0, n = 1; m != NULL; m = m0, n++) { m0 = m->m_nextpkt; m_freem(m); } if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, n); break; } if_inc_counter(sc->sc_ifp, IFCOUNTER_OPACKETS, 1); if_inc_counter(sc->sc_ifp, IFCOUNTER_OBYTES, len); if (mflags & M_MCAST) if_inc_counter(sc->sc_ifp, IFCOUNTER_OMCASTS, 1); } return (err); } /* * bridge_dummynet: * * Receive a queued packet from dummynet and pass it on to the output * interface. * * The mbuf has the Ethernet header already attached. */ static void bridge_dummynet(struct mbuf *m, struct ifnet *ifp) { struct bridge_iflist *bif = ifp->if_bridge; struct bridge_softc *sc = NULL; if (bif) sc = bif->bif_sc; /* * The packet didnt originate from a member interface. This should only * ever happen if a member interface is removed while packets are * queued for it. */ if (sc == NULL) { m_freem(m); return; } if (PFIL_HOOKED_OUT_46) { if (bridge_pfil(&m, sc->sc_ifp, ifp, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, ifp, m, NULL); } /* * bridge_output: * * Send output from a bridge member interface. This * performs the bridging function for locally originated * packets. * * The mbuf has the Ethernet header already attached. We must * enqueue or free the mbuf before returning. */ static int bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, struct rtentry *rt) { struct ether_header *eh; struct bridge_iflist *sbif; struct ifnet *bifp, *dst_if; struct bridge_softc *sc; ether_vlanid_t vlan; NET_EPOCH_ASSERT(); if (m->m_len < ETHER_HDR_LEN) { m = m_pullup(m, ETHER_HDR_LEN); if (m == NULL) return (0); } sbif = ifp->if_bridge; sc = sbif->bif_sc; bifp = sc->sc_ifp; eh = mtod(m, struct ether_header *); vlan = VLANTAGOF(m); /* * If bridge is down, but the original output interface is up, * go ahead and send out that interface. Otherwise, the packet * is dropped below. */ if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { dst_if = ifp; goto sendunicast; } /* * If the packet is a multicast, or we don't know a better way to * get there, send to all interfaces. */ if (ETHER_IS_MULTICAST(eh->ether_dhost)) dst_if = NULL; else dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan); /* Tap any traffic not passing back out the originating interface */ if (dst_if != ifp) ETHER_BPF_MTAP(bifp, m); if (dst_if == NULL) { struct bridge_iflist *bif; struct mbuf *mc; int used = 0; bridge_span(sc, m); CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { dst_if = bif->bif_ifp; if (dst_if->if_type == IFT_GIF) continue; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; /* * If this is not the original output interface, * and the interface is participating in spanning * tree, make sure the port is in a state that * allows forwarding. */ if (dst_if != ifp && (bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) continue; if (CK_LIST_NEXT(bif, bif_next) == NULL) { used = 1; mc = m; } else { mc = m_dup(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(bifp, IFCOUNTER_OERRORS, 1); continue; } } bridge_enqueue(sc, dst_if, mc, bif); } if (used == 0) m_freem(m); return (0); } sendunicast: /* * XXX Spanning tree consideration here? */ bridge_span(sc, m); if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) { m_freem(m); return (0); } bridge_enqueue(sc, dst_if, m, NULL); return (0); } /* * bridge_transmit: * * Do output on a bridge. * */ static int bridge_transmit(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc; struct ether_header *eh; struct ifnet *dst_if; int error = 0; ether_vlanid_t vlan; sc = ifp->if_softc; ETHER_BPF_MTAP(ifp, m); eh = mtod(m, struct ether_header *); vlan = VLANTAGOF(m); if (((m->m_flags & (M_BCAST|M_MCAST)) == 0) && (dst_if = bridge_rtlookup(sc, eh->ether_dhost, vlan)) != NULL) { error = bridge_enqueue(sc, dst_if, m, NULL); } else bridge_broadcast(sc, ifp, m, 0); return (error); } #ifdef ALTQ static void bridge_altq_start(if_t ifp) { struct ifaltq *ifq = &ifp->if_snd; struct mbuf *m; IFQ_LOCK(ifq); IFQ_DEQUEUE_NOLOCK(ifq, m); while (m != NULL) { bridge_transmit(ifp, m); IFQ_DEQUEUE_NOLOCK(ifq, m); } IFQ_UNLOCK(ifq); } static int bridge_altq_transmit(if_t ifp, struct mbuf *m) { int err; if (ALTQ_IS_ENABLED(&ifp->if_snd)) { IFQ_ENQUEUE(&ifp->if_snd, m, err); if (err == 0) bridge_altq_start(ifp); } else err = bridge_transmit(ifp, m); return (err); } #endif /* ALTQ */ /* * The ifp->if_qflush entry point for if_bridge(4) is no-op. */ static void bridge_qflush(struct ifnet *ifp __unused) { } /* * bridge_forward: * * The forwarding function of the bridge. * * NOTE: Releases the lock on return. */ static void bridge_forward(struct bridge_softc *sc, struct bridge_iflist *sbif, struct mbuf *m) { struct bridge_iflist *dbif; struct ifnet *src_if, *dst_if, *ifp; struct ether_header *eh; uint8_t *dst; int error; ether_vlanid_t vlan; NET_EPOCH_ASSERT(); src_if = m->m_pkthdr.rcvif; ifp = sc->sc_ifp; vlan = VLANTAGOF(m); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); if ((sbif->bif_flags & IFBIF_STP) && sbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) goto drop; eh = mtod(m, struct ether_header *); dst = eh->ether_dhost; /* If the interface is learning, record the address. */ if (sbif->bif_flags & IFBIF_LEARNING) { error = bridge_rtupdate(sc, eh->ether_shost, vlan, sbif, 0, IFBAF_DYNAMIC); /* * If the interface has addresses limits then deny any source * that is not in the cache. */ if (error && sbif->bif_addrmax) goto drop; } if ((sbif->bif_flags & IFBIF_STP) != 0 && sbif->bif_stp.bp_state == BSTP_IFSTATE_LEARNING) goto drop; #ifdef DEV_NETMAP /* * Hand the packet to netmap only if it wasn't injected by netmap * itself. */ if ((m->m_flags & M_BRIDGE_INJECT) == 0 && (if_getcapenable(ifp) & IFCAP_NETMAP) != 0) { ifp->if_input(ifp, m); return; } m->m_flags &= ~M_BRIDGE_INJECT; #endif /* * At this point, the port either doesn't participate * in spanning tree or it is in the forwarding state. */ /* * If the packet is unicast, destined for someone on * "this" side of the bridge, drop it. */ if ((m->m_flags & (M_BCAST|M_MCAST)) == 0) { dst_if = bridge_rtlookup(sc, dst, vlan); if (src_if == dst_if) goto drop; } else { /* * Check if its a reserved multicast address, any address * listed in 802.1D section 7.12.6 may not be forwarded by the * bridge. * This is currently 01-80-C2-00-00-00 to 01-80-C2-00-00-0F */ if (dst[0] == 0x01 && dst[1] == 0x80 && dst[2] == 0xc2 && dst[3] == 0x00 && dst[4] == 0x00 && dst[5] <= 0x0f) goto drop; /* ...forward it to all interfaces. */ if_inc_counter(ifp, IFCOUNTER_IMCASTS, 1); dst_if = NULL; } /* * If we have a destination interface which is a member of our bridge, * OR this is a unicast packet, push it through the bpf(4) machinery. * For broadcast or multicast packets, don't bother because it will * be reinjected into ether_input. We do this before we pass the packets * through the pfil(9) framework, as it is possible that pfil(9) will * drop the packet, or possibly modify it, making it difficult to debug * firewall issues on the bridge. */ if (dst_if != NULL || (m->m_flags & (M_BCAST | M_MCAST)) == 0) ETHER_BPF_MTAP(ifp, m); /* run the packet filter */ if (PFIL_HOOKED_IN_46) { if (bridge_pfil(&m, ifp, src_if, PFIL_IN) != 0) return; if (m == NULL) return; } if (dst_if == NULL) { bridge_broadcast(sc, src_if, m, 1); return; } /* * At this point, we're dealing with a unicast frame * going to a different interface. */ if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) goto drop; dbif = bridge_lookup_member_if(sc, dst_if); if (dbif == NULL) /* Not a member of the bridge (anymore?) */ goto drop; /* Private segments can not talk to each other */ if (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE) goto drop; /* Do VLAN filtering. */ if (!bridge_vfilter_out(dbif, m)) goto drop; if ((dbif->bif_flags & IFBIF_STP) && dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) goto drop; if (PFIL_HOOKED_OUT_46) { if (bridge_pfil(&m, ifp, dst_if, PFIL_OUT) != 0) return; if (m == NULL) return; } bridge_enqueue(sc, dst_if, m, dbif); return; drop: m_freem(m); } /* * bridge_input: * * Receive input from a member interface. Queue the packet for * bridging if it is not for us. */ static struct mbuf * bridge_input(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc = NULL; struct bridge_iflist *bif, *bif2; struct ifnet *bifp; struct ether_header *eh; struct mbuf *mc, *mc2; ether_vlanid_t vlan; int error; NET_EPOCH_ASSERT(); + /* We need the Ethernet header later, so make sure we have it now. */ + if (m->m_len < ETHER_HDR_LEN) { + m = m_pullup(m, ETHER_HDR_LEN); + if (m == NULL) { + if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); + m_freem(m); + return (NULL); + } + } + eh = mtod(m, struct ether_header *); vlan = VLANTAGOF(m); bif = ifp->if_bridge; if (bif) sc = bif->bif_sc; if (sc == NULL) { /* * This packet originated from the bridge itself, so it must * have been transmitted by netmap. Derive the "source" * interface from the source address and drop the packet if the * source address isn't known. */ KASSERT((m->m_flags & M_BRIDGE_INJECT) != 0, ("%s: ifnet %p missing a bridge softc", __func__, ifp)); sc = if_getsoftc(ifp); ifp = bridge_rtlookup(sc, eh->ether_shost, vlan); if (ifp == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); m_freem(m); return (NULL); } m->m_pkthdr.rcvif = ifp; } bifp = sc->sc_ifp; if ((bifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (m); /* * Implement support for bridge monitoring. If this flag has been * set on this interface, discard the packet once we push it through * the bpf(4) machinery, but before we do, increment the byte and * packet counters associated with this interface. */ if ((bifp->if_flags & IFF_MONITOR) != 0) { m->m_pkthdr.rcvif = bifp; ETHER_BPF_MTAP(bifp, m); if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); m_freem(m); return (NULL); } /* Do VLAN filtering. */ if (!bridge_vfilter_in(bif, m)) { if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); m_freem(m); return (NULL); } /* bridge_vfilter_in() may add a tag */ vlan = VLANTAGOF(m); bridge_span(sc, m); if (m->m_flags & (M_BCAST|M_MCAST)) { /* Tap off 802.1D packets; they do not get forwarded. */ if (memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) { bstp_input(&bif->bif_stp, ifp, m); /* consumes mbuf */ return (NULL); } if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { return (m); } /* * Make a deep copy of the packet and enqueue the copy * for bridge processing; return the original packet for * local processing. */ mc = m_dup(m, M_NOWAIT); if (mc == NULL) { return (m); } /* Perform the bridge forwarding function with the copy. */ bridge_forward(sc, bif, mc); #ifdef DEV_NETMAP /* * If netmap is enabled and has not already seen this packet, * then it will be consumed by bridge_forward(). */ if ((if_getcapenable(bifp) & IFCAP_NETMAP) != 0 && (m->m_flags & M_BRIDGE_INJECT) == 0) { m_freem(m); return (NULL); } #endif /* * Reinject the mbuf as arriving on the bridge so we have a * chance at claiming multicast packets. We can not loop back * here from ether_input as a bridge is never a member of a * bridge. */ KASSERT(bifp->if_bridge == NULL, ("loop created in bridge_input")); mc2 = m_dup(m, M_NOWAIT); if (mc2 != NULL) { /* Keep the layer3 header aligned */ int i = min(mc2->m_pkthdr.len, max_protohdr); mc2 = m_copyup(mc2, i, ETHER_ALIGN); } if (mc2 != NULL) { mc2->m_pkthdr.rcvif = bifp; mc2->m_flags &= ~M_BRIDGE_INJECT; sc->sc_if_input(bifp, mc2); } /* Return the original packet for local processing. */ return (m); } if ((bif->bif_flags & IFBIF_STP) && bif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) { return (m); } #if defined(INET) || defined(INET6) #define CARP_CHECK_WE_ARE_DST(iface) \ ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_dhost)) #define CARP_CHECK_WE_ARE_SRC(iface) \ ((iface)->if_carp && (*carp_forus_p)((iface), eh->ether_shost)) #else #define CARP_CHECK_WE_ARE_DST(iface) false #define CARP_CHECK_WE_ARE_SRC(iface) false #endif #ifdef DEV_NETMAP #define GRAB_FOR_NETMAP(ifp, m) do { \ if ((if_getcapenable(ifp) & IFCAP_NETMAP) != 0 && \ ((m)->m_flags & M_BRIDGE_INJECT) == 0) { \ (ifp)->if_input(ifp, m); \ return (NULL); \ } \ } while (0) #else #define GRAB_FOR_NETMAP(ifp, m) #endif #define GRAB_OUR_PACKETS(iface) \ if ((iface)->if_type == IFT_GIF) \ continue; \ /* It is destined for us. */ \ if (memcmp(IF_LLADDR(iface), eh->ether_dhost, ETHER_ADDR_LEN) == 0 || \ CARP_CHECK_WE_ARE_DST(iface)) { \ if (bif->bif_flags & IFBIF_LEARNING) { \ error = bridge_rtupdate(sc, eh->ether_shost, \ vlan, bif, 0, IFBAF_DYNAMIC); \ if (error && bif->bif_addrmax) { \ m_freem(m); \ return (NULL); \ } \ } \ m->m_pkthdr.rcvif = iface; \ if ((iface) == ifp) { \ /* Skip bridge processing... src == dest */ \ return (m); \ } \ /* It's passing over or to the bridge, locally. */ \ ETHER_BPF_MTAP(bifp, m); \ if_inc_counter(bifp, IFCOUNTER_IPACKETS, 1); \ if_inc_counter(bifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);\ /* Hand the packet over to netmap if necessary. */ \ GRAB_FOR_NETMAP(bifp, m); \ /* Filter on the physical interface. */ \ if (V_pfil_local_phys && PFIL_HOOKED_IN_46) { \ if (bridge_pfil(&m, NULL, ifp, \ PFIL_IN) != 0 || m == NULL) { \ return (NULL); \ } \ } \ if ((iface) != bifp) \ ETHER_BPF_MTAP(iface, m); \ /* Pass tagged packets to if_vlan, if it's loaded */ \ if (VLANTAGOF(m) != 0) { \ if (bifp->if_vlantrunk == NULL) { \ m_freem(m); \ return (NULL); \ } \ (*vlan_input_p)(bifp, m); \ return (NULL); \ } \ return (m); \ } \ \ /* We just received a packet that we sent out. */ \ if (memcmp(IF_LLADDR(iface), eh->ether_shost, ETHER_ADDR_LEN) == 0 || \ CARP_CHECK_WE_ARE_SRC(iface)) { \ m_freem(m); \ return (NULL); \ } /* * Unicast. Make sure it's not for the bridge. */ do { GRAB_OUR_PACKETS(bifp) } while (0); /* * Check the interface the packet arrived on. For tagged frames, * we need to do this even if member_ifaddrs is disabled because * vlan(4) might need to handle the traffic. */ if (V_member_ifaddrs || (vlan && ifp->if_vlantrunk)) do { GRAB_OUR_PACKETS(ifp) } while (0); /* * We only need to check other members interface if member_ifaddrs * is enabled; otherwise we should have never traffic destined for * a member's lladdr. */ if (V_member_ifaddrs) { CK_LIST_FOREACH(bif2, &sc->sc_iflist, bif_next) { GRAB_OUR_PACKETS(bif2->bif_ifp) } } #undef CARP_CHECK_WE_ARE_DST #undef CARP_CHECK_WE_ARE_SRC #undef GRAB_FOR_NETMAP #undef GRAB_OUR_PACKETS /* Perform the bridge forwarding function. */ bridge_forward(sc, bif, m); return (NULL); } /* * Inject a packet back into the host ethernet stack. This will generally only * be used by netmap when an application writes to the host TX ring. The * M_BRIDGE_INJECT flag ensures that the packet is re-routed to the bridge * interface after ethernet processing. */ static void bridge_inject(struct ifnet *ifp, struct mbuf *m) { struct bridge_softc *sc; if (ifp->if_type == IFT_L2VLAN) { /* * vlan(4) gives us the vlan ifnet, so we need to get the * bridge softc to get a pointer to ether_input to send the * packet to. */ struct ifnet *bifp = NULL; if (vlan_trunkdev_p == NULL) { m_freem(m); return; } bifp = vlan_trunkdev_p(ifp); if (bifp == NULL) { m_freem(m); return; } sc = if_getsoftc(bifp); sc->sc_if_input(ifp, m); return; } KASSERT((if_getcapenable(ifp) & IFCAP_NETMAP) != 0, ("%s: iface %s is not running in netmap mode", __func__, if_name(ifp))); KASSERT((m->m_flags & M_BRIDGE_INJECT) == 0, ("%s: mbuf %p has M_BRIDGE_INJECT set", __func__, m)); m->m_flags |= M_BRIDGE_INJECT; sc = if_getsoftc(ifp); sc->sc_if_input(ifp, m); } /* * bridge_broadcast: * * Send a frame to all interfaces that are members of * the bridge, except for the one on which the packet * arrived. * * NOTE: Releases the lock on return. */ static void bridge_broadcast(struct bridge_softc *sc, struct ifnet *src_if, struct mbuf *m, int runfilt) { struct bridge_iflist *dbif, *sbif; struct mbuf *mc; struct ifnet *dst_if; int used = 0, i; NET_EPOCH_ASSERT(); sbif = bridge_lookup_member_if(sc, src_if); /* Filter on the bridge interface before broadcasting */ if (runfilt && PFIL_HOOKED_OUT_46) { if (bridge_pfil(&m, sc->sc_ifp, NULL, PFIL_OUT) != 0) return; if (m == NULL) return; } CK_LIST_FOREACH(dbif, &sc->sc_iflist, bif_next) { dst_if = dbif->bif_ifp; if (dst_if == src_if) continue; /* Private segments can not talk to each other */ if (sbif && (sbif->bif_flags & dbif->bif_flags & IFBIF_PRIVATE)) continue; /* Do VLAN filtering. */ if (!bridge_vfilter_out(dbif, m)) continue; if ((dbif->bif_flags & IFBIF_STP) && dbif->bif_stp.bp_state == BSTP_IFSTATE_DISCARDING) continue; if ((dbif->bif_flags & IFBIF_DISCOVER) == 0 && (m->m_flags & (M_BCAST|M_MCAST)) == 0) continue; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; if (CK_LIST_NEXT(dbif, bif_next) == NULL) { mc = m; used = 1; } else { mc = m_dup(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } } /* * Filter on the output interface. Pass a NULL bridge interface * pointer so we do not redundantly filter on the bridge for * each interface we broadcast on. */ if (runfilt && PFIL_HOOKED_OUT_46) { if (used == 0) { /* Keep the layer3 header aligned */ i = min(mc->m_pkthdr.len, max_protohdr); mc = m_copyup(mc, i, ETHER_ALIGN); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } } if (bridge_pfil(&mc, NULL, dst_if, PFIL_OUT) != 0) continue; if (mc == NULL) continue; } bridge_enqueue(sc, dst_if, mc, dbif); } if (used == 0) m_freem(m); } /* * bridge_span: * * Duplicate a packet out one or more interfaces that are in span mode, * the original mbuf is unmodified. */ static void bridge_span(struct bridge_softc *sc, struct mbuf *m) { struct bridge_iflist *bif; struct ifnet *dst_if; struct mbuf *mc; NET_EPOCH_ASSERT(); if (CK_LIST_EMPTY(&sc->sc_spanlist)) return; CK_LIST_FOREACH(bif, &sc->sc_spanlist, bif_next) { dst_if = bif->bif_ifp; if ((dst_if->if_drv_flags & IFF_DRV_RUNNING) == 0) continue; mc = m_dup(m, M_NOWAIT); if (mc == NULL) { if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); continue; } bridge_enqueue(sc, dst_if, mc, bif); } } /* * Incoming VLAN filtering. Given a frame and the member interface it was * received on, decide whether the port configuration allows it. */ static bool bridge_vfilter_in(const struct bridge_iflist *sbif, struct mbuf *m) { ether_vlanid_t vlan; vlan = VLANTAGOF(m); /* Make sure the vlan id is reasonable. */ if (vlan > DOT1Q_VID_MAX) return (false); /* If VLAN filtering isn't enabled, pass everything. */ if ((sbif->bif_sc->sc_flags & IFBRF_VLANFILTER) == 0) return (true); + /* If Q-in-Q is disabled, check for stacked tags. */ + if ((sbif->bif_flags & IFBIF_QINQ) == 0) { + struct ether_header *eh; + uint16_t proto; + + eh = mtod(m, struct ether_header *); + proto = ntohs(eh->ether_type); + + if (proto == ETHERTYPE_VLAN || proto == ETHERTYPE_QINQ) + return (false); + } + if (vlan == DOT1Q_VID_NULL) { /* * The frame doesn't have a tag. If the interface does not * have an untagged vlan configured, drop the frame. */ if (sbif->bif_pvid == DOT1Q_VID_NULL) return (false); /* * Otherwise, insert a new tag based on the interface's * untagged vlan id. */ m->m_pkthdr.ether_vtag = sbif->bif_pvid; m->m_flags |= M_VLANTAG; } else { /* * The frame has a tag, so check it matches the interface's * vlan access list. We explicitly do not accept tagged * frames for the untagged vlan id here (unless it's also * in the access list). */ if (!BRVLAN_TEST(&sbif->bif_vlan_set, vlan)) return (false); } /* Accept the frame. */ return (true); } /* * Outgoing VLAN filtering. Given a frame, its vlan, and the member interface * we intend to send it to, decide whether the port configuration allows it to * be sent. */ static bool bridge_vfilter_out(const struct bridge_iflist *dbif, const struct mbuf *m) { struct ether_header *eh; ether_vlanid_t vlan; NET_EPOCH_ASSERT(); /* If VLAN filtering isn't enabled, pass everything. */ if ((dbif->bif_sc->sc_flags & IFBRF_VLANFILTER) == 0) return (true); vlan = VLANTAGOF(m); /* * Always allow untagged 802.1D STP frames, even if they would * otherwise be dropped. This is required for STP to work on * a filtering bridge. * * Tagged STP (Cisco PVST+) is a non-standard extension, so * handle those frames via the normal filtering path. */ eh = mtod(m, struct ether_header *); if (vlan == DOT1Q_VID_NULL && memcmp(eh->ether_dhost, bstp_etheraddr, ETHER_ADDR_LEN) == 0) return (true); /* * If the frame wasn't assigned to a vlan at ingress, drop it. * We can't forward these frames to filtering ports because we * don't know what VLAN they're supposed to be in. */ if (vlan == DOT1Q_VID_NULL) return (false); /* * If the frame's vlan matches the interfaces's untagged vlan, * allow it. */ if (vlan == dbif->bif_pvid) return (true); /* * If the frame's vlan is on the interface's tagged access list, * allow it. */ if (BRVLAN_TEST(&dbif->bif_vlan_set, vlan)) return (true); /* The frame was not permitted, so drop it. */ return (false); } /* * bridge_rtupdate: * * Add a bridge routing entry. */ static int bridge_rtupdate(struct bridge_softc *sc, const uint8_t *dst, ether_vlanid_t vlan, struct bridge_iflist *bif, int setflags, uint8_t flags) { struct bridge_rtnode *brt; struct bridge_iflist *obif; int error; BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); /* Check the source address is valid and not multicast. */ if (ETHER_IS_MULTICAST(dst)) return (EXTERROR(EINVAL, "Multicast address not permitted")); if (dst[0] == 0 && dst[1] == 0 && dst[2] == 0 && dst[3] == 0 && dst[4] == 0 && dst[5] == 0) return (EXTERROR(EINVAL, "Zero address not permitted")); /* * A route for this destination might already exist. If so, * update it, otherwise create a new one. */ if ((brt = bridge_rtnode_lookup(sc, dst, vlan)) == NULL) { BRIDGE_RT_LOCK(sc); /* Check again, now that we have the lock. There could have * been a race and we only want to insert this once. */ if (bridge_rtnode_lookup(sc, dst, vlan) != NULL) { BRIDGE_RT_UNLOCK(sc); return (0); } if (sc->sc_brtcnt >= sc->sc_brtmax) { sc->sc_brtexceeded++; BRIDGE_RT_UNLOCK(sc); return (EXTERROR(ENOSPC, "Address table is full")); } /* Check per interface address limits (if enabled) */ if (bif->bif_addrmax && bif->bif_addrcnt >= bif->bif_addrmax) { bif->bif_addrexceeded++; BRIDGE_RT_UNLOCK(sc); return (EXTERROR(ENOSPC, "Interface address limit exceeded")); } /* * Allocate a new bridge forwarding node, and * initialize the expiration time and Ethernet * address. */ brt = uma_zalloc(V_bridge_rtnode_zone, M_NOWAIT | M_ZERO); if (brt == NULL) { BRIDGE_RT_UNLOCK(sc); return (EXTERROR(ENOMEM, "Cannot allocate address node")); } brt->brt_vnet = curvnet; if (bif->bif_flags & IFBIF_STICKY) brt->brt_flags = IFBAF_STICKY; else brt->brt_flags = IFBAF_DYNAMIC; memcpy(brt->brt_addr, dst, ETHER_ADDR_LEN); brt->brt_vlan = vlan; brt->brt_dst = bif; if ((error = bridge_rtnode_insert(sc, brt)) != 0) { uma_zfree(V_bridge_rtnode_zone, brt); BRIDGE_RT_UNLOCK(sc); return (error); } bif->bif_addrcnt++; BRIDGE_RT_UNLOCK(sc); } if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC && (obif = brt->brt_dst) != bif) { MPASS(obif != NULL); BRIDGE_RT_LOCK(sc); brt->brt_dst->bif_addrcnt--; brt->brt_dst = bif; brt->brt_dst->bif_addrcnt++; BRIDGE_RT_UNLOCK(sc); if (V_log_mac_flap && ppsratecheck(&V_log_last, &V_log_count, V_log_interval)) { log(LOG_NOTICE, "%s: mac address %6D vlan %d moved from %s to %s\n", sc->sc_ifp->if_xname, &brt->brt_addr[0], ":", brt->brt_vlan, obif->bif_ifp->if_xname, bif->bif_ifp->if_xname); } } if ((flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) brt->brt_expire = time_uptime + sc->sc_brttimeout; if (setflags) brt->brt_flags = flags; return (0); } /* * bridge_rtlookup: * * Lookup the destination interface for an address. */ static struct ifnet * bridge_rtlookup(struct bridge_softc *sc, const uint8_t *addr, ether_vlanid_t vlan) { struct bridge_rtnode *brt; NET_EPOCH_ASSERT(); if ((brt = bridge_rtnode_lookup(sc, addr, vlan)) == NULL) return (NULL); return (brt->brt_ifp); } /* * bridge_rttrim: * * Trim the routine table so that we have a number * of routing entries less than or equal to the * maximum number. */ static void bridge_rttrim(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; NET_EPOCH_ASSERT(); BRIDGE_RT_LOCK_ASSERT(sc); /* Make sure we actually need to do this. */ if (sc->sc_brtcnt <= sc->sc_brtmax) return; /* Force an aging cycle; this might trim enough addresses. */ bridge_rtage(sc); if (sc->sc_brtcnt <= sc->sc_brtmax) return; CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { bridge_rtnode_destroy(sc, brt); if (sc->sc_brtcnt <= sc->sc_brtmax) return; } } } /* * bridge_timer: * * Aging timer for the bridge. */ static void bridge_timer(void *arg) { struct bridge_softc *sc = arg; BRIDGE_RT_LOCK_ASSERT(sc); /* Destruction of rtnodes requires a proper vnet context */ CURVNET_SET(sc->sc_ifp->if_vnet); bridge_rtage(sc); if (sc->sc_ifp->if_drv_flags & IFF_DRV_RUNNING) callout_reset(&sc->sc_brcallout, bridge_rtable_prune_period * hz, bridge_timer, sc); CURVNET_RESTORE(); } /* * bridge_rtage: * * Perform an aging cycle. */ static void bridge_rtage(struct bridge_softc *sc) { struct bridge_rtnode *brt, *nbrt; BRIDGE_RT_LOCK_ASSERT(sc); CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if ((brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) { if (time_uptime >= brt->brt_expire) bridge_rtnode_destroy(sc, brt); } } } /* * bridge_rtflush: * * Remove all dynamic addresses from the bridge. */ static void bridge_rtflush(struct bridge_softc *sc, int full) { struct bridge_rtnode *brt, *nbrt; BRIDGE_RT_LOCK_ASSERT(sc); CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) bridge_rtnode_destroy(sc, brt); } } /* * bridge_rtdaddr: * * Remove an address from the table. */ static int bridge_rtdaddr(struct bridge_softc *sc, const uint8_t *addr, ether_vlanid_t vlan) { struct bridge_rtnode *brt; int found = 0; BRIDGE_RT_LOCK(sc); /* * If vlan is DOT1Q_VID_RSVD_IMPL then we want to delete for all vlans * so the lookup may return more than one. */ while ((brt = bridge_rtnode_lookup(sc, addr, vlan)) != NULL) { bridge_rtnode_destroy(sc, brt); found = 1; } BRIDGE_RT_UNLOCK(sc); return (found ? 0 : ENOENT); } /* * bridge_rtdelete: * * Delete routes to a speicifc member interface. */ static void bridge_rtdelete(struct bridge_softc *sc, struct ifnet *ifp, int full) { struct bridge_rtnode *brt, *nbrt; BRIDGE_RT_LOCK_ASSERT(sc); CK_LIST_FOREACH_SAFE(brt, &sc->sc_rtlist, brt_list, nbrt) { if (brt->brt_ifp == ifp && (full || (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC)) bridge_rtnode_destroy(sc, brt); } } /* * bridge_rtable_init: * * Initialize the route table for this bridge. */ static void bridge_rtable_init(struct bridge_softc *sc) { int i; sc->sc_rthash = malloc(sizeof(*sc->sc_rthash) * BRIDGE_RTHASH_SIZE, M_DEVBUF, M_WAITOK); for (i = 0; i < BRIDGE_RTHASH_SIZE; i++) CK_LIST_INIT(&sc->sc_rthash[i]); sc->sc_rthash_key = arc4random(); CK_LIST_INIT(&sc->sc_rtlist); } /* * bridge_rtable_fini: * * Deconstruct the route table for this bridge. */ static void bridge_rtable_fini(struct bridge_softc *sc) { KASSERT(sc->sc_brtcnt == 0, ("%s: %d bridge routes referenced", __func__, sc->sc_brtcnt)); free(sc->sc_rthash, M_DEVBUF); } /* * The following hash function is adapted from "Hash Functions" by Bob Jenkins * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). */ #define mix(a, b, c) \ do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (/*CONSTCOND*/0) static __inline uint32_t bridge_rthash(struct bridge_softc *sc, const uint8_t *addr) { uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = sc->sc_rthash_key; b += addr[5] << 8; b += addr[4]; a += addr[3] << 24; a += addr[2] << 16; a += addr[1] << 8; a += addr[0]; mix(a, b, c); return (c & BRIDGE_RTHASH_MASK); } #undef mix static int bridge_rtnode_addr_cmp(const uint8_t *a, const uint8_t *b) { int i, d; for (i = 0, d = 0; i < ETHER_ADDR_LEN && d == 0; i++) { d = ((int)a[i]) - ((int)b[i]); } return (d); } /* * bridge_rtnode_lookup: * * Look up a bridge route node for the specified destination. Compare the * vlan id or if zero then just return the first match. */ static struct bridge_rtnode * bridge_rtnode_lookup(struct bridge_softc *sc, const uint8_t *addr, ether_vlanid_t vlan) { struct bridge_rtnode *brt; uint32_t hash; int dir; BRIDGE_RT_LOCK_OR_NET_EPOCH_ASSERT(sc); hash = bridge_rthash(sc, addr); CK_LIST_FOREACH(brt, &sc->sc_rthash[hash], brt_hash) { dir = bridge_rtnode_addr_cmp(addr, brt->brt_addr); if (dir == 0 && (brt->brt_vlan == vlan || vlan == DOT1Q_VID_RSVD_IMPL)) return (brt); if (dir > 0) return (NULL); } return (NULL); } /* * bridge_rtnode_insert: * * Insert the specified bridge node into the route table. We * assume the entry is not already in the table. */ static int bridge_rtnode_insert(struct bridge_softc *sc, struct bridge_rtnode *brt) { struct bridge_rtnode *lbrt; uint32_t hash; int dir; BRIDGE_RT_LOCK_ASSERT(sc); hash = bridge_rthash(sc, brt->brt_addr); lbrt = CK_LIST_FIRST(&sc->sc_rthash[hash]); if (lbrt == NULL) { CK_LIST_INSERT_HEAD(&sc->sc_rthash[hash], brt, brt_hash); goto out; } do { dir = bridge_rtnode_addr_cmp(brt->brt_addr, lbrt->brt_addr); if (dir == 0 && brt->brt_vlan == lbrt->brt_vlan) return (EXTERROR(EEXIST, "Address already exists")); if (dir > 0) { CK_LIST_INSERT_BEFORE(lbrt, brt, brt_hash); goto out; } if (CK_LIST_NEXT(lbrt, brt_hash) == NULL) { CK_LIST_INSERT_AFTER(lbrt, brt, brt_hash); goto out; } lbrt = CK_LIST_NEXT(lbrt, brt_hash); } while (lbrt != NULL); #ifdef DIAGNOSTIC panic("bridge_rtnode_insert: impossible"); #endif out: CK_LIST_INSERT_HEAD(&sc->sc_rtlist, brt, brt_list); sc->sc_brtcnt++; return (0); } static void bridge_rtnode_destroy_cb(struct epoch_context *ctx) { struct bridge_rtnode *brt; brt = __containerof(ctx, struct bridge_rtnode, brt_epoch_ctx); CURVNET_SET(brt->brt_vnet); uma_zfree(V_bridge_rtnode_zone, brt); CURVNET_RESTORE(); } /* * bridge_rtnode_destroy: * * Destroy a bridge rtnode. */ static void bridge_rtnode_destroy(struct bridge_softc *sc, struct bridge_rtnode *brt) { BRIDGE_RT_LOCK_ASSERT(sc); CK_LIST_REMOVE(brt, brt_hash); CK_LIST_REMOVE(brt, brt_list); sc->sc_brtcnt--; brt->brt_dst->bif_addrcnt--; NET_EPOCH_CALL(bridge_rtnode_destroy_cb, &brt->brt_epoch_ctx); } /* * bridge_rtable_expire: * * Set the expiry time for all routes on an interface. */ static void bridge_rtable_expire(struct ifnet *ifp, int age) { struct bridge_iflist *bif = NULL; struct bridge_softc *sc = NULL; struct bridge_rtnode *brt; CURVNET_SET(ifp->if_vnet); bif = ifp->if_bridge; if (bif) sc = bif->bif_sc; MPASS(sc != NULL); BRIDGE_RT_LOCK(sc); /* * If the age is zero then flush, otherwise set all the expiry times to * age for the interface */ if (age == 0) bridge_rtdelete(sc, ifp, IFBF_FLUSHDYN); else { CK_LIST_FOREACH(brt, &sc->sc_rtlist, brt_list) { /* Cap the expiry time to 'age' */ if (brt->brt_ifp == ifp && brt->brt_expire > time_uptime + age && (brt->brt_flags & IFBAF_TYPEMASK) == IFBAF_DYNAMIC) brt->brt_expire = time_uptime + age; } } BRIDGE_RT_UNLOCK(sc); CURVNET_RESTORE(); } /* * bridge_state_change: * * Callback from the bridgestp code when a port changes states. */ static void bridge_state_change(struct ifnet *ifp, int state) { struct bridge_iflist *bif = ifp->if_bridge; struct bridge_softc *sc = bif->bif_sc; static const char *stpstates[] = { "disabled", "listening", "learning", "forwarding", "blocking", "discarding" }; CURVNET_SET(ifp->if_vnet); if (V_log_stp) log(LOG_NOTICE, "%s: state changed to %s on %s\n", sc->sc_ifp->if_xname, stpstates[state], ifp->if_xname); CURVNET_RESTORE(); } /* * Send bridge packets through pfil if they are one of the types pfil can deal * with, or if they are ARP or REVARP. (pfil will pass ARP and REVARP without * question.) If *bifp or *ifp are NULL then packet filtering is skipped for * that interface. */ static int bridge_pfil(struct mbuf **mp, struct ifnet *bifp, struct ifnet *ifp, int dir) { int snap, error, i; struct ether_header *eh1, eh2; struct llc llc1; u_int16_t ether_type; pfil_return_t rv; #ifdef INET struct ip *ip = NULL; int hlen = 0; #endif snap = 0; error = -1; /* Default error if not error == 0 */ #if 0 /* we may return with the IP fields swapped, ensure its not shared */ KASSERT(M_WRITABLE(*mp), ("%s: modifying a shared mbuf", __func__)); #endif if (V_pfil_bridge == 0 && V_pfil_member == 0 && V_pfil_ipfw == 0) return (0); /* filtering is disabled */ i = min((*mp)->m_pkthdr.len, max_protohdr); if ((*mp)->m_len < i) { *mp = m_pullup(*mp, i); if (*mp == NULL) { printf("%s: m_pullup failed\n", __func__); return (-1); } } eh1 = mtod(*mp, struct ether_header *); ether_type = ntohs(eh1->ether_type); /* * Check for SNAP/LLC. */ if (ether_type < ETHERMTU) { struct llc *llc2 = (struct llc *)(eh1 + 1); if ((*mp)->m_len >= ETHER_HDR_LEN + 8 && llc2->llc_dsap == LLC_SNAP_LSAP && llc2->llc_ssap == LLC_SNAP_LSAP && llc2->llc_control == LLC_UI) { ether_type = htons(llc2->llc_un.type_snap.ether_type); snap = 1; } } /* * If we're trying to filter bridge traffic, only look at traffic for * protocols available in the kernel (IPv4 and/or IPv6) to avoid * passing traffic for an unsupported protocol to the filter. This is * lame since if we really wanted, say, an AppleTalk filter, we are * hosed, but of course we don't have an AppleTalk filter to begin * with. (Note that since pfil doesn't understand ARP it will pass * *ALL* ARP traffic.) */ switch (ether_type) { #ifdef INET case ETHERTYPE_ARP: case ETHERTYPE_REVARP: if (V_pfil_ipfw_arp == 0) return (0); /* Automatically pass */ /* FALLTHROUGH */ case ETHERTYPE_IP: #endif #ifdef INET6 case ETHERTYPE_IPV6: #endif /* INET6 */ break; default: /* * We get here if the packet isn't from a supported * protocol. Check to see if the user wants to pass * non-IP packets, these will not be checked by pfil(9) * and passed unconditionally so the default is to * drop. */ if (V_pfil_onlyip) goto bad; } /* Run the packet through pfil before stripping link headers */ if (PFIL_HOOKED_OUT(V_link_pfil_head) && V_pfil_ipfw != 0 && dir == PFIL_OUT && ifp != NULL) { switch (pfil_mbuf_out(V_link_pfil_head, mp, ifp, NULL)) { case PFIL_DROPPED: return (EACCES); case PFIL_CONSUMED: return (0); } } /* Strip off the Ethernet header and keep a copy. */ m_copydata(*mp, 0, ETHER_HDR_LEN, (caddr_t) &eh2); m_adj(*mp, ETHER_HDR_LEN); /* Strip off snap header, if present */ if (snap) { m_copydata(*mp, 0, sizeof(struct llc), (caddr_t) &llc1); m_adj(*mp, sizeof(struct llc)); } /* * Check the IP header for alignment and errors */ if (dir == PFIL_IN) { switch (ether_type) { #ifdef INET case ETHERTYPE_IP: error = bridge_ip_checkbasic(mp); break; #endif #ifdef INET6 case ETHERTYPE_IPV6: error = bridge_ip6_checkbasic(mp); break; #endif /* INET6 */ default: error = 0; } if (error) goto bad; } error = 0; /* * Run the packet through pfil */ rv = PFIL_PASS; switch (ether_type) { #ifdef INET case ETHERTYPE_IP: /* * Run pfil on the member interface and the bridge, both can * be skipped by clearing pfil_member or pfil_bridge. * * Keep the order: * in_if -> bridge_if -> out_if */ if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv = pfil_mbuf_out(V_inet_pfil_head, mp, bifp, NULL)) != PFIL_PASS) break; if (V_pfil_member && ifp != NULL) { rv = (dir == PFIL_OUT) ? pfil_mbuf_out(V_inet_pfil_head, mp, ifp, NULL) : pfil_mbuf_in(V_inet_pfil_head, mp, ifp, NULL); if (rv != PFIL_PASS) break; } if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv = pfil_mbuf_in(V_inet_pfil_head, mp, bifp, NULL)) != PFIL_PASS) break; /* check if we need to fragment the packet */ /* bridge_fragment generates a mbuf chain of packets */ /* that already include eth headers */ if (V_pfil_member && ifp != NULL && dir == PFIL_OUT) { i = (*mp)->m_pkthdr.len; if (i > ifp->if_mtu) { error = bridge_fragment(ifp, mp, &eh2, snap, &llc1); return (error); } } /* Recalculate the ip checksum. */ ip = mtod(*mp, struct ip *); hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) goto bad; if (hlen > (*mp)->m_len) { if ((*mp = m_pullup(*mp, hlen)) == NULL) goto bad; ip = mtod(*mp, struct ip *); if (ip == NULL) goto bad; } ip->ip_sum = 0; if (hlen == sizeof(struct ip)) ip->ip_sum = in_cksum_hdr(ip); else ip->ip_sum = in_cksum(*mp, hlen); break; #endif /* INET */ #ifdef INET6 case ETHERTYPE_IPV6: if (V_pfil_bridge && dir == PFIL_OUT && bifp != NULL && (rv = pfil_mbuf_out(V_inet6_pfil_head, mp, bifp, NULL)) != PFIL_PASS) break; if (V_pfil_member && ifp != NULL) { rv = (dir == PFIL_OUT) ? pfil_mbuf_out(V_inet6_pfil_head, mp, ifp, NULL) : pfil_mbuf_in(V_inet6_pfil_head, mp, ifp, NULL); if (rv != PFIL_PASS) break; } if (V_pfil_bridge && dir == PFIL_IN && bifp != NULL && (rv = pfil_mbuf_in(V_inet6_pfil_head, mp, bifp, NULL)) != PFIL_PASS) break; break; #endif } switch (rv) { case PFIL_CONSUMED: return (0); case PFIL_DROPPED: return (EACCES); default: break; } error = -1; /* * Finally, put everything back the way it was and return */ if (snap) { M_PREPEND(*mp, sizeof(struct llc), M_NOWAIT); if (*mp == NULL) return (error); bcopy(&llc1, mtod(*mp, caddr_t), sizeof(struct llc)); } M_PREPEND(*mp, ETHER_HDR_LEN, M_NOWAIT); if (*mp == NULL) return (error); bcopy(&eh2, mtod(*mp, caddr_t), ETHER_HDR_LEN); return (0); bad: m_freem(*mp); *mp = NULL; return (error); } #ifdef INET /* * Perform basic checks on header size since * pfil assumes ip_input has already processed * it for it. Cut-and-pasted from ip_input.c. * Given how simple the IPv6 version is, * does the IPv4 version really need to be * this complicated? * * XXX Should we update ipstat here, or not? * XXX Right now we update ipstat but not * XXX csum_counter. */ static int bridge_ip_checkbasic(struct mbuf **mp) { struct mbuf *m = *mp; struct ip *ip; int len, hlen; u_short sum; if (*mp == NULL) return (-1); if (IP_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { if ((m = m_copyup(m, sizeof(struct ip), (max_linkhdr + 3) & ~3)) == NULL) { /* XXXJRT new stat, please */ KMOD_IPSTAT_INC(ips_toosmall); goto bad; } } else if (__predict_false(m->m_len < sizeof (struct ip))) { if ((m = m_pullup(m, sizeof (struct ip))) == NULL) { KMOD_IPSTAT_INC(ips_toosmall); goto bad; } } ip = mtod(m, struct ip *); if (ip == NULL) goto bad; if (ip->ip_v != IPVERSION) { KMOD_IPSTAT_INC(ips_badvers); goto bad; } hlen = ip->ip_hl << 2; if (hlen < sizeof(struct ip)) { /* minimum header length */ KMOD_IPSTAT_INC(ips_badhlen); goto bad; } if (hlen > m->m_len) { if ((m = m_pullup(m, hlen)) == NULL) { KMOD_IPSTAT_INC(ips_badhlen); goto bad; } ip = mtod(m, struct ip *); if (ip == NULL) goto bad; } if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED) { sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID); } else { if (hlen == sizeof(struct ip)) { sum = in_cksum_hdr(ip); } else { sum = in_cksum(m, hlen); } } if (sum) { KMOD_IPSTAT_INC(ips_badsum); goto bad; } /* Retrieve the packet length. */ len = ntohs(ip->ip_len); /* * Check for additional length bogosity */ if (len < hlen) { KMOD_IPSTAT_INC(ips_badlen); goto bad; } /* * Check that the amount of data in the buffers * is as at least much as the IP header would have us expect. * Drop packet if shorter than we expect. */ if (m->m_pkthdr.len < len) { KMOD_IPSTAT_INC(ips_tooshort); goto bad; } /* Checks out, proceed */ *mp = m; return (0); bad: *mp = m; return (-1); } #endif /* INET */ #ifdef INET6 /* * Same as above, but for IPv6. * Cut-and-pasted from ip6_input.c. * XXX Should we update ip6stat, or not? */ static int bridge_ip6_checkbasic(struct mbuf **mp) { struct mbuf *m = *mp; struct ip6_hdr *ip6; /* * If the IPv6 header is not aligned, slurp it up into a new * mbuf with space for link headers, in the event we forward * it. Otherwise, if it is aligned, make sure the entire base * IPv6 header is in the first mbuf of the chain. */ if (IP6_HDR_ALIGNED_P(mtod(m, caddr_t)) == 0) { struct ifnet *inifp = m->m_pkthdr.rcvif; if ((m = m_copyup(m, sizeof(struct ip6_hdr), (max_linkhdr + 3) & ~3)) == NULL) { /* XXXJRT new stat, please */ IP6STAT_INC(ip6s_toosmall); in6_ifstat_inc(inifp, ifs6_in_hdrerr); goto bad; } } else if (__predict_false(m->m_len < sizeof(struct ip6_hdr))) { struct ifnet *inifp = m->m_pkthdr.rcvif; if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { IP6STAT_INC(ip6s_toosmall); in6_ifstat_inc(inifp, ifs6_in_hdrerr); goto bad; } } ip6 = mtod(m, struct ip6_hdr *); if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) { IP6STAT_INC(ip6s_badvers); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); goto bad; } /* Checks out, proceed */ *mp = m; return (0); bad: *mp = m; return (-1); } #endif /* INET6 */ #ifdef INET /* * bridge_fragment: * * Fragment mbuf chain in multiple packets and prepend ethernet header. */ static int bridge_fragment(struct ifnet *ifp, struct mbuf **mp, struct ether_header *eh, int snap, struct llc *llc) { struct mbuf *m = *mp, *nextpkt = NULL, *mprev = NULL, *mcur = NULL; struct ip *ip; int error = -1; if (m->m_len < sizeof(struct ip) && (m = m_pullup(m, sizeof(struct ip))) == NULL) goto dropit; ip = mtod(m, struct ip *); m->m_pkthdr.csum_flags |= CSUM_IP; error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist); if (error) goto dropit; /* * Walk the chain and re-add the Ethernet header for * each mbuf packet. */ for (mcur = m; mcur; mcur = mcur->m_nextpkt) { nextpkt = mcur->m_nextpkt; mcur->m_nextpkt = NULL; if (snap) { M_PREPEND(mcur, sizeof(struct llc), M_NOWAIT); if (mcur == NULL) { error = ENOBUFS; if (mprev != NULL) mprev->m_nextpkt = nextpkt; goto dropit; } bcopy(llc, mtod(mcur, caddr_t),sizeof(struct llc)); } M_PREPEND(mcur, ETHER_HDR_LEN, M_NOWAIT); if (mcur == NULL) { error = ENOBUFS; if (mprev != NULL) mprev->m_nextpkt = nextpkt; goto dropit; } bcopy(eh, mtod(mcur, caddr_t), ETHER_HDR_LEN); /* * The previous two M_PREPEND could have inserted one or two * mbufs in front so we have to update the previous packet's * m_nextpkt. */ mcur->m_nextpkt = nextpkt; if (mprev != NULL) mprev->m_nextpkt = mcur; else { /* The first mbuf in the original chain needs to be * updated. */ *mp = mcur; } mprev = mcur; } KMOD_IPSTAT_INC(ips_fragmented); return (error); dropit: for (mcur = *mp; mcur; mcur = m) { /* droping the full packet chain */ m = mcur->m_nextpkt; m_freem(mcur); } return (error); } #endif /* INET */ static void bridge_linkstate(struct ifnet *ifp) { struct bridge_softc *sc = NULL; struct bridge_iflist *bif; struct epoch_tracker et; NET_EPOCH_ENTER(et); bif = ifp->if_bridge; if (bif) sc = bif->bif_sc; if (sc != NULL) { bridge_linkcheck(sc); bstp_linkstate(&bif->bif_stp); } NET_EPOCH_EXIT(et); } static void bridge_linkcheck(struct bridge_softc *sc) { struct bridge_iflist *bif; int new_link, hasls; BRIDGE_LOCK_OR_NET_EPOCH_ASSERT(sc); new_link = LINK_STATE_DOWN; hasls = 0; /* Our link is considered up if at least one of our ports is active */ CK_LIST_FOREACH(bif, &sc->sc_iflist, bif_next) { if (bif->bif_ifp->if_capabilities & IFCAP_LINKSTATE) hasls++; if (bif->bif_ifp->if_link_state == LINK_STATE_UP) { new_link = LINK_STATE_UP; break; } } if (!CK_LIST_EMPTY(&sc->sc_iflist) && !hasls) { /* If no interfaces support link-state then we default to up */ new_link = LINK_STATE_UP; } if_link_state_change(sc->sc_ifp, new_link); } diff --git a/sys/net/if_bridgevar.h b/sys/net/if_bridgevar.h index 6718c5ebcc34..15194fecff7c 100644 --- a/sys/net/if_bridgevar.h +++ b/sys/net/if_bridgevar.h @@ -1,374 +1,374 @@ /* $NetBSD: if_bridgevar.h,v 1.4 2003/07/08 07:13:50 itojun Exp $ */ /* * SPDX-License-Identifier: BSD-4-Clause * * Copyright 2001 Wasabi Systems, Inc. * All rights reserved. * * Written by Jason R. Thorpe for Wasabi Systems, Inc. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the NetBSD Project by * Wasabi Systems, Inc. * 4. The name of Wasabi Systems, Inc. may not be used to endorse * or promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC * 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. */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jason L. Wright * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. * * OpenBSD: if_bridge.h,v 1.14 2001/03/22 03:48:29 jason Exp */ /* * Data structure and control definitions for bridge interfaces. */ #ifndef _NET_IF_BRIDGEVAR_H_ #define _NET_IF_BRIDGEVAR_H_ #include #include #include #include #include #include #include #include /* * Commands used in the SIOCSDRVSPEC ioctl. Note the lookup of the * bridge interface itself is keyed off the ifdrv structure. */ #define BRDGADD 0 /* add bridge member (ifbreq) */ #define BRDGDEL 1 /* delete bridge member (ifbreq) */ #define BRDGGIFFLGS 2 /* get member if flags (ifbreq) */ #define BRDGSIFFLGS 3 /* set member if flags (ifbreq) */ #define BRDGSCACHE 4 /* set cache size (ifbrparam) */ #define BRDGGCACHE 5 /* get cache size (ifbrparam) */ #define BRDGGIFS 6 /* get member list (ifbifconf) */ #define BRDGRTS 7 /* get address list (ifbaconf) */ #define BRDGSADDR 8 /* set static address (ifbareq) */ #define BRDGSTO 9 /* set cache timeout (ifbrparam) */ #define BRDGGTO 10 /* get cache timeout (ifbrparam) */ #define BRDGDADDR 11 /* delete address (ifbareq) */ #define BRDGFLUSH 12 /* flush address cache (ifbreq) */ #define BRDGGPRI 13 /* get priority (ifbrparam) */ #define BRDGSPRI 14 /* set priority (ifbrparam) */ #define BRDGGHT 15 /* get hello time (ifbrparam) */ #define BRDGSHT 16 /* set hello time (ifbrparam) */ #define BRDGGFD 17 /* get forward delay (ifbrparam) */ #define BRDGSFD 18 /* set forward delay (ifbrparam) */ #define BRDGGMA 19 /* get max age (ifbrparam) */ #define BRDGSMA 20 /* set max age (ifbrparam) */ #define BRDGSIFPRIO 21 /* set if priority (ifbreq) */ #define BRDGSIFCOST 22 /* set if path cost (ifbreq) */ #define BRDGADDS 23 /* add bridge span member (ifbreq) */ #define BRDGDELS 24 /* delete bridge span member (ifbreq) */ #define BRDGPARAM 25 /* get bridge STP params (ifbropreq) */ #define BRDGGRTE 26 /* get cache drops (ifbrparam) */ #define BRDGGIFSSTP 27 /* get member STP params list * (ifbpstpconf) */ #define BRDGSPROTO 28 /* set protocol (ifbrparam) */ #define BRDGSTXHC 29 /* set tx hold count (ifbrparam) */ #define BRDGSIFAMAX 30 /* set max interface addrs (ifbreq) */ #define BRDGSIFPVID 31 /* set if PVID */ #define BRDGSIFVLANSET 32 /* set if vlan set */ #define BRDGGIFVLANSET 33 /* get if vlan set */ #define BRDGGFLAGS 34 /* get bridge flags (ifbrparam) */ #define BRDGSFLAGS 35 /* set bridge flags (ifbrparam) */ #define BRDGGDEFPVID 36 /* get default pvid (ifbrparam) */ #define BRDGSDEFPVID 37 /* set default pvid (ifbrparam) */ /* BRDGSFLAGS, Bridge flags (non-interface-specific) */ typedef uint32_t ifbr_flags_t; #define IFBRF_VLANFILTER (1U<<0) /* VLAN filtering enabled */ +#define IFBRF_DEFQINQ (1U<<1) /* 802.1ad Q-in-Q allowed by default */ -#define IFBRFBITS "\020\01VLANFILTER" +#define IFBRFBITS "\020\01VLANFILTER\02DEFQINQ" /* * Generic bridge control request. */ struct ifbreq { char ifbr_ifsname[IFNAMSIZ]; /* member if name */ uint32_t ifbr_ifsflags; /* member if flags */ uint32_t ifbr_stpflags; /* member if STP flags */ uint32_t ifbr_path_cost; /* member if STP cost */ uint8_t ifbr_portno; /* member if port number */ uint8_t ifbr_priority; /* member if STP priority */ uint8_t ifbr_proto; /* member if STP protocol */ uint8_t ifbr_role; /* member if STP role */ uint8_t ifbr_state; /* member if STP state */ uint32_t ifbr_addrcnt; /* member if addr number */ uint32_t ifbr_addrmax; /* member if addr max */ uint32_t ifbr_addrexceeded; /* member if addr violations */ ether_vlanid_t ifbr_pvid; /* member if PVID */ uint8_t pad[32]; }; /* BRDGGIFFLAGS, BRDGSIFFLAGS */ #define IFBIF_LEARNING 0x0001 /* if can learn */ #define IFBIF_DISCOVER 0x0002 /* if sends packets w/ unknown dest. */ #define IFBIF_STP 0x0004 /* if participates in spanning tree */ #define IFBIF_SPAN 0x0008 /* if is a span port */ #define IFBIF_STICKY 0x0010 /* if learned addresses stick */ #define IFBIF_BSTP_EDGE 0x0020 /* member stp edge port */ #define IFBIF_BSTP_AUTOEDGE 0x0040 /* member stp autoedge enabled */ #define IFBIF_BSTP_PTP 0x0080 /* member stp point to point */ #define IFBIF_BSTP_AUTOPTP 0x0100 /* member stp autoptp enabled */ #define IFBIF_BSTP_ADMEDGE 0x0200 /* member stp admin edge enabled */ #define IFBIF_BSTP_ADMCOST 0x0400 /* member stp admin path cost */ #define IFBIF_PRIVATE 0x0800 /* if is a private segment */ -/* was IFBIF_VLANFILTER 0x1000 */ -#define IFBIF_QINQ 0x2000 /* if allows 802.1ad Q-in-Q */ +#define IFBIF_QINQ 0x1000 /* if allows 802.1ad Q-in-Q */ #define IFBIFBITS "\020\001LEARNING\002DISCOVER\003STP\004SPAN" \ "\005STICKY\014PRIVATE\006EDGE\007AUTOEDGE\010PTP" \ - "\011AUTOPTP" + "\011AUTOPTP\015QINQ" #define IFBIFMASK ~(IFBIF_BSTP_EDGE|IFBIF_BSTP_AUTOEDGE|IFBIF_BSTP_PTP| \ IFBIF_BSTP_AUTOPTP|IFBIF_BSTP_ADMEDGE| \ IFBIF_BSTP_ADMCOST) /* not saved */ /* BRDGFLUSH */ #define IFBF_FLUSHDYN 0x00 /* flush learned addresses only */ #define IFBF_FLUSHALL 0x01 /* flush all addresses */ /* * Interface list structure. */ struct ifbifconf { uint32_t ifbic_len; /* buffer size */ union { caddr_t ifbicu_buf; struct ifbreq *ifbicu_req; } ifbic_ifbicu; #define ifbic_buf ifbic_ifbicu.ifbicu_buf #define ifbic_req ifbic_ifbicu.ifbicu_req }; /* * Bridge address request. */ struct ifbareq { char ifba_ifsname[IFNAMSIZ]; /* member if name */ unsigned long ifba_expire; /* address expire time */ uint8_t ifba_flags; /* address flags */ uint8_t ifba_dst[ETHER_ADDR_LEN];/* destination address */ ether_vlanid_t ifba_vlan; /* vlan id */ }; #define IFBAF_TYPEMASK 0x03 /* address type mask */ #define IFBAF_DYNAMIC 0x00 /* dynamically learned address */ #define IFBAF_STATIC 0x01 /* static address */ #define IFBAF_STICKY 0x02 /* sticky address */ #define IFBAFBITS "\020\1STATIC\2STICKY" /* * Address list structure. */ struct ifbaconf { uint32_t ifbac_len; /* buffer size */ union { caddr_t ifbacu_buf; struct ifbareq *ifbacu_req; } ifbac_ifbacu; #define ifbac_buf ifbac_ifbacu.ifbacu_buf #define ifbac_req ifbac_ifbacu.ifbacu_req }; /* * Bridge parameter structure. */ struct ifbrparam { union { uint32_t ifbrpu_int32; uint16_t ifbrpu_int16; uint8_t ifbrpu_int8; } ifbrp_ifbrpu; }; #define ifbrp_csize ifbrp_ifbrpu.ifbrpu_int32 /* cache size */ #define ifbrp_ctime ifbrp_ifbrpu.ifbrpu_int32 /* cache time (sec) */ #define ifbrp_prio ifbrp_ifbrpu.ifbrpu_int16 /* bridge priority */ #define ifbrp_proto ifbrp_ifbrpu.ifbrpu_int8 /* bridge protocol */ #define ifbrp_txhc ifbrp_ifbrpu.ifbrpu_int8 /* bpdu tx holdcount */ #define ifbrp_hellotime ifbrp_ifbrpu.ifbrpu_int8 /* hello time (sec) */ #define ifbrp_fwddelay ifbrp_ifbrpu.ifbrpu_int8 /* fwd time (sec) */ #define ifbrp_maxage ifbrp_ifbrpu.ifbrpu_int8 /* max age (sec) */ #define ifbrp_cexceeded ifbrp_ifbrpu.ifbrpu_int32 /* # of cache dropped * addresses */ #define ifbrp_flags ifbrp_ifbrpu.ifbrpu_int32 /* bridge flags */ #define ifbrp_defpvid ifbrp_ifbrpu.ifbrpu_int16 /* default pvid */ /* * Bridge current operational parameters structure. */ struct ifbropreq { uint8_t ifbop_holdcount; uint8_t ifbop_maxage; uint8_t ifbop_hellotime; uint8_t ifbop_fwddelay; uint8_t ifbop_protocol; uint16_t ifbop_priority; uint16_t ifbop_root_port; uint32_t ifbop_root_path_cost; uint64_t ifbop_bridgeid; uint64_t ifbop_designated_root; uint64_t ifbop_designated_bridge; struct timeval ifbop_last_tc_time; }; /* * Bridge member operational STP params structure. */ struct ifbpstpreq { uint8_t ifbp_portno; /* bp STP port number */ uint32_t ifbp_fwd_trans; /* bp STP fwd transitions */ uint32_t ifbp_design_cost; /* bp STP designated cost */ uint32_t ifbp_design_port; /* bp STP designated port */ uint64_t ifbp_design_bridge; /* bp STP designated bridge */ uint64_t ifbp_design_root; /* bp STP designated root */ }; /* * Bridge STP ports list structure. */ struct ifbpstpconf { uint32_t ifbpstp_len; /* buffer size */ union { caddr_t ifbpstpu_buf; struct ifbpstpreq *ifbpstpu_req; } ifbpstp_ifbpstpu; #define ifbpstp_buf ifbpstp_ifbpstpu.ifbpstpu_buf #define ifbpstp_req ifbpstp_ifbpstpu.ifbpstpu_req }; #define STP_STATES \ "disabled", \ "listening", \ "learning", \ "forwarding", \ "blocking", \ "discarding" #define STP_PROTOS \ "stp", \ "-", \ "rstp" #define STP_ROLES \ "disabled", \ "root", \ "designated", \ "alternate", \ "backup" #define PV2ID(pv, epri, eaddr) do { \ epri = pv >> 48; \ eaddr[0] = pv >> 40; \ eaddr[1] = pv >> 32; \ eaddr[2] = pv >> 24; \ eaddr[3] = pv >> 16; \ eaddr[4] = pv >> 8; \ eaddr[5] = pv >> 0; \ } while (0) /* * Bridge VLAN access request. */ #define BRVLAN_SETSIZE 4096 typedef __BITSET_DEFINE(ifbvlan_set, BRVLAN_SETSIZE) ifbvlan_set_t; #define BRVLAN_SET(set, bit) __BIT_SET(BRVLAN_SETSIZE, (bit), set) #define BRVLAN_CLR(set, bit) __BIT_CLR(BRVLAN_SETSIZE, (bit), set) #define BRVLAN_TEST(set, bit) __BIT_ISSET(BRVLAN_SETSIZE, (bit), set) #define BRDG_VLAN_OP_SET 1 /* replace current vlan set */ #define BRDG_VLAN_OP_ADD 2 /* add vlans to current set */ #define BRDG_VLAN_OP_DEL 3 /* remove vlans from current set */ struct ifbif_vlan_req { char bv_ifname[IFNAMSIZ]; uint8_t bv_op; ifbvlan_set_t bv_set; }; #ifdef _KERNEL #define BRIDGE_INPUT(_ifp, _m) do { \ KASSERT((_ifp)->if_bridge_input != NULL, \ ("%s: if_bridge not loaded!", __func__)); \ _m = (*(_ifp)->if_bridge_input)(_ifp, _m); \ if (_m != NULL) { \ _ifp = _m->m_pkthdr.rcvif; \ m->m_flags &= ~M_BRIDGE_INJECT; \ } \ } while (0) #define BRIDGE_OUTPUT(_ifp, _m, _err) do { \ KASSERT((_ifp)->if_bridge_output != NULL, \ ("%s: if_bridge not loaded!", __func__)); \ _err = (*(_ifp)->if_bridge_output)(_ifp, _m, NULL, NULL); \ } while (0) extern void (*bridge_dn_p)(struct mbuf *, struct ifnet *); extern bool (*bridge_same_p)(const void *, const void *); extern void *(*bridge_get_softc_p)(struct ifnet *); extern bool (*bridge_member_ifaddrs_p)(void); #endif /* _KERNEL */ #endif /* _NET_IF_BRIDGEVAR_H_ */ diff --git a/tests/sys/net/if_bridge_test.sh b/tests/sys/net/if_bridge_test.sh index 534144f46632..8a6c730014ce 100755 --- a/tests/sys/net/if_bridge_test.sh +++ b/tests/sys/net/if_bridge_test.sh @@ -1,1409 +1,1419 @@ # # SPDX-License-Identifier: BSD-2-Clause # # Copyright (c) 2020 The FreeBSD Foundation # # This software was developed by Kristof Provost under sponsorship # from the FreeBSD Foundation. # # 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. . $(atf_get_srcdir)/../common/vnet.subr atf_test_case "bridge_transmit_ipv4_unicast" "cleanup" bridge_transmit_ipv4_unicast_head() { atf_set descr 'bridge_transmit_ipv4_unicast bridging test' atf_set require.user root } bridge_transmit_ipv4_unicast_body() { vnet_init vnet_init_bridge epair_alcatraz=$(vnet_mkepair) epair_singsing=$(vnet_mkepair) vnet_mkjail alcatraz ${epair_alcatraz}b vnet_mkjail singsing ${epair_singsing}b jexec alcatraz ifconfig ${epair_alcatraz}b 192.0.2.1/24 up jexec singsing ifconfig ${epair_singsing}b 192.0.2.2/24 up bridge=$(vnet_mkbridge) ifconfig ${bridge} up ifconfig ${epair_alcatraz}a up ifconfig ${epair_singsing}a up ifconfig ${bridge} addm ${epair_alcatraz}a ifconfig ${bridge} addm ${epair_singsing}a atf_check -s exit:0 -o ignore jexec alcatraz ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec singsing ping -c 3 -t 1 192.0.2.1 } bridge_transmit_ipv4_unicast_cleanup() { vnet_cleanup } atf_test_case "stp" "cleanup" stp_head() { atf_set descr 'Spanning tree test' atf_set require.user root } stp_body() { vnet_init vnet_init_bridge epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) bridge_a=$(vnet_mkbridge) bridge_b=$(vnet_mkbridge) vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b jexec a ifconfig ${epair_one}a up jexec a ifconfig ${epair_two}a up jexec a ifconfig ${bridge_a} addm ${epair_one}a jexec a ifconfig ${bridge_a} addm ${epair_two}a jexec b ifconfig ${epair_one}b up jexec b ifconfig ${epair_two}b up jexec b ifconfig ${bridge_b} addm ${epair_one}b jexec b ifconfig ${bridge_b} addm ${epair_two}b jexec a ifconfig ${bridge_a} 192.0.2.1/24 # Enable spanning tree jexec a ifconfig ${bridge_a} stp ${epair_one}a jexec a ifconfig ${bridge_a} stp ${epair_two}a jexec b ifconfig ${bridge_b} stp ${epair_one}b jexec b ifconfig ${bridge_b} stp ${epair_two}b jexec b ifconfig ${bridge_b} up jexec a ifconfig ${bridge_a} up # Give STP time to do its thing sleep 5 a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding) b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding) if [ -z "${a_discard}" ] && [ -z "${b_discard}" ] then atf_fail "STP failed to detect bridging loop" fi # We must also have at least some forwarding interfaces a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding) b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding) if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ] then atf_fail "STP failed to detect bridging loop" fi } stp_cleanup() { vnet_cleanup } atf_test_case "stp_vlan" "cleanup" stp_vlan_head() { atf_set descr 'Spanning tree on VLAN test' atf_set require.user root } stp_vlan_body() { vnet_init vnet_init_bridge epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) bridge_a=$(vnet_mkbridge) bridge_b=$(vnet_mkbridge) vnet_mkjail a ${bridge_a} ${epair_one}a ${epair_two}a vnet_mkjail b ${bridge_b} ${epair_one}b ${epair_two}b jexec a ifconfig ${epair_one}a up jexec a ifconfig ${epair_two}a up vlan_a_one=$(jexec a ifconfig vlan create vlandev ${epair_one}a vlan 42) vlan_a_two=$(jexec a ifconfig vlan create vlandev ${epair_two}a vlan 42) jexec a ifconfig ${vlan_a_one} up jexec a ifconfig ${vlan_a_two} up jexec a ifconfig ${bridge_a} addm ${vlan_a_one} jexec a ifconfig ${bridge_a} addm ${vlan_a_two} jexec b ifconfig ${epair_one}b up jexec b ifconfig ${epair_two}b up vlan_b_one=$(jexec b ifconfig vlan create vlandev ${epair_one}b vlan 42) vlan_b_two=$(jexec b ifconfig vlan create vlandev ${epair_two}b vlan 42) jexec b ifconfig ${vlan_b_one} up jexec b ifconfig ${vlan_b_two} up jexec b ifconfig ${bridge_b} addm ${vlan_b_one} jexec b ifconfig ${bridge_b} addm ${vlan_b_two} jexec a ifconfig ${bridge_a} 192.0.2.1/24 # Enable spanning tree jexec a ifconfig ${bridge_a} stp ${vlan_a_one} jexec a ifconfig ${bridge_a} stp ${vlan_a_two} jexec b ifconfig ${bridge_b} stp ${vlan_b_one} jexec b ifconfig ${bridge_b} stp ${vlan_b_two} jexec b ifconfig ${bridge_b} up jexec a ifconfig ${bridge_a} up # Give STP time to do its thing sleep 5 a_discard=$(jexec a ifconfig ${bridge_a} | grep discarding) b_discard=$(jexec b ifconfig ${bridge_b} | grep discarding) if [ -z "${a_discard}" ] && [ -z "${b_discard}" ] then atf_fail "STP failed to detect bridging loop" fi # We must also have at least some forwarding interfaces a_forwarding=$(jexec a ifconfig ${bridge_a} | grep forwarding) b_forwarding=$(jexec b ifconfig ${bridge_b} | grep forwarding) if [ -z "${a_forwarding}" ] && [ -z "${b_forwarding}" ] then atf_fail "STP failed to detect bridging loop" fi } stp_vlan_cleanup() { vnet_cleanup } atf_test_case "static" "cleanup" static_head() { atf_set descr 'Bridge static address test' atf_set require.user root } static_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) bridge=$(vnet_mkbridge) vnet_mkjail one ${bridge} ${epair}a ifconfig ${epair}b up jexec one ifconfig ${bridge} up jexec one ifconfig ${epair}a up jexec one ifconfig ${bridge} addm ${epair}a # Wrong interface atf_check -s exit:1 -o ignore -e ignore \ jexec one ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05 # Bad address format atf_check -s exit:1 -o ignore -e ignore \ jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04 # Correct add atf_check -s exit:0 -o ignore \ jexec one ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 # List addresses atf_check -s exit:0 \ -o match:"00:01:02:03:04:05 Vlan0 ${epair}a 0 flags=1" \ jexec one ifconfig ${bridge} addr # Delete with bad address format atf_check -s exit:1 -o ignore -e ignore \ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04 # Delete with unlisted address atf_check -s exit:1 -o ignore -e ignore \ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:06 # Correct delete atf_check -s exit:0 -o ignore \ jexec one ifconfig ${bridge} deladdr 00:01:02:03:04:05 } static_cleanup() { vnet_cleanup } atf_test_case "vstatic" "cleanup" vstatic_head() { atf_set descr 'Bridge VLAN static address test' atf_set require.user root } vstatic_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) bridge=$(vnet_mkbridge) vnet_mkjail one ${bridge} ${epair}a ifconfig ${epair}b up jexec one ifconfig ${bridge} up jexec one ifconfig ${epair}a up jexec one ifconfig ${bridge} addm ${epair}a # Wrong interface atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} static ${epair}b 00:01:02:03:04:05 vlan 10 # Bad address format atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} static ${epair}a 00:01:02:03:04 vlan 10 # Invalid VLAN ID atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 5000 # Correct add atf_check -s exit:0 -o ignore jexec one \ ifconfig ${bridge} static ${epair}a 00:01:02:03:04:05 vlan 10 # List addresses atf_check -s exit:0 \ -o match:"00:01:02:03:04:05 Vlan10 ${epair}a 0 flags=1" \ jexec one ifconfig ${bridge} addr # Delete with bad address format atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} deladdr 00:01:02:03:04 vlan 10 # Delete with unlisted address atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} deladdr 00:01:02:03:04:06 vlan 10 # Delete with wrong vlan id atf_check -s exit:1 -o ignore -e ignore jexec one \ ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 20 # Correct delete atf_check -s exit:0 -o ignore jexec one \ ifconfig ${bridge} deladdr 00:01:02:03:04:05 vlan 10 } vstatic_cleanup() { vnet_cleanup } atf_test_case "span" "cleanup" span_head() { atf_set descr 'Bridge span test' atf_set require.user root atf_set require.progs python3 scapy } span_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) epair_span=$(vnet_mkepair) bridge=$(vnet_mkbridge) vnet_mkjail one ${bridge} ${epair}a ${epair_span}a ifconfig ${epair}b up ifconfig ${epair_span}b up jexec one ifconfig ${bridge} up jexec one ifconfig ${epair}a up jexec one ifconfig ${epair_span}a up jexec one ifconfig ${bridge} addm ${epair}a jexec one ifconfig ${bridge} span ${epair_span}a jexec one ifconfig ${bridge} 192.0.2.1/24 # Send some traffic through the span jexec one ping -c 1 -t 1 192.0.2.2 # Check that we see the traffic on the span interface atf_check -s exit:0 \ $(atf_get_srcdir)/../netpfil/common/pft_ping.py \ --sendif ${epair}b \ --to 192.0.2.2 \ --recvif ${epair_span}b jexec one ifconfig ${bridge} -span ${epair_span}a # And no more traffic after we remove the span atf_check -s exit:1 \ $(atf_get_srcdir)/../netpfil/common/pft_ping.py \ --sendif ${epair}b \ --to 192.0.2.2 \ --recvif ${epair_span}b } span_cleanup() { vnet_cleanup } atf_test_case "delete_with_members" "cleanup" delete_with_members_head() { atf_set descr 'Delete a bridge which still has member interfaces' atf_set require.user root } delete_with_members_body() { vnet_init vnet_init_bridge bridge=$(vnet_mkbridge) epair=$(vnet_mkepair) ifconfig ${bridge} 192.0.2.1/24 up ifconfig ${epair}a up ifconfig ${bridge} addm ${epair}a ifconfig ${bridge} destroy } delete_with_members_cleanup() { vnet_cleanup } atf_test_case "mac_conflict" "cleanup" mac_conflict_head() { atf_set descr 'Ensure that bridges in different jails get different mac addresses' atf_set require.user root } mac_conflict_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) # Ensure the bridge module is loaded so jails can use it. tmpbridge=$(vnet_mkbridge) vnet_mkjail bridge_mac_conflict_one ${epair}a vnet_mkjail bridge_mac_conflict_two ${epair}b jexec bridge_mac_conflict_one ifconfig bridge create jexec bridge_mac_conflict_one ifconfig bridge0 192.0.2.1/24 up \ addm ${epair}a jexec bridge_mac_conflict_one ifconfig ${epair}a up jexec bridge_mac_conflict_two ifconfig bridge create jexec bridge_mac_conflict_two ifconfig bridge0 192.0.2.2/24 up \ addm ${epair}b jexec bridge_mac_conflict_two ifconfig ${epair}b up atf_check -s exit:0 -o ignore \ jexec bridge_mac_conflict_one ping -c 3 192.0.2.2 } mac_conflict_cleanup() { vnet_cleanup } atf_test_case "inherit_mac" "cleanup" inherit_mac_head() { atf_set descr 'Bridge inherit_mac test, #216510' atf_set require.user root } inherit_mac_body() { vnet_init vnet_init_bridge bridge=$(vnet_mkbridge) epair=$(vnet_mkepair) vnet_mkjail one ${bridge} ${epair}a jexec one sysctl net.link.bridge.inherit_mac=1 # Attempt to provoke the panic described in #216510 jexec one ifconfig ${bridge} 192.0.0.1/24 up jexec one ifconfig ${bridge} addm ${epair}a } inherit_mac_cleanup() { vnet_cleanup } atf_test_case "stp_validation" "cleanup" stp_validation_head() { atf_set descr 'Check STP validation' atf_set require.user root atf_set require.progs python3 scapy } stp_validation_body() { vnet_init vnet_init_bridge epair_one=$(vnet_mkepair) epair_two=$(vnet_mkepair) bridge=$(vnet_mkbridge) ifconfig ${bridge} up ifconfig ${bridge} addm ${epair_one}a addm ${epair_two}a ifconfig ${bridge} stp ${epair_one}a stp ${epair_two}a ifconfig ${epair_one}a up ifconfig ${epair_one}b up ifconfig ${epair_two}a up ifconfig ${epair_two}b up # Wait until the interfaces are no longer discarding while ifconfig ${bridge} | grep 'state discarding' >/dev/null do sleep 1 done # Now inject invalid STP BPDUs on epair_one and see if they're repeated # on epair_two atf_check -s exit:0 \ $(atf_get_srcdir)/stp.py \ --sendif ${epair_one}b \ --recvif ${epair_two}b } stp_validation_cleanup() { vnet_cleanup } atf_test_case "gif" "cleanup" gif_head() { atf_set descr 'gif as a bridge member' atf_set require.user root } gif_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) vnet_mkjail one ${epair}a vnet_mkjail two ${epair}b jexec one sysctl net.link.gif.max_nesting=2 jexec two sysctl net.link.gif.max_nesting=2 jexec one ifconfig ${epair}a 192.0.2.1/24 up jexec two ifconfig ${epair}b 192.0.2.2/24 up # Tunnel gif_one=$(jexec one ifconfig gif create) gif_two=$(jexec two ifconfig gif create) jexec one ifconfig ${gif_one} tunnel 192.0.2.1 192.0.2.2 jexec one ifconfig ${gif_one} up jexec two ifconfig ${gif_two} tunnel 192.0.2.2 192.0.2.1 jexec two ifconfig ${gif_two} up bridge_one=$(jexec one ifconfig bridge create) bridge_two=$(jexec two ifconfig bridge create) jexec one ifconfig ${bridge_one} 198.51.100.1/24 up jexec one ifconfig ${bridge_one} addm ${gif_one} jexec two ifconfig ${bridge_two} 198.51.100.2/24 up jexec two ifconfig ${bridge_two} addm ${gif_two} # Sanity check atf_check -s exit:0 -o ignore \ jexec one ping -c 1 192.0.2.2 # Test tunnel atf_check -s exit:0 -o ignore \ jexec one ping -c 1 198.51.100.2 atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -s 1200 198.51.100.2 atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -s 2000 198.51.100.2 # Higher MTU on the tunnel than on the underlying interface jexec one ifconfig ${epair}a mtu 1000 jexec two ifconfig ${epair}b mtu 1000 atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -s 1200 198.51.100.2 atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -s 2000 198.51.100.2 } gif_cleanup() { vnet_cleanup } atf_test_case "mtu" "cleanup" mtu_head() { atf_set descr 'Bridge MTU changes' atf_set require.user root } get_mtu() { intf=$1 ifconfig ${intf} | awk '$5 == "mtu" { print $6 }' } check_mtu() { intf=$1 expected=$2 mtu=$(get_mtu $intf) if [ "$mtu" -ne "$expected" ]; then atf_fail "Expected MTU of $expected on $intf but found $mtu" fi } mtu_body() { vnet_init vnet_init_bridge epair=$(vnet_mkepair) gif=$(ifconfig gif create) echo ${gif} >> created_interfaces.lst bridge=$(vnet_mkbridge) atf_check -s exit:0 \ ifconfig ${bridge} addm ${epair}a ifconfig ${gif} mtu 1500 atf_check -s exit:0 \ ifconfig ${bridge} addm ${gif} # Changing MTU changes it for all member interfaces atf_check -s exit:0 \ ifconfig ${bridge} mtu 2000 check_mtu ${bridge} 2000 check_mtu ${gif} 2000 check_mtu ${epair}a 2000 # Rejected MTUs mean none of the MTUs change atf_check -s exit:1 -e ignore \ ifconfig ${bridge} mtu 9000 check_mtu ${bridge} 2000 check_mtu ${gif} 2000 check_mtu ${epair}a 2000 # We're not allowed to change the MTU of a member interface atf_check -s exit:1 -e ignore \ ifconfig ${epair}a mtu 1900 check_mtu ${epair}a 2000 # Test adding an interface with a different MTU new_epair=$(vnet_mkepair) check_mtu ${new_epair}a 1500 atf_check -s exit:0 -e ignore \ ifconfig ${bridge} addm ${new_epair}a check_mtu ${bridge} 2000 check_mtu ${gif} 2000 check_mtu ${epair}a 2000 check_mtu ${new_epair}a 2000 } mtu_cleanup() { vnet_cleanup } atf_test_case "vlan" "cleanup" vlan_head() { atf_set descr 'Ensure the bridge takes vlan ID into account, PR#270559' atf_set require.user root } vlan_body() { vnet_init vnet_init_bridge vid=1 epaira=$(vnet_mkepair) epairb=$(vnet_mkepair) br=$(vnet_mkbridge) vnet_mkjail one ${epaira}b vnet_mkjail two ${epairb}b ifconfig ${br} up ifconfig ${epaira}a up ifconfig ${epairb}a up ifconfig ${br} addm ${epaira}a addm ${epairb}a jexec one ifconfig ${epaira}b up jexec one ifconfig ${epaira}b.${vid} create jexec two ifconfig ${epairb}b up jexec two ifconfig ${epairb}b.${vid} create # Create a MAC address conflict between an untagged and tagged interface jexec two ifconfig ${epairb}b.${vid} ether 02:05:6e:06:28:1a jexec one ifconfig ${epaira}b ether 02:05:6e:06:28:1a jexec one ifconfig ${epaira}b.${vid} ether 02:05:6e:06:28:1b # Add ip address, will also populate $br's fowarding table, by ARP announcement jexec one ifconfig ${epaira}b.${vid} 192.0.2.1/24 up jexec two ifconfig ${epairb}b.${vid} 192.0.2.2/24 up sleep 0.5 ifconfig ${br} jexec one ifconfig jexec two ifconfig ifconfig ${br} addr atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -t 1 192.0.2.2 # This will trigger a mac flap (by ARP announcement) jexec one ifconfig ${epaira}b 192.0.2.1/24 up sleep 0.5 ifconfig ${br} addr atf_check -s exit:0 -o ignore \ jexec one ping -c 1 -t 1 192.0.2.2 } vlan_cleanup() { vnet_cleanup } atf_test_case "many_bridge_members" "cleanup" many_bridge_members_head() { atf_set descr 'many_bridge_members ifconfig test' atf_set require.user root } many_bridge_members_body() { vnet_init vnet_init_bridge bridge=$(vnet_mkbridge) ifcount=256 for _ in $(seq 1 $ifcount); do epair=$(vnet_mkepair) ifconfig "${bridge}" addm "${epair}"a done atf_check -s exit:0 -o inline:"$ifcount\n" \ sh -c "ifconfig ${bridge} | grep member: | wc -l | xargs" } many_bridge_members_cleanup() { vnet_cleanup } atf_test_case "member_ifaddrs_enabled" "cleanup" member_ifaddrs_enabled_head() { atf_set descr 'bridge with member_ifaddrs=1' atf_set require.user root } member_ifaddrs_enabled_body() { vnet_init vnet_init_bridge ep=$(vnet_mkepair) ifconfig ${ep}a inet 192.0.2.1/24 up vnet_mkjail one ${ep}b jexec one sysctl net.link.bridge.member_ifaddrs=1 jexec one ifconfig ${ep}b inet 192.0.2.2/24 up jexec one ifconfig bridge0 create addm ${ep}b atf_check -s exit:0 -o ignore ping -c3 -t1 192.0.2.2 } member_ifaddrs_enabled_cleanup() { vnet_cleanup } atf_test_case "member_ifaddrs_disabled" "cleanup" member_ifaddrs_disabled_head() { atf_set descr 'bridge with member_ifaddrs=0' atf_set require.user root } member_ifaddrs_disabled_body() { vnet_init vnet_init_bridge vnet_mkjail one jexec one sysctl net.link.bridge.member_ifaddrs=0 bridge=$(jexec one ifconfig bridge create) # adding an interface with an IPv4 address ep=$(jexec one ifconfig epair create) jexec one ifconfig ${ep} 192.0.2.1/32 atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} # adding an interface with an IPv6 address ep=$(jexec one ifconfig epair create) jexec one ifconfig ${ep} inet6 2001:db8::1/128 atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} # adding an interface with an IPv6 link-local address ep=$(jexec one ifconfig epair create) jexec one ifconfig ${ep} inet6 -ifdisabled auto_linklocal up atf_check -s exit:1 -e ignore jexec one ifconfig ${bridge} addm ${ep} # adding an IPv4 address to a member ep=$(jexec one ifconfig epair create) jexec one ifconfig ${bridge} addm ${ep} atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet 192.0.2.2/32 # adding an IPv6 address to a member ep=$(jexec one ifconfig epair create) jexec one ifconfig ${bridge} addm ${ep} atf_check -s exit:1 -e ignore jexec one ifconfig ${ep} inet6 2001:db8::1/128 } member_ifaddrs_disabled_cleanup() { vnet_cleanup } # # Test kern/287150: when member_ifaddrs=0, and a physical interface which is in # a bridge also has a vlan(4) on it, tagged packets are not correctly passed to # vlan(4). atf_test_case "member_ifaddrs_vlan" "cleanup" member_ifaddrs_vlan_head() { atf_set descr 'kern/287150: vlan and bridge on the same interface' atf_set require.user root } member_ifaddrs_vlan_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) # The first jail has an epair with an IP address on vlan 20. vnet_mkjail one ${epone}a atf_check -s exit:0 jexec one ifconfig ${epone}a up atf_check -s exit:0 jexec one \ ifconfig ${epone}a.20 create inet 192.0.2.1/24 up # The second jail has an epair with an IP address on vlan 20, # which is also in a bridge. vnet_mkjail two ${epone}b jexec two ifconfig atf_check -s exit:0 -o save:bridge jexec two ifconfig bridge create bridge=$(cat bridge) atf_check -s exit:0 jexec two ifconfig ${bridge} addm ${epone}b up atf_check -s exit:0 -o ignore jexec two \ sysctl net.link.bridge.member_ifaddrs=0 atf_check -s exit:0 jexec two ifconfig ${epone}b up atf_check -s exit:0 jexec two \ ifconfig ${epone}b.20 create inet 192.0.2.2/24 up # Make sure the two jails can communicate over the vlan. atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } member_ifaddrs_vlan_cleanup() { vnet_cleanup } atf_test_case "vlan_pvid" "cleanup" vlan_pvid_head() { atf_set descr 'bridge with two ports with pvid set' atf_set require.user root } vlan_pvid_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b jexec one ifconfig ${epone}b 192.0.2.1/24 up jexec two ifconfig ${eptwo}b 192.0.2.2/24 up bridge=$(vnet_mkbridge) ifconfig ${bridge} vlanfilter up ifconfig ${epone}a up ifconfig ${eptwo}a up ifconfig ${bridge} addm ${epone}a untagged ${epone}a 20 ifconfig ${bridge} addm ${eptwo}a untagged ${eptwo}a 20 # With VLAN filtering enabled, traffic should be passed. atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 # Removed the untagged VLAN on one port; traffic should not be passed. ifconfig ${bridge} -untagged ${epone}a atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_pvid_cleanup() { vnet_cleanup } atf_test_case "vlan_pvid_filtered" "cleanup" vlan_pvid_filtered_head() { atf_set descr 'bridge with two ports with different pvids' atf_set require.user root } vlan_pvid_filtered_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b atf_check -s exit:0 jexec one ifconfig ${epone}b 192.0.2.1/24 up atf_check -s exit:0 jexec two ifconfig ${eptwo}b 192.0.2.2/24 up bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${epone}a up atf_check -s exit:0 ifconfig ${eptwo}a up atf_check -s exit:0 ifconfig ${bridge} \ addm ${epone}a untagged ${epone}a 20 atf_check -s exit:0 ifconfig ${bridge} \ addm ${eptwo}a untagged ${eptwo}a 30 atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_pvid_filtered_cleanup() { vnet_cleanup } atf_test_case "vlan_pvid_tagged" "cleanup" vlan_pvid_tagged_head() { atf_set descr 'bridge pvid with tagged frames for pvid' atf_set require.user root } vlan_pvid_tagged_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b # Create two tagged interfaces on the appropriate VLANs atf_check -s exit:0 jexec one ifconfig ${epone}b up atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ create 192.0.2.1/24 up atf_check -s exit:0 jexec two ifconfig ${eptwo}b up atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 \ create 192.0.2.2/24 up bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${epone}a up atf_check -s exit:0 ifconfig ${eptwo}a up atf_check -s exit:0 ifconfig ${bridge} \ addm ${epone}a untagged ${epone}a 20 atf_check -s exit:0 ifconfig ${bridge} \ addm ${eptwo}a untagged ${eptwo}a 20 # Tagged frames should not be passed. atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_pvid_tagged_cleanup() { vnet_cleanup } atf_test_case "vlan_pvid_1q" "cleanup" vlan_pvid_1q_head() { atf_set descr '802.1q tag addition and removal' atf_set require.user root } vlan_pvid_1q_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b # Set up one jail with an access port, and the other with a trunk port. # This forces the bridge to add and remove .1q tags to bridge the # traffic. atf_check -s exit:0 jexec one ifconfig ${epone}b 192.0.2.1/24 up atf_check -s exit:0 jexec two ifconfig ${eptwo}b up atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 create 192.0.2.2/24 up bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${bridge} \ addm ${epone}a untagged ${epone}a 20 atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a \ tagged ${eptwo}a 20 atf_check -s exit:0 ifconfig ${epone}a up atf_check -s exit:0 ifconfig ${eptwo}a up atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_pvid_1q_cleanup() { vnet_cleanup } # # Test vlan filtering. # atf_test_case "vlan_filtering" "cleanup" vlan_filtering_head() { atf_set descr 'tagged traffic with filtering' atf_set require.user root } vlan_filtering_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b atf_check -s exit:0 jexec one ifconfig ${epone}b up atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ create 192.0.2.1/24 up atf_check -s exit:0 jexec two ifconfig ${eptwo}b up atf_check -s exit:0 jexec two ifconfig ${eptwo}b.20 \ create 192.0.2.2/24 up bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${epone}a up atf_check -s exit:0 ifconfig ${eptwo}a up atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a # Right now there are no VLANs on the access list, so everything # should be blocked. atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 # Set the untagged vlan on both ports to 20 and make sure traffic is # still blocked. We intentionally do not pass tagged traffic for the # untagged vlan. atf_check -s exit:0 ifconfig ${bridge} untagged ${epone}a 20 atf_check -s exit:0 ifconfig ${bridge} untagged ${eptwo}a 20 atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 atf_check -s exit:0 ifconfig ${bridge} -untagged ${epone}a atf_check -s exit:0 ifconfig ${bridge} -untagged ${eptwo}a # Add VLANs 10-30 to the access list; now access should be allowed. atf_check -s exit:0 ifconfig ${bridge} +tagged ${epone}a 10-30 atf_check -s exit:0 ifconfig ${bridge} +tagged ${eptwo}a 10-30 atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 # Remove vlan 20 from the access list, now access should be blocked # again. atf_check -s exit:0 ifconfig ${bridge} -tagged ${epone}a 20 atf_check -s exit:0 ifconfig ${bridge} -tagged ${eptwo}a 20 atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_filtering_cleanup() { vnet_cleanup } # # Test the ifconfig 'tagged' option. # atf_test_case "vlan_ifconfig_tagged" "cleanup" vlan_ifconfig_tagged_head() { atf_set descr 'test the ifconfig tagged option' atf_set require.user root } vlan_ifconfig_tagged_body() { vnet_init vnet_init_bridge ep=$(vnet_mkepair) bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${bridge} addm ${ep}a atf_check -s exit:0 ifconfig ${ep}a up # To start with, no vlans should be configured. atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge} # Add vlans 100-149. atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a 100-149 atf_check -s exit:0 -o match:"tagged 100-149" ifconfig ${bridge} # Replace the vlan list with 139-199. atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a 139-199 atf_check -s exit:0 -o match:"tagged 139-199" ifconfig ${bridge} # Add vlans 100-170. atf_check -s exit:0 ifconfig ${bridge} +tagged ${ep}a 100-170 atf_check -s exit:0 -o match:"tagged 100-199" ifconfig ${bridge} # Remove vlans 104, 105, and 150-159 atf_check -s exit:0 ifconfig ${bridge} -tagged ${ep}a 104,105,150-159 atf_check -s exit:0 -o match:"tagged 100-103,106-149,160-199" \ ifconfig ${bridge} # Remove the entire vlan list. atf_check -s exit:0 ifconfig ${bridge} tagged ${ep}a none atf_check -s exit:0 -o not-match:"tagged" ifconfig ${bridge} # Test some invalid vlans sets. for bad_vlan in -1 0 4096 4097 foo 0-10 4000-5000 foo-40 40-foo; do atf_check -s exit:1 -e ignore \ ifconfig ${bridge} tagged "$bad_vlan" done } vlan_ifconfig_tagged_cleanup() { vnet_cleanup } # # Test a vlan(4) "SVI" interface on top of a bridge. # atf_test_case "vlan_svi" "cleanup" vlan_svi_head() { atf_set descr 'vlan bridge with an SVI' atf_set require.user root } vlan_svi_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) vnet_mkjail one ${epone}b atf_check -s exit:0 jexec one ifconfig ${epone}b up atf_check -s exit:0 jexec one ifconfig ${epone}b.20 \ create 192.0.2.1/24 up bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter up atf_check -s exit:0 ifconfig ${epone}a up atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a \ tagged ${epone}a 20 svi=$(vnet_mkvlan) atf_check -s exit:0 ifconfig ${svi} vlan 20 vlandev ${bridge} atf_check -s exit:0 ifconfig ${svi} inet 192.0.2.2/24 up atf_check -s exit:0 -o ignore ping -c 3 -t 1 192.0.2.1 } vlan_svi_cleanup() { vnet_cleanup } # # Test QinQ (802.1ad). # atf_test_case "vlan_qinq" "cleanup" vlan_qinq_head() { atf_set descr 'vlan filtering with QinQ traffic' atf_set require.user root } vlan_qinq_body() { vnet_init vnet_init_bridge epone=$(vnet_mkepair) eptwo=$(vnet_mkepair) vnet_mkjail one ${epone}b vnet_mkjail two ${eptwo}b # Create a QinQ trunk between the two jails. The outer (provider) tag # is 5, and the inner tag is 10. - jexec one ifconfig ${epone}b up - jexec one ifconfig ${epone}b.5 create vlanproto 802.1ad up - jexec one ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up + atf_check -s exit:0 jexec one ifconfig ${epone}b up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec one \ + ifconfig ${epone}b.5.10 create inet 192.0.2.1/24 up - jexec two ifconfig ${eptwo}b up - jexec two ifconfig ${eptwo}b.5 create vlanproto 802.1ad up - jexec two ifconfig ${eptwo}b.5.10 create inet 192.0.2.2/24 up + atf_check -s exit:0 jexec two ifconfig ${eptwo}b up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5 create vlanproto 802.1ad up + atf_check -s exit:0 jexec two ifconfig \ + ${eptwo}b.5.10 create inet 192.0.2.2/24 up bridge=$(vnet_mkbridge) - ifconfig ${bridge} up - ifconfig ${epone}a up - ifconfig ${eptwo}a up - ifconfig ${bridge} addm ${epone}a vlanfilter ${epone}a - ifconfig ${bridge} addm ${eptwo}a vlanfilter ${eptwo}a + atf_check -s exit:0 ifconfig ${bridge} vlanfilter defqinq up + atf_check -s exit:0 ifconfig ${epone}a up + atf_check -s exit:0 ifconfig ${eptwo}a up + atf_check -s exit:0 ifconfig ${bridge} addm ${epone}a + atf_check -s exit:0 ifconfig ${bridge} addm ${eptwo}a # Right now there are no VLANs on the access list, so everything # should be blocked. atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 # Add the provider tag to the access list; now traffic should be passed. - ifconfig ${bridge} +tagged ${epone}a 5 - ifconfig ${bridge} +tagged ${eptwo}a 5 + atf_check -s exit:0 ifconfig ${bridge} +tagged ${epone}a 5 + atf_check -s exit:0 ifconfig ${bridge} +tagged ${eptwo}a 5 atf_check -s exit:0 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 atf_check -s exit:0 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 + + # Remove the qinq flag from one of the interfaces; traffic should + # be blocked again. + atf_check -s exit:0 ifconfig ${bridge} -qinq ${epone}a + atf_check -s exit:2 -o ignore jexec one ping -c 3 -t 1 192.0.2.2 + atf_check -s exit:2 -o ignore jexec two ping -c 3 -t 1 192.0.2.1 } vlan_qinq_cleanup() { vnet_cleanup } # Adding a bridge SVI to a bridge should not be allowed. atf_test_case "bridge_svi_in_bridge" "cleanup" bridge_svi_in_bridge_head() { atf_set descr 'adding a bridge SVI to a bridge is not allowed (1)' atf_set require.user root } bridge_svi_in_bridge_body() { vnet_init vnet_init_bridge bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge}.1 create atf_check -s exit:1 -e ignore ifconfig ${bridge} addm ${bridge}.1 } bridge_svi_in_bridge_cleanup() { vnet_cleanup } atf_test_case "vlan_defuntagged" "cleanup" vlan_defuntagged_head() { atf_set descr 'defuntagged (defpvid) bridge option' atf_set require.user root } vlan_defuntagged_body() { vnet_init vnet_init_bridge bridge=$(vnet_mkbridge) atf_check -s exit:0 ifconfig ${bridge} vlanfilter # Invalid VLAN IDs atf_check -s exit:1 -ematch:"invalid vlan id: 0" \ ifconfig ${bridge} defuntagged 0 atf_check -s exit:1 -ematch:"invalid vlan id: 4095" \ ifconfig ${bridge} defuntagged 4095 atf_check -s exit:1 -ematch:"invalid vlan id: 5000" \ ifconfig ${bridge} defuntagged 5000 # Check the bridge option is set and cleared correctly atf_check -s exit:0 -onot-match:"defuntagged=" \ ifconfig ${bridge} atf_check -s exit:0 ifconfig ${bridge} defuntagged 10 atf_check -s exit:0 -omatch:"defuntagged=10$" \ ifconfig ${bridge} atf_check -s exit:0 ifconfig ${bridge} -defuntagged atf_check -s exit:0 -onot-match:"defuntagged=" \ ifconfig ${bridge} # Check the untagged option is correctly set on a member atf_check -s exit:0 ifconfig ${bridge} defuntagged 10 epair=$(vnet_mkepair) atf_check -s exit:0 ifconfig ${bridge} addm ${epair}a tag=$(ifconfig ${bridge} | sed -Ene \ "/member: ${epair}a/ { N;s/.*untagged ([0-9]+).*/\\1/p;q; }") if [ "$tag" != "10" ]; then atf_fail "wrong untagged vlan: ${tag}" fi } vlan_defuntagged_cleanup() { vnet_cleanup } atf_init_test_cases() { atf_add_test_case "bridge_transmit_ipv4_unicast" atf_add_test_case "stp" atf_add_test_case "stp_vlan" atf_add_test_case "static" atf_add_test_case "vstatic" atf_add_test_case "span" atf_add_test_case "inherit_mac" atf_add_test_case "delete_with_members" atf_add_test_case "mac_conflict" atf_add_test_case "stp_validation" atf_add_test_case "gif" atf_add_test_case "mtu" atf_add_test_case "vlan" atf_add_test_case "many_bridge_members" atf_add_test_case "member_ifaddrs_enabled" atf_add_test_case "member_ifaddrs_disabled" atf_add_test_case "member_ifaddrs_vlan" atf_add_test_case "vlan_pvid" atf_add_test_case "vlan_pvid_1q" atf_add_test_case "vlan_pvid_filtered" atf_add_test_case "vlan_pvid_tagged" atf_add_test_case "vlan_filtering" atf_add_test_case "vlan_ifconfig_tagged" atf_add_test_case "vlan_svi" atf_add_test_case "vlan_qinq" atf_add_test_case "vlan_defuntagged" atf_add_test_case "bridge_svi_in_bridge" }