Index | Thread | Search

From:
"Peter J. Philipp" <pjp@delphinusdns.org>
Subject:
Re: question regarding pppoe(4) inspection in a bridge(4) or veb(4)
To:
dlg@openbsd.org
Cc:
tech@openbsd.org
Date:
Thu, 1 Feb 2024 15:44:12 +0100

Download raw body.

Thread
This should filter PPPoE session in a bridge(4).  The reason I need this is
because I'm intending to utilize an older AVM router with an external modem
for VDSL 250.  However I want this router to be protected as it is old and
no updated firmwares for it exist anymore.  It is a Fritzbox 7390 or something.

Let me know what you think of this patch:

Index: bridgectl.c
===================================================================
RCS file: /cvs/src/sys/net/bridgectl.c,v
retrieving revision 1.25
diff -u -p -u -r1.25 bridgectl.c
--- bridgectl.c	25 Feb 2021 02:48:21 -0000	1.25
+++ bridgectl.c	1 Feb 2024 14:06:12 -0000
@@ -44,6 +44,7 @@
 #include <crypto/siphash.h>
 
 #include <net/if.h>
+#include <net/if_pppoe.h>
 
 #include <netinet/in.h>
 #include <netinet/if_ether.h>
@@ -623,6 +624,7 @@ bridge_brlconf(struct bridge_iflist *bif
 		req->ifbr_src = n->brl_src;
 		req->ifbr_dst = n->brl_dst;
 		req->ifbr_arpf = n->brl_arpf;
+		req->ifbr_pppoef = n->brl_pppoef;
 #if NPF > 0
 		req->ifbr_tagname[0] = '\0';
 		if (n->brl_tag)
@@ -642,6 +644,7 @@ bridge_brlconf(struct bridge_iflist *bif
 		req->ifbr_src = n->brl_src;
 		req->ifbr_dst = n->brl_dst;
 		req->ifbr_arpf = n->brl_arpf;
+		req->ifbr_pppoef = n->brl_pppoef;
 #if NPF > 0
 		req->ifbr_tagname[0] = '\0';
 		if (n->brl_tag)
@@ -658,6 +661,30 @@ done:
 }
 
 u_int8_t
+bridge_pppoesfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
+{
+	struct pppoehdr	hdr;
+
+	if (!(n->brl_pppoef.brla_flags & (BRLA_PPPOE)))
+		return (1);
+
+	if (ntohs(eh->ether_type) != ETHERTYPE_PPPOE)
+		return (0);
+
+	if (m->m_pkthdr.len < ETHER_HDR_LEN + sizeof(hdr))
+		return (0);	/* log error? */
+
+	m_copydata(m, ETHER_HDR_LEN, sizeof(hdr), &hdr);
+
+	if ((ntohs(hdr.session) != n->brl_pppoef.brla_session) &&
+		(n->brl_pppoef.brla_flags & BRLA_PPPOESESS)) {
+		return (0);
+	}
+
+	return (1);
+}
+
+u_int8_t
 bridge_arpfilter(struct brl_node *n, struct ether_header *eh, struct mbuf *m)
 {
 	struct ether_arp	 ea;
@@ -715,6 +742,11 @@ bridge_filterrule(struct brl_head *h, st
 	SIMPLEQ_FOREACH(n, h, brl_next) {
 		if (!bridge_arpfilter(n, eh, m))
 			continue;
+
+		if (!bridge_pppoesfilter(n, eh, m)) {
+			continue;
+		}
+
 		flags = n->brl_flags & (BRL_FLAG_SRCVALID|BRL_FLAG_DSTVALID);
 		if (flags == 0)
 			goto return_action;
@@ -765,6 +797,7 @@ bridge_addrule(struct bridge_iflist *bif
 	n->brl_action = req->ifbr_action;
 	n->brl_flags = req->ifbr_flags;
 	n->brl_arpf = req->ifbr_arpf;
+	n->brl_pppoef = req->ifbr_pppoef;
 #if NPF > 0
 	if (req->ifbr_tagname[0])
 		n->brl_tag = pf_tagname2tag(req->ifbr_tagname, 1);
Index: if_bridge.c
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.c,v
retrieving revision 1.368
diff -u -p -u -r1.368 if_bridge.c
--- if_bridge.c	16 May 2023 14:32:54 -0000	1.368
+++ if_bridge.c	1 Feb 2024 14:06:12 -0000
@@ -48,6 +48,8 @@
 #include <net/if_types.h>
 #include <net/if_llc.h>
 #include <net/netisr.h>
+#include <net/if_pppoe.h>
+#include <net/ppp_defs.h>
 
 #include <netinet/in.h>
 #include <netinet/ip.h>
@@ -1670,9 +1672,12 @@ bridge_ip(struct ifnet *brifp, int dir, 
 {
 	struct llc llc;
 	int hassnap = 0;
+	int haspppoe = 0;
 	struct ip *ip;
+	struct pppoehdr phdr;
 	int hlen;
 	u_int16_t etype;
+	int llclen = ETHER_HDR_LEN;
 
 #if NVLAN > 0
 	if (m->m_flags & M_VLANTAG)
@@ -1681,7 +1686,8 @@ bridge_ip(struct ifnet *brifp, int dir, 
 
 	etype = ntohs(eh->ether_type);
 
-	if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6) {
+	if ((etype != ETHERTYPE_IP) && (etype != ETHERTYPE_IPV6) && 
+		(etype != ETHERTYPE_PPPOE)) {
 		if (etype > ETHERMTU ||
 		    m->m_pkthdr.len < (LLC_SNAPFRAMELEN +
 		    ETHER_HDR_LEN))
@@ -1698,17 +1704,44 @@ bridge_ip(struct ifnet *brifp, int dir, 
 			return (m);
 
 		etype = ntohs(llc.llc_snap.ether_type);
-		if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6)
+		if (etype != ETHERTYPE_IP && etype != ETHERTYPE_IPV6 &&
+			etype != ETHERTYPE_PPPOE)
 			return (m);
+
 		hassnap = 1;
 	}
 
+	if (etype == ETHERTYPE_PPPOE) {
+		u_int16_t lcp;
+
+		if (hassnap) {
+			llclen += LLC_SNAPFRAMELEN;
+		}
+
+		m_copydata(m, llclen, sizeof(struct pppoehdr), &phdr);
+		llclen += sizeof(struct pppoehdr);
+
+		m_copydata(m, llclen, sizeof(u_int16_t), &lcp);
+
+		if (ntohs(lcp) == PPP_IP)
+			etype = ETHERTYPE_IP;
+		else if (ntohs(lcp) == PPP_IPV6)
+			etype = ETHERTYPE_IPV6;
+		else
+			return (m);
+
+		haspppoe = 1;
+	}
+
+
 	m_adj(m, ETHER_HDR_LEN);
+
 	if (hassnap)
 		m_adj(m, LLC_SNAPFRAMELEN);
+	if (haspppoe)
+		m_adj(m, sizeof(phdr) + sizeof(u_int16_t));
 
 	switch (etype) {
-
 	case ETHERTYPE_IP:
 		m = ipv4_check(ifp, m);
 		if (m == NULL)
@@ -1793,11 +1826,19 @@ bridge_ip(struct ifnet *brifp, int dir, 
 		break;
 	}
 
-	/* Reattach SNAP header */
+	/* Reattach PPPoE and SNAP headers */
+	if (haspppoe) {
+		M_PREPEND(m, sizeof(phdr), M_DONTWAIT);
+		if (m == NULL)
+			goto dropit;
+
+		bcopy(&phdr, mtod(m, caddr_t), sizeof(phdr));
+	}
 	if (hassnap) {
 		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
 		if (m == NULL)
 			goto dropit;
+
 		bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
 	}
 
Index: if_bridge.h
===================================================================
RCS file: /cvs/src/sys/net/if_bridge.h,v
retrieving revision 1.73
diff -u -p -u -r1.73 if_bridge.h
--- if_bridge.h	11 Nov 2021 10:03:10 -0000	1.73
+++ if_bridge.h	1 Feb 2024 14:06:12 -0000
@@ -208,6 +208,14 @@ struct ifbrarpf {
 #define	BRLA_THA	0x40
 #define	BRLA_TPA	0x80
 
+struct ifbrpppoef {
+	u_int16_t		brla_flags;
+	u_int16_t		brla_session;
+};
+
+#define BRLA_PPPOE	0x100
+#define BRLA_PPPOESESS	0x200
+
 struct ifbrlreq {
 	char			ifbr_name[IFNAMSIZ];	/* bridge ifs name */
 	char			ifbr_ifsname[IFNAMSIZ];	/* member ifs name */
@@ -217,6 +225,7 @@ struct ifbrlreq {
 	struct ether_addr	ifbr_dst;		/* destination mac */
 	char			ifbr_tagname[PF_TAG_NAME_SIZE];	/* pf tagname */
 	struct ifbrarpf		ifbr_arpf;		/* arp filter */
+	struct ifbrpppoef	ifbr_pppoef;		/* pppoe ses filter */
 };
 #define	BRL_ACTION_BLOCK	0x01			/* block frame */
 #define	BRL_ACTION_PASS		0x02			/* pass frame */
@@ -286,6 +295,7 @@ struct brl_node {
 	u_int8_t		brl_action;	/* what to do with match */
 	u_int8_t		brl_flags;	/* comparison flags */
 	struct ifbrarpf		brl_arpf;	/* arp filter */
+	struct ifbrpppoef	brl_pppoef;	/* pppoe sess filter */
 };
 
 struct bstp_timer {
Index: if_pppoe.c
===================================================================
RCS file: /cvs/src/sys/net/if_pppoe.c,v
retrieving revision 1.83
diff -u -p -u -r1.83 if_pppoe.c
--- if_pppoe.c	14 Jul 2022 11:03:15 -0000	1.83
+++ if_pppoe.c	1 Feb 2024 14:06:12 -0000
@@ -59,61 +59,6 @@
 
 #define PPPOEDEBUG(a)	((sc->sc_sppp.pp_if.if_flags & IFF_DEBUG) ? printf a : 0)
 
-struct pppoehdr {
-	u_int8_t vertype;
-	u_int8_t code;
-	u_int16_t session;
-	u_int16_t plen;
-} __packed;
-
-struct pppoetag {
-	u_int16_t tag;
-	u_int16_t len;
-} __packed;
-
-#define	PPPOE_HEADERLEN		sizeof(struct pppoehdr)
-#define	PPPOE_OVERHEAD		(PPPOE_HEADERLEN + 2)
-#define	PPPOE_VERTYPE		0x11		/* VER=1, TYPE = 1 */
-
-#define	PPPOE_TAG_EOL		0x0000		/* end of list */
-#define	PPPOE_TAG_SNAME		0x0101		/* service name */
-#define	PPPOE_TAG_ACNAME	0x0102		/* access concentrator name */
-#define	PPPOE_TAG_HUNIQUE	0x0103		/* host unique */
-#define	PPPOE_TAG_ACCOOKIE	0x0104		/* AC cookie */
-#define	PPPOE_TAG_VENDOR	0x0105		/* vendor specific */
-#define	PPPOE_TAG_RELAYSID	0x0110		/* relay session id */
-#define	PPPOE_TAG_MAX_PAYLOAD	0x0120		/* RFC 4638 max payload */
-#define	PPPOE_TAG_SNAME_ERR	0x0201		/* service name error */
-#define	PPPOE_TAG_ACSYS_ERR	0x0202		/* AC system error */
-#define	PPPOE_TAG_GENERIC_ERR	0x0203		/* generic error */
-
-#define	PPPOE_CODE_PADI		0x09		/* Active Discovery Initiation */
-#define	PPPOE_CODE_PADO		0x07		/* Active Discovery Offer */
-#define	PPPOE_CODE_PADR		0x19		/* Active Discovery Request */
-#define	PPPOE_CODE_PADS		0x65		/* Active Discovery Session confirmation */
-#define	PPPOE_CODE_PADT		0xA7		/* Active Discovery Terminate */
-
-/* two byte PPP protocol discriminator, then IP data */
-#define	PPPOE_MTU	(ETHERMTU - PPPOE_OVERHEAD)
-#define	PPPOE_MAXMTU	PP_MAX_MRU
-
-/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
-#define	PPPOE_ADD_16(PTR, VAL)			\
-		*(PTR)++ = (VAL) / 256;		\
-		*(PTR)++ = (VAL) % 256
-
-/* Add a complete PPPoE header to the buffer pointed to by PTR */
-#define	PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN)	\
-		*(PTR)++ = PPPOE_VERTYPE;	\
-		*(PTR)++ = (CODE);		\
-		PPPOE_ADD_16(PTR, SESS);	\
-		PPPOE_ADD_16(PTR, LEN)
-
-#define	PPPOE_DISC_TIMEOUT	5	/* base for quick timeout calculation (seconds) */
-#define	PPPOE_SLOW_RETRY	60	/* persistent retry interval (seconds) */
-#define	PPPOE_DISC_MAXPADI	4	/* retry PADI four times (quickly) */
-#define	PPPOE_DISC_MAXPADR	2	/* retry PADR twice */
-
 /*
  * Locks used to protect struct members and global data
  *       I       immutable after creation
Index: if_pppoe.h
===================================================================
RCS file: /cvs/src/sys/net/if_pppoe.h,v
retrieving revision 1.8
diff -u -p -u -r1.8 if_pppoe.h
--- if_pppoe.h	29 Jun 2022 09:08:07 -0000	1.8
+++ if_pppoe.h	1 Feb 2024 14:06:12 -0000
@@ -64,6 +64,61 @@ struct pppoeconnectionstate {
 
 #define PPPOEGETSESSION	_IOWR('i', 112, struct pppoeconnectionstate)
 
+struct pppoehdr {
+	u_int8_t vertype;
+	u_int8_t code;
+	u_int16_t session;
+	u_int16_t plen;
+} __packed;
+
+struct pppoetag {
+	u_int16_t tag;
+	u_int16_t len;
+} __packed;
+
+#define	PPPOE_HEADERLEN		sizeof(struct pppoehdr)
+#define	PPPOE_OVERHEAD		(PPPOE_HEADERLEN + 2)
+#define	PPPOE_VERTYPE		0x11		/* VER=1, TYPE = 1 */
+
+#define	PPPOE_TAG_EOL		0x0000		/* end of list */
+#define	PPPOE_TAG_SNAME		0x0101		/* service name */
+#define	PPPOE_TAG_ACNAME	0x0102		/* access concentrator name */
+#define	PPPOE_TAG_HUNIQUE	0x0103		/* host unique */
+#define	PPPOE_TAG_ACCOOKIE	0x0104		/* AC cookie */
+#define	PPPOE_TAG_VENDOR	0x0105		/* vendor specific */
+#define	PPPOE_TAG_RELAYSID	0x0110		/* relay session id */
+#define	PPPOE_TAG_MAX_PAYLOAD	0x0120		/* RFC 4638 max payload */
+#define	PPPOE_TAG_SNAME_ERR	0x0201		/* service name error */
+#define	PPPOE_TAG_ACSYS_ERR	0x0202		/* AC system error */
+#define	PPPOE_TAG_GENERIC_ERR	0x0203		/* generic error */
+
+#define	PPPOE_CODE_PADI		0x09		/* Active Discovery Initiation */
+#define	PPPOE_CODE_PADO		0x07		/* Active Discovery Offer */
+#define	PPPOE_CODE_PADR		0x19		/* Active Discovery Request */
+#define	PPPOE_CODE_PADS		0x65		/* Active Discovery Session confirmation */
+#define	PPPOE_CODE_PADT		0xA7		/* Active Discovery Terminate */
+
+/* two byte PPP protocol discriminator, then IP data */
+#define	PPPOE_MTU	(ETHERMTU - PPPOE_OVERHEAD)
+#define	PPPOE_MAXMTU	PP_MAX_MRU
+
+/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
+#define	PPPOE_ADD_16(PTR, VAL)			\
+		*(PTR)++ = (VAL) / 256;		\
+		*(PTR)++ = (VAL) % 256
+
+/* Add a complete PPPoE header to the buffer pointed to by PTR */
+#define	PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN)	\
+		*(PTR)++ = PPPOE_VERTYPE;	\
+		*(PTR)++ = (CODE);		\
+		PPPOE_ADD_16(PTR, SESS);	\
+		PPPOE_ADD_16(PTR, LEN)
+
+#define	PPPOE_DISC_TIMEOUT	5	/* base for quick timeout calculation (seconds) */
+#define	PPPOE_SLOW_RETRY	60	/* persistent retry interval (seconds) */
+#define	PPPOE_DISC_MAXPADI	4	/* retry PADI four times (quickly) */
+#define	PPPOE_DISC_MAXPADR	2	/* retry PADR twice */
+
 #ifdef _KERNEL
 
 extern struct mbuf_queue pppoediscinq;
Index: brconfig.c
===================================================================
RCS file: /cvs/src/sbin/ifconfig/brconfig.c,v
retrieving revision 1.32
diff -u -p -u -r1.32 brconfig.c
--- brconfig.c	23 Nov 2023 03:38:34 -0000	1.32
+++ brconfig.c	1 Feb 2024 14:02:16 -0000
@@ -57,6 +57,7 @@ void bridge_cfg(const char *);
 void bridge_badrule(int, char **, int);
 void bridge_showrule(struct ifbrlreq *);
 int bridge_arprule(struct ifbrlreq *, int *, char ***);
+int bridge_pppoerule(struct ifbrlreq *, int *, char ***);
 
 #define	IFBAFBITS	"\020\1STATIC"
 #define	IFBIFBITS	\
@@ -907,6 +908,13 @@ bridge_showrule(struct ifbrlreq *r)
 	if (r->ifbr_arpf.brla_flags & BRLA_TPA)
 		printf(" tpa %s", inet_ntoa(r->ifbr_arpf.brla_tpa));
 
+	if (r->ifbr_pppoef.brla_flags & BRLA_PPPOE) {
+		printf(" pppoe");
+	
+		if (r->ifbr_pppoef.brla_flags & BRLA_PPPOESESS)
+			printf(" sessid %u", r->ifbr_pppoef.brla_session);
+	}
+
 	printf("\n");
 }
 
@@ -1005,6 +1013,11 @@ bridge_rule(int targc, char **targv, int
 			argc--; argv++;
 			if (bridge_arprule(&rule, &argc, &argv) == -1)
 				goto bad_rule;
+		} else if (strcmp(argv[0], "pppoe") == 0) {
+			rule.ifbr_pppoef.brla_flags |= BRLA_PPPOE;
+			argc--; argv++;
+			if (bridge_pppoerule(&rule, &argc, &argv) == -1)
+				goto bad_rule;
 		} else
 			goto bad_rule;
 
@@ -1030,6 +1043,30 @@ bridge_rule(int targc, char **targv, int
 bad_rule:
 	bridge_badrule(targc, targv, ln);
 	return (1);
+}
+
+
+int
+bridge_pppoerule(struct ifbrlreq *rule, int *argc, char ***argv)
+{
+	while (*argc) {
+		u_short			*sess;
+
+		if (strcmp((*argv)[0], "sessid") == 0) {
+			sess = &rule->ifbr_pppoef.brla_session;
+		} else
+			return (0);
+
+		(*argc)--; (*argv)++;
+		if (sess != NULL) {
+			if (*argc == 0)
+				return (-1);
+			*sess = atoi((*argv)[0]);
+			rule->ifbr_pppoef.brla_flags |= BRLA_PPPOESESS;
+			(*argc)--; (*argv)++;
+		}
+	}
+	return (0);
 }
 
 int
Index: ifconfig.8
===================================================================
RCS file: /cvs/src/sbin/ifconfig/ifconfig.8,v
retrieving revision 1.399
diff -u -p -u -r1.399 ifconfig.8
--- ifconfig.8	11 Jan 2024 17:22:04 -0000	1.399
+++ ifconfig.8	1 Feb 2024 14:02:17 -0000
@@ -801,6 +801,12 @@ like a hub or a wireless network.
 .Op Cm tpa Ar ipaddr
 .Oc
 .Ek
+.Bk -words
+.Oo
+.Cm pppoe
+.Op Cm sessid Ar integer
+.Oc
+.Ek
 .Xc
 Add a filtering rule to an interface.
 Rules have a similar syntax to those in

On Sun, Jan 28, 2024 at 10:09:01AM +0100, Peter J. Philipp wrote:
> Hi David,
> 
> I was wondering what the best way is to add code to inspect and tag code
> on a bridge or veb that is passing PPPoE.
> 
> I was thinking perhaps a vlan(4)-like pseudo-interface that pulls up the 
> mbuf to the start of layer 3 (IP) and then allows tagging to be performed 
> based on pf(4).
> 
> If there is a simpler way in ifconfig bridge rules, then I could use that but
> I had a quick look at: /sys/net/bridgectl.c and one example could be adding
> a modified bridge_arpfilter() to allow tagging of pppoe.
> 
> You have a lot better insight into this than I, hence I'm asking you.  The
> reason I ask is because I just got a new provider for next month and I didn't
> get the router because I have a similar type hardware, but I'd like to protect
> it a little bit with pf on a bridge, since it is outdated and has no new
> firmwares.
> 
> Thanks for any pointers,
> -peter
>