Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 3 Mar 2023 17:40:34 +0100
From:      Michael Tuexen <michael.tuexen@lurchi.franken.de>
To:        "Rodney W. Grimes" <freebsd-rwg@gndrsh.dnsmgr.net>
Cc:        "Scheffenegger, Richard" <rscheff@FreeBSD.org>, "freebsd-net@freebsd.org" <freebsd-net@FreeBSD.org>
Subject:   Re: BPF to filter/mod ARP
Message-ID:  <2A57C212-8FF0-406F-9AE5-58DCA856D716@lurchi.franken.de>
In-Reply-To: <202303031352.323DqPjh066348@gndrsh.dnsmgr.net>
References:  <202303031352.323DqPjh066348@gndrsh.dnsmgr.net>

next in thread | previous in thread | raw e-mail | index | archive | help
> On 3. Mar 2023, at 14:52, Rodney W. Grimes =
<freebsd-rwg@gndrsh.dnsmgr.net> wrote:
>=20
>>> On 2. Mar 2023, at 18:20, Rodney W. Grimes =
<freebsd-rwg@gndrsh.dnsmgr.net> wrote:
>>>=20
>>>>> On 2. Mar 2023, at 02:24, Rodney W. Grimes =
<freebsd-rwg@gndrsh.dnsmgr.net> wrote:
>>>>>=20
>>>>>> Hi group,
>>>>>>=20
>>>>>> Maybe someone can help me with this question - as I am usually =
only=20
>>>>>> looking at L4 and the top side of L3 ;)
>>>>>>=20
>>>>>> In order to validate a peculiar switches behavior, I want to =
adjust some=20
>>>>>> fields in gracious arps sent out by an interface, after a new IP =
is=20
>>>>>> assigned or changed.
>>>>>=20
>>>>> Gracious or Gratuitous?
>>>>>=20
>>>>>>=20
>>>>>> I believe BPF can effectively filter on arbitrary bit patterns =
and=20
>>>>>> modify packets on the fly.
>>>>>=20
>>>>> It can.
>>>>>=20
>>>>>>=20
>>>>>> However, as ARP doesn't seem to be accessible in the ipfw=20
>>>>>> infrastructure, I was wondering how to go about setting up an BPF =
to=20
>>>>>> tweak (temporarily) some of these ARPs to validate how the switch =
will=20
>>>>>> behave.
>>>>>=20
>>>>> ipfw is IP firewall, a layer 3 function.  Arp is a layer 2 =
protocol,
>>>>> so very hard to do much with it in ipfw, but perhaps the layer2
>>>>> keyword, and some use of mac-type can get it to match an arp
>>>>> packet.  Arp is ethernet type 0x806.
>>>>>=20
>>>>> ipfw add 111 count log all from any to any layer2 mac-type arp
>>>>> That does seem to work
>>>>> ipfw -a list 111
>>>>> 00111    4       0 count log ip from any to any layer2 mac-type =
0x0806
>>>>>=20
>>>>> Also normally ipfw does NOT pick packets up early enough to see
>>>>> them, to get the layer2 option to work you need:
>>>>> sysctl net.link.ether.ipfw=3D1 so that the filters at ether_demux
>>>>> get turned on.
>>>>>=20
>>>>> So perhaps use a divert rule and send them to a socket where
>>>>> a program can mangle them, and then return them to ipfw
>>>>> and hopefully the kernel does what you want after that...
>>>> I thought that you receive/send an IP packet on a divert socket, =
not
>>>> an ethernet frame. Am I wrong?
>>>=20
>>> That is unclear to me, technically it should just be a binary
>>> blob and the kernel and userland just have to agree as to
>>> what it is.  Understand that ipfw originally only had IP layer
>>> functionality.  The ability to muck with layer2 was added
>>> later, so I suspect the documentation about what is sent
>>> over the divert socket may be out of date.  Simple enough
>>> to test though, just setup as I show above only change
>>> to:
>>> ipfw add 111 divert 4444 all from any to any layer2 mac-type arp
>>> and write a program to dump what you get on the divert socket.
>>> I suspect you get an ethernet frame.
>>>=20
>>> And finally divert(4) says: NAME: divert kernel packet diversion =
mechanism
>>> That says packet, so again, IMHO, it should be arbitrary to what =
layer.
>>> It also later says "Divert sockets are similar to raw IP sockets",
>>> I think similar is the key aspect here, they are not identical.
>> I can confirm that using
>> sudo sysctl net.link.ether.ipfw=3D1
>> sudo ipfw add 111 count log all from any to any layer2 mac-type arp
>> ... wait some time and observe ARP traffic via tcpdump
>> sudo ipfw show
>> 00111   22      0 count log logamount 5 ip from any to any layer2 =
mac-type 0x0806
>> 65535 7892 849004 allow ip from any to any
>> So the rule is hit.
>>=20
>> However, now doing
>> sudo ipfw delete 111
>> sudo ipfw add 111 divert 1234 all from any to any layer2 mac-type arp
>> ... wait some time and observe ARP traffic via tcpdump
>> tuexen@head:~ % sudo ipfw show
>> 00111     0       0 divert 1234 ip from any to any layer2 mac-type =
0x0806
>> 65535 10048 1000948 allow ip from any to any
>> So this time, rule 111 is not hit. I also ran
>=20
> Nice work, to  me I would classify this behavior as some form of bug,
> the action verb of a rule in ipfw should in no way change what is =
matched
> by the rule filter.
>=20
> I am assuming you either had IPDIVERT compiled into your kernel, or =
you
> you had loaded the module, as you dont clearly state this.   I am also
> uncertain on what the results are if you use the divert keyword =
without
> ipdivert.ko loaded, is it an error when the rule gets created, or is =
it
> silently ignored?
Before compiling IPDIVERT into the kernel, I got an error message. So I
used the following kernel config for the testing:

