From: Vadim Kochan <[email protected]>

Add suporting Linux "cooked" header:

        http://www.tcpdump.org/linktypes/LINKTYPE_NETLINK.html

Added -w,--cooked option which replaces L2 header frame with cooked
header for each frame and allows to dump & dissect Ethernet & Netlink
packets together.

Added dissector_sll.c to dissect packets with Linux "cooked" header.
Meanwhile it can dump only Netlink & Ethernet protos (L3 layer). This
dissector is used if link type is LINKTYPE_LINUX_SLL.

Used dgram packet socket when sniffing with "--cooked" option
to pull L2 header and wrap packet with Linux "cooked" header, it allows
compatibility with Wireshark.

Moved some device's stuff string conversion functions from proto_nlmsg.c
to dev.c to be used also in dissector_sll.c

Signed-off-by: Vadim Kochan <[email protected]>
---
v2:
    1) Added -w,--cooked option to replace link header with Linux "cooked"
       header, added some man(8) explanations. With this option the new
       LINKTYPE_LINUX_SLL link type is used.

    2) Added dissector_sll.c to dissect packets with Linux "cooked" header.
       Meanwhile it can dump only Netlink & Ethernet protos (L3 layer). This
       dissector is used if link type is LINKTYPE_LINUX_SLL.

    3) Used dgram packet socket when sniffing with "--cooked" option
       to pull L2 header and wrap packet with Linux "cooked" header, it allows
       compatibility with Wireshark.

    4) Moved some device's stuff string conversion functions from proto_nlmsg.c
       to dev.c to be used also in dissector_sll.c

    5) Converting pcap_pkthdr_ll fields to big endian when dump to file &
       back to cpu when reading from file. Also added copying HW addr from
       sockaddr_ll->sll_addr to pcap_pkthdr_ll.

RFC -> PATCH:
    1) Add some clarifications about pcap cooked header in
        netsniff-ng.8 man page.

    2) Consider pcap type when checking if pcap cooked header
        is needed in pcap_pkthdr_has_ll(...) func.

    3) Fix wrong sll_protocol converting in pcap file. Removed
        converting to big endian in sockaddr_to_pcap_ll(...) func.

 dev.c                | 111 +++++++++++++++
 dev.h                |   3 +
 dissector.c          |  12 +-
 dissector.h          |   2 +-
 dissector_sll.c      | 114 +++++++++++++++
 dissector_sll.h      |  18 +++
 linktype.h           |   1 +
 netsniff-ng.8        |  16 ++-
 netsniff-ng.c        |  35 +++--
 netsniff-ng/Makefile |   1 +
 pcap_io.h            | 390 ++++++++++++++++++++++++++++++++++++---------------
 pcap_mm.c            |  28 +++-
 pcap_rw.c            |  30 +++-
 pcap_sg.c            |  83 +++++------
 pkt_buff.h           |   2 +-
 proto_nlmsg.c        | 120 +---------------
 sock.c               |   9 ++
 sock.h               |   1 +
 18 files changed, 687 insertions(+), 289 deletions(-)
 create mode 100644 dissector_sll.c
 create mode 100644 dissector_sll.h

diff --git a/dev.c b/dev.c
index 2b3a7a5..292e15e 100644
--- a/dev.c
+++ b/dev.c
@@ -5,6 +5,7 @@
 #include <sys/socket.h>
 #include <linux/if_arp.h>
 #include <ifaddrs.h>
+#include <arpa/inet.h>
 
 #include "dev.h"
 #include "str.h"
@@ -221,3 +222,113 @@ void device_leave_promiscuous_mode(const char *ifname, 
short oldflags)
 
        device_set_flags(ifname, oldflags);
 }
+
+char *device_type2str(uint16_t type)
+{
+       switch (type) {
+       case ARPHRD_ETHER: return "ether";
+       case ARPHRD_EETHER: return "eether";
+       case ARPHRD_AX25: return "ax25";
+       case ARPHRD_PRONET: return "pronet";
+       case ARPHRD_CHAOS: return "chaos";
+       case ARPHRD_IEEE802: return "ieee802";
+       case ARPHRD_ARCNET: return "arcnet";
+       case ARPHRD_APPLETLK: return "appletlk";
+       case ARPHRD_DLCI: return "dlci";
+       case ARPHRD_ATM: return "atm";
+       case ARPHRD_METRICOM: return "metricom";
+       case ARPHRD_IEEE1394: return "ieee1394";
+       case ARPHRD_INFINIBAND: return "infiniband";
+       case ARPHRD_SLIP: return "slip";
+       case ARPHRD_CSLIP: return "cslip";
+       case ARPHRD_SLIP6: return "slip6";
+       case ARPHRD_CSLIP6: return "cslip6";
+       case ARPHRD_RSRVD: return "RSRVD";
+       case ARPHRD_ADAPT: return "adapt";
+       case ARPHRD_ROSE: return "rose";
+       case ARPHRD_X25: return "x25";
+       case ARPHRD_HWX25: return "hwx25";
+       case ARPHRD_CAN: return "can";
+       case ARPHRD_PPP: return "ppp";
+       case ARPHRD_HDLC: return "hdlc";
+       case ARPHRD_LAPB: return "lapb";
+       case ARPHRD_DDCMP: return "ddcmp";
+       case ARPHRD_RAWHDLC: return "rawhdlc";
+       case ARPHRD_TUNNEL: return "tunnel";
+       case ARPHRD_TUNNEL6: return "tunnel6";
+       case ARPHRD_FRAD: return "frad";
+       case ARPHRD_SKIP: return "skip";
+       case ARPHRD_LOOPBACK: return "loopback";
+       case ARPHRD_LOCALTLK: return "localtlk";
+       case ARPHRD_FDDI: return "fddi";
+       case ARPHRD_BIF: return "bif";
+       case ARPHRD_SIT: return "sit";
+       case ARPHRD_IPDDP: return "ipddp";
+       case ARPHRD_IPGRE: return "ipgre";
+       case ARPHRD_PIMREG: return "pimreg";
+       case ARPHRD_HIPPI: return "hippi";
+       case ARPHRD_ASH: return "ash";
+       case ARPHRD_ECONET: return "econet";
+       case ARPHRD_IRDA: return "irda";
+       case ARPHRD_FCPP: return "fcpp";
+       case ARPHRD_FCAL: return "fcal";
+       case ARPHRD_FCPL: return "fcpl";
+       case ARPHRD_FCFABRIC: return "fcfb0";
+       case ARPHRD_FCFABRIC + 1: return "fcfb1";
+       case ARPHRD_FCFABRIC + 2: return "fcfb2";
+       case ARPHRD_FCFABRIC + 3: return "fcfb3";
+       case ARPHRD_FCFABRIC + 4: return "fcfb4";
+       case ARPHRD_FCFABRIC + 5: return "fcfb5";
+       case ARPHRD_FCFABRIC + 6: return "fcfb6";
+       case ARPHRD_FCFABRIC + 7: return "fcfb7";
+       case ARPHRD_FCFABRIC + 8: return "fcfb8";
+       case ARPHRD_FCFABRIC + 9: return "fcfb9";
+       case ARPHRD_FCFABRIC + 10: return "fcfb10";
+       case ARPHRD_FCFABRIC + 11: return "fcfb11";
+       case ARPHRD_FCFABRIC + 12: return "fcfb12";
+       case ARPHRD_IEEE802_TR: return "ieee802_tr";
+       case ARPHRD_IEEE80211: return "ieee80211";
+       case ARPHRD_IEEE80211_PRISM: return "ieee80211_prism";
+       case ARPHRD_IEEE80211_RADIOTAP: return "ieee80211_radiotap";
+       case ARPHRD_IEEE802154: return "ieee802154";
+       case ARPHRD_PHONET: return "phonet";
+       case ARPHRD_PHONET_PIPE: return "phonet_pipe";
+       case ARPHRD_CAIF: return "caif";
+       case ARPHRD_IP6GRE: return "ip6gre";
+       case ARPHRD_NETLINK: return "netlink";
+       case ARPHRD_NONE: return "none";
+       case ARPHRD_VOID: return "void";
+
+       default: return "Unknown";
+       }
+}
+
+/* Taken from iproute2 ll_addr_n2a func */
+const char *device_addr2str(const unsigned char *addr, int alen, int type,
+               char *buf, int blen)
+{
+       int i;
+       int l;
+
+       if (alen == 4 &&
+           (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == 
ARPHRD_IPGRE)) {
+               return inet_ntop(AF_INET, addr, buf, blen);
+       }
+       if (alen == 16 && type == ARPHRD_TUNNEL6) {
+               return inet_ntop(AF_INET6, addr, buf, blen);
+       }
+       l = 0;
+       for (i=0; i<alen; i++) {
+               if (i==0) {
+                       snprintf(buf+l, blen, "%02x", addr[i]);
+                       blen -= 2;
+                       l += 2;
+               } else {
+                       snprintf(buf+l, blen, ":%02x", addr[i]);
+                       blen -= 3;
+                       l += 3;
+               }
+       }
+       return buf;
+}
+
diff --git a/dev.h b/dev.h
index adb1c43..3b26d86 100644
--- a/dev.h
+++ b/dev.h
@@ -15,5 +15,8 @@ extern int device_up_and_running(const char *ifname);
 extern u32 device_bitrate(const char *ifname);
 extern short device_enter_promiscuous_mode(const char *ifname);
 extern void device_leave_promiscuous_mode(const char *ifname, short oldflags);
+extern char *device_type2str(uint16_t type);
+extern const char *device_addr2str(const unsigned char *addr, int alen,
+               int type, char *buf, int blen);
 
 #endif /* DEV_H */
