\section{Linux Kernel}
\label{sec:tour}
This Section is an attempt at explaining some parts of the Linux
kernel which had to be investigated before the DPF functionality could
be added. 

\section{General notes}

The entire source code is available to the Linux kernel. However, as Linux 
is fairly new and started as a hackers dream of creating his own operating
system, there are few explanations and examples of how the kernel works.

The problem is thus where to find what one is looking for in the kernel.
In my case I had trouble finding what functions are called and in what 
order. I also had to figure out where to insert the DPF code, and
what had to be modified  to make it work.

It is possible to use profiling on the kernel to find where time is
spent. To be able to use profiling of the kernel, the kernel must be
compiled with the CONFIG\_KERNEL option enabled. I never got around to
use profiling so I don't know how useful it might have been.
The kernel configuration program says the following when asking for
help about the CONFIG\_KERNEL option:
\begin{quote}
``Kernel profiling support.
  This is for kernel hackers who want to know how much time the kernel
  spends in the various procedures. The information is stored in
  /proc/profile (enable the /proc filesystem!) and in order to read
  it, you need the readprofile package from sunsite.unc.edu. Its
  manpage gives information regarding the format of profiling data. To
  become a kernel hacker, you can start with the Kernel Hacker's
  Guide, available via ftp (user: anonymous) from
  sunsite.unc.edu:/pub/Linux/docs/LDP. Mere mortals say N.''
\end{quote}

The Kernel Hacker's guide mentioned gives some starting points but it
is very outdated. It is also no longer available at the ftp site
mentioned. It has become an online version at the \cite{http:khg} website.
There is a mailing group for the developers of the Linux kernel. The mailing group is
archived at \cite{http:kernel} and can be interesting to read. 
At the same archive a networking mailing group is stored as well. 

The following chapter is my working document over what I found. I hope
it can be used as a road map for further studies using Linux.
The document is actually meant to be used in the Emacs editor, where
you can make the editor to open the file at the mark you are currently at.
Thus a simple kind of a hypertext document is created as it is possible to
see what's in the source file. The explanations are brief, but should make
some sense.

The Linux kernel is large....
\begin{itemize}
  \item
        112,080 lines of .h source for all the currently supported architectures: 
        \begin{itemize}
          \item x86
          \item Alpha
          \item Sparc
        \end{itemize}
  \item
        41,601 lines of assembly source for the same architectures.
  \item
        572,808 lines of .c source. The C-source is basically the same for
        all architectures.
\end{itemize}

The line numbers in the document may not exactly match the current kernel.
I started out using the v2.0.0 kernel, but when I was finished writing
the document I was using the v2.0.6 kernel. The Linux kernels can be retrieved by
ftp from ftp.funet.fi either as a complete tar archive, or a patch from
the last patch-level. To apply a patch the \emph{patch} program is
used. The patch program reads a data-file with the changes, and applies them to the
current files. For instance to apply the patch to create a 2.0.7
version of kernel the following can be used: 
\emph{gzip -c patch-2.0.7.gz $|$ patch -p0}. To see if the patch was
successful, the \emph{find} program may be used: 
\emph{find . -name '*.rej' -print}.

The version of the last kernel (when I checked the last time) was 2.0.18.
The eighteen revisions were made in about two months of time, which in itself is
an indication of how fast the Linux kernel is evolving.

The kernel numbering in the Linux kernel has been as quickly changing as the
rest. The first time I installed Linux it came with the 1.2.13 version of the
Linux kernel. At this time the nature of the kernel could be judged by
the middle number. If it was even it was considered a stable 
kernel, if it was odd it was considered to be a development kernel.
The last digit is the patch-level. 

As the numbers in the 1.3.x series of kernels started to be around 1.3.9x, 
many assumed that the next stable release would be 1.4.x, but Linus thought
that so many changes/improvements had been done that the new stable release
would be 2.0 instead.

The changes from the 1.2 series to the 2.0 series includes, according to Linus
himself, \cite{art:linux} the following:

\begin{itemize}
  \item        
        The NFS performance has been greatly enhanced, as in the 1.2 series
        there where no daemons for prefetching/writing data.
  \item
        The Linux kernel now supports Symmetrical Multiprocessing.
  \item
        Better multi architecture support.
  \item
        Improved process handling.
  \item
        Improved file-system throughput.
