1. Overview

In this tutorial, we’ll learn how to filter network packets by MAC address using the tcpdump command-line tool.

2. MAC Address and Ethernet Frame

A media access control (MAC) address is a unique identifier assigned to each network interface controller (NIC). It comes in the form of a 48-bit hexadecimal number, such as 23:45:aa:7d:33:b8, and each device will have its MAC address assigned by its manufacturer.

The uniqueness of the MAC address is essential in a network. This is because it helps ensure that the network packets can be directed to the intended recipient. Additionally, the uniqueness of the MAC address ensures that the sender of the network packets can also be correctly identified.

As a hardware address, the MAC address lives on layer 2 of the OSI model. Protocol on that layer, such as the Ethernet, addresses the source and destination using the MAC address. Specifically, in every Ethernet frame, there’s a destination MAC address and source MAC address contained within. These addresses can then be used as a way for us to selectively capture the packets related to different NICs.

Let’s look at how we can capture packets that match a certain MAC address using tcpdump.

3. MAC Address Filter With tcpdump

The tcpdump is a network packet sniffer command-line tool in Linux. It’s a powerful tool for capturing all the packets going through a network interface. To start capturing packets on a network interface, we can run the tcpdump with the -i option targeting the network interface:

$ tcpdump -i enp0s3

The command above listens to the network interface enp0s3 and reports all the packets that it captures. For a system with a high number of network traffic, the simple command above might result in a capture file that is huge and difficult for analysis.

To restrict the kind of packets we capture, we can apply a pcap filter on the tcpdump command. The pcap filter syntax supports various protocol-specific information filters, including the Ethernet protocol. Using its ether protocol primitive, we can write predicates that match conditions based on the information on the Ethernet frame, including the MAC address. For example, we can apply a pcap filter to our tcpdump to perform MAC address filtering:

$ tcpdump -i enp0s3 ether host 23:45:aa:7d:33:b8

The ether keyword in the filter restricts the matching to the Ethernet protocol layer. Furthermore, the host keyword matches both the source and destination MAC address on the frame. This means that if the MAC address 23:45:aa:7d:33:b8 shows up as either the source or destination address in a packet, we consider it a match.

Let’s look at more pcap filter expressions that support more advanced MAC address filtering.

3.1. Filter by Source or Destination

Other than the host keyword, the pcap filter allows a more restrictive matching using the src and dst keywords. Specifically, we can capture all the packets that are originating from the MAC address using ether src:

$ tcpdump ether src 23:45:aa:7d:33:b8

Contrary to the host syntax, the filter expression above will not include packets that are going to the given MAC address. This is useful for isolating packets that are originating from a specific NIC.

Similarly, to match packets that are going to the MAC address only, we can use the dst keyword:

$ tcpdump ether dst 23:45:aa:7d:33:b8

3.2. Boolean Algebra

We can further enhance our filter expression using boolean algebra. This allows us to combine several independent filters to form a more complex filter.

Firstly, we can do a negation using the not syntax to capture all the packets except for that one MAC address:

$ tcpdump not ether dst 23:45:aa:7d:33:b8

The command above will capture all the packets except for the packets that are going to the NIC with the MAC address 23:45:aa:7d:33:b8.

Furthermore, we can also use the and or keywords to combine several filters to build a more powerful filter. For instance, we can use the and keyword to restrict the capturing to packets that are coming from and going to a specific pair of MAC addresses:

$ tcpdump ether src 23:45:aa:7d:33:b8 and ether dst 1d:c4:98:84:8a:ea

Similarly, using the or operator, we can capture packets that are coming from either one of the MAC addresses:

$ tcpdump ether src 23:45:aa:7d:33:b8 or ether src 1d:c4:98:84:8a:ea

3.3. Partial Matching

The pcap filter also allows us to match the MAC address pair in the packet partially. Specifically, the pcap filter supports the extraction of protocol data for comparison purposes in the form of index operation using the protooffset, and size syntax:

proto [ offset : size ]

The proto is the protocol keyword that the index operation will work on. When we specify ether, it means that the data extraction will work on the Ethernet layer. Then, the offset is an integer value of bytes offset beginning from the first byte of the frame. Finally, the size specifies the number of octets we want to extract. The size is an optional value and defaults to one if not specified.

For example, we can write an expression to extract the first two octets of the source MAC address:

ether [6:2]

In each Ethernet frame, the destination MAC address begins at offset 0, and it is 6 octets in length. Therefore, we specify an offset of 6 to skip the destination MAC address and extract the first two octets of the source MAC address.

Then, we can form the filter using the equality comparator to a prefix we want to match:

ether [6:2] == "0x2345"

With the filter expression above, the tcpdump will capture packets with a source MAC address that starts at 23:45.

4. Examples

Let’s take the building blocks from previous sections and write filters for various kinds of MAC address filtering. To make the examples concise, we’ll use placeholder MAC addresses like A and B instead of their complete 48-bit hexadecimal form. When using the filter in the actual application, we’ll have to replace the placeholder with the actual MAC addresses.

To capture all the packets coming from MAC address A and going to destination B, we can use the src, dst, and and keywords:

$ tcpdump -i enp0s3 ether src A and ether dst B

If we want to select packets that are either going to MAC address A or originating from MAC address A, we use the host keyword:

$ tcpdump -i enp0s3 ether host A

To select all the packets except for packets that are related to MAC address A, we use the not negation:

$ tcpdump -i enp0s3 not ether host A

To select all the packets from MAC address A except for those that are going to MAC address B, we can combine the filters with the and keyword:

$ tcpdump -i enp0s3 "ether src A and (not ether dst B)"

Note that we’ll need the double quote to prevent the shell from interpreting the parentheses.

To select all the packets with source MAC address starting with 23:ab, we can use the data extraction syntax:

$ tcpdump -i enp0s3 ether[6:2] == 0x23ab

Similarly, we can select packets with source MAC address suffix matching 45:bc using offset 10:

$ tcpdump -i enp0s3 ether[10:2] == 0x45bc

5. Conclusion

In this tutorial, we’ve learned that the MAC address is the hardware address of every network interface controller. Additionally, we’ve learned that using the ether primitive of pcap filter syntax, we can capture packets with a specific MAC address with tcpdump. Then, we’ve seen how to construct more complicated filters using boolean algebra. Finally, we looked at some examples of how we can construct the different filters using the building blocks we’ve introduced.