Index | Thread | Search

From:
David Gwynne <david@gwynne.id.au>
Subject:
Re: libpcap: teach gencode how to match ipv6 and ipv4 packets for DLT_RAW
To:
tech@openbsd.org
Date:
Thu, 5 Jun 2025 16:43:51 +1000

Download raw body.

Thread
On Thu, Jun 05, 2025 at 08:25:03AM +0200, Claudio Jeker wrote:
> On Thu, Jun 05, 2025 at 04:00:06PM +1000, David Gwynne wrote:
> > this allows you filter on ip and ip6 packets like you do on other DLTs
> > by looking at the first nibble.
> > 
> > before this libpcap would just say "yes" if you asked for just ip or
> > ip6:
> > 
> > xdlg@oak ~$ tcpdump -r erspan.raw.pcap -d ip
> > (000) ret      #116
> > xdlg@oak ~$ tcpdump -r erspan.raw.pcap -d ip6
> > (000) ret      #116
> > 
> > or worse:
> > 
> > xdlg@oak ~$ tcpdump -r erspan.raw.pcap -d mpls 
> > (000) ldh      [-1]
> > (001) jeq      #0x8847          jt 2	jf 3
> > (002) ret      #116
> > (003) ret      #0
> > xdlg@oak ~$ tcpdump -r erspan.raw.pcap -d arp  
> > (000) ldh      [-1]
> > (001) jeq      #0x806           jt 2	jf 3
> > (002) ret      #116
> > (003) ret      #0
> > 
> > with this diff it can match on the packet:
> > 
> > xdlg@pitcher ~$ tcpdump -r erspan.raw.pcap -d ip
> > (000) ldb      [x + 0]
> > (001) rsh      #4
> > (002) jeq      #0x4             jt 3	jf 4
> > (003) ret      #116
> > (004) ret      #0
> > xdlg@pitcher ~$ tcpdump -r erspan.raw.pcap -d ip6
> > (000) ldb      [x + 0]
> > (001) rsh      #4
> > (002) jeq      #0x6             jt 3	jf 4
> > (003) ret      #116
> > (004) ret      #0
> > 
> > we also get this:
> > 
> > xdlg@pitcher ~$ tcpdump -r erspan.raw.pcap -d arp 
> > tcpdump: expression rejects all packets
> > 
> > ok?
> > 
> > Index: gencode.c
> > ===================================================================
> > RCS file: /cvs/src/lib/libpcap/gencode.c,v
> > diff -u -p -r1.67 gencode.c
> > --- gencode.c	15 Sep 2024 07:14:58 -0000	1.67
> > +++ gencode.c	5 Jun 2025 05:51:30 -0000
> > @@ -768,12 +768,14 @@ init_linktype(int type)
> >  		off_nl = 12;
> >  		return;
> >  
> > -	case DLT_USBPCAP:
> > -		/* FALLTHROUGH */
> >  	case DLT_RAW:
> > -		off_linktype = -1;
> > +		off_linktype = 0;
> >  		off_nl = 0;
> >  		return;
> > +	case DLT_USBPCAP:
> > +		off_linktype = -1;
> > +		off_nl = 0;
> > +		break;
> 
> Shouldn't this be a return?

yep.

> > +		/* A = p[X+off_linktype] */
> > +		s0 = new_stmt(BPF_LD|BPF_B|BPF_IND);
> > +		s0->s.k = off_linktype;
> > +
> > +		/* A = A >> 4 */
> > +		s1 = new_stmt(BPF_ALU|BPF_RSH|BPF_K);
> > +		s1->s.k = 4;
> > +		sappend(s0, s1);
> > +
> > +		b0 = new_block(JMP(BPF_JEQ));
> > +		b0->stmts = s0;
> > +		b0->s.k = v;
> 
> I trust you that this is producing the right bpf commands. It kind of
> makes sense to me.

i shouldn't be using indirect loads here.

Index: gencode.c
===================================================================
RCS file: /cvs/src/lib/libpcap/gencode.c,v
diff -u -p -r1.67 gencode.c
--- gencode.c	15 Sep 2024 07:14:58 -0000	1.67
+++ gencode.c	5 Jun 2025 06:41:41 -0000
@@ -768,9 +768,11 @@ init_linktype(int type)
 		off_nl = 12;
 		return;
 
-	case DLT_USBPCAP:
-		/* FALLTHROUGH */
 	case DLT_RAW:
+		off_linktype = 0;
+		off_nl = 0;
+		return;
+	case DLT_USBPCAP:
 		off_linktype = -1;
 		off_nl = 0;
 		return;
@@ -910,6 +912,37 @@ gen_linktype(int proto)
 		return (gen_cmp(0, BPF_W, (bpf_int32)v));
 		break;
 	}
+	case DLT_RAW: {
+		struct slist *s0, *s1;
+		int ipv;
+
+		switch (proto) {
+		case ETHERTYPE_IP:
+			ipv = 4;
+			break;
+		case ETHERTYPE_IPV6:
+			ipv = 6;
+			break;
+		default:
+			return gen_false();
+		}
+
+		/* A = p[X+off_linktype] */
+		s0 = new_stmt(BPF_LD|BPF_ABS|BPF_B);
+		s0->s.k = off_linktype;
+
+		/* A = A >> 4 */
+		s1 = new_stmt(BPF_ALU|BPF_RSH|BPF_K);
+		s1->s.k = 4;
+		sappend(s0, s1);
+
+		b0 = new_block(JMP(BPF_JEQ));
+		b0->stmts = s0;
+		b0->s.k = ipv;
+
+		return (b0);
+	}
+
 	case DLT_PFLOG:
 		if (proto == ETHERTYPE_IP)
 			return (gen_cmp(offsetof(struct pfloghdr, af), BPF_B,