diff --git a/dissector.c b/dissector.c
index 4cad588..6aa253d 100644
--- a/dissector.c
+++ b/dissector.c
@@ -14,6 +14,7 @@
 #include "proto.h"
 #include "dissector.h"
 #include "dissector_eth.h"
+#include "dissector_sll.h"
 #include "dissector_80211.h"
 #include "dissector_netlink.h"
 #include "linktype.h"
@@ -61,7 +62,7 @@ static void dissector_main(struct pkt_buff *pkt, struct 
protocol *start,
 }
 
 void dissector_entry_point(uint8_t *packet, size_t len, int linktype, int mode,
-                          uint16_t proto)
+                          struct sockaddr_ll *sll)
 {
        struct protocol *proto_start, *proto_end;
        struct pkt_buff *pkt;
@@ -71,7 +72,7 @@ void dissector_entry_point(uint8_t *packet, size_t len, int 
linktype, int mode,
 
        pkt = pkt_alloc(packet, len);
        pkt->link_type = linktype;
-       pkt->proto = proto;
+       pkt->sll = sll;
 
        switch (linktype) {
        case LINKTYPE_EN10MB:
@@ -91,6 +92,11 @@ void dissector_entry_point(uint8_t *packet, size_t len, int 
linktype, int mode,
                proto_start = dissector_get_netlink_entry_point();
                proto_end = dissector_get_netlink_exit_point();
                break;
+       case LINKTYPE_LINUX_SLL:
+       case ___constant_swab32(LINKTYPE_LINUX_SLL):
+               proto_start = dissector_get_sll_entry_point();
+               proto_end = dissector_get_sll_exit_point();
+               break;
        default:
                proto_start = &none_ops;
                proto_end = NULL;
@@ -120,6 +126,7 @@ void dissector_init_all(int fnttype)
        dissector_init_ethernet(fnttype);
        dissector_init_ieee80211(fnttype);
        dissector_init_netlink(fnttype);
+       dissector_init_sll(fnttype);
 }
 
 void dissector_cleanup_all(void)
@@ -127,4 +134,5 @@ void dissector_cleanup_all(void)
        dissector_cleanup_ethernet();
        dissector_cleanup_ieee80211();
        dissector_cleanup_netlink();
+       dissector_cleanup_sll();
 }
diff --git a/dissector.h b/dissector.h
index a99442e..5580110 100644
--- a/dissector.h
+++ b/dissector.h
@@ -105,7 +105,7 @@ static inline void show_frame_hdr(uint8_t *packet, size_t 
len, int linktype,
 
 extern void dissector_init_all(int fnttype);
 extern void dissector_entry_point(uint8_t *packet, size_t len, int linktype,
-                                 int mode, uint16_t proto);
+                                 int mode, struct sockaddr_ll *sll);
 extern void dissector_cleanup_all(void);
 extern int dissector_set_print_type(void *ptr, int type);
 
diff --git a/dissector_sll.c b/dissector_sll.c
new file mode 100644
index 0000000..ff42a61
--- /dev/null
+++ b/dissector_sll.c
@@ -0,0 +1,114 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Subject to the GPL, version 2.
+ */
+
+#include "oui.h"
+#include "protos.h"
+#include "pcap_io.h"
+#include "pkt_buff.h"
+#include "dissector.h"
+#include "dissector_sll.h"
+#include "dissector_eth.h"
+
+static char *pkt_type2str(uint8_t pkttype)
+{
+       switch (pkttype) {
+       case PACKET_HOST:
+               return "host";
+       case PACKET_BROADCAST:
+               return "broadcast";
+       case PACKET_MULTICAST:
+               return "multicast";
+       case PACKET_OTHERHOST:
+               return "other host";
+       case PACKET_OUTGOING:
+               return "outgoing";
+       case PACKET_USER:
+               return "user";
+       case PACKET_KERNEL:
+               return "kernel";
+       }
+
+       return "Unknown";
+}
+
+static void sll_print_full(struct pkt_buff *pkt)
+{
+       struct sockaddr_ll *sll = pkt->sll;
+       char addr_str[40] = {};
+
+       if (!pkt || !sll)
+               return;
+
+       tprintf(" [ Linux \"cooked\"");
+       tprintf(" Pkt Type %d (%s)", sll->sll_pkttype,
+                       pkt_type2str(sll->sll_pkttype));
+       tprintf(", If Type %d (%s)", sll->sll_hatype,
+                       device_type2str(sll->sll_hatype));
+       tprintf(", Addr Len %d", sll->sll_halen);
+       tprintf(", Src (%s)", device_addr2str(sll->sll_addr, sll->sll_halen,
+                       sll->sll_hatype, addr_str, sizeof(addr_str)));
+       tprintf(", Proto 0x%x", ntohs(sll->sll_protocol));
+
+       tprintf(" ]\n");
+
+       switch (pcap_devtype_to_linktype(sll->sll_hatype)) {
+       case LINKTYPE_EN10MB:
+       case ___constant_swab32(LINKTYPE_EN10MB):
+               pkt_set_dissector(pkt, &eth_lay2, ntohs(sll->sll_protocol));
+               break;
+       case LINKTYPE_NETLINK:
+       case ___constant_swab32(LINKTYPE_NETLINK):
+               pkt->dissector = &nlmsg_ops;
+               break;
+       default:
+               tprintf(" [ Uknown protocol ]\n");
+       }
+}
+
+static void sll_print_less(struct pkt_buff *pkt)
+{
+       struct sockaddr_ll *sll = pkt->sll;
+       char addr_str[40] = {};
+
+       if (!pkt || !sll)
+               return;
+
+       tprintf(" Pkt Type %d (%s)", sll->sll_pkttype,
+                       pkt_type2str(sll->sll_pkttype));
+       tprintf(", If Type %d (%s)", sll->sll_hatype,
+                       device_type2str(sll->sll_hatype));
+       tprintf(", Addr Len %d", sll->sll_halen);
+       tprintf(", Src (%s)", device_addr2str(sll->sll_addr, sll->sll_halen,
+                       sll->sll_hatype, addr_str, sizeof(addr_str)));
+       tprintf(", Proto 0x%x", ntohs(sll->sll_protocol));
+}
+
+struct protocol sll_ops = {
+       .key = 0,
+       .print_full = sll_print_full,
+       .print_less = sll_print_less,
+};
+
+struct protocol *dissector_get_sll_entry_point(void)
+{
+       return &sll_ops;
+}
+
+struct protocol *dissector_get_sll_exit_point(void)
+{
+       return &none_ops;
+}
+
+void dissector_init_sll(int fnttype)
+{
+       dissector_set_print_type(&sll_ops, fnttype);
+       dissector_set_print_type(&none_ops, fnttype);
+       dissector_init_oui();
+}
+
+void dissector_cleanup_sll(void)
+{
+       dissector_cleanup_oui();
+}
diff --git a/dissector_sll.h b/dissector_sll.h
new file mode 100644
index 0000000..2067942
--- /dev/null
+++ b/dissector_sll.h
@@ -0,0 +1,18 @@
+/*
+ * netsniff-ng - the packet sniffing beast
+ * Subject to the GPL, version 2.
+ */
+
+#ifndef DISSECTOR_SLL_H
+#define DISSECTOR_SLL_H
+
+#include "hash.h"
+#include "proto.h"
+
+extern void dissector_init_sll(int fnttype);
+extern void dissector_cleanup_sll(void);
+
+extern struct protocol *dissector_get_sll_entry_point(void);
+extern struct protocol *dissector_get_sll_exit_point(void);
+
+#endif /* DISSECTOR_SLL_H */
diff --git a/linktype.h b/linktype.h
index a78f8be..80b5f2d 100644
--- a/linktype.h
+++ b/linktype.h
@@ -21,6 +21,7 @@
 #define LINKTYPE_C_HDLC                                104
 #define LINKTYPE_IEEE802_11                    105
 #define LINKTYPE_FRELAY                                107
+#define LINKTYPE_LINUX_SLL                     113
 #define LINKTYPE_ECONET                                115
 #define LINKTYPE_IEEE802_11_RADIOTAP           127
 #define LINKTYPE_ARCNET_LINUX                  129
diff --git a/netsniff-ng.8 b/netsniff-ng.8
index 677a78c..f71eb92 100644
--- a/netsniff-ng.8
+++ b/netsniff-ng.8
@@ -69,7 +69,9 @@ netsniff-ng can also be used to debug netlink traffic.
 Defines an input device. This can either be a networking device, a pcap file
 or stdin (\[lq]\-\[rq]). In case of a pcap file, the pcap type (\[lq]\-D\[rq]
 option) is determined automatically by the pcap file magic. In case of stdin,
-it is assumed that the input stream is a pcap file.
+it is assumed that the input stream is a pcap file. If the pcap link type is a
+Netlink and pcap type is default or nsec then each packet should be wrapped
+with pcap cooked header [2].
 .PP
 .SS -o <dev|pcap|dir|cfg|->, --out <dev|pcap|dir|cfg|->
 Defines the output device. This can either be a networking device, a pcap file,
@@ -84,7 +86,10 @@ input device is a pcap file. To specify a  pcap file as the 
output device, the
 file name must have \[lq].pcap\[rq] as its extension. If stdout is given as a
 device, then a trafgen configuration will be written to stdout if the input
 device is a pcap file, or a pcap file if the input device is a networking
-device.
+device. In case if the input device is a Netlink monitor device and pcap type
+is default or nsec then each packet will be wrapped with pcap cooked header [2]
+to keep Netlink family number (Kuznetzov's and netsniff-ng pcap types already
+contains family number as protocol number).
 .PP
 .SS -C <id>, --fanout-group <id>
 If multiple netsniff-ng instances are being started that all have the same 
packet
@@ -254,6 +259,11 @@ possible addresses. Thus, to save bandwidth or for 
mirroring of Maxmind's
 databases (to bypass their traffic limit policy), different hosts or IP
 addresses can be placed into geoip.conf, separated by a newline.
 .PP
+.SS -w, --cooked
+Replace each frame link header with Linux "cooked" header [3] which keeps info
+about link type and protocol. It allows to dump and dissect frames captured
+from different link types when -i "any" was specified.
+.PP
 .SS -V, --verbose
 Be more verbose during startup i.e. show detailed ring setup information.
 .PP
@@ -588,6 +598,8 @@ in the payload itself as reported here. However, the 
filtering for VLANs works
 reliable if your NIC supports it. See bpfc(8) for an example.
 .PP
    [1] http://lkml.indiana.edu/hypermail/linux/kernel/0710.3/3816.html
+   [2] http://www.tcpdump.org/linktypes/LINKTYPE_NETLINK.html
+   [3] http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
 .PP
 .SH LEGAL
 netsniff-ng is licensed under the GNU GPL version 2.0.
diff --git a/netsniff-ng.c b/netsniff-ng.c
index d3e9a13..0a6efc5 100644
--- a/netsniff-ng.c
+++ b/netsniff-ng.c
@@ -69,7 +69,8 @@ struct ctx {
 static volatile sig_atomic_t sigint = 0, sighup = 0;
 static volatile bool next_dump = false;
 
-static const char *short_options = 
"d:i:o:rf:MNJt:S:k:n:b:HQmcsqXlvhF:RGAP:Vu:g:T:DBUC:K:L:";
+static const char *short_options =
+       "d:i:o:rf:MNJt:S:k:n:b:HQmcsqXlvhF:RGAP:Vu:g:T:DBUC:K:L:w";
 static const struct option long_options[] = {
        {"dev",                 required_argument,      NULL, 'd'},
        {"in",                  required_argument,      NULL, 'i'},
@@ -106,6 +107,7 @@ static const struct option long_options[] = {
        {"ascii",               no_argument,            NULL, 'l'},
        {"no-sock-mem",         no_argument,            NULL, 'A'},
        {"update",              no_argument,            NULL, 'U'},
+       {"cooked",              no_argument,            NULL, 'w'},
        {"verbose",             no_argument,            NULL, 'V'},
        {"version",             no_argument,            NULL, 'v'},
        {"help",                no_argument,            NULL, 'h'},
@@ -237,6 +239,8 @@ static void pcap_to_xmit(struct ctx *ctx)
        if (ret)
                panic("Error reading pcap header!\n");
 
+       pcap_pkthdr_init(&phdr, ctx->link_type);
+
        if (__pcap_io->prepare_access_pcap) {
                ret = __pcap_io->prepare_access_pcap(fd, PCAP_MODE_RD, 
ctx->jumbo);
                if (ret)
@@ -316,7 +320,7 @@ static void pcap_to_xmit(struct ctx *ctx)
 
                        dissector_entry_point(out, hdr->tp_h.tp_snaplen,
                                              ctx->link_type, ctx->print_mode,
-                                             hdr->s_ll.sll_protocol);
+                                             &hdr->s_ll);
 
                        kernel_may_pull_from_tx(&hdr->tp_h);
 
@@ -467,7 +471,7 @@ static void receive_to_xmit(struct ctx *ctx)
 
                        dissector_entry_point(in, hdr_in->tp_h.tp_snaplen,
                                              ctx->link_type, ctx->print_mode,
-                                             hdr_in->s_ll.sll_protocol);
+                                             &hdr_in->s_ll);
 
                        if (frame_count_max != 0) {
                                if (frame_count >= frame_count_max) {
@@ -589,6 +593,8 @@ static void read_pcap(struct ctx *ctx)
        if (ret)
                panic("Error reading pcap header!\n");
 
+       pcap_pkthdr_init(&phdr, ctx->link_type);
+
        if (__pcap_io->prepare_access_pcap) {
                ret = __pcap_io->prepare_access_pcap(fd, PCAP_MODE_RD, 
ctx->jumbo);
                if (ret)
@@ -660,7 +666,7 @@ static void read_pcap(struct ctx *ctx)
 
                dissector_entry_point(out, fm.tp_h.tp_snaplen,
                                      ctx->link_type, ctx->print_mode,
-                                     fm.s_ll.sll_protocol);
+                                     &fm.s_ll);
 
                if (is_out_pcap) {
                        size_t pcap_len = pcap_get_length(&phdr, ctx->magic);
@@ -906,6 +912,8 @@ static void walk_t3_block(struct block_desc *pbd, struct 
ctx *ctx,
                uint8_t *packet = ((uint8_t *) hdr + hdr->tp_mac);
                pcap_pkthdr_t phdr;
 
+               pcap_pkthdr_init(&phdr, ctx->link_type);
+
                if (ctx->packet_type != -1)
                        if (ctx->packet_type != sll->sll_pkttype)
                                goto next;
@@ -927,7 +935,7 @@ static void walk_t3_block(struct block_desc *pbd, struct 
ctx *ctx,
                                 hdr, ctx->print_mode, true, *frame_count);
 
                dissector_entry_point(packet, hdr->tp_snaplen, ctx->link_type,
-                                     ctx->print_mode, sll->sll_protocol);
+                                     ctx->print_mode, sll);
 next:
                 hdr = (void *) ((uint8_t *) hdr + hdr->tp_next_offset);
                sll = (void *) ((uint8_t *) hdr + TPACKET_ALIGN(sizeof(*hdr)));
@@ -956,7 +964,10 @@ static void recv_only_or_dump(struct ctx *ctx)
        struct timeval start, end, diff;
        unsigned long frame_count = 0;
 
-       sock = pf_socket();
+       if (ctx->link_type == LINKTYPE_LINUX_SLL)
+               sock = pf_socket_dgram();
+       else
+               sock = pf_socket();
 
        ifindex = device_ifindex(ctx->device_in);
 
@@ -1036,6 +1047,8 @@ static void recv_only_or_dump(struct ctx *ctx)
                        uint8_t *packet = ((uint8_t *) hdr) + hdr->tp_h.tp_mac;
                        pcap_pkthdr_t phdr;
 
+                       pcap_pkthdr_init(&phdr, ctx->link_type);
+
                        if (ctx->packet_type != -1)
                                if (ctx->packet_type != hdr->s_ll.sll_pkttype)
                                        goto next;
@@ -1063,7 +1076,7 @@ static void recv_only_or_dump(struct ctx *ctx)
 
                        dissector_entry_point(packet, hdr->tp_h.tp_snaplen,
                                              ctx->link_type, ctx->print_mode,
-                                             hdr->s_ll.sll_protocol);
+                                             &hdr->s_ll);
 
                        if (frame_count_max != 0) {
                                if (unlikely(frame_count >= frame_count_max)) {
@@ -1138,6 +1151,7 @@ static void init_ctx(struct ctx *ctx)
        ctx->magic = ORIGINAL_TCPDUMP_MAGIC;
        ctx->print_mode = PRINT_NORM;
        ctx->pcap = PCAP_OPS_SG;
+       ctx->link_type = 0;
 
        ctx->dump_mode = DUMP_INTERVAL_TIME;
        ctx->dump_interval = 60;
@@ -1196,6 +1210,7 @@ static void __noreturn help(void)
             "  -X|--hex                       Print packet data in hex 
format\n"
             "  -l|--ascii                     Print human-readable packet 
data\n"
             "  -U|--update                    Update GeoIP databases\n"
+            "  -w|--cooked                    Use Linux sll \"cooked\" header 
instead of link header\n"
             "  -V|--verbose                   Be more verbose\n"
             "  -v|--version                   Show version and exit\n"
             "  -h|--help                      Guess what?!\n\n"
@@ -1441,6 +1456,9 @@ int main(int argc, char **argv)
                        update_geoip();
                        die();
                        break;
+               case 'w':
+                       ctx.link_type = LINKTYPE_LINUX_SLL;
+                       break;
                case 'v':
                        version();
                        break;
@@ -1512,7 +1530,8 @@ int main(int argc, char **argv)
                if (ctx.rfraw)
                        setup_rfmon_mac80211_dev(&ctx, &ctx.device_in);
 
-               ctx.link_type = pcap_devtype_to_linktype(ctx.device_in);
+               if (!ctx.link_type)
+                       ctx.link_type = pcap_dev_to_linktype(ctx.device_in);
 
                if (!ctx.device_out) {
                        ctx.dump = 0;
diff --git a/netsniff-ng/Makefile b/netsniff-ng/Makefile
index 92990ff..33701a4 100644
--- a/netsniff-ng/Makefile
+++ b/netsniff-ng/Makefile
@@ -12,6 +12,7 @@ netsniff-ng-libs +=   -lGeoIP \
 endif
 
 netsniff-ng-objs =     dissector.o \
+                       dissector_sll.o \
                        dissector_eth.o \
                        dissector_80211.o \
                        dissector_netlink.o \
diff --git a/pcap_io.h b/pcap_io.h
index 35faa51..81f8ebd 100644
--- a/pcap_io.h
+++ b/pcap_io.h
@@ -90,12 +90,25 @@ struct pcap_pkthdr_bkm {
        uint8_t pkttype;
 };
 
-typedef union {
-       struct pcap_pkthdr      ppo;
-       struct pcap_pkthdr_ns   ppn;
-       struct pcap_pkthdr_kuz  ppk;
-       struct pcap_pkthdr_bkm  ppb;
-       uint8_t                 raw;
+/* DLT_LINUX_SLL - fake link-layer header */
+struct pcap_pkthdr_ll {
+       uint16_t pkttype;
+       uint16_t hatype;
+       uint16_t len;
+       uint8_t addr[8];
+       uint16_t protocol;
+};
+
+typedef struct {
+       union {
+               struct pcap_pkthdr      ppo;
+               struct pcap_pkthdr_ns   ppn;
+               struct pcap_pkthdr_kuz  ppk;
+               struct pcap_pkthdr_bkm  ppb;
+               uint8_t                 raw;
+       } h;
+       struct pcap_pkthdr_ll ll_hdr;
+       uint32_t link_type;
 } pcap_pkthdr_t;
 
 enum pcap_type {
@@ -150,9 +163,9 @@ static inline uint16_t tp_to_pcap_tsource(uint32_t status)
                return 0;
 }
 
-static inline int pcap_devtype_to_linktype(const char *ifname)
+static inline int pcap_devtype_to_linktype(int dev_type)
 {
-       switch (device_type(ifname)) {
+       switch (dev_type) {
        case ARPHRD_TUNNEL:
        case ARPHRD_TUNNEL6:
        case ARPHRD_LOOPBACK:
@@ -192,6 +205,11 @@ static inline int pcap_devtype_to_linktype(const char 
*ifname)
        }
 }
 
+static inline int pcap_dev_to_linktype(const char *ifname)
+{
+       return pcap_devtype_to_linktype(device_type(ifname));
+}
+
 static inline void pcap_check_magic(uint32_t magic)
 {
        switch (magic) {
@@ -232,8 +250,8 @@ static inline u32 pcap_get_length(pcap_pkthdr_t *phdr, enum 
pcap_type type)
        switch (type) {
 #define CASE_RET_CAPLEN(what, member, swap) \
        case (what): \
-               return (swap ? ___constant_swab32(phdr->member.caplen) : \
-                       phdr->member.caplen)
+               return (swap ? ___constant_swab32(phdr->h.member.caplen) : \
+                       phdr->h.member.caplen)
 
        CASE_RET_CAPLEN(DEFAULT, ppo, 0);
        CASE_RET_CAPLEN(NSEC, ppn, 0);
@@ -255,7 +273,7 @@ static inline void pcap_set_length(pcap_pkthdr_t *phdr, 
enum pcap_type type, u32
        switch (type) {
 #define CASE_SET_CAPLEN(what, member, swap) \
        case (what): \
-               phdr->member.caplen = (swap ? ___constant_swab32(len) : len); \
+               phdr->h.member.caplen = (swap ? ___constant_swab32(len) : len); 
\
                break
 
        CASE_SET_CAPLEN(DEFAULT, ppo, 0);
@@ -278,7 +296,7 @@ static inline u32 pcap_get_hdr_length(pcap_pkthdr_t *phdr, 
enum pcap_type type)
        switch (type) {
 #define CASE_RET_HDRLEN(what, member) \
        case (what): \
-               return sizeof(phdr->member)
+               return sizeof(phdr->h.member)
 
        CASE_RET_HDRLEN(DEFAULT, ppo);
        CASE_RET_HDRLEN(NSEC, ppn);
@@ -300,8 +318,8 @@ static inline u32 pcap_get_total_length(pcap_pkthdr_t 
*phdr, enum pcap_type type
        switch (type) {
 #define CASE_RET_TOTLEN(what, member, swap) \
        case (what): \
-               return ((swap ? ___constant_swab32(phdr->member.caplen) : \
-                        phdr->member.caplen) + sizeof(phdr->member))
+               return ((swap ? ___constant_swab32(phdr->h.member.caplen) : \
+                        phdr->h.member.caplen) + sizeof(phdr->h.member))
 
        CASE_RET_TOTLEN(DEFAULT, ppo, 0);
        CASE_RET_TOTLEN(NSEC, ppn, 0);
@@ -318,6 +336,103 @@ static inline u32 pcap_get_total_length(pcap_pkthdr_t 
*phdr, enum pcap_type type
        }
 }
 
+static inline void pcap_pkthdr_add_hdr(pcap_pkthdr_t *phdr,
+               enum pcap_type type, uint32_t len)
+{
+       switch (type) {
+#define CASE_ADD_TOTLEN(what, member, swap) \
+       case (what): \
+               phdr->h.member.caplen += swap ? ___constant_swab32(len) : len; \
+               phdr->h.member.len += swap ? ___constant_swab32(len) : len; \
+               break;
+
+       CASE_ADD_TOTLEN(DEFAULT, ppo, 0);
+       CASE_ADD_TOTLEN(NSEC, ppn, 0);
+       CASE_ADD_TOTLEN(KUZNETZOV, ppk, 0);
+       CASE_ADD_TOTLEN(BORKMANN, ppb, 0);
+
+       CASE_ADD_TOTLEN(DEFAULT_SWAPPED, ppo, 1);
+       CASE_ADD_TOTLEN(NSEC_SWAPPED, ppn, 1);
+       CASE_ADD_TOTLEN(KUZNETZOV_SWAPPED, ppk, 1);
+       CASE_ADD_TOTLEN(BORKMANN_SWAPPED, ppb, 1);
+
+       default:
+               bug();
+       }
+}
+
+static inline void pcap_pkthdr_sub_hdr(pcap_pkthdr_t *phdr,
+               enum pcap_type type, uint32_t len)
+{
+       switch (type) {
+#define CASE_SUB_TOTLEN(what, member, swap) \
+       case (what): \
+               phdr->h.member.caplen -= swap ? ___constant_swab32(len) : len; \
+               phdr->h.member.len -= swap ? ___constant_swab32(len) : len; \
+               break;
+
+       CASE_SUB_TOTLEN(DEFAULT, ppo, 0);
+       CASE_SUB_TOTLEN(NSEC, ppn, 0);
+       CASE_SUB_TOTLEN(KUZNETZOV, ppk, 0);
+       CASE_SUB_TOTLEN(BORKMANN, ppb, 0);
+
+       CASE_SUB_TOTLEN(DEFAULT_SWAPPED, ppo, 1);
+       CASE_SUB_TOTLEN(NSEC_SWAPPED, ppn, 1);
+       CASE_SUB_TOTLEN(KUZNETZOV_SWAPPED, ppk, 1);
+       CASE_SUB_TOTLEN(BORKMANN_SWAPPED, ppb, 1);
+
+       default:
+               bug();
+       }
+}
+
+static inline void sockaddr_to_pcap_ll(struct pcap_pkthdr_ll *pcap_ll,
+               struct sockaddr_ll *sll, enum pcap_type type)
+{
+       pcap_ll->protocol       = sll->sll_protocol;
+       pcap_ll->hatype         = cpu_to_be16(sll->sll_hatype);
+       pcap_ll->pkttype        = cpu_to_be16(sll->sll_pkttype);
+       pcap_ll->len            = cpu_to_be16(sll->sll_halen);
+       memcpy(pcap_ll->addr, sll->sll_addr, 8);
+}
+
+static inline void pcap_pkthdr_add_ll_hdr(pcap_pkthdr_t *phdr,
+               enum pcap_type type, struct sockaddr_ll *sll)
+{
+       pcap_pkthdr_add_hdr(phdr, type, sizeof(struct pcap_pkthdr_ll));
+       sockaddr_to_pcap_ll(&phdr->ll_hdr, sll, type);
+}
+
+static inline bool pcap_pkthdr_has_ll(pcap_pkthdr_t *phdr, enum pcap_type type)
+{
+       bool is_swapped = false;
+       int link_type;
+
+       switch (type) {
+       case DEFAULT_SWAPPED:
+       case NSEC_SWAPPED:
+               is_swapped = true;
+       case DEFAULT:
+       case NSEC:
+               if (is_swapped)
+                       link_type = ___constant_swab32(phdr->link_type);
+               else
+                       link_type = phdr->link_type;
+
+               if (link_type == LINKTYPE_NETLINK || link_type ==
+                               LINKTYPE_LINUX_SLL)
+                       return true;
+
+               return false;
+       case BORKMANN_SWAPPED:
+       case KUZNETZOV_SWAPPED:
+       case KUZNETZOV:
+       case BORKMANN:
+       default:
+               return false;
+       }
+}
+
 static inline void
 __tpacket_hdr_to_pcap_pkthdr(uint32_t sec, uint32_t nsec, uint32_t snaplen,
                             uint32_t len, uint32_t status,
@@ -326,80 +441,83 @@ __tpacket_hdr_to_pcap_pkthdr(uint32_t sec, uint32_t nsec, 
uint32_t snaplen,
 {
        switch (type) {
        case DEFAULT:
-               phdr->ppo.ts.tv_sec = sec;
-               phdr->ppo.ts.tv_usec = nsec / 1000;
-               phdr->ppo.caplen = snaplen;
-               phdr->ppo.len = len;
+               phdr->h.ppo.ts.tv_sec = sec;
+               phdr->h.ppo.ts.tv_usec = nsec / 1000;
+               phdr->h.ppo.caplen = snaplen;
+               phdr->h.ppo.len = len;
                break;
 
        case DEFAULT_SWAPPED:
-               phdr->ppo.ts.tv_sec = ___constant_swab32(sec);
-               phdr->ppo.ts.tv_usec = ___constant_swab32(nsec / 1000);
-               phdr->ppo.caplen = ___constant_swab32(snaplen);
-               phdr->ppo.len = ___constant_swab32(len);
+               phdr->h.ppo.ts.tv_sec = ___constant_swab32(sec);
+               phdr->h.ppo.ts.tv_usec = ___constant_swab32(nsec / 1000);
+               phdr->h.ppo.caplen = ___constant_swab32(snaplen);
+               phdr->h.ppo.len = ___constant_swab32(len);
                break;
 
        case NSEC:
-               phdr->ppn.ts.tv_sec = sec;
-               phdr->ppn.ts.tv_nsec = nsec;
-               phdr->ppn.caplen = snaplen;
-               phdr->ppn.len = len;
+               phdr->h.ppn.ts.tv_sec = sec;
+               phdr->h.ppn.ts.tv_nsec = nsec;
+               phdr->h.ppn.caplen = snaplen;
+               phdr->h.ppn.len = len;
                break;
 
        case NSEC_SWAPPED:
-               phdr->ppn.ts.tv_sec = ___constant_swab32(sec);
-               phdr->ppn.ts.tv_nsec = ___constant_swab32(nsec);
-               phdr->ppn.caplen = ___constant_swab32(snaplen);
-               phdr->ppn.len = ___constant_swab32(len);
+               phdr->h.ppn.ts.tv_sec = ___constant_swab32(sec);
+               phdr->h.ppn.ts.tv_nsec = ___constant_swab32(nsec);
+               phdr->h.ppn.caplen = ___constant_swab32(snaplen);
+               phdr->h.ppn.len = ___constant_swab32(len);
                break;
 
        case KUZNETZOV:
-               phdr->ppk.ts.tv_sec = sec;
-               phdr->ppk.ts.tv_usec = nsec / 1000;
-               phdr->ppk.caplen = snaplen;
-               phdr->ppk.len = len;
-               phdr->ppk.ifindex = sll->sll_ifindex;
-               phdr->ppk.protocol = sll->sll_protocol;
-               phdr->ppk.pkttype = sll->sll_pkttype;
+               phdr->h.ppk.ts.tv_sec = sec;
+               phdr->h.ppk.ts.tv_usec = nsec / 1000;
+               phdr->h.ppk.caplen = snaplen;
+               phdr->h.ppk.len = len;
+               phdr->h.ppk.ifindex = sll->sll_ifindex;
+               phdr->h.ppk.protocol = sll->sll_protocol;
+               phdr->h.ppk.pkttype = sll->sll_pkttype;
                break;
 
        case KUZNETZOV_SWAPPED:
-               phdr->ppk.ts.tv_sec = ___constant_swab32(sec);
-               phdr->ppk.ts.tv_usec = ___constant_swab32(nsec / 1000);
-               phdr->ppk.caplen = ___constant_swab32(snaplen);
-               phdr->ppk.len = ___constant_swab32(len);
-               phdr->ppk.ifindex = ___constant_swab32(sll->sll_ifindex);
-               phdr->ppk.protocol = ___constant_swab16(sll->sll_protocol);
-               phdr->ppk.pkttype = sll->sll_pkttype;
+               phdr->h.ppk.ts.tv_sec = ___constant_swab32(sec);
+               phdr->h.ppk.ts.tv_usec = ___constant_swab32(nsec / 1000);
+               phdr->h.ppk.caplen = ___constant_swab32(snaplen);
+               phdr->h.ppk.len = ___constant_swab32(len);
+               phdr->h.ppk.ifindex = ___constant_swab32(sll->sll_ifindex);
+               phdr->h.ppk.protocol = ___constant_swab16(sll->sll_protocol);
+               phdr->h.ppk.pkttype = sll->sll_pkttype;
                break;
 
        case BORKMANN:
-               phdr->ppb.ts.tv_sec = sec;
-               phdr->ppb.ts.tv_nsec = nsec;
-               phdr->ppb.caplen = snaplen;
-               phdr->ppb.len = len;
-               phdr->ppb.tsource = tp_to_pcap_tsource(status);
-               phdr->ppb.ifindex = (u16) sll->sll_ifindex;
-               phdr->ppb.protocol = sll->sll_protocol;
-               phdr->ppb.hatype = sll->sll_hatype;
-               phdr->ppb.pkttype = sll->sll_pkttype;
+               phdr->h.ppb.ts.tv_sec = sec;
+               phdr->h.ppb.ts.tv_nsec = nsec;
+               phdr->h.ppb.caplen = snaplen;
+               phdr->h.ppb.len = len;
+               phdr->h.ppb.tsource = tp_to_pcap_tsource(status);
+               phdr->h.ppb.ifindex = (u16) sll->sll_ifindex;
+               phdr->h.ppb.protocol = sll->sll_protocol;
+               phdr->h.ppb.hatype = sll->sll_hatype;
+               phdr->h.ppb.pkttype = sll->sll_pkttype;
                break;
 
        case BORKMANN_SWAPPED:
-               phdr->ppb.ts.tv_sec = ___constant_swab32(sec);
-               phdr->ppb.ts.tv_nsec = ___constant_swab32(nsec);
-               phdr->ppb.caplen = ___constant_swab32(snaplen);
-               phdr->ppb.len = ___constant_swab32(len);
-               phdr->ppb.tsource = 
___constant_swab16(tp_to_pcap_tsource(status));
-               phdr->ppb.ifindex = ___constant_swab16((u16) sll->sll_ifindex);
-               phdr->ppb.protocol = ___constant_swab16(sll->sll_protocol);
-               phdr->ppb.hatype = sll->sll_hatype;
-               phdr->ppb.pkttype = sll->sll_pkttype;
+               phdr->h.ppb.ts.tv_sec = ___constant_swab32(sec);
+               phdr->h.ppb.ts.tv_nsec = ___constant_swab32(nsec);
+               phdr->h.ppb.caplen = ___constant_swab32(snaplen);
+               phdr->h.ppb.len = ___constant_swab32(len);
+               phdr->h.ppb.tsource = 
___constant_swab16(tp_to_pcap_tsource(status));
+               phdr->h.ppb.ifindex = ___constant_swab16((u16) 
sll->sll_ifindex);
+               phdr->h.ppb.protocol = ___constant_swab16(sll->sll_protocol);
+               phdr->h.ppb.hatype = sll->sll_hatype;
+               phdr->h.ppb.pkttype = sll->sll_pkttype;
                break;
 
        default:
                bug();
        }
+
+       if (pcap_pkthdr_has_ll(phdr, type))
+               pcap_pkthdr_add_ll_hdr(phdr, type, sll);
 }
 
 /* We need to do this crap here since member offsets are not interleaved,
@@ -428,6 +546,47 @@ static inline void tpacket3_hdr_to_pcap_pkthdr(struct 
tpacket3_hdr *thdr,
 }
 #endif
 
+static inline void pcap_pkthdr_to_sockaddr(pcap_pkthdr_t *phdr,
+               struct sockaddr_ll *sll, enum pcap_type type)
+{
+       switch (type) {
+       case DEFAULT:
+       case DEFAULT_SWAPPED:
+       case NSEC:
+       case NSEC_SWAPPED:
+               if (pcap_pkthdr_has_ll(phdr, type)) {
+                       sll->sll_protocol = phdr->ll_hdr.protocol;
+                       sll->sll_hatype   = be16_to_cpu(phdr->ll_hdr.hatype);
+                       sll->sll_pkttype  = be16_to_cpu(phdr->ll_hdr.pkttype);
+                       sll->sll_halen    = be16_to_cpu(phdr->ll_hdr.len);
+                       memcpy(sll->sll_addr, phdr->ll_hdr.addr, 8);
+               }
+               break;
+       case KUZNETZOV:
+               sll->sll_ifindex = phdr->h.ppk.ifindex;
+               sll->sll_protocol = phdr->h.ppk.protocol;
+               sll->sll_pkttype = phdr->h.ppk.pkttype;
+               break;
+       case KUZNETZOV_SWAPPED:
+               sll->sll_ifindex = ___constant_swab32(phdr->h.ppk.ifindex);
+               sll->sll_protocol = ___constant_swab16(phdr->h.ppk.protocol);
+               sll->sll_pkttype = phdr->h.ppk.pkttype;
+               break;
+       case BORKMANN:
+               sll->sll_ifindex = phdr->h.ppb.ifindex;
+               sll->sll_protocol = phdr->h.ppb.protocol;
+               sll->sll_hatype = phdr->h.ppb.hatype;
+               sll->sll_pkttype = phdr->h.ppb.pkttype;
+               break;
+       case BORKMANN_SWAPPED:
+               sll->sll_ifindex = ___constant_swab16(phdr->h.ppb.ifindex);
+               sll->sll_protocol = ___constant_swab16(phdr->h.ppb.protocol);
+               sll->sll_hatype = phdr->h.ppb.hatype;
+               sll->sll_pkttype = phdr->h.ppb.pkttype;
+               break;
+       }
+}
+
 static inline void pcap_pkthdr_to_tpacket_hdr(pcap_pkthdr_t *phdr,
                                              enum pcap_type type,
                                              struct tpacket2_hdr *thdr,
@@ -435,86 +594,84 @@ static inline void 
pcap_pkthdr_to_tpacket_hdr(pcap_pkthdr_t *phdr,
 {
        switch (type) {
        case DEFAULT:
-               thdr->tp_sec = phdr->ppo.ts.tv_sec;
-               thdr->tp_nsec = phdr->ppo.ts.tv_usec * 1000;
-               thdr->tp_snaplen = phdr->ppo.caplen;
-               thdr->tp_len = phdr->ppo.len;
+               if (pcap_pkthdr_has_ll(phdr, type))
+                       pcap_pkthdr_sub_hdr(phdr, type, sizeof(phdr->ll_hdr));
+
+               thdr->tp_sec = phdr->h.ppo.ts.tv_sec;
+               thdr->tp_nsec = phdr->h.ppo.ts.tv_usec * 1000;
+               thdr->tp_snaplen = phdr->h.ppo.caplen;
+               thdr->tp_len = phdr->h.ppo.len;
                break;
 
        case DEFAULT_SWAPPED:
-               thdr->tp_sec = ___constant_swab32(phdr->ppo.ts.tv_sec);
-               thdr->tp_nsec = ___constant_swab32(phdr->ppo.ts.tv_usec) * 1000;
-               thdr->tp_snaplen = ___constant_swab32(phdr->ppo.caplen);
-               thdr->tp_len = ___constant_swab32(phdr->ppo.len);
+               if (pcap_pkthdr_has_ll(phdr, type))
+                       pcap_pkthdr_sub_hdr(phdr, type, sizeof(phdr->ll_hdr));
+
+               thdr->tp_sec = ___constant_swab32(phdr->h.ppo.ts.tv_sec);
+               thdr->tp_nsec = ___constant_swab32(phdr->h.ppo.ts.tv_usec) * 
1000;
+               thdr->tp_snaplen = ___constant_swab32(phdr->h.ppo.caplen);
+               thdr->tp_len = ___constant_swab32(phdr->h.ppo.len);
                break;
 
        case NSEC:
-               thdr->tp_sec = phdr->ppn.ts.tv_sec;
-               thdr->tp_nsec = phdr->ppn.ts.tv_nsec;
-               thdr->tp_snaplen = phdr->ppn.caplen;
-               thdr->tp_len = phdr->ppn.len;
+               if (pcap_pkthdr_has_ll(phdr, type))
+                       pcap_pkthdr_sub_hdr(phdr, type, sizeof(phdr->ll_hdr));
+
+               thdr->tp_sec = phdr->h.ppn.ts.tv_sec;
+               thdr->tp_nsec = phdr->h.ppn.ts.tv_nsec;
+               thdr->tp_snaplen = phdr->h.ppn.caplen;
+               thdr->tp_len = phdr->h.ppn.len;
                break;
 
        case NSEC_SWAPPED:
-               thdr->tp_sec = ___constant_swab32(phdr->ppn.ts.tv_sec);
-               thdr->tp_nsec = ___constant_swab32(phdr->ppn.ts.tv_nsec);
-               thdr->tp_snaplen = ___constant_swab32(phdr->ppn.caplen);
-               thdr->tp_len = ___constant_swab32(phdr->ppn.len);
+               if (pcap_pkthdr_has_ll(phdr, type))
+                       pcap_pkthdr_sub_hdr(phdr, type, sizeof(phdr->ll_hdr));
+
+               thdr->tp_sec = ___constant_swab32(phdr->h.ppn.ts.tv_sec);
+               thdr->tp_nsec = ___constant_swab32(phdr->h.ppn.ts.tv_nsec);
+               thdr->tp_snaplen = ___constant_swab32(phdr->h.ppn.caplen);
+               thdr->tp_len = ___constant_swab32(phdr->h.ppn.len);
                break;
 
        case KUZNETZOV:
-               thdr->tp_sec = phdr->ppk.ts.tv_sec;
-               thdr->tp_nsec = phdr->ppk.ts.tv_usec * 1000;
-               thdr->tp_snaplen = phdr->ppk.caplen;
-               thdr->tp_len = phdr->ppk.len;
-               if (sll) {
-                       sll->sll_ifindex = phdr->ppk.ifindex;
-                       sll->sll_protocol = phdr->ppk.protocol;
-                       sll->sll_pkttype = phdr->ppk.pkttype;
-               }
+               thdr->tp_sec = phdr->h.ppk.ts.tv_sec;
+               thdr->tp_nsec = phdr->h.ppk.ts.tv_usec * 1000;
+               thdr->tp_snaplen = phdr->h.ppk.caplen;
+               thdr->tp_len = phdr->h.ppk.len;
                break;
 
        case KUZNETZOV_SWAPPED:
-               thdr->tp_sec = ___constant_swab32(phdr->ppk.ts.tv_sec);
-               thdr->tp_nsec = ___constant_swab32(phdr->ppk.ts.tv_usec) * 1000;
-               thdr->tp_snaplen = ___constant_swab32(phdr->ppk.caplen);
-               thdr->tp_len = ___constant_swab32(phdr->ppk.len);
-               if (sll) {
-                       sll->sll_ifindex = 
___constant_swab32(phdr->ppk.ifindex);
-                       sll->sll_protocol = 
___constant_swab16(phdr->ppk.protocol);
-                       sll->sll_pkttype = phdr->ppk.pkttype;
-               }
+               thdr->tp_sec = ___constant_swab32(phdr->h.ppk.ts.tv_sec);
+               thdr->tp_nsec = ___constant_swab32(phdr->h.ppk.ts.tv_usec) * 
1000;
+               thdr->tp_snaplen = ___constant_swab32(phdr->h.ppk.caplen);
+               thdr->tp_len = ___constant_swab32(phdr->h.ppk.len);
                break;
 
        case BORKMANN:
-               thdr->tp_sec = phdr->ppb.ts.tv_sec;
-               thdr->tp_nsec = phdr->ppb.ts.tv_nsec;
-               thdr->tp_snaplen = phdr->ppb.caplen;
-               thdr->tp_len = phdr->ppb.len;
-               if (sll) {
-                       sll->sll_ifindex = phdr->ppb.ifindex;
-                       sll->sll_protocol = phdr->ppb.protocol;
-                       sll->sll_hatype = phdr->ppb.hatype;
-                       sll->sll_pkttype = phdr->ppb.pkttype;
-               }
+               thdr->tp_sec = phdr->h.ppb.ts.tv_sec;
+               thdr->tp_nsec = phdr->h.ppb.ts.tv_nsec;
+               thdr->tp_snaplen = phdr->h.ppb.caplen;
+               thdr->tp_len = phdr->h.ppb.len;
                break;
 
        case BORKMANN_SWAPPED:
-               thdr->tp_sec = ___constant_swab32(phdr->ppb.ts.tv_sec);
-               thdr->tp_nsec = ___constant_swab32(phdr->ppb.ts.tv_nsec);
-               thdr->tp_snaplen = ___constant_swab32(phdr->ppb.caplen);
-               thdr->tp_len = ___constant_swab32(phdr->ppb.len);
-               if (sll) {
-                       sll->sll_ifindex = 
___constant_swab16(phdr->ppb.ifindex);
-                       sll->sll_protocol = 
___constant_swab16(phdr->ppb.protocol);
-                       sll->sll_hatype = phdr->ppb.hatype;
-                       sll->sll_pkttype = phdr->ppb.pkttype;
-               }
+               thdr->tp_sec = ___constant_swab32(phdr->h.ppb.ts.tv_sec);
+               thdr->tp_nsec = ___constant_swab32(phdr->h.ppb.ts.tv_nsec);
+               thdr->tp_snaplen = ___constant_swab32(phdr->h.ppb.caplen);
+               thdr->tp_len = ___constant_swab32(phdr->h.ppb.len);
                break;
 
        default:
                bug();
        }
+
+       if (sll)
+               pcap_pkthdr_to_sockaddr(phdr, sll, type);
+}
+
+static inline void pcap_pkthdr_init(pcap_pkthdr_t *phdr, uint32_t link_type)
+{
+       phdr->link_type = link_type;
 }
 
 #define FEATURE_UNKNOWN                (0 << 0)
@@ -659,6 +816,7 @@ static const bool pcap_supported_linktypes[LINKTYPE_MAX] 
__maybe_unused = {
        [LINKTYPE_IEEE802_15_4_LINUX] = true,
        [LINKTYPE_INFINIBAND] = true,
        [LINKTYPE_NETLINK] = true,
+       [LINKTYPE_LINUX_SLL] = true,
 };
 
 static inline void pcap_validate_header(const struct pcap_filehdr *hdr)
diff --git a/pcap_mm.c b/pcap_mm.c
index f7b248e..b4bbae4 100644
--- a/pcap_mm.c
+++ b/pcap_mm.c
@@ -56,8 +56,18 @@ static ssize_t pcap_mm_write(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
        if ((off_t) (ptr_va_curr - ptr_va_start) + hdrsize + len > map_size)
                __pcap_mmap_write_need_remap(fd);
 
-       fmemcpy(ptr_va_curr, &phdr->raw, hdrsize);
+       fmemcpy(ptr_va_curr, &phdr->h.raw, hdrsize);
        ptr_va_curr += hdrsize;
+
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               size_t ll_len = sizeof(struct pcap_pkthdr_ll);
+
+               fmemcpy(ptr_va_curr, &phdr->ll_hdr, ll_len);
+               ptr_va_curr     += ll_len;
+               len             -= ll_len;
+               hdrsize         += ll_len;
+       }
+
        fmemcpy(ptr_va_curr, packet, len);
        ptr_va_curr += len;
 
@@ -72,8 +82,22 @@ static ssize_t pcap_mm_read(int fd __maybe_unused, 
pcap_pkthdr_t *phdr,
        if (unlikely((off_t) (ptr_va_curr + hdrsize - ptr_va_start) > (off_t) 
map_size))
                return -EIO;
 
-       fmemcpy(&phdr->raw, ptr_va_curr, hdrsize);
+       fmemcpy(&phdr->h.raw, ptr_va_curr, hdrsize);
        ptr_va_curr += hdrsize;
+
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               size_t ll_len = sizeof(struct pcap_pkthdr_ll);
+
+               if (unlikely((off_t) (ptr_va_curr + ll_len - ptr_va_start) >
+                                       (off_t) map_size))
+                       return -EIO;
+
+               fmemcpy(&phdr->ll_hdr, ptr_va_curr, ll_len);
+               ptr_va_curr     += hdrsize;
+               hdrsize         += ll_len;
+               len             -= ll_len;
+       }
+
        hdrlen = pcap_get_length(phdr, type);
 
        if (unlikely((off_t) (ptr_va_curr + hdrlen - ptr_va_start) > (off_t) 
map_size))
diff --git a/pcap_rw.c b/pcap_rw.c
index b6be922..651cef7 100644
--- a/pcap_rw.c
+++ b/pcap_rw.c
@@ -21,12 +21,25 @@ static ssize_t pcap_rw_write(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
                             const uint8_t *packet, size_t len)
 {
        ssize_t ret, hdrsize = pcap_get_hdr_length(phdr, type), hdrlen = 0;
+       ssize_t ll_len = 0;
 
-       ret = write_or_die(fd, &phdr->raw, hdrsize);
+       ret = write_or_die(fd, &phdr->h.raw, hdrsize);
        if (unlikely(ret != hdrsize))
                panic("Failed to write pkt header!\n");
 
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               ll_len = sizeof(struct pcap_pkthdr_ll);
+               int ret_ll = write_or_die(fd, (uint8_t *)&phdr->ll_hdr, ll_len);
+
+               if (unlikely(ret_ll != sizeof(phdr->ll_hdr)))
+                       panic("Failed to write pkt header!\n");
+       }
+
        hdrlen = pcap_get_length(phdr, type);
+       hdrlen  -= ll_len;
+       len     -= ll_len;
+       hdrsize += ll_len;
+
        if (unlikely(hdrlen != (ssize_t) len))
                return -EINVAL;
 
@@ -41,12 +54,25 @@ static ssize_t pcap_rw_read(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
                            uint8_t *packet, size_t len)
 {
        ssize_t ret, hdrsize = pcap_get_hdr_length(phdr, type), hdrlen = 0;
+       ssize_t ll_len = 0;
 
-       ret = read_or_die(fd, &phdr->raw, hdrsize);
+       ret = read_or_die(fd, &phdr->h.raw, hdrsize);
        if (unlikely(ret != hdrsize))
                return -EIO;
 
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               ll_len = sizeof(struct pcap_pkthdr_ll);
+               int ret_ll = read(fd, &phdr->ll_hdr, ll_len);
+
+               if (unlikely(ret_ll != ll_len))
+                       return -EIO;
+       }
+
        hdrlen = pcap_get_length(phdr, type);
+       hdrlen  -= ll_len;
+       len     -= ll_len;
+       hdrsize += ll_len;
+
        if (unlikely(hdrlen == 0 || hdrlen > (ssize_t) len))
                 return -EINVAL;
 
diff --git a/pcap_sg.c b/pcap_sg.c
index 80c2c5d..fdfef9a 100644
--- a/pcap_sg.c
+++ b/pcap_sg.c
@@ -33,61 +33,40 @@ static ssize_t pcap_sg_write(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
                iov_slot = 0;
        }
 
-       fmemcpy(iov[iov_slot].iov_base, &phdr->raw, hdrsize);
+       fmemcpy(iov[iov_slot].iov_base, &phdr->h.raw, hdrsize);
        iov[iov_slot].iov_len = hdrsize;
 
-       fmemcpy(iov[iov_slot].iov_base + iov[iov_slot].iov_len, packet, len);
-       ret = (iov[iov_slot].iov_len += len);
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               ssize_t ll_hdrlen = sizeof(struct pcap_pkthdr_ll);
 
-       iov_slot++;
-       return ret;
-}
-
-static ssize_t __pcap_sg_inter_iov_hdr_read(int fd, pcap_pkthdr_t *phdr,
-                                           size_t hdrsize)
-{
-       int ret;
-       size_t offset = 0;
-       ssize_t remainder;
-
-       offset = iov[iov_slot].iov_len - iov_off_rd;
-       remainder = hdrsize - offset;
-       if (remainder < 0)
-               remainder = 0;
-
-       bug_on(offset + remainder != hdrsize);
-
-       fmemcpy(&phdr->raw, iov[iov_slot].iov_base + iov_off_rd, offset);
-       iov_off_rd = 0;
-       iov_slot++;
+               fmemcpy(iov[iov_slot].iov_base + iov[iov_slot].iov_len,
+                               &phdr->ll_hdr, ll_hdrlen);
 
-       if (iov_slot == array_size(iov)) {
-               iov_slot = 0;
-               ret = readv(fd, iov, array_size(iov));
-               if (unlikely(ret <= 0))
-                       return -EIO;
+               ret = (iov[iov_slot].iov_len += ll_hdrlen);
+               len -= ll_hdrlen;
        }
 
-       fmemcpy(&phdr->raw + offset, iov[iov_slot].iov_base + iov_off_rd, 
remainder);
-       iov_off_rd += remainder;
+       fmemcpy(iov[iov_slot].iov_base + iov[iov_slot].iov_len, packet, len);
+       ret = (iov[iov_slot].iov_len += len);
 
-       return hdrsize;
+       iov_slot++;
+       return ret;
 }
 
-static ssize_t __pcap_sg_inter_iov_data_read(int fd, uint8_t *packet, size_t 
hdrlen)
+static ssize_t __pcap_sg_inter_iov_read(int fd, uint8_t *buf, size_t len)
 {
        int ret;
        size_t offset = 0;
        ssize_t remainder;
 
        offset = iov[iov_slot].iov_len - iov_off_rd;
-       remainder = hdrlen - offset;
+       remainder = len - offset;
        if (remainder < 0)
                remainder = 0;
 
-       bug_on(offset + remainder != hdrlen);
+       bug_on(offset + remainder != len);
 
-       fmemcpy(packet, iov[iov_slot].iov_base + iov_off_rd, offset);
+       fmemcpy(buf, iov[iov_slot].iov_base + iov_off_rd, offset);
        iov_off_rd = 0;
        iov_slot++;
 
@@ -98,10 +77,10 @@ static ssize_t __pcap_sg_inter_iov_data_read(int fd, 
uint8_t *packet, size_t hdr
                        return -EIO;
        }
 
-       fmemcpy(packet + offset, iov[iov_slot].iov_base + iov_off_rd, 
remainder);
+       fmemcpy(buf + offset, iov[iov_slot].iov_base + iov_off_rd, remainder);
        iov_off_rd += remainder;
 
-       return hdrlen;
+       return len;
 }
 
 static ssize_t pcap_sg_read(int fd, pcap_pkthdr_t *phdr, enum pcap_type type,
@@ -109,17 +88,39 @@ static ssize_t pcap_sg_read(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
 {
        ssize_t ret = 0;
        size_t hdrsize = pcap_get_hdr_length(phdr, type), hdrlen;
+       ssize_t ll_len = 0;
 
        if (likely(iov[iov_slot].iov_len - iov_off_rd >= hdrsize)) {
-               fmemcpy(&phdr->raw, iov[iov_slot].iov_base + iov_off_rd, 
hdrsize);
+               fmemcpy(&phdr->h.raw, iov[iov_slot].iov_base + iov_off_rd, 
hdrsize);
                iov_off_rd += hdrsize;
        } else {
-               ret = __pcap_sg_inter_iov_hdr_read(fd, phdr, hdrsize);
+               ret = __pcap_sg_inter_iov_read(fd, (uint8_t *)phdr, hdrsize);
                if (unlikely(ret < 0))
                        return ret;
        }
 
+       if (pcap_pkthdr_has_ll(phdr, type)) {
+               ll_len = sizeof(phdr->ll_hdr);
+
+               if (likely(iov[iov_slot].iov_len - iov_off_rd >= ll_len)) {
+                       fmemcpy((uint8_t *)&phdr->ll_hdr, 
iov[iov_slot].iov_base +
+                                       iov_off_rd, ll_len);
+
+                       iov_off_rd += ll_len;
+               } else {
+                       ret = __pcap_sg_inter_iov_read(fd, (uint8_t *)phdr,
+                                       ll_len);
+
+                       if (unlikely(ret < 0))
+                               return ret;
+               }
+       }
+
        hdrlen = pcap_get_length(phdr, type);
+       len     -= ll_len;
+       hdrlen  -= ll_len;
+       hdrsize += ll_len;
+
        if (unlikely(hdrlen == 0 || hdrlen > len))
                return -EINVAL;
 
@@ -127,7 +128,7 @@ static ssize_t pcap_sg_read(int fd, pcap_pkthdr_t *phdr, 
enum pcap_type type,
                fmemcpy(packet, iov[iov_slot].iov_base + iov_off_rd, hdrlen);
                iov_off_rd += hdrlen;
        } else {
-               ret = __pcap_sg_inter_iov_data_read(fd, packet, hdrlen);
+               ret = __pcap_sg_inter_iov_read(fd, (uint8_t *)packet, hdrlen);
                if (unlikely(ret < 0))
                        return ret;
        }
diff --git a/pkt_buff.h b/pkt_buff.h
index 28872ef..30b999e 100644
--- a/pkt_buff.h
+++ b/pkt_buff.h
@@ -21,7 +21,7 @@ struct pkt_buff {
 
        struct protocol *dissector;
        uint32_t link_type;
-       uint16_t proto;
+       struct sockaddr_ll *sll;
 };
 
 static inline struct pkt_buff *pkt_alloc(uint8_t *packet, unsigned int len)
diff --git a/proto_nlmsg.c b/proto_nlmsg.c
index 50a0d3f..e7f60c8 100644
--- a/proto_nlmsg.c
+++ b/proto_nlmsg.c
@@ -15,6 +15,7 @@
 #include <linux/if_arp.h>
 #include <arpa/inet.h>
 
+#include "dev.h"
 #include "pkt_buff.h"
 #include "proto.h"
 #include "protos.h"
@@ -165,115 +166,6 @@ static const char *nlmsg_rtnl_type2str(uint16_t type)
        }
 }
 
-static char *if_type2str(uint16_t type)
-{
-       switch (type) {
-       case ARPHRD_ETHER: return "ether";
-       case ARPHRD_EETHER: return "eether";
-       case ARPHRD_AX25: return "ax25";
-       case ARPHRD_PRONET: return "pronet";
-       case ARPHRD_CHAOS: return "chaos";
-       case ARPHRD_IEEE802: return "ieee802";
-       case ARPHRD_ARCNET: return "arcnet";
-       case ARPHRD_APPLETLK: return "appletlk";
-       case ARPHRD_DLCI: return "dlci";
-       case ARPHRD_ATM: return "atm";
-       case ARPHRD_METRICOM: return "metricom";
-       case ARPHRD_IEEE1394: return "ieee1394";
-       case ARPHRD_INFINIBAND: return "infiniband";
-       case ARPHRD_SLIP: return "slip";
-       case ARPHRD_CSLIP: return "cslip";
-       case ARPHRD_SLIP6: return "slip6";
-       case ARPHRD_CSLIP6: return "cslip6";
-       case ARPHRD_RSRVD: return "RSRVD";
-       case ARPHRD_ADAPT: return "adapt";
-       case ARPHRD_ROSE: return "rose";
-       case ARPHRD_X25: return "x25";
-       case ARPHRD_HWX25: return "hwx25";
-       case ARPHRD_CAN: return "can";
-       case ARPHRD_PPP: return "ppp";
-       case ARPHRD_HDLC: return "hdlc";
-       case ARPHRD_LAPB: return "lapb";
-       case ARPHRD_DDCMP: return "ddcmp";
-       case ARPHRD_RAWHDLC: return "rawhdlc";
-       case ARPHRD_TUNNEL: return "tunnel";
-       case ARPHRD_TUNNEL6: return "tunnel6";
-       case ARPHRD_FRAD: return "frad";
-       case ARPHRD_SKIP: return "skip";
-       case ARPHRD_LOOPBACK: return "loopback";
-       case ARPHRD_LOCALTLK: return "localtlk";
-       case ARPHRD_FDDI: return "fddi";
-       case ARPHRD_BIF: return "bif";
-       case ARPHRD_SIT: return "sit";
-       case ARPHRD_IPDDP: return "ipddp";
-       case ARPHRD_IPGRE: return "ipgre";
-       case ARPHRD_PIMREG: return "pimreg";
-       case ARPHRD_HIPPI: return "hippi";
-       case ARPHRD_ASH: return "ash";
-       case ARPHRD_ECONET: return "econet";
-       case ARPHRD_IRDA: return "irda";
-       case ARPHRD_FCPP: return "fcpp";
-       case ARPHRD_FCAL: return "fcal";
-       case ARPHRD_FCPL: return "fcpl";
-       case ARPHRD_FCFABRIC: return "fcfb0";
-       case ARPHRD_FCFABRIC + 1: return "fcfb1";
-       case ARPHRD_FCFABRIC + 2: return "fcfb2";
-       case ARPHRD_FCFABRIC + 3: return "fcfb3";
-       case ARPHRD_FCFABRIC + 4: return "fcfb4";
-       case ARPHRD_FCFABRIC + 5: return "fcfb5";
-       case ARPHRD_FCFABRIC + 6: return "fcfb6";
-       case ARPHRD_FCFABRIC + 7: return "fcfb7";
-       case ARPHRD_FCFABRIC + 8: return "fcfb8";
-       case ARPHRD_FCFABRIC + 9: return "fcfb9";
-       case ARPHRD_FCFABRIC + 10: return "fcfb10";
-       case ARPHRD_FCFABRIC + 11: return "fcfb11";
-       case ARPHRD_FCFABRIC + 12: return "fcfb12";
-       case ARPHRD_IEEE802_TR: return "ieee802_tr";
-       case ARPHRD_IEEE80211: return "ieee80211";
-       case ARPHRD_IEEE80211_PRISM: return "ieee80211_prism";
-       case ARPHRD_IEEE80211_RADIOTAP: return "ieee80211_radiotap";
-       case ARPHRD_IEEE802154: return "ieee802154";
-       case ARPHRD_PHONET: return "phonet";
-       case ARPHRD_PHONET_PIPE: return "phonet_pipe";
-       case ARPHRD_CAIF: return "caif";
-       case ARPHRD_IP6GRE: return "ip6gre";
-       case ARPHRD_NETLINK: return "netlink";
-       case ARPHRD_NONE: return "none";
-       case ARPHRD_VOID: return "void";
-
-       default: return "Unknown";
-       }
-}
-
-/* Taken from iproute2 */
-static const char *ll_addr_n2a(const unsigned char *addr, int alen, int type,
-               char *buf, int blen)
-{
-       int i;
-       int l;
-
-       if (alen == 4 &&
-           (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == 
ARPHRD_IPGRE)) {
-               return inet_ntop(AF_INET, addr, buf, blen);
-       }
-       if (alen == 16 && type == ARPHRD_TUNNEL6) {
-               return inet_ntop(AF_INET6, addr, buf, blen);
-       }
-       l = 0;
-       for (i=0; i<alen; i++) {
-               if (i==0) {
-                       snprintf(buf+l, blen, "%02x", addr[i]);
-                       blen -= 2;
-                       l += 2;
-               } else {
-                       snprintf(buf+l, blen, ":%02x", addr[i]);
-                       blen -= 3;
-                       l += 3;
-               }
-       }
-       return buf;
-}
-
 static char *nlmsg_type2str(uint16_t proto, uint16_t type, char *buf, int len)
 {
        if (proto == NETLINK_ROUTE && type < RTM_MAX) {
@@ -336,7 +228,7 @@ static void rtnl_print_ifinfo(struct nlmsghdr *hdr)
                        colorize_start(bold), af_link, colorize_end());
        tprintf(", Type %d (%s%s%s)", ifi->ifi_type,
                        colorize_start(bold),
-                       if_type2str(ifi->ifi_type),
+                       device_type2str(ifi->ifi_type),
                        colorize_end());
        tprintf(", Index %d", ifi->ifi_index);
        tprintf(", Flags 0x%x (%s%s%s)", ifi->ifi_flags,
@@ -354,13 +246,13 @@ static void rtnl_print_ifinfo(struct nlmsghdr *hdr)
                switch (attr->rta_type) {
                case IFLA_ADDRESS:
                        attr_fmt(attr, "Address %s",
-                                       ll_addr_n2a(RTA_DATA(attr),
+                                       device_addr2str(RTA_DATA(attr),
                                                RTA_LEN(attr), ifi->ifi_type,
                                                if_addr, sizeof(if_addr)));
                        break;
                case IFLA_BROADCAST:
                        attr_fmt(attr, "Broadcast %s",
-                                       ll_addr_n2a(RTA_DATA(attr),
+                                       device_addr2str(RTA_DATA(attr),
                                                RTA_LEN(attr), ifi->ifi_type,
                                                if_addr, sizeof(if_addr)));
                        break;
@@ -747,7 +639,7 @@ static void nlmsg(struct pkt_buff *pkt)
        struct nlmsghdr *hdr = (struct nlmsghdr *) pkt_pull(pkt, sizeof(*hdr));
 
        while (hdr) {
-               nlmsg_print(ntohs(pkt->proto), hdr);
+               nlmsg_print(ntohs(pkt->sll->sll_protocol), hdr);
 
                if (!pkt_pull(pkt, NLMSG_PAYLOAD(hdr, 0)))
                        break;
@@ -762,7 +654,7 @@ static void nlmsg(struct pkt_buff *pkt)
 static void nlmsg_less(struct pkt_buff *pkt)
 {
        struct nlmsghdr *hdr = (struct nlmsghdr *) pkt_pull(pkt, sizeof(*hdr));
-       uint16_t family = ntohs(pkt->proto);
+       uint16_t family = ntohs(pkt->sll->sll_protocol);
        char type[32];
 
        if (hdr == NULL)
diff --git a/sock.c b/sock.c
index 7cfa4a0..26b78ee 100644
--- a/sock.c
+++ b/sock.c
@@ -33,6 +33,15 @@ int pf_socket(void)
        return sock;
 }
 
+int pf_socket_dgram(void)
+{
+       int sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (unlikely(sock < 0))
+               panic("Creation of PF socket failed: %s\n", strerror(errno));
+
+       return sock;
+}
+
 /* Avail in kernel >= 3.14
  * in commit d346a3fae3 (packet: introduce PACKET_QDISC_BYPASS socket option)
  */
diff --git a/sock.h b/sock.h
index 50f7102..e63cd32 100644
--- a/sock.h
+++ b/sock.h
@@ -3,6 +3,7 @@
 
 extern int af_socket(int af);
 extern int pf_socket(void);
+extern int pf_socket_dgram(void);
 extern void set_nonblocking(int fd);
 extern int set_nonblocking_sloppy(int fd);
 extern int set_reuseaddr(int fd);
-- 
2.4.2

-- 
You received this message because you are subscribed to the Google Groups 
"netsniff-ng" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to