DNS – Domain Protocol

RFC 1034 – Domain names – concepts and facilities, November 1987
RFC 1035 – Domain names – implementation and specification, November 1987
RFC 1886 – DNS Extensions to support IP version 6, December 1995
RFC 2136 – Dynamic Updates in the Domain Name System (DNS UPDATE), April 1997
RFC 2308 – Negative Caching of DNS Queries (DNS NCACHE), March 1998
RFC 2535 – Domain Name System Security Extensions, March 1999
RFC DRAFT – A New Scheme for the Compression of Domain Names, June 1999 (not used?)
RFC 2874 – DNS Extensions to Support IPv6 Address Aggregation and Renumbering, July 2000
RFC 3225 – Indicating Resolver Support of DNSSEC, December 2001
RFC 3775 – Legacy Resolver Compatibility for Delegation Signer (DS), May 2004
RFC 4033 – DNS Security Introduction and Requirements, March 2005
RFC 4034 – Resource Records for the DNS Security Extensions, March 2005
RFC 4035 – Protocol Modifications for the DNS Security Extensions, March 2005
RFC 5155 – DNS Security (DNSSEC) Hashed Authenticated Denial of Existence, March 2008
RFC 6840 – Clarifications and Implementation Notes for DNS Security (DNSSEC), February 2013
RFC 6891 – Extension Mechanisms for DNS (EDNS(0)), April 2013 (OPT RR)
RFC 6895 – Domain Name System (DNS) IANA Considerations, April 2013

DNS Message Header and Question Section Format

O’Reilly DNS & BIND: C Programming with the Resolver Library Routines

Compressed Data

|   64 32 16| 8  4  2  1|   64 32 16| 8  4  2  1|
| 8  4  2  1| 8  4  2  1| 8  4  2  1| 8  4  2  1|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1 1 |                OFFSET                   |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

The first two bits are ones. This allows a pointer to be distinguished from a label, since the label
must begin with two zero bits because labels are restricted to 63 octets or less.
00 0D B9 35 88 B4 00 1B  21 5C 22 01 08 00 45 00  ...5....!\"...E.
00 77 5D B5 00 00 3B 11  AC CA A0 55 C0 64 0A 29  .w]...;....U.d.)
0A 14 00 35 83 7B 00 63  CE A1 A2 20 81 80 00 01  ...5.{.c... .�..
00 04 00 00 00 00 06 67  6F 6F 67 6C 65 02 63 68  .......google.ch
00 00 01 00 01 C0 0C 00  01 00 01 00 00 00 CE 00  ................
04 AD C2 74 2F C0 0C 00  01 00 01 00 00 00 CE 00  ...t/...........
04 AD C2 74 37 C0 0C 00  01 00 01 00 00 00 CE 00  ...t7...........
04 AD C2 74 38 C0 0C 00  01 00 01 00 00 00 CE 00  ...t8...........
04 AD C2 74 3F                                    ...t?

Ethernet Header

00 0D B9 35 88 B4 00 1B  21 5C 22 01 08 00        ...5....!\"...
Ethernet
   |-Destination MAC                    00:0d:b9:35:88:b4
   |-Source MAC                         00:1b:21:5c:22:01
   |-Type                               IPv4            (0x0800)

IP Header

                                           45 00                E.
00 77 5D B5 00 00 3B 11  AC CA A0 55 C0 64 0A 29  .w]...;....U.d.)
0A 14
IPv4 Header
   |-IP Version                         4
   |-IP Header Length                   5 dwords or 20 bytes
   |-Differentiated Service             0x00
   |-IP Total Length                    119 bytes
   |-Identification                     0x5db5          (23989)
   |-Flags                              0x0000          (0)
      |-Don't Fragment Field            no set         
      |-More Fragment Field             no set         
   |-Fragment Offset                    0x0000          (0)
   |-TTL                                59
   |-Protocol                           UDP             (17)
   |-Checksum                           0xacca          (44234)
   |-Source IP                          160.85.192.100  (0x64c055a0)
   |-Destination IP                     10.41.10.20     (0x140a290a)

UDP Header