\end{itemize}

There is currently no development kernels based on the 2.0 series
as Linus wants that all bugs should be ironed out from the 2.0 series first 
before development starts again.

One problem is finding \textbf{where} lines of code are in the kernel.
The \emph{grep} is useful, but as different parts of the kernel 
is placed in hierarchical order, it does not do a good job if stuff are 
in another directory.

The following script might thus be useful, it combines the \emph{find} and
the \emph{grep} program and lines of code can be easily found.

\tiny\begin{verbatim}
#!/usr/bin/perl

if ($#ARGV < 2) {
    print "Usage: find-grep path files [grep-options] searchpattern\n";
    print "Example: find-grep . \"*.[chS]\" searchpattern\n";
    print "Grep-options is default -n\n";
    exit;
}

print "find $ARGV[0] -name \"$ARGV[1]\"\n";
$find = `find $ARGV[0] -name "$ARGV[1]"`;

@files = split(/\n/, $find);
$gp = "-n";
for ($i = 2; $i <= $#ARGV; $i++) {
    $gp .= " ".$ARGV[$i];
}
$_ = $gp;
if (!/\/dev\/null/) {
  $gp .= "/dev/null";
}

print "foreach file: grep $gp file\n";

foreach $file (@files) {
    split(/\//, $file);
    # Remove preceding ./ if found
    if ($_[0] eq ".") {
        shift(@_);
    }
    $file = join("/", @_);
    $grep = `grep $gp $file`;
    if ($grep ne "") {
        print "$grep";
    }
}
\end{verbatim}\normalsize

\section{The details}
I apologize if the following sections are hard to read, but I have not
found a good way to format the following sections in \LaTeX2e. Instead
I include the sections as they are in the original text document used
in Emacs.

\subsection{Kernel startup}
One of the chapter of the Linux kernel hackers guide\cite{http:khg}
explains how the Linux kernel is bootstrapped. To summarize very
briefly: After a reset the i386 starts executing code at 0xffff0 where the BIOS is
resident. The BIOS checks in particular that there is some kind of
boot device like a floppy or a harddrive. When a boot device is found,
the BIOS starts executing code on the device. The code is at the
master boot record (MBR) of the device, which is the first sector.
This is where the Linux LOader (LILO) is resident. LILO in turn lets 
you select what to boot next. If Linux is chosen, the processor starts executing 
the assembly code in the architecture specific directory, and it will
eventually reach the following where the Linux kernel starts
executing. 

\tiny\begin{verbatim}
arch/i386/kernel/head.S:28:startup_32:
init/main.c:754:asmlinkage void start_kernel(void)
        Calls sock_init, see Socket setup below
ipc/util.c:20:void ipc_init (void)
        The ipc_init calls 
ipc/sem.c:55:void sem_init (void)
\end{verbatim}\normalsize

When the above is done, the init() is started, and the system comes up.

\subsection{General device setup}

Questions:
        Who calls sys\_setup() ?
                On the alpha systems this is done by assembly code, on the
                i386 systems I have not found where this is done.

Program sequence:

\tiny\begin{verbatim}
fs/filesystems.c:33:asmlinkage int sys_setup(void)
drivers/block/genhd.c:643:void device_setup(void)
net/core/dev.c:1369:int net_dev_init(void)
                Setup the bh_base structure
net/core/dev.c:1469:    init_bh(NET_BH, net_bh);        
                ...which sets the net_bh as handling routine
\end{verbatim}\normalsize


\subsection{Socket setup}
Sequence preceded by Kernel startup, sock\_init is before init()

\tiny\begin{verbatim}
init/main.c:754:asmlinkage void start_kernel(void)
net/socket.c:1391:void sock_init(void)
        Various init, like firewall init and ...
net/socket.c:1375:void proto_init(void)
        Uses the protocols[] array specified in net/protocols.c to init

net/ipv4/af_inet.c:1594:void inet_proto_init(struct net_proto *pro)
        Adds all protocols to the inet_protos global hashed list of protocols
        by inet_add_protocol
        The default protocols are ICMP, UDP and TCP

The protocols known are in the inet_protocol_base
-------------------------------------------------
(different depending if Multicast is on or not, check the next 
        pointer in each structure).
net/ipv4/protocol.c:107:struct inet_protocol *inet_protocol_base = 
        &icmp_protocol;

The protocols are initialized by
--------------------------------
arp_init();
icmp_init(&inet_proto_ops);
ip_fw_init(); <-- See Firewall setup below
net/ipv4/ip_output.c:1084:void ip_init(void)

ip_init sets up what ip functions the network devices should call
-----------------------------------------------------------------
net/ipv4/ip_output.c:1006:static struct packet_type ip_packet_type =
        ip_rcv is setup as receiving function by the following call

net/core/dev.c:149:void dev_add_pack(struct packet_type *pt)
\end{verbatim}\normalsize

\subsection{ARP setup}
Called from inet\_proto\_init, see Socket setup

\tiny\begin{verbatim}
net/ipv4/arp.c:2386:void arp_init (void)
        Sets up the arp_rcv as receive by calling 
        dev_add_pack(&arp_packet_type);
core/dev.c:149:void dev_add_pack(struct packet_type *pt)
        The adds the protocol to the ptype_base protocol list, and
        the thus the arp_rcv will be called if stuff arrives.
        This also means that the filter is bypassed, and the filter has
        no influence on arp packets.
        
        Setup the /proc file
        file: /proc/net/arp     func:arp_get_info

Arp receive ------------
net/ipv4/arp.c:1743:int arp_rcv(struct sk_buff *skb, struct device *dev, 
        struct packet_type *pt)
\end{verbatim}\normalsize

\subsection{ICMP setup}
Called from inet\_proto\_init, see Socket setup

\tiny\begin{verbatim}
net/ipv4/icmp.c:1142:void icmp_init(struct proto_ops *ops)

The ip_rcv function is set in the icmp_protocol, and statically set in the 
inet_protocol list.
In order to receive anything, the firewall has to accept ICMP packets.
The icmp_handler (and all other protocol handlers) is called from ip_rcv:
        for (ipprot = (struct inet_protocol *)inet_protos[hash];
                ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
        
net/ipv4/icmp.c:1017:int icmp_rcv(struct sk_buff *skb, struct device *dev, 
        struct options *opt,
\end{verbatim}\normalsize

\subsection{Firewall setup}
The chains of the firewall are initialized in the sock\_init by a call
to:

\tiny\begin{verbatim}
net/core/firewall.c:159:void fwchain_init(void)
        Sets all chains to FW_ACCEPT.
        Set up the module entry points by:
static struct symbol_table firewall_syms = {
#include <linux/symtab_begin.h>
        X(register_firewall),
        X(unregister_firewall),
        X(call_in_firewall),
        X(call_out_firewall),
        X(call_fw_firewall),
#include <linux/symtab_end.h>
};
\end{verbatim}\normalsize
See socket setup to see how we end up here.

\tiny\begin{verbatim}
net/ipv4/ip_fw.c:1247:void ip_fw_init(void)
        Assuming that CONFIG_PROC_FS, CONFIG_IP_ACCT and CONFIG_IP_FIREWALL is set,
        the following files are setup in the /proc directory:

        File:           Handled by:
        ip_acct         ip_acct_procinfo
        ip_input        ip_fw_in_procinfo
        ip_output       ip_fw_out_procinfo
        ip_forward      ip_fw_fwd_procinfo

        The firewall is registered:
        if(register_firewall(PF_INET,&ipfw_ops)<0)
net/core/firewall.c:21:int register_firewall(int pf, struct firewall_ops *fw)
        As this is the first time register_firewall is called, the internal
        chains are NULL, thus the current firewall becomes first in the linked list.
        
        Important variables:
static int firewall_policy[NPROTO];
static struct firewall_ops *firewall_chain[NPROTO];

        The struct firewall_ops is the following:
struct firewall_ops
{
        struct firewall_ops *next;
        int (*fw_forward)(struct firewall_ops *this, int pf, 
                        struct device *dev, void *phdr, void *arg);
        int (*fw_input)(struct firewall_ops *this, int pf, 
                        struct device *dev, void *phdr, void *arg);
        int (*fw_output)(struct firewall_ops *this, int pf, 
                        struct device *dev, void *phdr, void *arg);
        /* Data falling in the second 486 cache line isn't used directly
           during a firewall call and scan, only by insert/delete and other
           unusual cases
         */
        int fw_pf;              /* Protocol family                      */      
        int fw_priority;        /* Priority of chosen firewalls         */
};      
        
        ipfw_ops is set to:
struct firewall_ops ipfw_ops=
{
        NULL,
        ipfw_forward_check,
        ipfw_input_check,
        ipfw_output_check,
        PF_INET,
        0       /* We don't even allow a fall through so we are last */
};

        The firewall is thus setup, check Packets Arrive to see what then happens.

        And finally a pointer is set s.t. the device can be UP or DOWN.
        register_netdevice_notifier(&ipfw_dev_notifier);
\end{verbatim}\normalsize


\subsection{Network device setup}
The Linux kernel has to be compiled with different ethernet network 
drivers to be able to use them. With the 2.0.0 kernels it is also
possible to use loadable modules. A loadable module can be installed
and removed on the fly. It may be useful to use loadable modules if
you are rarely using a certain feature, and thus want to have a leaner
kernel. It takes however almost as long time to
compile the loadable modules as it takes to compile the kernel, so I
have rarely used any loadable modules. 

If several network devices are compiled into the kernel, the kernel
has to find out which one to use of them. The function responsible for
scanning for devices is called ethif\_probe(). The default behavior
is to only scan for one network device. If more devices are to be
used, the kernel must be told. This is done by appending an option in
the /etc/lilo.conf file. LILO will then in turn pass that option to
the kernel when it is started. Example of option string:\\
\emph{append = "ether=0,0,eth1"}. The option tells the kernel that it
should scan for a second ethernet device. If you are using two or more
ethernet devices make sure that the do not have any hardware conflicts.

Questions: 
        Who calls ethif\_probe() ?\\
        How is the dev\_base list of devices setup ?\\
                Some are statically included, like the loopback device
        below, but the rest ?

\tiny\begin{verbatim}
drivers/net/Space.c:450:struct device *dev_base = &loopback_dev;

Sequence (case study of NE2000 ethercard):
drivers/net/Space.c:88:ethif_probe(struct device *dev)
        Probes all compiled drives, for instance 

drivers/net/ne.c:146:int ne_probe(struct device *dev)
        If device is found, the dev structure is set for the device
        at the end of:

drivers/net/ne.c:204:static int ne_probe1(struct device *dev, int ioaddr)
        Also sets up the IRQ routine for rx-tx for all 8390 cards. 
        The NS8390 chip by National Semiconductor commonly used,
        and common features are in the 8390.c file.
\end{verbatim}\normalsize

\subsection{Packets are received}
\label{sec:rxpackets}
This is a case study for a NE2000 ethercard.

\tiny\begin{verbatim}
drivers/net/8390.c:285:void ei_interrupt(int irq, void *dev_id, 
        struct pt_regs * regs)

drivers/net/8390.c:488:static void ei_receive(struct device *dev)
        Allocates a skb_buffer
        When a page is found, it calls the actual ne2000 card
        ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
        (ei_block_input is a macro that returns a pointer to a function)

drivers/net/ne.c:513:ne_block_input(struct device *dev, int count, 
        struct sk_buff *skb, int ring_offset)

        NE2000 specific handling, read the actual block: 
                insb(NE_BASE + NE_DATAPORT, buf, count);
        When the NE2000 call returns, the 8390.c then sets calls the actual 
        network layer receive

net/core/dev.c:479:void netif_rx(struct sk_buff *skb)
        Stores the buffer (possibly dropping it) in linked list and calls
                mark_bh(NET_BH);
        to mark processing of packet

include/linux/interrupt.h:51:extern inline void mark_bh(int nr)

The bh_queue is checked periodically by
--------------------------------------
kernel/sched.c:293:asmlinkage void schedule(void)
kernel/softirq.c:32:asmlinkage void do_bottom_half(void)
        ... And we end up here, check Socket Setup for more details ...

net/core/dev.c:561:void net_bh(void)
        Unless the kernel is a bridge, the MAC header is removed from the buffer.
        The ptype_base hashed array contains the known protocols.
        ... And by calling pt_prev->func(skb2, skb->dev, pt_prev);
            we end up here ...

net/ipv4/ip_input.c:223:int ip_rcv(struct sk_buff *skb, struct device *dev, 
        struct packet_type *pt)
        The sk_buff struct is this:
include/linux/skbuff.h:48:struct sk_buff 
        The giant device struct is this:
include/linux/netdevice.h:86:struct device 
        The packet_type struct is this:
include/linux/netdevice.h:195:struct packet_type {


        Finally the IP packets are processed!
        Lots of stuff is done, updates statistics, checks if packet is ok,
        defragments a fragmented packet (if it is fragmented and this is not the
        last packet, a return is done).
        Also checks if the device has an Aliased IP address.

        Check if packet is to pass:
        if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT)
net/core/firewall.c:120:int call_in_firewall(int pf, struct device *dev, 
        void *phdr, void *arg)
                While the chain is not empty, this is done:
                        int rc=fw->fw_input(fw,pf,dev,phdr,arg);
                        The fw_input was set in Firewall Setup to ipfw_input_check,
                        and this is thus where we end up:
net/ipv4/ip_fw.c:1180:int ipfw_input_check(struct firewall_ops *this, int pf, 
        struct device *dev, void *phdr, void *arg)
                        Which simply does this:
                        return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, 
                                ip_fw_in_policy, IP_FW_MODE_FW);

                        The ip_fw_in_chain is of the ip_fw structure type.
                        The ip_fw structure has the following contents:
struct ip_fw 
{
   struct ip_fw  *fw_next;                 /* Next firewall on chain */
   struct in_addr fw_src, fw_dst;          /* Source and destination IP addr */
   struct in_addr fw_smsk, fw_dmsk;        /* Mask for src and dest IP addr */
   struct in_addr fw_via;                  /* IP address of interface "via" */
   struct device *fw_viadev;               /* device of interface "via" */
   unsigned short fw_flg;                  /* Flags word */
   unsigned short fw_nsp, fw_ndp;          /* N'of src ports and # of dst ports */
                                           /* in ports array (dst ports follow */
                                           /* src ports; max of 10 ports in all; */
                                           /* count of 0 means match all ports) */
#define IP_FW_MAX_PORTS 10                 /* A reasonable maximum */
   unsigned short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */
   unsigned long  fw_pcnt,fw_bcnt;         /* Packet and byte counters */
   unsigned char  fw_tosand, fw_tosxor;    /* Revised packet priority */
   char           fw_vianame[IFNAMSIZ];    /* name of interface "via" */
};

                        Thus this is actually done:
net/ipv4/ip_fw.c:204:int ip_fw_chk(struct iphdr *ip, struct device *rif, 
        __u16 *redirport, struct ip_fw *chain, int policy, int mode)

                If the chain becomes empty, the default policy is returned:
                        return firewall_policy[pf];

        If the packet gets past the IN firewall, and the packet is for us, deliver
        it to the protcols:

                for (ipprot = (struct inet_protocol *)inet_protos[hash];
                        ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)

                ...
                ipprot->handler(skb2, dev, opt, daddr,
                                (ntohs(iph->tot_len) - (iph->ihl * 4)),
                                iph->saddr, 0, ipprot);

        If the packet was not for us, try to forward it.
        if (ip_forward(skb, dev, is_frag, iph->daddr))
net/ipv4/ip_forward.c:93:int ip_forward(struct sk_buff *skb, 
        struct device *dev, int is_frag,

        Checks that packet is possible to forward, calcs on what device
        to forward on, adds possible masquerading (add additional IP-header with
        masqueraded ports). Masquerading should be turned off, I have not been able
        to check what happens if it's not...
        The packet has to pass the Forward chain, and also the Output chain if
        the packet is to pass.
        
        Check if packet is to be forwarded
        fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
net/core/firewall.c:102:int call_fw_firewall(int pf, struct device *dev, 
        void *phdr, void *arg)

        int rc=fw->fw_forward(fw,pf,dev,phdr,arg);

net/ipv4/ip_fw.c:1191:int ipfw_forward_check(struct firewall_ops *this, 
        int pf, struct device *dev, void *phdr, void *arg)
        
        return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy, 
        IP_FW_MODE_FW);
        
net/ipv4/ip_fw.c:204:int ip_fw_chk(struct iphdr *ip, struct device *rif, 
        __u16 *redirport, struct ip_fw *chain, int policy, int mode)


        Check the out packet filter chain:
        if((fw_res = call_out_firewall(PF_INET, skb2->dev, iph, NULL)) < FW_ACCEPT)
net/core/firewall.c:135:int call_out_firewall(int pf, struct device *dev, 
        void *phdr, void *arg)

        int rc=fw->fw_output(fw,pf,dev,phdr,arg);
net/ipv4/ip_fw.c:1191:int ipfw_forward_check(struct firewall_ops *this, 
        int pf, struct device *dev, void *phdr, void *arg)

        return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy, 
        IP_FW_MODE_FW);

net/ipv4/ip_fw.c:204:int ip_fw_chk(struct iphdr *ip, struct device *rif, 
        __u16 *redirport, struct ip_fw *chain, int policy, int mode)
\end{verbatim}\normalsize

\subsection{Packets are sent}
This is a case study for a NE2000 ethercard.

Some of the upper layers decide to send something and calls:
\tiny\begin{verbatim}
net/ipv4/ip_output.c:370:void ip_queue_xmit(struct sock *sk, struct device *dev,
        if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
net/core/firewall.c:134:int call_out_firewall(int pf, struct device *dev, 
        void *phdr, void *arg)

        int rc=fw->fw_output(fw,pf,dev,phdr,arg);

net/ipv4/ip_fw.c:1185:int ipfw_output_check(struct firewall_ops *this, int pf, 
        struct device *dev, void *phdr, void *arg)

        return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy, 
        IP_FW_MODE_FW);

net/ipv4/ip_fw.c:204:int ip_fw_chk(struct iphdr *ip, struct device *rif, 
        __u16 *redirport, struct ip_fw *chain, int policy, int mode)


        Checks the Out firewall, and check Accounting rules.
        If everything OK, calls the actual device: 
                dev_queue_xmit(skb, dev, priority);

net/core/dev.c:466:void dev_queue_xmit(struct sk_buff *skb, 
        struct device *dev, int pri)

        In turn calls
net/core/dev.c:335:static void do_dev_queue_xmit(struct sk_buff *skb, 
        struct device *dev, int pri)

        The pointer is set when initialized to:
drivers/net/8390.c:718:    dev->hard_start_xmit = &ei_start_xmit;
drivers/net/8390.c:135:static int ei_start_xmit(struct sk_buff *skb, 
        struct device *dev)

        This in turn calls the pointer:
                ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
                ... which is set to...
drivers/net/ne.c:576:ne_block_output(struct device *dev, int count,
        Done!
\end{verbatim}\normalsize

\subsection{How the fire-wall chains are changed}
\label{sec:chains}

This is done by the ipfwadm utility.
After checking the syntax of the command, the following func is called:

\tiny\begin{verbatim}
~jhjulkun/ipfwadm-2.2/ipfwadm.c:1703:do_setsockopt(int cmd, void *data, int length)
        This will do a setsockopt on the raw socket.
        if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) {
        ret = setsockopt(sockfd, IPPROTO_IP, cmd, (char *) data, length);
net/socket.c:1056:asmlinkage int sys_setsockopt(int fd, int level, 
        int optname, char *optval, int optlen)

        sock is set to: sockfd_lookup(fd, NULL) (fd==AF_INET)
        return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
        
/usr/src/hlinux/net/ipv4/af_inet.c:476:static int inet_setsockopt(
        struct socket *sock, int level, int optname,

        return sk->prot->setsockopt(sk,level,optname,optval,optlen);
        Hmm, the code does not really give any clues to where to continue, 
        but as the proto is IP the following might not be a bad guess...

net/ipv4/ip_sockglue.c:112:int ip_setsockopt(struct sock *sk, int level, 
        int optname, char *optval, int optlen)

        Bingo! The following was found:
                case IP_FW_POLICY_FWD:
                case IP_FW_MASQ_TIMEOUTS:
                        if(!suser())
                                return -EPERM;
                        if(optlen>sizeof(tmp_fw) || optlen<1)
                                return -EINVAL;
                        err=verify_area(VERIFY_READ,optval,optlen);
                        if(err)
                                return err;
                        memcpy_fromfs(&tmp_fw,optval,optlen);
                        err=ip_fw_ctl(optname, &tmp_fw,optlen);
                        return -err;    /* -0 is 0 after all */
        Which finally leads us to:
net/ipv4/ip_fw.c:890:int ip_fw_ctl(int stage, void *m, int len)
\end{verbatim}\normalsize

\subsection{Timers}
\label{sec:timers}

The internal counter of the Linux kernel is a `jiffie'.
Now, how are the jiffies updated ?
The following function is called by the hardware timer interrupt, which is set to
to interrupt at 100 times/sec.

\tiny\begin{verbatim}
linux/arch/i386/time.c:342:static inline void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        The first thing interrupt handler does is to call the timer function.
        do_timer(regs);

linux/kernel/sched.c:1006:void do_timer(struct pt_regs * regs)
        One of the lines increases the jiffies counter.
        There must be a reason for the complicated syntax (two lines
        down). Why not a simple: jiffies++ ?

        (*(unsigned long *)&jiffies)++;

        This is the declaration:
linux/include/linux/sched.h:321:extern unsigned long volatile jiffies;
        
        The next thing the interrupt function does it to set handling
        of a higher level function by:
        mark_bh(TIMER_BH);
linux/include/linux/interrupt.h:51:extern inline void mark_bh(int nr)
        Now continue at the schedule function ....
\end{verbatim}\normalsize


The schedule task is called when no other tasks can run. It cannot be killed.
The scheduler task checks periodically a list of timer entries that are checked
against a `volatile' counter that counts `jiffies'. 

\tiny\begin{verbatim}
kernel/sched.c294:asmlinkage void schedule(void)
        One of the first things the scheduler checks is to see if there are 
        things to be done.

        if (bh_active & bh_mask) {
                do_bottom_half();
linux/include/linux/interrupt.h:24:asmlinkage void do_bottom_half(void);
        In this case the timer_bh function is kicked
linux/kernel/sched.c:999:static void timer_bh(void)
        run_timer_list();
linux/kernel/sched.c:578:static inline void run_timer_list(void)
{
        struct timer_list * timer;

        cli();
        while ((timer = timer_head.next) != &timer_head && timer->expires <= jiffies) {
                void (*fn)(unsigned long) = timer->function;
                unsigned long data = timer->data;
                timer->next->prev = timer->prev;
                timer->prev->next = timer->next;
                timer->next = timer->prev = NULL;
                sti();
                fn(data);
                cli();
        }
        sti();
}
\end{verbatim}\normalsize

The above makes sure that after the number of jiffies has been
decreased to zero, the funtion in the timer structure is called. The
cli() and sti() mean clear interrupt and set interrupt. They turn off
and on the hardware interrupts. Used to prevent race conditions. The
line `void (*fn)(unsigned long) = timer->function;' assigns a pointer
to a function. The function should be provided when a timer entry is
created. The function is called by `fn(data)'.

A timer is added by:

\tiny\begin{verbatim}
linux/kernel/sched.c:535:void add_timer(struct timer_list * timer)
\end{verbatim}\normalsize

A timer is removed by:

\tiny\begin{verbatim}
/usr/src/linux/kernel/sched.c:560:int del_timer(struct timer_list * timer)
\end{verbatim}\normalsize

\subsection{Entry points}
\label{sec:entry}
The entry points to the DPF would be easiest to set at the entry
function to the physical device. 
However, as the Linux kernel knows how to use various protocols other
than IP this might not be a good idea! If the driver assumes that it
is passed a packet which contains an IP header and it is not, 
some `funny' effects would probably happen.
Instead the additions are done at various points in the IP layer code:

\tiny\begin{verbatim}
net/ipv4/ip_output.c:508:ip_dyn_check(skb, dev, iph, IP_DYN_F_OUT);     
        ip_queue_xmit   Hook for packets to queued to device

net/ipv4/ip_output.c:713:ip_dyn_check(skb, dev, iph, IP_DYN_F_OUT);             
        ip_build_xmit   By some reason some packets are missed by the above.
                        By adding this call all packets are found.

net/ipv4/ip_output.c:992:ip_dyn_check(skb, dev, iph, IP_DYN_F_OUT);     
        ip_build_xmit   The ip_build_xmit may call the device at two places
                        Both calls are trapped.

net/ipv4/ip_forward.c:278:ip_dyn_check(skb, dev2, skb->h.iph, IP_DYN_F_FORWARD);
        ip_forward      All packets that are forwarded are checked

net/ipv4/ip_input.c:534:ip_dyn_refresh(skb, dev, skb->h.iph);
        ip_rcv          All incoming packets
\end{verbatim}\normalsize

%%% Local Variables: 
%%% mode: latex
%%% TeX-master: "main"
%%% End: 