tuexen@head:~ % cat freebsd-src/sys/arm64/conf/TCP
include		GENERIC
ident	 	TCP

makeoptions     WITH_EXTRA_TCP_STACKS=3D1
options		TCPHPTS
options		VIMAGE
options		TCP_BLACKBOX
options		TCPPCAP
options		SCTP_DEBUG
options		RATELIMIT
options		DEBUG_REDZONE
options		IPFIREWALL
options		IPFIREWALL_VERBOSE
options		IPFIREWALL_VERBOSE_LIMIT=3D5
options		IPFIREWALL_DEFAULT_TO_ACCEPT
options		IPDIVERT

Best regards
Michael
>=20
>>=20
>> #include <sys/types.h>
>> #include <sys/socket.h>
>> #include <netinet/in.h>
>> #include <unistd.h>
>> #include <stdio.h>
>> #include <string.h>
>>=20
>> #define BUFFER_SIZE (1<<16)
>> #define PORT        1234
>>=20
>> int
>> main(void)
>> {
>> char buffer[BUFFER_SIZE];
>> struct sockaddr_in addr;
>> ssize_t n;
>> int fd;
>>=20
>> if ((fd =3D socket(PF_DIVERT, SOCK_RAW, 0)) < 0) {
>> perror("socket()");
>> }
>> bzero(&addr, sizeof(addr));
>> addr.sin_family      =3D AF_INET;
>> addr.sin_len         =3D sizeof(struct sockaddr_in);
>> addr.sin_addr.s_addr =3D INADDR_ANY;
>> addr.sin_port        =3D htons(PORT);
>>=20
>> if (bind(fd, (struct sockaddr *)&addr, (socklen_t)sizeof(struct =
sockaddr_in)) < 0) {
>> perror("bind()");
>> }
>> for (;;) {
>> n =3D recv(fd, buffer, sizeof(buffer), 0);
>> printf("Received %zd bytes.\n", n);
>> }
>> if (close(fd) < 0) {
>> perror("close()");
>> }
>> return (0);
>> }
>>=20
>> but nothing was printed...
>>=20
>> Best regards
>> Michael
>>>=20
>>>>=20
>>>> Best regards
>>>> Michael
>>>>>=20
>>>>>> (I need to validate, if there is some difference when the target=20=

>>>>>> hardware address doesn't conform to RFC5227 - which states it =
SHOULD be=20
>>>>>> zero and is ignored on the receiving side; i have reasons to =
believe=20
>>>>>> that the switch needs either a target hardware address of=20
>>>>>> ff:ff:ff:ff:ff:ff or the local interface MAC, to properly update =
it's=20
>>>>>> entries.)
>>>>>>=20
>>>>>> Thanks a lot!
>>>>>>=20
>>>>>> Richard
>>>>>>=20
>>>>>=20
>>>>> --=20
>>>>> Rod Grimes                                                 =
rgrimes@freebsd.org
>>>>>=20
>>>>=20
>>>>=20
>>>>=20
>>>=20
>>> --=20
>>> Rod Grimes                                                 =
rgrimes@freebsd.org
>>=20
>>=20
>>=20
>>=20
>=20
> --=20
> Rod Grimes                                                 =
rgrimes@freebsd.org




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?2A57C212-8FF0-406F-9AE5-58DCA856D716>