.     00 35 83 7B 00 63  CE A1                      .5.{.c..
UDPv4 Header
   |-Source Port                        DNS             (53)
   |-Destination Port                   unknow          (33659)
   |-UDP Length                         99 Bytes
   |-UDP Checksum                       0xcea1          (52897)

DNS Header

                               A2 20 81 80 00 01            . .�..
00 04 00 00 00 00                                 ......
DNS Header
   |-Identifier                         0xa220          (41504)
   |-Flags                              0x8180          (33152)
      |-Query / Response     (qr)       Response
      |-Operation Code       (opcode)   Query           (0x0000)
      |-Authoritative Answer (aa)       not set
      |-Truncation           (tc)       not set
      |-Recursion Desired    (rd)       set
      |-Recursion Available  (ra)       set
      |-Authentic Data       (ad)       not set
      |-Checking Disabled    (cd)       not set
      |-Response Code        (rcode)    No Error (0)
   |-Questions                          1               (0x0001)
   |-Answer RRs                         4               (0x0004)
   |-Authority RRs                      0               (0x0000)
   |-Additional RRs                     0               (0x0000)

Query

QNAME (n labels), QTYPE, QCLASS

                 len value             len value
len = zero        06 67  6F 6F 67 6C 65 02 63 68        .google.ch
00 00 01 00 01
   qtype qclass

Answer

NAME (n labels), TYPE, CLASS, TTL, RDLENGTH, RDATA

               link (16-bit)
   value       C0 0C 00  01 00 01 00 00 00 CE 00       ...........
04 AD C2 74 2F       type   class ttl         len ...t/

               C0 0C 00  01 00 01 00 00 00 CE 00       ...........
04 AD C2 74 37                                    ...t7

               C0 0C 00  01 00 01 00 00 00 CE 00       ...........
04 AD C2 74 38                                    ...t8

               C0 0C 00  01 00 01 00 00 00 CE 00       ...........
04 AD C2 74 3F                                    ...t?


cb f3 81 80 00 01 00 02 00 00 00 00 07 61 6e 64
72 6f 69 64 0a 77 65 61 74 68 65 72 70 72 6f 0a
6d 65 74 65 6f 67 72 6f 75 70 02 64 65 00 00 01
00 01 c0 0c 00 05 00 01 00 00 00 af 00 1d 0c 6c
62 77 65 61 74 68 65 72 70 72 6f 0a 6d 65 74 65
6f 67 72 6f 75 70 03 63 6f 6d 00 c0 3e 00 01 00
01 00 00 02 53 00 04 c2 35 00 aa

DNS Header:

cb f3 81 80 00 01 00 02 00 00 00 00

Query:

07 61 6e 64
72 6f 69 64 0a 77 65 61 74 68 65 72 70 72 6f 0a
6d 65 74 65 6f 67 72 6f 75 70 02 64 65 00 00 01
00 01

Answer:

c0 0c 00 05 00 01 00 00 00 af 00 1d 0c 6c
62 77 65 61 74 68 65 72 70 72 6f 0a 6d 65 74 65
6f 67 72 6f 75 70 03 63 6f 6d 00


c0 0c 00 05 00 01 00 00 00 af 00 1d 0c 6c ………….l
62 77 65 61 74 68 65 72 70 72 6f 0a 6d 65 74 65 bweatherpro.mete
6f 67 72 6f 75 70 03 63 6f 6d 00 c0 3e 00 01 00 ogroup.com..>…
01 00 00 02 53 00 04 c2 35 00 aa ….S…5..

c0 0c 00 05 00 01 00 00 00 af 00 1d 0c 6c ………….l
62 77 65 61 74 68 65 72 70 72 6f 0a 6d 65 74 65 bweatherpro.mete
6f 67 72 6f 75 70 03 63 6f 6d 00 c0 3e 00 01 00 ogroup.com..>…
01 00 00 02 53 00 04 c2 35 00 aa ….S…5..

cb f3 81 80 00 01 00 02 00 00 00 00 07 61 6e 64 ………….and
72 6f 69 64 0a 77 65 61 74 68 65 72 70 72 6f 0a roid.weatherpro.
6d 65 74 65 6f 67 72 6f 75 70 02 64 65 00 00 01 meteogroup.de…
00 01 c0 0c 00 05 00 01 00 00 00 af 00 1d 0c 6c ……………l
62 77 65 61 74 68 65 72 70 72 6f 0a 6d 65 74 65 bweatherpro.mete
6f 67 72 6f 75 70 03 63 6f 6d 00 c0 3e 00 01 00 ogroup.com..>…
01 00 00 02 53 00 04 c2 35 00 aa ….S…5..

Multiple Questions in the same Request-Packet

can a dns packet have (question section > 1)
Some thoughts on QDCOUNT
Requesting A and AAAA records in single DNS query

4.1.1. Header section format
The header contains the following fields:
                                    1  1  1  1  1  1
      0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                      ID                       |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    QDCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ANCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    NSCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    |                    ARCOUNT                    |
    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3.2.2. The CD Bit
   The CD bit exists in order to allow a security-aware resolver to
   disable signature validation in a security-aware name server's
   processing of a particular query

3.2.3. The AD Bit
   The name server side of a security-aware recursive name server MUST
   NOT set the AD bit in a response unless the name server considers all
   RRsets in the Answer and Authority sections of the response to be
   authentic.  The name server side SHOULD set the AD bit if and only if
   the resolver side considers all RRsets in the Answer section and any
   relevant negative response RRs in the Authority section to be
   authentic.
2. DNS Query/Response Headers


   The header for DNS queries and responses contains field/bits in the
   following diagram taken from [RFC2136]:

                                            1  1  1  1  1  1
              0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |                      ID                       |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |QR|   OpCode  |AA|TC|RD|RA| Z|AD|CD|   RCODE   |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |                QDCOUNT/ZOCOUNT                |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |                ANCOUNT/PRCOUNT                |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |                NSCOUNT/UPCOUNT                |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
             |                    ARCOUNT                    |
             +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

Wireshark

Filter

Capture Filter (tshark -f): BPF syntax
Display Filter (tshark -Y): Wireshark syntax

Wireshark Wiki: Capture Filters
Ask Wireshark: what is the difference between capture filter and display filter?
Ask Wireshark: Changing Display Filter to Capture Filter

TShark

Tshark column fields
Bug 10201 – col.Protocol missing from tshark 1.11.3 and 1.12.0-rc2

# tshark -i re0 -T fields -e frame.number -e ip.addr -e udp -e _ws.col.info
Capturing on 're0'
[...]
44      172.21.5.130,224.0.0.252        User Datagram Protocol, Src Port: 55317 (55317), Dst Port: 5355 (5355)
45      172.21.5.69,239.255.255.250     User Datagram Protocol, Src Port: 1900 (1900), Dst Port: 1900 (1900)

Video

Troubleshooting with Wireshark – Virtual Tour

ICMP Echo

opensource.apple.com: ping.c

/*
 * pinger --
 *	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
 * will be added on by the kernel.  The ID field is our UNIX process ID,
 * and the sequence number is an ascending integer.  The first TIMEVAL_LEN
 * bytes of the data portion are used to hold a UNIX "timeval" struct in
 * host byte-order, to compute the round-trip time.
 */
static void
pinger(void)
{
    [...]
    if ((options & F_TIME) || timing) {
        (void)gettimeofday(&now, NULL);

        if (options & F_TIME)
            icp->icmp_otime = htonl((now.tv_sec % (24*60*60)) * 1000 + now.tv_usec / 1000);
        if (timing)
            bcopy((void *)&now, (void *)&outpack[ICMP_MINLEN + phdr_len], sizeof(struct timeval));
    }
    [...]
}
typedef u_int32_t n_time;       /* ms since 00:00 GMT, byte rev */
#define icmp_otime      icmp_dun.id_ts.its_otime
#define icmp_rtime      icmp_dun.id_ts.its_rtime
#define icmp_ttime      icmp_dun.id_ts.its_ttime

struct icmp {
        u_char  icmp_type;              /* type of message, see below */
        u_char  icmp_code;              /* type sub code */
        u_short icmp_cksum;             /* ones complement cksum of struct */
        union {
                u_char ih_pptr;                 /* ICMP_PARAMPROB */
                struct in_addr ih_gwaddr;       /* ICMP_REDIRECT */
                struct ih_idseq {
                        n_short icd_id;
                        n_short icd_seq;
                } ih_idseq;
                int ih_void;

                /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
                struct ih_pmtu {
                        n_short ipm_void;
                        n_short ipm_nextmtu;
                } ih_pmtu;

                struct ih_rtradv {
                        u_char irt_num_addrs;
                        u_char irt_wpa;
                        u_int16_t irt_lifetime;
                } ih_rtradv;
        } icmp_hun;
        union {
                struct id_ts {                  /* ICMP Timestamp */
                        n_time its_otime;       /* Originate */
                        n_time its_rtime;       /* Receive */
                        n_time its_ttime;       /* Transmit */
                } id_ts;
                struct id_ip  {
                        struct ip idi_ip;
                        /* options and then 64 bits of data */
                } id_ip;
                struct icmp_ra_addr id_radv;
                u_int32_t id_mask;
                char    id_data[1];
        } icmp_dun;
};

Wireshark-bugs: For ICMP Time Response, In detail pane, Timestamp is incorrectly decoded for MS Windows

Reference (1):
http://tools.ietf.org/html/rfc778

“The timestamp values are in milliseconds from midnight
UT and are stored right-justified in the 32-bit fields shown
above. Ordinarily, all time calculations are performed
modulo-24 hours in milliseconds.”


/* Converts a little-endian byte order unsigned long to host byte order. */
uint32 LETOHL(uint32 ul);

/*
 * RFC 792 for basic ICMP.
 * RFC 1191 for ICMP_FRAG_NEEDED (with MTU of next hop).
 * RFC 1256 for router discovery messages.
 * RFC 2002 and 3012 for Mobile IP stuff.
 */
static void
dissect_icmp(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
{
    [...]

    /* Decode the second 4 bytes of the packet. */
    switch (icmp_type) {
    
    [...]
    
    case ICMP_ECHOREPLY:
    case ICMP_ECHO:
        
        [...]
        
        /* Interpret the first 8 bytes of the icmp data as a timestamp
         * But only if it does look like it's a timestamp.
         *
         * FIXME:
         *    Timestamps could be in different formats depending on the OS
         */
        ts.secs  = tvb_get_ntohl(tvb, 8);
        ts.nsecs = tvb_get_ntohl(tvb, 8 + 4);   /* Leave at microsec resolution for now */
        
        if (abs((guint32) (ts.secs - pinfo->fd->abs_ts.secs)) >=
            3600 * 24 || ts.nsecs >= 1000000) {
            /* Timestamp does not look right in BE, try LE representation */
            ts.secs  = tvb_get_letohl(tvb, 8);
            ts.nsecs = tvb_get_letohl(tvb, 8 + 4);  /* Leave at microsec resolution for now */
        }
        if (abs((guint32) (ts.secs - pinfo->fd->abs_ts.secs)) < 3600 * 24 && ts.nsecs < 1000000) {
            ts.nsecs *= 1000;   /* Convert to nanosec resolution */
            proto_tree_add_time(icmp_tree, hf_icmp_data_time,
                                tvb, 8, 8, &ts);
            nstime_delta(&time_relative, &pinfo->fd->abs_ts,
                         &ts);
            ti = proto_tree_add_time(icmp_tree,
                                     hf_icmp_data_time_relative,
                                     tvb, 8, 8,
                                     &time_relative);
            PROTO_ITEM_SET_GENERATED(ti);
            call_dissector(data_handle,
                       tvb_new_subset_remaining(tvb,
                                8 + 8),
                       pinfo, icmp_tree);
        } else {
            call_dissector(data_handle,
                       tvb_new_subset_remaining(tvb, 8),
                       pinfo, icmp_tree);
        }
        break;
        
        [...]
    }
    
    [...]
}

Packet Filter (PF) + ALTQ

Presentation

PF, The OpenBSD Packet Filter: Building The Network You Need, EuroBSDCon 2015, Stockholm, Sweden, October 1st 2015

Tutorials & HowTos

Pf Firewall “how to” – FreeBSD and OpenBSD ( pf.conf )
Paket Filter (PF) von OpenBSD und ALTQ
Getting AltQ working in pf.conf (limiting inbound Tor traffic)
PF Firewall Quick Guide
FreeBSD Tuning and Optimization – performance modifications for 1gig and 10gig networks

Statistics

pfstat
ALTQ statistics?

ntop (Official)
ntopng – High-Speed Web-based Traffic Analysis and Flow Collection (Official)
ntop (Wikipedia)
NTop
Network Monitoring Using Free Linux Tools
Unveiling Application Visibility in ntop and nProbe (both in NetFlow v9 and IPFIX)

FAQ

Table not found

Couldn't manipulate device /dev/pf: No such process
table <hacker> persist {
}

$ pfctl -n -f /etc/pf.conf       # Parse the configuration file, do not actually load rules
$ pfctl -T load -f /etc/pf.conf  # Load only the table definitions
$ pfctl -t hacker -T show        # Show the content of a table

RRDTool

RRDTool – tutorial and graph examples (OpenBSD with pf)
rrd-beginners

Network usage

Network Traffic Monitoring with RRDTool
Monitoring network traffic with iptraf and rrdtool
Monitorix Project by Jordi Sanfeliu
RRDtool with pfctl and spamd
Setting up traffic monitoring using rrdtool (and snmp)
Using SNMP and RRD to monitor your LEAF system

Alternatives

Alternatives to rrdtool?
Java RRD library
rrd4j, RRD4J 3.1 (released 2017-01-01)

BIND9 Reject DNS Root Queries

bind: blackhole for invalid recursive queries?
Disabling Root DNS Server queries on Redhat linux
Ubuntu server 12.04 bind9 dns query rejected

Using FreeBSD’s BPF device with C/C++

Socket Compiler Error

Compile Error in using /usr/include/net/if.h
compile problems on freebsd

SVNWEB

sys/pf
sbin/pfctl

[root@gateway ~]# pfctl -t hacker -T add 192.168.0.2 192.168.0.3 192.168.0.4
1 table created.
3/3 addresses added.

[root@gateway ~]# pfctl -f /etc/pf.conf

[root@gateway ~]# pfctl -t hacker -T show
   192.168.1.1

[root@gateway ~]# pfctl -t hacker -T add 192.168.0.2 192.168.0.3 192.168.0.4
3/3 addresses added.

[root@gateway ~]# pfctl -t hacker -T show
   192.168.0.2
   192.168.0.3
   192.168.0.4
   192.168.1.1
     DIOCRADDADDRS struct pfioc_table *io
	     Add one or	more addresses to a table.  On entry, pfrio_table con-
	     tains the table ID	and pfrio_buffer must point to an array	of
	     struct pfr_addr containing	at least pfrio_size elements to	add to
	     the table.	 pfrio_esize must be the size of struct	pfr_addr.  On
	     exit, pfrio_nadd contains the number of addresses effectively
	     added.

	     struct pfr_addr {
		     union {
			     struct in_addr   _pfra_ip4addr;
			     struct in6_addr  _pfra_ip6addr;
		     }		      pfra_u;
		     u_int8_t	      pfra_af;
		     u_int8_t	      pfra_net;
		     u_int8_t	      pfra_not;
		     u_int8_t	      pfra_fback;
	     };
	     #define pfra_ip4addr    pfra_u._pfra_ip4addr
	     #define pfra_ip6addr    pfra_u._pfra_ip6addr
/usr/include/sys/ioctl.h: ioctl                (dev, DIOCRADDADDRS, &io)
sbin/pfctl/pfctl_radix.c: pfr_add_addrs        (tbl=0xbfbfd198, addr=0x28826100, size=3, nadd=0xbfbfd16c, flags=0)
sbin/pfctl/pfctl_table.c: pfctl_table          (argc=3, argv=0xbfbfdc90, tname=0xbfbfddd3 "hacker", command=0x808831c "add", file=0x0, anchor=0xbfbfd808 "", opts=0)
sbin/pfctl/pfctl_table.c: pfctl_command_tables (argc=3, argv=0xbfbfdc90, tname=0xbfbfddd3 "hacker", command=0x808831c "add", file=0x0, anchor=0xbfbfd808 "", opts=0)
sbin/pfctl/pfctl.c:       main                 (argc=3, argv=0xbfbfdc90)


tbl:
$1 = (struct pfr_table *) 0xbfbfd198
$2 = { pfrt_anchor = '\0' 
       pfrt_name   = "hacker", 
       pfrt_flags  = 0,
       pfrt_fback  = 0 '\0'}

addr:
$3 = (struct pfr_addr *) 0x28826100
$4 = { pfra_u = { _pfra_ip4addr = { s_addr = 33597632 },
                  _pfra_ip6addr = { [...] }
                },
       pfra_af = 2 '\002',
       pfra_net = 32 ' ', 
       pfra_not = 0 '\0',
       pfra_fback = 0 '\0'}

pfctl_radix.c:418    pfr_buf_add          (b=0xbfbfd188, e=0xbfbfcfb0)
pfctl_parser.c:1704  append_addr_host     (b=0xbfbfd188, n=0x28814460, test=0, not=0)
pfctl_parser.c:1659  append_addr          (b=0xbfbfd188, s=0xbfbfddf9 "192.168.0.4", test=0)
pfctl_table.c:418    load_addr            (b=0xbfbfd188, argc=0, argv=0xbfbfdc9c, file=0x0, nonetwork=0)
pfctl_table.c:201    pfctl_table          (argc=3, argv=0xbfbfdc90, tname=0xbfbfddd3 "hacker", command=0x808831c "add", file=0x0, anchor=0xbfbfd808 "", opts=0)
pfctl_table.c:124    pfctl_command_tables (argc=3, argv=0xbfbfdc90, tname=0xbfbfddd3 "hacker", command=0x808831c "add", file=0x0, anchor=0xbfbfd808 "", opts=0)
pfctl.c:2328         main                 (argc=3, argv=0xbfbfdc90)
typedef char *          caddr_t;        /* core address */
typedef const char *    c_caddr_t;      /* core address, pointer to const */
/*
 * Internet address (a structure for historical reasons)
 */
struct in_addr {
	in_addr_t s_addr;
};

Essential Socket Functions

int
main(int argc, char *argv[])
{
    [...]
    while ((ch = getopt(argc, argv,
        "a:AdD:eqf:F:ghi:k:K:mnNOo::Pp:rRs:t:T:vx:z")) != -1) {
        switch (ch) {
        [...]
        case 't':
            tableopt = optarg;
            break;
        case 'T':
            tblcmdopt = pfctl_lookup_option(optarg, tblcmdopt_list);
            if (tblcmdopt == NULL) {
                warnx("Unknown table command '%s'", optarg);
                usage();
            }
            break;
        [...]
        }
    }
    [...]
    if (tblcmdopt != NULL) {
        error = pfctl_command_tables(argc, argv, tableopt,
            tblcmdopt, rulesopt, anchorname, opts);
        rulesopt = NULL;
    }
    [...]
}
enum {
    PFRB_TABLES = 1,
    PFRB_TSTATS,
    PFRB_ADDRS,
    PFRB_ASTATS,
    PFRB_IFACES,
    PFRB_TRANS,
    PFRB_MAX
};

struct pfr_buffer {
    int                 pfrb_type;      /* type of content, see enum above */
    int                 pfrb_size;      /* number of objects in buffer */
    int                 pfrb_msize;     /* maximum number of objects in buffer */
    void               *pfrb_caddr;     /* malloc'ated memory area */
};

/*  int            int            int              void *                */
   {pfrb_type = 3, pfrb_size = 0, pfrb_msize = 0,  pfrb_caddr = 0x0}
   {pfrb_type = 3, pfrb_size = 1, pfrb_msize = 64, pfrb_caddr = 0x28826100}
   {pfrb_type = 3, pfrb_size = 2, pfrb_msize = 64, pfrb_caddr = 0x28826100}


struct pfioc_table {
    struct pfr_table    pfrio_table;
    void               *pfrio_buffer;
    int                 pfrio_esize;
    int                 pfrio_size;
    int                 pfrio_size2;
    int                 pfrio_nadd;
    int                 pfrio_ndel;
    int                 pfrio_nchange;
    int                 pfrio_flags;
    u_int32_t           pfrio_ticket;
};

struct pfr_table {
    char                pfrt_anchor[MAXPATHLEN];
    char                pfrt_name[PF_TABLE_NAME_SIZE];
    u_int32_t           pfrt_flags;
    u_int8_t            pfrt_fback;
};

/*  char *              char *                       u_int32_t       u_int8_t          */
   {pfrt_anchor = '\0', pfrt_name = "hacker", '\0' , pfrt_flags = 0, pfrt_fback = 0 '\0'}

pfrt_flags:
#define PFR_TFLAG_PERSIST       0x00000001
#define PFR_TFLAG_CONST         0x00000002
#define PFR_TFLAG_ACTIVE        0x00000004
#define PFR_TFLAG_INACTIVE      0x00000008
#define PFR_TFLAG_REFERENCED    0x00000010
#define PFR_TFLAG_REFDANCHOR    0x00000020
#define PFR_TFLAG_USRMASK       0x00000003
#define PFR_TFLAG_SETMASK       0x0000003C
#define PFR_TFLAG_ALLMASK       0x0000003F

struct pfr_table.pfrt_fback:
struct pfr_addr.pfra_fback:
enum {
    PFR_FB_NONE,
    PFR_FB_MATCH,
    PFR_FB_ADDED,
    PFR_FB_DELETED,
    PFR_FB_CHANGED,
    PFR_FB_CLEARED,
    PFR_FB_DUPLICATE,
    PFR_FB_NOTMATCH,
    PFR_FB_CONFLICT,
    PFR_FB_MAX
};

struct pfr_addr {
    union {
        struct in_addr   _pfra_ip4addr;
        struct in6_addr  _pfra_ip6addr;
    }                pfra_u;
    u_int8_t         pfra_af;        /**< AF_INET or AF_INET6 */
    u_int8_t         pfra_net;
    u_int8_t         pfra_not;
    u_int8_t         pfra_fback;
};
#define pfra_ip4addr    pfra_u._pfra_ip4addr
#define pfra_ip6addr    pfra_u._pfra_ip6addr

/* union                                                           */
   { pfra_u = { _pfra_ip4addr = { s_addr = 33597632 },
                _pfra_ip6addr = {__u6_addr = { __u6_addr8  = { ... },
                                               __u6_addr16 = { ... },
                                               __u6_addr32 = { ... }
                                             }
                                }
              },
/*   u_int8_t      u_int8_t        u_int8_t       u_int8_t         */
     pfra_af = 2 , pfra_net = 32 , pfra_not = 0 , pfra_fback = 0   }

/***************************************************************/

#define v4      pfa.v4
#define v6      pfa.v6
#define addr8   pfa.addr8
#define addr16  pfa.addr16
#define addr32  pfa.addr32

struct pf_addr {
    union {
        struct in_addr          v4;
        struct in6_addr         v6;
        u_int8_t                addr8[16];
        u_int16_t               addr16[8];
        u_int32_t               addr32[4];
    }                           pfa;           /* 128-bit address */
};

struct pf_addr_wrap {
    union {
        struct {
            struct pf_addr      addr;
            struct pf_addr      mask;
        }                       a;
        char                    ifname[IFNAMSIZ];
        char                    tblname[PF_TABLE_NAME_SIZE];
    }                           v;
    union {
        struct pfi_dynaddr     *dyn;
        struct pfr_ktable      *tbl;
        int                     dyncnt;
        int                     tblcnt;
    }                           p;
    u_int8_t                    type;          /* PF_ADDR_* */
    u_int8_t                    iflags;        /* PFI_AFLAG_* */
};

#define CREATE_TABLE do {                                   \
    table.pfrt_flags |= PFR_TFLAG_PERSIST;                  \
    if ((!(opts & PF_OPT_NOACTION) ||                       \
        (opts & PF_OPT_DUMMYACTION)) &&                     \
        (pfr_add_tables(&table, 1, &nadd, flags)) &&        \
        (errno != EPERM)) {                                 \
            radix_perror();                                 \
            goto _error;                                    \
    }                                                       \
    if (nadd) {                                             \
        warn_namespace_collision(table.pfrt_name);          \
        xprintf(opts, "%d table created", nadd);            \
        if (opts & PF_OPT_NOACTION)                         \
            return (0);                                     \
    }                                                       \
    table.pfrt_flags &= ~PFR_TFLAG_PERSIST;                 \
} while(0)                           

int
pfctl_command_tables(int argc, char *argv[], char *tname,
    const char *command, char *file, const char *anchor, int opts)
{
	if (tname == NULL || command == NULL)
		usage();
	return pfctl_table(argc, argv, tname, command, file, anchor, opts);
}

int
pfctl_table(int argc, char *argv[], char *tname, const char *command,
    char *file, const char *anchor, int opts)
{
    struct pfr_table    table;
    struct pfr_buffer   b, b2;
    struct pfr_addr    *a, *a2;
    int                 nadd = 0;

    [...]

    strlcpy(table.pfrt_name, tname, sizeof(table.pfrt_name);      /**< copy table name */

    [...]
    } else if (!strcmp(command, "add")) {
        b.pfrb_type = PFRB_ADDRS;                                 /**< set type to ADDR */
        if (load_addr(&b, argc, argv, file, 0))                   /**< load_addr(): parse arguments and pass it to struct pfr_buffer */
            goto _error;
        CREATE_TABLE;
        if (opts & PF_OPT_VERBOSE)
            flags |= PFR_FLAG_FEEDBACK;
        RVTEST(pfr_add_addrs(&table, b.pfrb_caddr, b.pfrb_size, &nadd, flags));    /**< pfr_add_addrs(): 
        xprintf(opts, "%d/%d addresses added", nadd, b.pfrb_size);
        if (opts & PF_OPT_VERBOSE)
            PFRB_FOREACH(a, &b)
                if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
                    print_addrx(a, NULL, opts & PF_OPT_USEDNS);
    }
    [...]
}

int
load_addr(struct pfr_buffer *b, int argc, char *argv[], char *file,
    int nonetwork)
{
    while (argc--)
        if (append_addr(b, *argv++, nonetwork)) {
            if (errno)
                warn("cannot decode %s", argv[-1]);
            return (-1);
        }
    if (pfr_buf_load(b, file, nonetwork, append_addr)) {
        warn("cannot load %s", file);
        return (-1);
    }
    return (0);
}
struct node_host {
    struct pf_addr_wrap  addr;
    struct pf_addr       bcast;
    struct pf_addr       peer;
    sa_family_t          af;
    u_int8_t             not;
    u_int32_t            ifindex;   /* link-local IPv6 addrs */
    char                *ifname;
    u_int                ifa_flags;
    struct node_host    *next;
    struct node_host    *tail;
};
/*
 * convert a hostname to a list of addresses and put them in the given buffer.
 * test:
 *  if set to 1, only simple addresses are accepted (no netblock, no "!").
 */
int
append_addr(struct pfr_buffer *b, char *s, int test)
{
    char             *r;
    struct node_host    *h, *n;
    int          rv, not = 0;

    for (r = s; *r == '!'; r++)
        not = !not;
    if ((n = host(r)) == NULL) {
        errno = 0;
        return (-1);
    }
    rv = append_addr_host(b, n, test, not);
    do {
        h = n;
        n = n->next;
        free(h);
    } while (n != NULL);
    return (rv);
}

/*
 * same as previous function, but with a pre-parsed input and the ability
 * to "negate" the result. Does not free the node_host list.
 * not:
 *      setting it to 1 is equivalent to adding "!" in front of parameter s.
 */
int
append_addr_host(struct pfr_buffer *b, struct node_host *n, int test, int not)
{
    int          bits;
    struct pfr_addr      addr;

    do {
        bzero(&addr, sizeof(addr));
        addr.pfra_not = n->not ^ not;
        addr.pfra_af = n->af;
        addr.pfra_net = unmask(&n->addr.v.a.mask, n->af);            /**< assign netmask, node_host -> pfr_addr */
        switch (n->af) {
        case AF_INET:
            addr.pfra_ip4addr.s_addr = n->addr.v.a.addr.addr32[0];   /**< assign address, node_host -> pfr_addr */
            bits = 32;
            break;
        case AF_INET6:
            memcpy(&addr.pfra_ip6addr, &n->addr.v.a.addr.v6,
                sizeof(struct in6_addr));
            bits = 128;
            break;
        default:
            errno = EINVAL;
            return (-1);
        }
        if ((test && (not || addr.pfra_net != bits)) ||              /**< test = 0, not = 0 => bypass these lines */
            addr.pfra_net > bits) {
            errno = EINVAL;
            return (-1);
        }
        if (pfr_buf_add(b, &addr))
            return (-1);
    } while ((n = n->next) != NULL);

    return (0);
}

struct node_host *
host(const char *s)
{
    struct node_host    *h = NULL;
    int          mask, v4mask, v6mask, cont = 1;
    char            *p, *q, *ps;

    if ((p = strrchr(s, '/')) != NULL) {
        mask = strtol(p+1, &q, 0);
        if (!q || *q || mask > 128 || q == (p+1)) {
            fprintf(stderr, "invalid netmask '%s'\n", p);
            return (NULL);
        }
        if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
            err(1, "host: malloc");
        strlcpy(ps, s, strlen(s) - strlen(p) + 1);
        v4mask = v6mask = mask;
    } else {
        if ((ps = strdup(s)) == NULL)
            err(1, "host: strdup");
        v4mask = 32;
        v6mask = 128;
        mask = -1;
    }

    /* interface with this name exists? */
    if (cont && (h = host_if(ps, mask)) != NULL)
        cont = 0;

    /* IPv4 address? */
    if (cont && (h = host_v4(s, mask)) != NULL)
        cont = 0;

    /* IPv6 address? */
    if (cont && (h = host_v6(ps, v6mask)) != NULL)
        cont = 0;

    /* dns lookup */
    if (cont && (h = host_dns(ps, v4mask, v6mask)) != NULL)
        cont = 0;
    free(ps);

    if (h == NULL || cont == 1) {
        fprintf(stderr, "no IP address found for %s\n", s);
        return (NULL);
    }
    return (h);
}

struct node_host *
host_v4(const char *s, int mask)
{
    struct node_host    *h = NULL;
    struct in_addr       ina;
    int          bits = 32;

    memset(&ina, 0, sizeof(struct in_addr));
    if (strrchr(s, '/') != NULL) {
        if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)     /**< parse string, return netmask bits */
            return (NULL);
    } else {
        if (inet_pton(AF_INET, s, &ina) != 1)
            return (NULL);
    }

    h = calloc(1, sizeof(struct node_host));
    if (h == NULL)
        err(1, "address: calloc");
    h->ifname = NULL;
    h->af = AF_INET;
    h->addr.v.a.addr.addr32[0] = ina.s_addr;
    set_ipmask(h, bits);                                                     /**< set IP mask */
    h->next = NULL;
    h->tail = h;

    return (h);
}

void
set_ipmask(struct node_host *h, u_int8_t b)
{
    struct pf_addr  *m, *n;
    int      i, j = 0;

    m = &h->addr.v.a.mask;
    memset(m, 0, sizeof(*m));

    while (b >= 32) {
        m->addr32[j++] = 0xffffffff;
        b -= 32;
    }
    for (i = 31; i > 31-b; --i)
        m->addr32[j] |= (1 << i);
    if (b)
        m->addr32[j] = htonl(m->addr32[j]);

    /* Mask off bits of the address that will never be used. */
    n = &h->addr.v.a.addr;
    if (h->addr.type == PF_ADDR_ADDRMASK)
        for (i = 0; i < 4; i++)
            n->addr32[i] = n->addr32[i] & m->addr32[i];
}
/* buffer management code */

size_t buf_esize[PFRB_MAX] = { 0,
    sizeof(struct pfr_table), sizeof(struct pfr_tstats),
    sizeof(struct pfr_addr), sizeof(struct pfr_astats),
    sizeof(struct pfi_kif), sizeof(struct pfioc_trans_e)
};

/*
 * add one element to the buffer
 */
int
pfr_buf_add(struct pfr_buffer *b, const void *e)
{
    size_t bs;

    if (b == NULL || b->pfrb_type <= 0 || b->pfrb_type >= PFRB_MAX ||
        e == NULL) {
        errno = EINVAL;
        return (-1);
    }
    bs = buf_esize[b->pfrb_type];           /**< choose buffer size, ex. sizeof(struct pfr_addr) */
    if (b->pfrb_size == b->pfrb_msize)      /**< no space left */
        if (pfr_buf_grow(b, 0))             /**< increase buffer */
            return (-1);
    memcpy(((caddr_t)b->pfrb_caddr) + bs * b->pfrb_size, e, bs);
    b->pfrb_size++;
    return (0);
}

int
pfr_add_addrs(struct pfr_table *tbl, struct pfr_addr *addr, int size,
    int *nadd, int flags)
{
    struct pfioc_table io;

    if (tbl == NULL || size < 0 || (size && addr == NULL)) {
        errno = EINVAL;
        return (-1);
    }
    bzero(&io, sizeof io);
    io.pfrio_flags = flags;
    io.pfrio_table = *tbl;
    io.pfrio_buffer = addr;
    io.pfrio_esize = sizeof(*addr);
    io.pfrio_size = size;
    if (ioctl(dev, DIOCRADDADDRS, &io))
        return (-1);
    if (nadd != NULL)
        *nadd = io.pfrio_nadd;
    return (0);
}

Pointer Handling

Pointer Arithmetic

arr[ i ] == * ( arr + i )

ex.
sizeof(unsigned long) = 64-bit = 8 byte
unsigned long arr[2];
&arr[0] = (arr + 0) = 0x608ea0
&arr[1] = (arr + 1) = 0x608ea8
(gdb) p &entry.allocator[0]         (gdb) p &((ethernet_header_t *) entry.allocator)[0]  
$19 = (header_t *) 0x608e90         $21 = (struct _ethernet_header_t *) 0x608e90


(gdb) p &entry.allocator[1]         (gdb) p &((ethernet_header_t *) entry.allocator)[1]
$20 = (header_t *) 0x608eb8         $22 = (struct _ethernet_header_t *) 0x608ed0
   
            
(gdb) p sizeof(header_t)            (gdb) p sizeof(ethernet_header_t)
$24 = 40 = 0x28                     $23 = 64 = 0x40

  0x608e90                            0x608e90
+ 0x000028                          + 0x000040
----------                          ----------
  0x608eb8                            0x608ed0
===========                         ===========