Table of Contents
I’ll show how to capture packets on a local network with Pcap4J container.
Docker network
By default, Docker containers are not connected to a local network. They are connected only to a virtual network Docker creates as like below:
Refer to the Docker doc for more details.
What’s a challenge
In order to let a Pcap4J container capture packets in a local (real) network, we need to directly connect the container to the local network, because docker0 forwards only packets the destinations of which are in the virtual network.
How to do it is explained in some articles. I referred to one of them, Four ways to connect a docker container to a local network in Odd Bits blog, and succeeded in local network capturing using the 4th way.
What I actually did is as follows.
What I did
Environment
OS: CentOS 7.0 (on VMware Player 7.1.0 on Windows 7)
# uname -a Linux localhost.localdomain 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
user: root
Pcap4J version: 1.5.1-SNAPSHOT
Docker version: 1.6.2
Network interfaces:
# ip addr show 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 00:0c:29:8e:95:27 brd ff:ff:ff:ff:ff:ff inet 192.168.1.123/24 brd 192.168.1.255 scope global dynamic eth0 valid_lft 85975sec preferred_lft 85975sec inet6 2601:282:8102:2623:20c:29ff:fe8e:9527/64 scope global dynamic valid_lft 221469sec preferred_lft 221469sec inet6 fe80::20c:29ff:fe8e:9527/64 scope link valid_lft forever preferred_lft forever 3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff inet 172.17.42.1/16 scope global docker0 valid_lft forever preferred_lft forever
- Prerequisites:
- Docker is installed and Docker service is started
- nsenter
Step by step
Preparing
Create a utility script
docker-pid
with the following content and place it somewhere in thePATH
.#!/bin/sh exec docker inspect --format '{{ .State.Pid }}' "$@"
This script show the PID of a docker container by name or ID.
Pull the latest Pcap4J image
# docker pull kaitoy/pcap4j
Start a Pcap4J container with wait mode
# docker run --name pcap4j-br kaitoy/pcap4j:latest eth1 true
This container (
pcap4j-br
) waits for a ping toeth0
on the container before staring capturing packets witheth1
on the container. After the container starts, you will see messages like below:17:49:21.196 [main] INFO org.pcap4j.core.Pcaps - 3 NIF(s) found. eth0 (null) IP address: /172.17.0.3 IP address: /fe80:0:0:0:42:acff:fe11:3
The messages say IP address of
eth0
is172.17.0.3
. We will use it later.Configure a bridge interface
Open another terminal and do the following:
# ip link add eth1 link eth0 type macvlan mode bridge # ip link set netns $(docker-pid pcap4j-br) eth1 # nsenter -t $(docker-pid pcap4j-br) -n ip link set eth1 up # nsenter -t $(docker-pid pcap4j-br) -n ip route del default # nsenter -t $(docker-pid pcap4j-br) -n ip addr add 192.168.1.200/24 dev eth1 # nsenter -t $(docker-pid pcap4j-br) -n ip route add default via 192.168.1.1 dev eth1
The above commands 1) add an interface
eth1
bridged toeth0
to the Docker host machine, 2) move theeth1
to the name space ofpcap4j-br
, 3) starteth1
, 4) delete the default route inpcap4j-br
, 5) add an IP address192.168.1.200/24
toeth1
, 6) and set the default route inpcap4j-br
to192.168.1.1
.Too much hassle? I agree. Let’s use an awesome tool, pipework. This tool accomplishes the above 6 steps in easier way as shown below:
# git clone https://github.com/jpetazzo/pipework.git # cd pipework # ./pipework eth0 pcap4j-br 192.168.1.200/[email protected]
pipework uses
ip netns exec
command instead ofnsenter
to manipulate a container. Incidentally,docker exec
didn’t work for the step 3 due to an error “RTNETLINK answers: Operation not permitted
”.In addition, in my case, because I was doing it on a VMware VM, I needed to enable the promiscuous mode of
eth0
(on the docker host machine) as follows:# ip link set dev eth0 promisc on
Try to poke the container
You can now communicate with
pcap4j-br
usingeth1
from another host. I tried some pings from the VM’s host topcap4j-br
and saw replies.Note that you can NOT communicate with
pcap4j-br
viaeth1
from the docker host. See the Odd Bits blog for the details.Start packet capturing
Ping to
eth0
ofpcap4j-br
form the docker host to start packet capturing.# ping -c 1 172.17.0.3