From d6404d18806056a3c60a2de3a22f4dd33af6730e Mon Sep 17 00:00:00 2001 From: henning Date: Mon, 5 Feb 2018 03:51:53 +0000 Subject: [PATCH] implement an arp filter allows arp (and rarp) requests and replies to be matched, including matching based on the source and target host and protocol adresses, and thus control over arp traffic and learning. written for medical x-ray machines, but useful in many spread out L2 networks ok claudio benno --- sbin/ifconfig/brconfig.c | 122 ++++++++++++++++++++++++++++++++++----- sbin/ifconfig/ifconfig.8 | 28 +++++++-- sys/net/bridgectl.c | 52 ++++++++++++++++- sys/net/if_bridge.c | 9 ++- sys/net/if_bridge.h | 19 +++++- 5 files changed, 210 insertions(+), 20 deletions(-) diff --git a/sbin/ifconfig/brconfig.c b/sbin/ifconfig/brconfig.c index 1f240b133ce..1e3415b0ebb 100644 --- a/sbin/ifconfig/brconfig.c +++ b/sbin/ifconfig/brconfig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: brconfig.c,v 1.16 2017/07/31 02:32:11 jsg Exp $ */ +/* $OpenBSD: brconfig.c,v 1.17 2018/02/05 03:51:53 henning Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -46,6 +46,7 @@ #include #include #include +#include #include "brconfig.h" @@ -57,6 +58,7 @@ void bridge_cfg(const char *); void bridge_badrule(int, char **, int); void bridge_showrule(struct ifbrlreq *); int is_switch(char *); +int bridge_arprule(struct ifbrlreq *, int *, char ***); #define IFBAFBITS "\020\1STATIC" #define IFBIFBITS \ @@ -821,6 +823,25 @@ bridge_showrule(struct ifbrlreq *r) if (r->ifbr_tagname[0]) printf(" tag %s", r->ifbr_tagname); + if (r->ifbr_arpf.brla_flags & BRLA_ARP) + printf(" arp"); + if (r->ifbr_arpf.brla_flags & BRLA_RARP) + printf(" rarp"); + if (r->ifbr_arpf.brla_op == ARPOP_REQUEST || + r->ifbr_arpf.brla_op == ARPOP_REVREQUEST) + printf(" request"); + if (r->ifbr_arpf.brla_op == ARPOP_REPLY || + r->ifbr_arpf.brla_op == ARPOP_REVREPLY) + printf(" reply"); + if (r->ifbr_arpf.brla_flags & BRLA_SHA) + printf(" sha %s", ether_ntoa(&r->ifbr_arpf.brla_sha)); + if (r->ifbr_arpf.brla_flags & BRLA_THA) + printf(" tha %s", ether_ntoa(&r->ifbr_arpf.brla_tha)); + if (r->ifbr_arpf.brla_flags & BRLA_SPA) + printf(" spa %s", inet_ntoa(r->ifbr_arpf.brla_spa)); + if (r->ifbr_arpf.brla_flags & BRLA_TPA) + printf(" tpa %s", inet_ntoa(r->ifbr_arpf.brla_tpa)); + printf("\n"); } @@ -837,14 +858,13 @@ bridge_rule(int targc, char **targv, int ln) int argc = targc; struct ifbrlreq rule; struct ether_addr *ea, *dea; + struct in_addr *ia; if (argc == 0) { warnx("invalid rule"); return (1); } - rule.ifbr_tagname[0] = 0; - rule.ifbr_flags = 0; - rule.ifbr_action = 0; + bzero(&rule, sizeof(rule)); strlcpy(rule.ifbr_name, name, sizeof(rule.ifbr_name)); if (strcmp(argv[0], "block") == 0) @@ -882,16 +902,19 @@ bridge_rule(int targc, char **targv, int ln) argc--; argv++; while (argc) { + dea = NULL; if (strcmp(argv[0], "dst") == 0) { if (rule.ifbr_flags & BRL_FLAG_DSTVALID) goto bad_rule; rule.ifbr_flags |= BRL_FLAG_DSTVALID; dea = &rule.ifbr_dst; + argc--; argv++; } else if (strcmp(argv[0], "src") == 0) { if (rule.ifbr_flags & BRL_FLAG_SRCVALID) goto bad_rule; rule.ifbr_flags |= BRL_FLAG_SRCVALID; dea = &rule.ifbr_src; + argc--; argv++; } else if (strcmp(argv[0], "tag") == 0) { if (argc < 2) { warnx("missing tag name"); @@ -901,28 +924,37 @@ bridge_rule(int targc, char **targv, int ln) warnx("tag already defined"); goto bad_rule; } - if (strlcpy(rule.ifbr_tagname, argv[1], + argc--; argv++; + if (strlcpy(rule.ifbr_tagname, argv[0], PF_TAG_NAME_SIZE) > PF_TAG_NAME_SIZE) { - warnx("tag name '%s' too long", argv[1]); + warnx("tag name '%s' too long", argv[0]); goto bad_rule; } - dea = NULL; + argc--; argv++; + } else if (strcmp(argv[0], "arp") == 0) { + rule.ifbr_arpf.brla_flags |= BRLA_ARP; + argc--; argv++; + if (bridge_arprule(&rule, &argc, &argv) == -1) + goto bad_rule; + } else if (strcmp(argv[0], "rarp") == 0) { + rule.ifbr_arpf.brla_flags |= BRLA_RARP; + argc--; argv++; + if (bridge_arprule(&rule, &argc, &argv) == -1) + goto bad_rule; } else goto bad_rule; - argc--; argv++; - - if (argc == 0) - goto bad_rule; if (dea != NULL) { + if (argc == 0) + goto bad_rule; ea = ether_aton(argv[0]); if (ea == NULL) { warnx("invalid address: %s", argv[0]); return (1); } bcopy(ea, dea, sizeof(*dea)); + argc--; argv++; } - argc--; argv++; } if (ioctl(s, SIOCBRDGARL, &rule) < 0) { @@ -936,7 +968,71 @@ bad_rule: return (1); } -#define MAXRULEWORDS 8 +int +bridge_arprule(struct ifbrlreq *rule, int *argc, char ***argv) +{ + while (*argc) { + struct ether_addr *ea, *dea = NULL; + struct in_addr ia, *dia = NULL; + + if (strcmp((*argv)[0], "request") == 0) { + if (rule->ifbr_arpf.brla_flags & BRLA_ARP) + rule->ifbr_arpf.brla_op = ARPOP_REQUEST; + else if (rule->ifbr_arpf.brla_flags & BRLA_RARP) + rule->ifbr_arpf.brla_op = ARPOP_REVREQUEST; + else + errx(1, "bridge_arprule: arp/rarp undefined"); + } else if (strcmp((*argv)[0], "reply") == 0) { + if (rule->ifbr_arpf.brla_flags & BRLA_ARP) + rule->ifbr_arpf.brla_op = ARPOP_REPLY; + else if (rule->ifbr_arpf.brla_flags & BRLA_RARP) + rule->ifbr_arpf.brla_op = ARPOP_REVREPLY; + else + errx(1, "bridge_arprule: arp/rarp undefined"); + } else if (strcmp((*argv)[0], "sha") == 0) { + rule->ifbr_arpf.brla_flags |= BRLA_SHA; + dea = &rule->ifbr_arpf.brla_sha; + } else if (strcmp((*argv)[0], "tha") == 0) { + rule->ifbr_arpf.brla_flags |= BRLA_THA; + dea = &rule->ifbr_arpf.brla_tha; + } else if (strcmp((*argv)[0], "spa") == 0) { + rule->ifbr_arpf.brla_flags |= BRLA_SPA; + dia = &rule->ifbr_arpf.brla_spa; + } else if (strcmp((*argv)[0], "tpa") == 0) { + rule->ifbr_arpf.brla_flags |= BRLA_TPA; + dia = &rule->ifbr_arpf.brla_tpa; + } else + return (0); + + (*argc)--; (*argv)++; + if (dea != NULL) { + if (*argc == 0) + return (-1); + ea = ether_aton((*argv)[0]); + if (ea == NULL) { + warnx("invalid address: %s", (*argv)[0]); + return (-1); + } + bcopy(ea, dea, sizeof(*dea)); + (*argc)--; (*argv)++; + } + if (dia != NULL) { + if (*argc == 0) + return (-1); + ia.s_addr = inet_addr((*argv)[0]); + if (ia.s_addr == INADDR_NONE) { + warnx("invalid address: %s", (*argv)[0]); + return (-1); + } + bcopy(&ia, dia, sizeof(*dia)); + (*argc)--; (*argv)++; + } + } + return (0); +} + + +#define MAXRULEWORDS 32 void bridge_rulefile(const char *fname, int d) diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8 index ef9592cf3e3..ba2290fb92c 100644 --- a/sbin/ifconfig/ifconfig.8 +++ b/sbin/ifconfig/ifconfig.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: ifconfig.8,v 1.292 2018/01/16 10:33:55 mpi Exp $ +.\" $OpenBSD: ifconfig.8,v 1.293 2018/02/05 03:51:53 henning Exp $ .\" $NetBSD: ifconfig.8,v 1.11 1996/01/04 21:27:29 pk Exp $ .\" $FreeBSD: ifconfig.8,v 1.16 1998/02/01 07:03:29 steve Exp $ .\" @@ -31,7 +31,7 @@ .\" .\" @(#)ifconfig.8 8.4 (Berkeley) 6/1/94 .\" -.Dd $Mdocdate: January 16 2018 $ +.Dd $Mdocdate: February 5 2018 $ .Dt IFCONFIG 8 .Os .Sh NAME @@ -689,9 +689,10 @@ like a hub or a wireless network. .Cm block Ns | Ns Cm pass .Op Cm in | out .Cm on Ar interface -.Op Cm src Ar address -.Op Cm dst Ar address +.Op Cm src Ar lladdr +.Op Cm dst Ar lladdr .Op Cm tag Ar tagname +.Op Cm arp | rarp Ar [ request | reply ] [ Cm sha Ar lladdr ] [ Cm spa Ar ipaddr ] [ Cm tha Ar lladdr ] [ Cm tpa Ar ipaddr ] .Xc Add a filtering rule to an interface. Rules have a similar syntax to those in @@ -701,6 +702,25 @@ MAC addresses. They can also tag packets for .Xr pf 4 to filter on. +.Xr arp 4 +packets can be matched with the +.Cm arp +keyword for regular and +.Cm rarp +for reverse arp packets. +.Ar request +and +.Ar reply +limit matches to requests or replies. +The source and target host addresses can be matched with the +.Cm sha +and +.Cm tha +keywords, +the protocol addresses with +.Cm spa +and +.Cm tpa . Rules are processed in the order in which they were added to the interface, and the first rule matched takes the action (block or pass) and, if given, the tag of the rule. diff --git a/sys/net/bridgectl.c b/sys/net/bridgectl.c index 86949163bf8..5e431fe5f03 100644 --- a/sys/net/bridgectl.c +++ b/sys/net/bridgectl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bridgectl.c,v 1.6 2017/05/04 15:00:24 bluhm Exp $ */ +/* $OpenBSD: bridgectl.c,v 1.7 2018/02/05 03:51:53 henning Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -573,6 +573,7 @@ bridge_brlconf(struct bridge_softc *sc, struct ifbrlconf *bc) req.ifbr_flags = n->brl_flags; req.ifbr_src = n->brl_src; req.ifbr_dst = n->brl_dst; + req.ifbr_arpf = n->brl_arpf; #if NPF > 0 req.ifbr_tagname[0] = '\0'; if (n->brl_tag) @@ -596,6 +597,7 @@ bridge_brlconf(struct bridge_softc *sc, struct ifbrlconf *bc) req.ifbr_flags = n->brl_flags; req.ifbr_src = n->brl_src; req.ifbr_dst = n->brl_dst; + req.ifbr_arpf = n->brl_arpf; #if NPF > 0 req.ifbr_tagname[0] = '\0'; if (n->brl_tag) @@ -614,6 +616,51 @@ done: return (error); } +u_int8_t +bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m) +{ + struct ether_arp ea; + + if (!(n->brl_arpf.brla_flags & (BRLA_ARP|BRLA_RARP))) + return (1); + + if (ntohs(eh->ether_type) != ETHERTYPE_ARP) + return (0); + if (m->m_pkthdr.len <= ETHER_HDR_LEN + sizeof(ea)) + return (0); /* log error? */ + m_copydata(m, ETHER_HDR_LEN, sizeof(ea), (caddr_t)&ea); + + if (ntohs(ea.arp_hrd) != ARPHRD_ETHER || + ntohs(ea.arp_pro) != ETHERTYPE_IP || + ea.arp_hln != ETHER_ADDR_LEN || + ea.arp_pln != sizeof(struct in_addr)) + return (0); + if ((n->brl_arpf.brla_flags & BRLA_ARP) && + ntohs(ea.arp_op) != ARPOP_REQUEST && + ntohs(ea.arp_op) != ARPOP_REPLY) + return (0); + if ((n->brl_arpf.brla_flags & BRLA_RARP) && + ntohs(ea.arp_op) != ARPOP_REVREQUEST && + ntohs(ea.arp_op) != ARPOP_REVREPLY) + return (0); + if (n->brl_arpf.brla_op && ntohs(ea.arp_op) != n->brl_arpf.brla_op) + return (0); + if (n->brl_arpf.brla_flags & BRLA_SHA && + bcmp(ea.arp_sha, &n->brl_arpf.brla_sha, ETHER_ADDR_LEN)) + return (0); + if (n->brl_arpf.brla_flags & BRLA_THA && + bcmp(ea.arp_tha, &n->brl_arpf.brla_tha, ETHER_ADDR_LEN)) + return (0); + if (n->brl_arpf.brla_flags & BRLA_SPA && + bcmp(ea.arp_spa, &n->brl_arpf.brla_spa, sizeof(struct in_addr))) + return (0); + if (n->brl_arpf.brla_flags & BRLA_TPA && + bcmp(ea.arp_tpa, &n->brl_arpf.brla_tpa, sizeof(struct in_addr))) + return (0); + + return (1); +} + u_int8_t bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m) { @@ -621,6 +668,8 @@ bridge_filterrule(struct brl_head *h, struct ether_header *eh, struct mbuf *m) u_int8_t flags; SIMPLEQ_FOREACH(n, h, brl_next) { + if (!bridge_arpfilter(n, eh, m)) + continue; flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID); if (flags == 0) goto return_action; @@ -663,6 +712,7 @@ bridge_addrule(struct bridge_iflist *bif, struct ifbrlreq *req, int out) bcopy(&req->ifbr_dst, &n->brl_dst, sizeof(struct ether_addr)); n->brl_action = req->ifbr_action; n->brl_flags = req->ifbr_flags; + n->brl_arpf = req->ifbr_arpf; #if NPF > 0 if (req->ifbr_tagname[0]) n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1); diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index fa37dfd12e9..a54f5babd5d 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.c,v 1.301 2018/01/10 23:50:39 dlg Exp $ */ +/* $OpenBSD: if_bridge.c,v 1.302 2018/02/05 03:51:53 henning Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -699,6 +699,7 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, struct ether_addr *dst; struct bridge_softc *sc; struct bridge_tunneltag *brtag; + struct bridge_iflist *ifl; int error; /* ifp must be a member interface of the bridge. */ @@ -785,6 +786,12 @@ bridge_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, } } + ifl = (struct bridge_iflist *)dst_if->if_bridgeport; + KASSERT(ifl != NULL); + if (bridge_filterrule(&ifl->bif_brlout, eh, mc) == + BRL_ACTION_BLOCK) + continue; + error = bridge_ifenqueue(sc, dst_if, mc); if (error) continue; diff --git a/sys/net/if_bridge.h b/sys/net/if_bridge.h index 55a3fa70a9a..75596a5b040 100644 --- a/sys/net/if_bridge.h +++ b/sys/net/if_bridge.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_bridge.h,v 1.55 2017/01/20 05:03:48 claudio Exp $ */ +/* $OpenBSD: if_bridge.h,v 1.56 2018/02/05 03:51:53 henning Exp $ */ /* * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net) @@ -196,6 +196,21 @@ struct ifbropreq { /* * Bridge mac rules */ +struct ifbrarpf { + u_int16_t brla_flags; + u_int16_t brla_op; + struct ether_addr brla_sha; + struct in_addr brla_spa; + struct ether_addr brla_tha; + struct in_addr brla_tpa; +}; +#define BRLA_ARP 0x01 +#define BRLA_RARP 0x02 +#define BRLA_SHA 0x10 +#define BRLA_SPA 0x20 +#define BRLA_THA 0x40 +#define BRLA_TPA 0x80 + struct ifbrlreq { char ifbr_name[IFNAMSIZ]; /* bridge ifs name */ char ifbr_ifsname[IFNAMSIZ]; /* member ifs name */ @@ -204,6 +219,7 @@ struct ifbrlreq { struct ether_addr ifbr_src; /* source mac */ struct ether_addr ifbr_dst; /* destination mac */ char ifbr_tagname[PF_TAG_NAME_SIZE]; /* pf tagname */ + struct ifbrarpf ifbr_arpf; /* arp filter */ }; #define BRL_ACTION_BLOCK 0x01 /* block frame */ #define BRL_ACTION_PASS 0x02 /* pass frame */ @@ -269,6 +285,7 @@ struct brl_node { u_int16_t brl_tag; /* pf tag ID */ u_int8_t brl_action; /* what to do with match */ u_int8_t brl_flags; /* comparision flags */ + struct ifbrarpf brl_arpf; /* arp filter */ }; struct bstp_timer { -- 2.20.1