diff options
author | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2013-07-27 21:08:48 +0000 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2013-07-27 21:08:48 +0000 |
commit | 21adb5284111190057db245cfc2b54091920c373 (patch) | |
tree | eb7f1709f14f18c7fc705be8da2e941bd07fb49b /eth-filter | |
parent | bf5c437c82625cd44897faaa84ac81c470423846 (diff) |
New upstream snapshot
Diffstat (limited to 'eth-filter')
-rw-r--r-- | eth-filter/ChangeLog | 127 | ||||
-rw-r--r-- | eth-filter/Makefile | 35 | ||||
-rw-r--r-- | eth-filter/README | 39 | ||||
-rw-r--r-- | eth-filter/bpf_impl.c | 872 | ||||
-rw-r--r-- | eth-filter/bpf_impl.h | 158 | ||||
-rw-r--r-- | eth-filter/filter.c | 901 | ||||
-rw-r--r-- | eth-filter/pcap_filter.c | 77 | ||||
-rw-r--r-- | eth-filter/queue.c | 131 | ||||
-rw-r--r-- | eth-filter/queue.h | 329 | ||||
-rw-r--r-- | eth-filter/util.h | 78 |
10 files changed, 2747 insertions, 0 deletions
diff --git a/eth-filter/ChangeLog b/eth-filter/ChangeLog new file mode 100644 index 00000000..1b3eb8ac --- /dev/null +++ b/eth-filter/ChangeLog @@ -0,0 +1,127 @@ +2009-04-18 Zheng Da <zhengda1936@gmail.com> + + * filter.c (trivfs_allow_open): Change its value. + (ds_device_open): Change the mode for file_name_lookup(). + +2009-01-02 Zheng Da <zhengda1936@gmail.com> + + * filter.c (ds_device_write): Deallocate the out-of-line data. + +2008-10-03 Zheng Da <zhengda1936@gmail.com> + + * Makefile: Remove the include paths from pfinet. + + * util.h: Remove the line of including linux/if_ether.h + (ETH_ALEN): New macro. + (ethhdr): New structure. + +2008-09-26 Zheng Da <zhengda1936@gmail.com> + + * filter.c (ethernet_demuxer): Save and restore the message header. + (deliver_msg): Use the non-block send. + +2008-08-22 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + + * filter.c (proxy_info): Removed. + (proxy_pfinetpi_ht): Removed. + (proxy_devicepi_ht): Removed. + (create_proxy): Removed. + (destroy_proxy): Removed. + (proxy_device): New structure. + (proxy_user): New structure. + (proxy): New structure. + (create_proxy_user): New function. + (create_proxy_device): New function. + (clean_proxy_user): New function. + (clean_proxy_device): New function. + (ethernet_demuxer): Get the data from proxy object instead of from + proxy_info. + (do_mach_notify_dead_name): Likewise. + (ds_device_write): Likewise. + (ds_device_write_inband): Likewise. + (ds_device_read): Likewise. + (ds_device_read_inband): Likewise. + (ds_device_map): Likewise. + (ds_device_set_status): Likewise. + (ds_device_get_status): Likewise. + (ds_device_set_filter): Likewise. Create the proxy_device object. + (do_mach_notify_no_senders): Use ports_do_mach_notify_no_senders(). + (ds_device_open): Create proxy_user and proxy objects. + (main): Add the cleaning routine when creating the port class. + +2008-08-20 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + + * filter.c (options): Update. + +2008-08-20 Zheng Da <zhengda1936@gmail.com> + + * filter.c (destroy_proxy): Test p_deliverport_hashloc before removing + it from proxy_deliverport_ht, and reverse the order of calling + ports_destroy_right and ports_port_deref. + (ds_device_open): Test device_file and replace "eth" with the variable + name. + +2008-08-20 Zheng Da <zhengda1936@gmail.com> + + * filter.c (device_file): Change the name of a variable. + (ds_device_open): Use device_file directly. + +2008-08-19 Zheng Da <zhengda1936@gmail.com> + + * filter.c (ds_device_open): Generate the device file name, and use it + to open the device. + +2008-08-18 Zheng Da <zhengda1936@gmail.com> + + * README: New file. + + +2008-08-17 Zheng Da <zhengda1936@gmail.com> + + * filter.c (device_name): Remove its default value. + (options): Remove the option '-M'. + (create_proxy): Get the port directly from port_right field in port_info. + (ds_device_open): Remove the code of checking the device name, + and open the device from the device file. + (parse_opt): Remove the code of handling the option '-M'. + (main): Remove the code of getting the master device port. + + +2008-08-14 Zheng Da <zhengda1936@gmail.com> + + * filter.c (pfinet_portclass, device_portclass, other_portclass): + New variables. + (create_proxy): Use pfinet_portclass and device_portclass to create the + port. + (destroy_proxy): Dereference the port before destroying it. + (trivfs_goaway): Test the number of ports in pfinet_portclass and destroy + the master_device. + (main): Create pfinet_portclass, device_portclass, other_portclass. + (print_msg): Deleted. + (deliver_msg): Remove debuging print. + + * ChangeLog: New file. + + +2008-08-13 Zheng Da <zhengda1936@gmail.com> + + * bpf_impl.c: New file. + + * Makefile: New file. + + * queue.c: New file. + + * util.h: New file. + + * bpf_impl.h: New file. + + * filter.c: New file. + + * pcap_filter.c: New file. + + * queue.h: New file. + diff --git a/eth-filter/Makefile b/eth-filter/Makefile new file mode 100644 index 00000000..16f5d73b --- /dev/null +++ b/eth-filter/Makefile @@ -0,0 +1,35 @@ +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 2008 Free Software Foundation, Inc. +# This file is part of the GNU Hurd. +# +# The GNU Hurd is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# The GNU Hurd is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with the GNU Hurd; see the file COPYING. If not, write to +# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +dir := eth-filter +makemode := server + +SRCS = bpf_impl.c filter.c queue.c pcap_filter.c +LCLHDRS = bpf_impl.h queue.h util.h +DIST_FILES = ourdevice.defs notify.defs +HURDLIBS = ports trivfs fshelp ihash shouldbeinlibc +target = eth-filter +MIGSTUBS = ourdeviceServer.o notifyServer.o +OBJS = $(SRCS:.c=.o) $(MIGSTUBS) + +include ../Makeconf + +#CFLAGS += -I../pfinet/linux-src/include -I../pfinet/glue-include +LDFLAGS += -lpcap + +ourdevice.defs: device.defs + $(CPP) $(CPPFLAGS) -x c $< | sed -e '/out[ ]*device[ ]*:[ ]*device_t/s/device_t/mach_port_send_t/' > $@ diff --git a/eth-filter/README b/eth-filter/README new file mode 100644 index 00000000..2ef16eda --- /dev/null +++ b/eth-filter/README @@ -0,0 +1,39 @@ +[Introduce] + +eth-filter is a filter translator that runs on the network device. The goal of eth-filter is to allow the user to setup the policy to control the traffic to the network. For example, it can block the invalid packet or the packet with the wrong destination address. It can work with eth-multiplexer to control and reduce the traffic between eth-multiplexer and pfinet, and can also work alone, running directly on the real network device. + + +[Usage] + +Usage: eth-filter [OPTION...] +Hurd filter translator. + + -i, --interface=DEVICE Network interface to use + -s, --send-filter=string The filter rule which applies to the outgoing + packet + -r, --receive-filter=string The filter rule which applies to the ingoing + packet + -S, --send-ip-range=IP range A range of IP to create the send filter + -R, --receive-ip-range=IP range + A range of IP to create the receive filter + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + +Mandatory or optional arguments to long options are also mandatory or optional +for any corresponding short options. + + +The '-i' option specifies the network interface the translator sits on. eth-filter can only connect to one network interface and the '-i' option should be only used once. DEVICE is a device file created by devnode translator. +The '-s' and '-r' options give the user a full control to specify the filter rules which applies to the outgoing packet and the incoming packet, respectively. The expression of the rule is the same as the one in TCPDUMP. +The '-S' and '-R' options specify a range of IP that the user wants to filter. They are used to create the sending filter and the receiving filter, respectively. The generated rule is "arp or (ip and src net addr/prefixlen)" or "arp or (ip and dst net addr/prefixlen)". If prefixlen is 32, it can be omitted. +NOTE: '-s' and '-S' cannot be used together. One will replace the other if they are both used. So are '-r' and '-R' options. +An example: settrans -acfg /servers/feth0 /hurd/eth-filter -i /dev/eth0 -S 192.168.8.0/24 -R 192.168.8.0/24 + +[Internal] + +eth-filter works as a proxy, forwarding the packet between the user program and the network interface. In order to forward packets, eth-filter runs as a client to the network device. It opens the device and writes the packet to the network device as pfinet does. It calls device_set_filter() to set the filter rule and give its own port to the device so it can receive packets from the device. The rule passed to the network device is from the user program that connects to eth-filter. +eth-filter works as a RPC server to communicate with the user program and implements the server side functions in device.defs. It gets the packet in the server side function ds_device_write and gets the port to deliver packets to the user program in ds_device_set_filter. +Three structures are used for one pair of the user program and the device: proxy_user, proxy_device, proxy. When the ds_device_open() is called, a proxy_user and proxy objectis created. A proxy_device object is created when the ds_device_set_filter() is called. The proxy_user and proxy_device extend port_info structure. When a packet is received from the user program or from the device, we have to retrieve the proxy object to get the corresponding information. This process is very similar as pflocal. When a user program exits, we need to destroy its proxy_user object and proxy object, and meanwhile, the proxy_device object related to the proxy object is also destroyed. +Two filters exist in eth-filter, one for outgoing packets and the other for incoming packets. These are BPF filters, which are ported from GNU Mach. These BPF filters only decide whether the packet should be forwarded, and they cannot decide the destination of the packet. The BPF instructions are generated by libpcap from the filter rules given by '-s' and '-r' or '-S' and '-R' options. + diff --git a/eth-filter/bpf_impl.c b/eth-filter/bpf_impl.c new file mode 100644 index 00000000..e9202a5f --- /dev/null +++ b/eth-filter/bpf_impl.c @@ -0,0 +1,872 @@ + /* + * Mach Operating System + * Copyright (c) 1993-1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/98 + * + * Network IO. + * + * Packet filter code taken from vaxif/enet.c written + * CMU and Stanford. + */ + +/* the code copied from device/net_io.c in Mach */ + +#include <arpa/inet.h> +#include <string.h> + +#include <mach.h> +#include <hurd.h> + +#include "bpf_impl.h" +#include "queue.h" +#include "util.h" + +queue_head_t rcv_port_list; /* input filter list */ +queue_head_t snd_port_list; /* output filter list */ + +/* + * Execute the filter program starting at pc on the packet p + * wirelen is the length of the original packet + * buflen is the amount of data present + * + * @p: packet data. + * @wirelen: data_count (in bytes) + * @hlen: header len (in bytes) + */ + +int +mach_bpf_do_filter(net_rcv_port_t infp, char *p, unsigned int wirelen, + char *header, unsigned int hlen, net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp) +{ + register bpf_insn_t pc, pc_end; + register unsigned int buflen; + + register unsigned long A, X; + register int k; + unsigned int mem[BPF_MEMWORDS]; + + /* Generic pointer to either HEADER or P according to the specified offset. */ + char *data = NULL; + + pc = ((bpf_insn_t) infp->filter) + 1; + /* filter[0].code is (NETF_BPF | flags) */ + pc_end = (bpf_insn_t)infp->filter_end; + buflen = NET_RCV_MAX; + *entpp = 0; /* default */ + + A = 0; + X = 0; + for (; pc < pc_end; ++pc) { + switch (pc->code) { + + default: + abort(); + case BPF_RET|BPF_K: + if (infp->rcv_port == MACH_PORT_NULL && + *entpp == 0) { + return 0; + } + return ((u_int)pc->k <= wirelen) ? + pc->k : wirelen; + + case BPF_RET|BPF_A: + if (infp->rcv_port == MACH_PORT_NULL && + *entpp == 0) { + return 0; + } + return ((u_int)A <= wirelen) ? + A : wirelen; + + case BPF_RET|BPF_MATCH_IMM: + if (mach_bpf_match ((net_hash_header_t)infp, pc->jt, mem, + hash_headpp, entpp)) { + return ((u_int)pc->k <= wirelen) ? + pc->k : wirelen; + } + return 0; + + case BPF_LD|BPF_W|BPF_ABS: + k = pc->k; + +load_word: + if ((u_int)k + sizeof(long) <= hlen) + data = header; + else if ((u_int)k + sizeof(long) <= buflen) { + k -= hlen; + data = p; + } else + return 0; + +#ifdef BPF_ALIGN + if (((int)(data + k) & 3) != 0) + A = EXTRACT_LONG(&data[k]); + else +#endif + A = ntohl(*(long *)(data + k)); + continue; + + case BPF_LD|BPF_H|BPF_ABS: + k = pc->k; + +load_half: + if ((u_int)k + sizeof(short) <= hlen) + data = header; + else if ((u_int)k + sizeof(short) <= buflen) { + k -= hlen; + data = p; + } else + return 0; + + A = EXTRACT_SHORT(&data[k]); + continue; + + case BPF_LD|BPF_B|BPF_ABS: + k = pc->k; + +load_byte: + if ((u_int)k < hlen) + data = header; + else if ((u_int)k < buflen) { + data = p; + k -= hlen; + } else + return 0; + + A = data[k]; + continue; + + case BPF_LD|BPF_W|BPF_LEN: + A = wirelen; + continue; + + case BPF_LDX|BPF_W|BPF_LEN: + X = wirelen; + continue; + + case BPF_LD|BPF_W|BPF_IND: + k = X + pc->k; + goto load_word; + + case BPF_LD|BPF_H|BPF_IND: + k = X + pc->k; + goto load_half; + + case BPF_LD|BPF_B|BPF_IND: + k = X + pc->k; + goto load_byte; + + case BPF_LDX|BPF_MSH|BPF_B: + k = pc->k; + if (k < hlen) + data = header; + else if (k < buflen) { + data = p; + k -= hlen; + } else + return 0; + + X = (data[k] & 0xf) << 2; + continue; + + case BPF_LD|BPF_IMM: + A = pc->k; + continue; + + case BPF_LDX|BPF_IMM: + X = pc->k; + continue; + + case BPF_LD|BPF_MEM: + A = mem[pc->k]; + continue; + + case BPF_LDX|BPF_MEM: + X = mem[pc->k]; + continue; + + case BPF_ST: + mem[pc->k] = A; + continue; + + case BPF_STX: + mem[pc->k] = X; + continue; + + case BPF_JMP|BPF_JA: + pc += pc->k; + continue; + + case BPF_JMP|BPF_JGT|BPF_K: + pc += (A > pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_K: + pc += (A >= pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_K: + pc += (A == pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_K: + pc += (A & pc->k) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGT|BPF_X: + pc += (A > X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JGE|BPF_X: + pc += (A >= X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JEQ|BPF_X: + pc += (A == X) ? pc->jt : pc->jf; + continue; + + case BPF_JMP|BPF_JSET|BPF_X: + pc += (A & X) ? pc->jt : pc->jf; + continue; + + case BPF_ALU|BPF_ADD|BPF_X: + A += X; + continue; + + case BPF_ALU|BPF_SUB|BPF_X: + A -= X; + continue; + + case BPF_ALU|BPF_MUL|BPF_X: + A *= X; + continue; + + case BPF_ALU|BPF_DIV|BPF_X: + if (X == 0) + return 0; + A /= X; + continue; + + case BPF_ALU|BPF_AND|BPF_X: + A &= X; + continue; + + case BPF_ALU|BPF_OR|BPF_X: + A |= X; + continue; + + case BPF_ALU|BPF_LSH|BPF_X: + A <<= X; + continue; + + case BPF_ALU|BPF_RSH|BPF_X: + A >>= X; + continue; + + case BPF_ALU|BPF_ADD|BPF_K: + A += pc->k; + continue; + + case BPF_ALU|BPF_SUB|BPF_K: + A -= pc->k; + continue; + + case BPF_ALU|BPF_MUL|BPF_K: + A *= pc->k; + continue; + + case BPF_ALU|BPF_DIV|BPF_K: + A /= pc->k; + continue; + + case BPF_ALU|BPF_AND|BPF_K: + A &= pc->k; + continue; + + case BPF_ALU|BPF_OR|BPF_K: + A |= pc->k; + continue; + + case BPF_ALU|BPF_LSH|BPF_K: + A <<= pc->k; + continue; + + case BPF_ALU|BPF_RSH|BPF_K: + A >>= pc->k; + continue; + + case BPF_ALU|BPF_NEG: + A = -A; + continue; + + case BPF_MISC|BPF_TAX: + X = A; + continue; + + case BPF_MISC|BPF_TXA: + A = X; + continue; + } + } + + return 0; +} + +/* + * Return 1 if the 'f' is a valid filter program without a MATCH + * instruction. Return 2 if it is a valid filter program with a MATCH + * instruction. Otherwise, return 0. + * The constraints are that each jump be forward and to a valid + * code. The code must terminate with either an accept or reject. + * 'valid' is an array for use by the routine (it must be at least + * 'len' bytes long). + * + * The kernel needs to be able to verify an application's filter code. + * Otherwise, a bogus program could easily crash the system. + */ +int +mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match) +{ + register int i, j, len; + register bpf_insn_t p; + + len = BPF_BYTES2LEN(bytes); + + /* + * f[0].code is already checked to be (NETF_BPF | flags). + * So skip f[0]. + */ + + for (i = 1; i < len; ++i) { + /* + * Check that that jumps are forward, and within + * the code block. + */ + p = &f[i]; + if (BPF_CLASS(p->code) == BPF_JMP) { + register int from = i + 1; + + if (BPF_OP(p->code) == BPF_JA) { + if (from + p->k >= len) + return 0; + } + else if (from + p->jt >= len || from + p->jf >= len) + return 0; + } + /* + * Check that memory operations use valid addresses. + */ + if ((BPF_CLASS(p->code) == BPF_ST || + (BPF_CLASS(p->code) == BPF_LD && + (p->code & 0xe0) == BPF_MEM)) && + (p->k >= BPF_MEMWORDS || p->k < 0)) { + return 0; + } + /* + * Check for constant division by 0. + */ + if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) { + return 0; + } + /* + * Check for match instruction. + * Only one match instruction per filter is allowed. + */ + if (p->code == (BPF_RET|BPF_MATCH_IMM)) { + if (*match != 0 || + p->jt == 0 || + p->jt > N_NET_HASH_KEYS) + return 0; + i += p->jt; /* skip keys */ + if (i + 1 > len) + return 0; + + for (j = 1; j <= p->jt; j++) { + if (p[j].code != (BPF_MISC|BPF_KEY)) + return 0; + } + + *match = p; + } + } + if (BPF_CLASS(f[len - 1].code) == BPF_RET) + return ((*match == 0) ? 1 : 2); + else + return 0; +} + +int +mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes) +{ + register int count; + + count = BPF_BYTES2LEN(bytes); + for (; count--; f1++, f2++) { + if (!BPF_INSN_EQ(f1, f2)) { + if ( f1->code == (BPF_MISC|BPF_KEY) && + f2->code == (BPF_MISC|BPF_KEY) ) + continue; + return FALSE; + } + }; + return TRUE; +} + +unsigned int +mach_bpf_hash (int n, unsigned int *keys) +{ + register unsigned int hval = 0; + + while (n--) { + hval += *keys++; + } + return (hval % NET_HASH_SIZE); +} + + +int +mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys, + net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp) +{ + register net_hash_entry_t head, entp; + register int i; + + if (n_keys != hash->n_keys) + return FALSE; + + *hash_headpp = &hash->table[mach_bpf_hash(n_keys, keys)]; + head = **hash_headpp; + + if (head == 0) + return FALSE; + + HASH_ITERATE (head, entp) + { + for (i = 0; i < n_keys; i++) { + if (keys[i] != entp->keys[i]) + break; + } + if (i == n_keys) { + *entpp = entp; + return TRUE; + } + } + HASH_ITERATE_END (head, entp) + return FALSE; +} + +/* + * Removes a hash entry (ENTP) from its queue (HEAD). + * If the reference count of filter (HP) becomes zero and not USED, + * HP is removed from the corresponding port lists and is freed. + */ + +int +hash_ent_remove (net_hash_header_t hp, int used, + net_hash_entry_t *head, net_hash_entry_t entp, queue_entry_t *dead_p) +{ + hp->ref_count--; + + if (*head == entp) { + if (queue_empty((queue_t) entp)) { + *head = 0; + ENQUEUE_DEAD(*dead_p, entp, chain); + if (hp->ref_count == 0 && !used) { + if (((net_rcv_port_t)hp)->filter[0] & NETF_IN) + queue_remove(&rcv_port_list, + (net_rcv_port_t)hp, + net_rcv_port_t, chain); + if (((net_rcv_port_t)hp)->filter[0] & NETF_OUT) + queue_remove(&snd_port_list, + (net_rcv_port_t)hp, + net_rcv_port_t, chain); + hp->n_keys = 0; + return TRUE; + } + return FALSE; + } else { + *head = (net_hash_entry_t)queue_next((queue_t) entp); + } + } + + remqueue((queue_t)*head, (queue_entry_t)entp); + ENQUEUE_DEAD(*dead_p, entp, chain); + return FALSE; +} + +/* + * net_free_dead_infp (dead_infp) + * queue_entry_t dead_infp; list of dead net_rcv_port_t. + * + * Deallocates dead net_rcv_port_t. + * No locks should be held when called. + */ +void +net_free_dead_infp (queue_entry_t dead_infp) +{ + register net_rcv_port_t infp, nextfp; + + for (infp = (net_rcv_port_t) dead_infp; infp != 0; infp = nextfp) { + nextfp = (net_rcv_port_t) queue_next(&infp->chain); + mach_port_deallocate(mach_task_self(), infp->rcv_port); + free(infp); + debug ("a dead infp is freed\n"); + } +} + +/* + * net_free_dead_entp (dead_entp) + * queue_entry_t dead_entp; list of dead net_hash_entry_t. + * + * Deallocates dead net_hash_entry_t. + * No locks should be held when called. + */ +void +net_free_dead_entp (queue_entry_t dead_entp) +{ + register net_hash_entry_t entp, nextentp; + + for (entp = (net_hash_entry_t)dead_entp; entp != 0; entp = nextentp) { + nextentp = (net_hash_entry_t) queue_next(&entp->chain); + + mach_port_deallocate(mach_task_self(), entp->rcv_port); + free(entp); + debug ("a dead entp is freed\n"); + } +} + +/* + * Set a filter for a network interface. + * + * We are given a naked send right for the rcv_port. + * If we are successful, we must consume that right. + */ +error_t +net_set_filter (mach_port_t rcv_port, int priority, + filter_t *filter, unsigned int filter_count) +{ + int filter_bytes; + bpf_insn_t match; + register net_rcv_port_t infp, my_infp; + net_rcv_port_t nextfp; + net_hash_header_t hhp; + register net_hash_entry_t entp, hash_entp=NULL; + net_hash_entry_t *head, nextentp; + queue_entry_t dead_infp, dead_entp; + int i; + int ret, is_new_infp; + io_return_t rval; + boolean_t in, out; + + /* Check the filter syntax. */ + + debug ("filter_count: %d, filter[0]: %d\n", filter_count, filter[0]); + + filter_bytes = CSPF_BYTES (filter_count); + match = (bpf_insn_t) 0; + + if (filter_count == 0) { + return (D_INVALID_OPERATION); + } else if (!((filter[0] & NETF_IN) || (filter[0] & NETF_OUT))) { + return (D_INVALID_OPERATION); /* NETF_IN or NETF_OUT required */ + } else if ((filter[0] & NETF_TYPE_MASK) == NETF_BPF) { + ret = mach_bpf_validate((bpf_insn_t)filter, filter_bytes, &match); + if (!ret) + return (D_INVALID_OPERATION); + } else { + return (D_INVALID_OPERATION); + } + debug ("net_set_filter: check over\n"); + + rval = D_SUCCESS; /* default return value */ + dead_infp = dead_entp = 0; + + if (match == (bpf_insn_t) 0) { + /* + * If there is no match instruction, we allocate + * a normal packet filter structure. + */ + my_infp = (net_rcv_port_t) calloc(1, sizeof(struct net_rcv_port)); + my_infp->rcv_port = rcv_port; + is_new_infp = TRUE; + } else { + /* + * If there is a match instruction, we assume there will be + * multiple sessions with a common substructure and allocate + * a hash table to deal with them. + */ + my_infp = 0; + hash_entp = (net_hash_entry_t) calloc(1, sizeof(struct net_hash_entry)); + is_new_infp = FALSE; + } + + /* + * Look for an existing filter on the same reply port. + * Look for filters with dead ports (for GC). + * Look for a filter with the same code except KEY insns. + */ + void check_filter_list(queue_head_t *if_port_list) + { + FILTER_ITERATE(if_port_list, infp, nextfp, &infp->chain) { + if (infp->rcv_port == MACH_PORT_NULL) { + if (match != 0 + && infp->priority == priority + && my_infp == 0 + && (infp->filter_end - infp->filter) == filter_count + && mach_bpf_eq((bpf_insn_t)infp->filter, + (bpf_insn_t)filter, filter_bytes)) { + my_infp = infp; + } + + for (i = 0; i < NET_HASH_SIZE; i++) { + head = &((net_hash_header_t) infp)->table[i]; + if (*head == 0) + continue; + + /* + * Check each hash entry to make sure the + * destination port is still valid. Remove + * any invalid entries. + */ + entp = *head; + do { + nextentp = (net_hash_entry_t) entp->he_next; + + /* checked without + ip_lock(entp->rcv_port) */ + if (entp->rcv_port == rcv_port) { + ret = hash_ent_remove ((net_hash_header_t)infp, + (my_infp == infp), + head, + entp, + &dead_entp); + if (ret) + goto hash_loop_end; + } + + entp = nextentp; + /* While test checks head since hash_ent_remove + * might modify it. + */ + } while (*head != 0 && entp != *head); + } + +hash_loop_end: + ; + } else if (infp->rcv_port == rcv_port) { + /* Remove the old filter from lists */ + if (infp->filter[0] & NETF_IN) + queue_remove(&rcv_port_list, infp, + net_rcv_port_t, chain); + if (infp->filter[0] & NETF_OUT) + queue_remove(&snd_port_list, infp, + net_rcv_port_t, chain); + + ENQUEUE_DEAD(dead_infp, infp, chain); + } + } + FILTER_ITERATE_END + } + + in = (filter[0] & NETF_IN) != 0; + out = (filter[0] & NETF_OUT) != 0; + + if (in) + check_filter_list(&rcv_port_list); + if (out) + check_filter_list(&snd_port_list); + + if (my_infp == 0) { + /* Allocate a dummy infp */ + for (i = 0; i < N_NET_HASH; i++) { + if (filter_hash_header[i].n_keys == 0) + break; + } + if (i == N_NET_HASH) { + + mach_port_deallocate(mach_task_self() , rcv_port); + if (match != 0) + free(hash_entp); + + rval = D_NO_MEMORY; + goto clean_and_return; + } + + hhp = &filter_hash_header[i]; + hhp->n_keys = match->jt; + + hhp->ref_count = 0; + for (i = 0; i < NET_HASH_SIZE; i++) + hhp->table[i] = 0; + + my_infp = (net_rcv_port_t)hhp; + my_infp->rcv_port = MACH_PORT_NULL; /* indication of dummy */ + is_new_infp = TRUE; + } + + if (is_new_infp) { + my_infp->priority = priority; + my_infp->rcv_count = 0; + + /* Copy filter program. */ + memcpy (my_infp->filter, filter, filter_bytes); + my_infp->filter_end = + (filter_t *)((char *)my_infp->filter + filter_bytes); + + /* Insert my_infp according to priority */ + if (in) { + queue_iterate(&rcv_port_list, infp, net_rcv_port_t, chain) + if (priority > infp->priority) + break; + + queue_enter(&rcv_port_list, my_infp, net_rcv_port_t, chain); + } + + if (out) { + queue_iterate(&snd_port_list, infp, net_rcv_port_t, chain) + if (priority > infp->priority) + break; + + queue_enter(&snd_port_list, my_infp, net_rcv_port_t, chain); + } + } + + if (match != 0) { + /* Insert to hash list */ + net_hash_entry_t *p; + + hash_entp->rcv_port = rcv_port; + for (i = 0; i < match->jt; i++) /* match->jt is n_keys */ + hash_entp->keys[i] = match[i+1].k; + p = &((net_hash_header_t)my_infp)-> + table[mach_bpf_hash(match->jt, hash_entp->keys)]; + + /* Not checking for the same key values */ + if (*p == 0) { + queue_init ((queue_t) hash_entp); + *p = hash_entp; + } else { + enqueue_tail((queue_t)*p, (queue_entry_t)hash_entp); + } + + ((net_hash_header_t)my_infp)->ref_count++; + } + +clean_and_return: + /* No locks are held at this point. */ + + if (dead_infp != 0) + net_free_dead_infp(dead_infp); + if (dead_entp != 0) + net_free_dead_entp(dead_entp); + + return (rval); +} + +/* Remove the filter in if_port_list whose port for delivering is dead_port. */ +void +remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port) +{ + net_rcv_port_t infp; + net_rcv_port_t nextfp; + net_hash_entry_t *head, nextentp; + queue_entry_t dead_infp, dead_entp; + net_hash_entry_t entp = NULL; + int i, ret; + + dead_infp = dead_entp = 0; + FILTER_ITERATE (if_port_list, infp, nextfp, &infp->chain) { + if (infp->rcv_port == MACH_PORT_NULL) { + for (i = 0; i < NET_HASH_SIZE; i++) { + head = &((net_hash_header_t) infp)->table[i]; + if (*head == 0) + continue; + + /* + * Check each hash entry to make sure the + * destination port is still valid. Remove + * any invalid entries. + */ + entp = *head; + do { + nextentp = (net_hash_entry_t) entp->he_next; + + /* checked without + ip_lock(entp->rcv_port) */ + if (entp->rcv_port == dead_port) { + ret = hash_ent_remove ((net_hash_header_t) infp, + 0, + head, + entp, + &dead_entp); + if (ret) + goto hash_loop_end; + } + + entp = nextentp; + /* While test checks head since hash_ent_remove + * might modify it. + */ + } while (*head != 0 && entp != *head); + } + +hash_loop_end: + ; + } else if (infp->rcv_port == dead_port) { + /* Remove the old filter from lists */ + if (infp->filter[0] & NETF_IN) + queue_remove(&rcv_port_list, infp, + net_rcv_port_t, chain); + if (infp->filter[0] & NETF_OUT) + queue_remove(&snd_port_list, infp, + net_rcv_port_t, chain); + + ENQUEUE_DEAD(dead_infp, infp, chain); + } + } + FILTER_ITERATE_END + + if (dead_infp != 0) + net_free_dead_infp(dead_infp); + if (dead_entp != 0) + net_free_dead_entp(dead_entp); +} + +/* Remove all filters whose port for delivering is dead_port. */ +void +remove_dead_filter_from_all (mach_port_t dead_port) +{ + remove_dead_filter (&snd_port_list, dead_port); + remove_dead_filter (&rcv_port_list, dead_port); +} + +void +bpf_init() +{ + queue_init (&rcv_port_list); + queue_init (&snd_port_list); +} diff --git a/eth-filter/bpf_impl.h b/eth-filter/bpf_impl.h new file mode 100644 index 00000000..de61df12 --- /dev/null +++ b/eth-filter/bpf_impl.h @@ -0,0 +1,158 @@ + /* + * Mach Operating System + * Copyright (c) 1993-1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Author: David B. Golub, Carnegie Mellon University + * Date: 3/98 + * + * Network IO. + * + * Packet filter code taken from vaxif/enet.c written + * CMU and Stanford. + */ + +/* the code copied from device/net_io.c in Mach */ + +#ifndef BPF_IMPL_H +#define BPF_IMPL_H + +#include <mach.h> +#include <hurd.h> +#include <device/bpf.h> + +#include "queue.h" + +typedef unsigned short filter_t; +typedef filter_t *filter_array_t; + +#define NET_MAX_FILTER 128 /* was 64, bpf programs are big */ + +#define NET_HASH_SIZE 256 +#define N_NET_HASH 4 +#define N_NET_HASH_KEYS 4 + +#ifndef BPF_ALIGN +#define EXTRACT_SHORT(p) ((u_short)ntohs(*(u_short *)p)) +#define EXTRACT_LONG(p) (ntohl(*(u_long *)p)) +#else +#define EXTRACT_SHORT(p)\ + ((u_short)\ + ((u_short)*((u_char *)p+0)<<8|\ + (u_short)*((u_char *)p+1)<<0)) +#define EXTRACT_LONG(p)\ + ((u_long)*((u_char *)p+0)<<24|\ + (u_long)*((u_char *)p+1)<<16|\ + (u_long)*((u_char *)p+2)<<8|\ + (u_long)*((u_char *)p+3)<<0) +#endif + +#define HASH_ITERATE(head, elt) (elt) = (net_hash_entry_t) (head); do { +#define HASH_ITERATE_END(head, elt) \ + (elt) = (net_hash_entry_t) queue_next((queue_entry_t) (elt)); \ +} while ((elt) != (head)); + +#define FILTER_ITERATE(if_port_list, fp, nextfp, chain) \ + for ((fp) = (net_rcv_port_t) queue_first(if_port_list); \ + !queue_end(if_port_list, (queue_entry_t)(fp)); \ + (fp) = (nextfp)) { \ + (nextfp) = (net_rcv_port_t) queue_next(chain); +#define FILTER_ITERATE_END } + +/* entry_p must be net_rcv_port_t or net_hash_entry_t */ +#define ENQUEUE_DEAD(dead, entry_p, chain) { \ + queue_next(&(entry_p)->chain) = (queue_entry_t) (dead); \ + (dead) = (queue_entry_t)(entry_p); \ +} + +#define CSPF_BYTES(n) ((n) * sizeof (filter_t)) + +/* + * Receive port for net, with packet filter. + * This data structure by itself represents a packet + * filter for a single session. + */ +struct net_rcv_port { + queue_chain_t chain; + mach_port_t rcv_port; /* port to send packet to */ + int rcv_count; /* number of packets received */ + int priority; /* priority for filter */ + filter_t *filter_end; /* pointer to end of filter */ + filter_t filter[NET_MAX_FILTER]; + /* filter operations */ +}; +typedef struct net_rcv_port *net_rcv_port_t; + +/* + * A single hash entry. + */ +struct net_hash_entry { + queue_chain_t chain; /* list of entries with same hval */ +#define he_next chain.next +#define he_prev chain.prev + mach_port_t rcv_port; /* destination port */ + unsigned int keys[N_NET_HASH_KEYS]; +}; +typedef struct net_hash_entry *net_hash_entry_t; + +/* + * This structure represents a packet filter with multiple sessions. + * + * For example, all application level TCP sessions might be + * represented by one of these structures. It looks like a + * net_rcv_port struct so that both types can live on the + * same packet filter queues. + */ +struct net_hash_header { + struct net_rcv_port rcv; + int n_keys; /* zero if not used */ + int ref_count; /* reference count */ + net_hash_entry_t table[NET_HASH_SIZE]; +} filter_hash_header[N_NET_HASH]; + +typedef struct net_hash_header *net_hash_header_t; + +int mach_bpf_do_filter(net_rcv_port_t infp, char *p, unsigned int wirelen, + char *header, unsigned int hlen, net_hash_entry_t **hash_headpp, + net_hash_entry_t *entpp); +int mach_bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match); +int mach_bpf_eq (bpf_insn_t f1, bpf_insn_t f2, int bytes); +unsigned int mach_bpf_hash (int n, unsigned int *keys); +int mach_bpf_match (net_hash_header_t hash, int n_keys, unsigned int *keys, + net_hash_entry_t **hash_headpp, net_hash_entry_t *entpp); + +error_t net_set_filter(mach_port_t rcv_port, int priority, + filter_t *filter, unsigned int filter_count); +int hash_ent_remove (net_hash_header_t hp, int used, net_hash_entry_t *head, + net_hash_entry_t entp, queue_entry_t *dead_p); +void net_free_dead_infp (queue_entry_t dead_infp); +void net_free_dead_entp (queue_entry_t dead_entp); +void remove_dead_filter (queue_head_t *if_port_list, mach_port_t dead_port); +void remove_dead_filter_from_all (mach_port_t dead_port); +void bpf_init(); + +extern queue_head_t rcv_port_list; /* input filter list */ +extern queue_head_t snd_port_list; /* output filter list */ + +#endif /* _DEVICE_BPF_H_ */ diff --git a/eth-filter/filter.c b/eth-filter/filter.c new file mode 100644 index 00000000..7720b0c7 --- /dev/null +++ b/eth-filter/filter.c @@ -0,0 +1,901 @@ +/* + Copyright (C) 2008 Free Software Foundation, Inc. + Written by Zheng Da. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + The GNU Hurd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * This program is a filter translator which sits on the top of the network + * interface. + * It provides two filters: for outgoing packets and for incoming packets. + * Only one pfinet server are allowed to run on the top of the translator. + */ + +#include <argp.h> +#include <errno.h> +#include <error.h> +#include <stddef.h> + +#include <fcntl.h> +#include <hurd.h> +#include <mach.h> +#include <device/device.h> +#include <hurd/trivfs.h> +#include <hurd/ports.h> +#include <hurd/ihash.h> + +#include "ourdevice_S.h" +#include "notify_S.h" +#include "bpf_impl.h" +#include "util.h" + +struct proxy_user +{ + struct port_info pi; + struct proxy *proxy; +}; + +struct proxy_device +{ + struct port_info pi; + struct proxy *proxy; +}; + +struct proxy +{ + struct proxy_device *device; + mach_port_t deliver_port; + hurd_ihash_locp_t p_deliverport_hashloc; + mach_port_t device_port; +}; + +int deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, + mach_port_t dest); + +static struct hurd_ihash proxy_deliverport_ht += HURD_IHASH_INITIALIZER (offsetof (struct proxy, p_deliverport_hashloc)); + +/* The name of the network interface that the filter translator sits on. */ +static char *device_file; +const char *argp_program_version = "eth-filter 0.1"; +const char *argp_program_bug_address = "<bug-hurd@gnu.org>"; +static const char doc[] = "Hurd filter translator."; +static const struct argp_option options[] = +{ + {"interface", 'i', "DEVICE", 0, + "Network interface to use", 2}, + {"send-filter", 's', "string", 0, + "The filter rule which applies to the outgoing packet", 4}, + {"receive-filter", 'r', "string", 0, + "The filter rule which applies to the ingoing packet", 5}, + {"send-ip-range", 'S', "IP range", 0, + "A range of IP to create the send filter", 6}, + {"receive-ip-range", 'R', "IP range", 0, + "A range of IP to create the receive filter", 7}, + {0} +}; + +/* A filter allows every packet to be sent and be received. */ +static struct bpf_insn default_snd_filter[] = +{ + {NETF_OUT|NETF_BPF, 0, 0, 0}, + {6, 0, 0, 1500} +}; +static struct bpf_insn default_rcv_filter[] = +{ + {NETF_IN|NETF_BPF, 0, 0, 0}, + {6, 0, 0, 1500} +}; +static char *snd_string = NULL; +static struct bpf_insn *snd_filter = NULL; +static int snd_filter_length; +static char *rcv_string = NULL; +static struct bpf_insn *rcv_filter = NULL; +static int rcv_filter_length; + +/* Port bucket we service requests on. */ +struct port_bucket *port_bucket; + +struct port_class *user_portclass; +struct port_class *device_portclass; +struct port_class *other_portclass; + +/* Trivfs hooks. */ +int trivfs_fstype = FSTYPE_MISC; +int trivfs_fsid = 0; +int trivfs_support_read = 0; +int trivfs_support_write = 0; +int trivfs_support_exec = 0; +int trivfs_allow_open = O_READ | O_WRITE; + +struct port_class *trivfs_protid_portclasses[1]; +struct port_class *trivfs_cntl_portclasses[1]; +int trivfs_protid_nportclasses = 1; +int trivfs_cntl_nportclasses = 1; + +/* For getting the notification of ports from the kernel. */ +struct port_info *notify_pi; + +error_t +create_proxy_user (struct proxy *proxy, mach_port_t *port) +{ + error_t err; + struct proxy_user *user; + + err = ports_create_port (user_portclass, port_bucket, sizeof (*user), &user); + if (err) + return err; + user->proxy = proxy; + + *port = ports_get_right (user); + ports_port_deref (user); + return 0; +} + +error_t +create_proxy_device (struct proxy *proxy, mach_port_t *port) +{ + error_t err; + struct proxy_device *device; + + err = ports_create_port (device_portclass, port_bucket, sizeof (*device), &device); + if (err) + return err; + device->proxy = proxy; + proxy->device = device; + + *port = ports_get_right (device); + ports_port_deref (device); + return 0; +} + +void +clean_proxy_user (void *p) +{ + struct proxy_user *user = p; + struct proxy *proxy = user->proxy; + + if (proxy->p_deliverport_hashloc) + hurd_ihash_locp_remove (&proxy_deliverport_ht, proxy->p_deliverport_hashloc); + + if (proxy->deliver_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), proxy->deliver_port); + if (proxy->device_port != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), proxy->device_port); + + if (proxy->device) + ports_destroy_right (proxy->device); + + free (proxy); +} + +void +clean_proxy_device (void *p) +{ + struct proxy_device *device = p; + if (device->proxy) + device->proxy->device = NULL; +} + +static int +filter_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + extern int device_server (mach_msg_header_t *, mach_msg_header_t *); + extern int notify_server (mach_msg_header_t *, mach_msg_header_t *); + extern int ethernet_demuxer (mach_msg_header_t *, mach_msg_header_t *); + return device_server (inp, outp) || notify_server (inp, outp) + || ethernet_demuxer (inp, outp) || trivfs_demuxer (inp, outp); +} + +int +ethernet_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + struct net_rcv_msg *msg = (struct net_rcv_msg *) inp; + struct proxy_device *device; + struct proxy *proxy; + mach_msg_header_t header; + + if (inp->msgh_id != NET_RCV_MSG_ID) + return 0; + + device = ports_lookup_port (port_bucket, inp->msgh_local_port, device_portclass); + if (device == NULL) + return 0; + proxy = device->proxy; + ports_port_deref (device); + + if (proxy && proxy->deliver_port != MACH_PORT_NULL) + { + header = msg->msg_hdr; + deliver_msg (msg, &rcv_port_list, proxy->deliver_port); + msg->msg_hdr = header; + } + + return 1; +} + +/* Implementation of notify interface */ +kern_return_t +do_mach_notify_port_deleted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_msg_accepted (mach_port_t notify, + mach_port_t name) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_port_destroyed (mach_port_t notify, + mach_port_t port) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_no_senders (mach_port_t notify, + mach_port_mscount_t mscount) +{ + debug ("do_mach_notify_no_senders is called\n"); + return ports_do_mach_notify_no_senders (notify, mscount); +} + +kern_return_t +do_mach_notify_send_once (mach_port_t notify) +{ + return EOPNOTSUPP; +} + +kern_return_t +do_mach_notify_dead_name (mach_port_t notify, + mach_port_t name) +{ + struct proxy *proxy; + + debug ("do_mach_notify_dead_name is called\n"); + mach_port_deallocate (mach_task_self (), name); + proxy = hurd_ihash_find (&proxy_deliverport_ht, name); + if (proxy) + { + proxy->deliver_port = MACH_PORT_NULL; + return 0; + } + return EINVAL; +} + +/* Implementation of device interface */ +kern_return_t +ds_xxx_device_set_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t statu_cnt) +{ + return D_INVALID_OPERATION; +} + +kern_return_t +ds_xxx_device_get_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t *statuscnt) +{ + return D_INVALID_OPERATION; +} + +kern_return_t +ds_xxx_device_set_filter (device_t device, mach_port_t rec, + int pri, filter_array_t filt, size_t len) +{ + return D_INVALID_OPERATION; +} + +kern_return_t +ds_device_open (mach_port_t master_port, mach_port_t reply_port, + mach_msg_type_name_t reply_portPoly, + dev_mode_t mode, dev_name_t name, mach_port_t *device, + mach_msg_type_name_t *devicetype) +{ + kern_return_t err; + mach_port_t master_device; + mach_port_t user_port; + int file_mode = 0; + struct proxy *proxy; + + if (device_file == NULL) + return D_NO_SUCH_DEVICE; + + if (mode | D_WRITE) + file_mode |= O_WRITE; + if (mode | D_READ) + file_mode |= O_READ; + master_device = file_name_lookup (device_file, file_mode, 0); + if (master_device == MACH_PORT_NULL) + return errno; + + proxy = (struct proxy *)calloc (1, sizeof (*proxy)); + if (proxy == NULL) + { + mach_port_deallocate (mach_task_self (), master_device); + return D_NO_MEMORY; + } + + err = device_open (master_device, mode, name, &proxy->device_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err != KERN_SUCCESS) + { + free (proxy); + return err; + } + + err = create_proxy_user (proxy, &user_port); + if (err) + { + mach_port_deallocate (mach_task_self (), master_device); + free (proxy); + return err; + } + + *device = user_port; + *devicetype = MACH_MSG_TYPE_MAKE_SEND; + + return 0; +} + +kern_return_t +ds_device_close (device_t device) +{ + return 0; +} + +kern_return_t +ds_device_write (device_t device, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, io_buf_ptr_t data, size_t datalen, + int *bytes_written) +{ + int ret_count = 0; + int has_filter = 0; + net_hash_entry_t entp, *hash_headp; + net_rcv_port_t infp, nextfp; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + { + vm_deallocate (mach_task_self (), data, datalen); + return D_INVALID_OPERATION; + } + proxy = user->proxy; + ports_port_deref (user); + + /* The packet can be sent as long as it passes one filter, + * even thought there is usually only one filter in the list. */ + FILTER_ITERATE (&snd_port_list, infp, nextfp, &infp->chain) + { + has_filter = 1; + ret_count = mach_bpf_do_filter (infp, + data + sizeof (struct ethhdr), + datalen - sizeof (struct ethhdr), + data, sizeof (struct ethhdr), + &hash_headp, &entp); + if (ret_count) + break; + } + FILTER_ITERATE_END + + error_t err; + if (ret_count || !has_filter) + { + print_pack (data, datalen); + debug ("before writing a packet from the device.\n"); + err = device_write (proxy->device_port, mode , recnum , + data, datalen, bytes_written); + debug ("after writing a packet from the device.\n"); + } + else + { + *bytes_written = datalen; + err = 0; + } + vm_deallocate (mach_task_self (), data, datalen); + return err; +} + +kern_return_t +ds_device_write_inband (device_t device, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, io_buf_ptr_inband_t data, + size_t datalen, int *bytes_written) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_write_inband (proxy->device_port, mode, recnum, data, + datalen, bytes_written); + return ret; +} + +kern_return_t +ds_device_read (device_t device, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, int bytes_wanted, + io_buf_ptr_t *data, size_t *datalen) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_read (proxy->device_port, mode, recnum, + bytes_wanted, data, datalen); + return ret; +} + +kern_return_t +ds_device_read_inband (device_t device, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, int bytes_wanted, + io_buf_ptr_inband_t data, size_t *datalen) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_read_inband (proxy->device_port, mode, recnum, + bytes_wanted, data, datalen); + return ret; +} + +kern_return_t +ds_device_map (device_t device, vm_prot_t prot, vm_offset_t offset, + vm_size_t size, memory_object_t *pager, int unmap) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_map (proxy->device_port, prot, offset, + size, pager, unmap); + return ret; +} + +kern_return_t +ds_device_set_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t statuslen) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_set_status (proxy->device_port, flavor, + status, statuslen); + return ret; +} + +kern_return_t +ds_device_get_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t *statuslen) +{ + kern_return_t ret; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + ret = device_get_status (proxy->device_port, flavor, status, statuslen); + return ret; +} + +kern_return_t +ds_device_set_filter (device_t device, mach_port_t receive_port, + int priority, filter_array_t filter, size_t filterlen) +{ + mach_port_t tmp; + kern_return_t err; + mach_port_t device_receive_port; + struct proxy_user *user; + struct proxy *proxy; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + ports_port_deref (user); + + if (proxy->device == NULL) + { + error_t err; + err = create_proxy_device (proxy, &device_receive_port); + if (err) + return err; + } + else + device_receive_port = ports_get_right (proxy->device); + + /* Set the filter from pfinet into the interface, + * but the packet will be delivered to the translator, + * so the translator has the chance to filter some packets. */ + err = device_set_filter (proxy->device_port, + device_receive_port, + MACH_MSG_TYPE_MAKE_SEND, priority, + filter, filterlen); + if (err) + return err; + + proxy->deliver_port = receive_port; + hurd_ihash_add (&proxy_deliverport_ht, receive_port, proxy); + + err = mach_port_request_notification (mach_task_self (), receive_port, + MACH_NOTIFY_DEAD_NAME, 0, + ports_get_right (notify_pi), + MACH_MSG_TYPE_MAKE_SEND_ONCE, &tmp); + if (tmp != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), tmp); + + return err; +} + +void +trivfs_modify_stat (struct trivfs_protid *cred, io_statbuf_t *stat) +{ +} + +error_t +trivfs_goaway (struct trivfs_control *fsys, int flags) +{ + int count; + + /* Stop new requests. */ + ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]); + ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]); + + count = ports_count_class (user_portclass); + debug ("the number of ports alive: %d\n", count); + + if (count && !(flags & FSYS_GOAWAY_FORCE)) + { + /* We won't go away, so start things going again... */ + ports_enable_class (trivfs_protid_portclasses[0]); + ports_resume_class_rpcs (trivfs_cntl_portclasses[0]); + ports_resume_class_rpcs (trivfs_protid_portclasses[0]); + return EBUSY; + } + + debug ("the translator is gone away\n"); + exit (0); +} + +/* Convert the network address input by the user into + * a form that is accepted by libpcap. */ +int +correct_net_addr (char *orig, char *result, int result_len) +{ + char *ptr; + int netmask_len; + int remain_bits; + int remain_bytes; + char netmask; + char addr[4]; + char buf[INET_ADDRSTRLEN]; + int i; + + ptr = strstr (orig, "/"); + if (ptr == NULL) + { + strncpy (result, orig, result_len); + return 0; + } + + *ptr = 0; + ptr++; + netmask_len = atoi (ptr); + if (inet_pton (AF_INET, orig, addr) < 0) + { + perror ("inet_pton"); + return -1; + } + remain_bits = netmask_len % 8; + netmask = ~0; + netmask >>= 8 - remain_bits; + netmask <<= 8 - remain_bits; + remain_bytes = netmask_len / 8; + addr[remain_bytes] &= netmask; + for (i=remain_bytes+1 ; i < 4 ; i++) + addr[i] = 0; + + snprintf (result, result_len, "%s/%s", + inet_ntop (AF_INET, addr, buf, INET_ADDRSTRLEN), ptr); + return 0; +} + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + struct bpf_insn *trans_filter_program (char *str, int send, + int *filter_len); + char buf[1024]; + char addr[INET_ADDRSTRLEN+4]; + + switch (opt) + { + case 'i': + device_file = arg; + break; + case 's': + if (snd_filter && snd_filter != default_snd_filter) + free (snd_filter); + if (snd_string) + free (snd_string); + snd_string = strdup (arg); + snd_filter = trans_filter_program (arg, 1, &snd_filter_length); + if (!snd_filter) + return ARGP_ERR_UNKNOWN; + break; + case 'r': + if (rcv_filter && rcv_filter != default_rcv_filter) + free (rcv_filter); + if (rcv_string) + free (rcv_string); + rcv_string = strdup (arg); + rcv_filter = trans_filter_program (arg, 0, &rcv_filter_length); + if (!rcv_filter) + return ARGP_ERR_UNKNOWN; + break; + case 'S': + if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0) + return 0; + snprintf (buf, sizeof (buf), "arp or (ip and src net %s)", addr); + if (snd_filter && snd_filter != default_snd_filter) + free (snd_filter); + if (snd_string) + free (snd_string); + snd_string = strdup (buf); + snd_filter = trans_filter_program (buf, 1, &snd_filter_length); + if (!snd_filter) + return ARGP_ERR_UNKNOWN; + break; + case 'R': + if (correct_net_addr (arg, addr, INET_ADDRSTRLEN+4) < 0) + return 0; + snprintf (buf, sizeof (buf), "arp or (ip and dst net %s)", addr); + if (rcv_filter && rcv_filter != default_rcv_filter) + free (rcv_filter); + if (rcv_string) + free (rcv_string); + rcv_string = strdup (buf); + rcv_filter = trans_filter_program (buf, 0, &rcv_filter_length); + if (!rcv_filter) + return ARGP_ERR_UNKNOWN; + break; + case ARGP_KEY_ERROR: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_INIT: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static const struct argp argp = { options, parse_opt, 0, doc }; + +error_t +trivfs_append_args (struct trivfs_control *fsys, + char **argz, size_t *argz_len) +{ + error_t err; + char *opt; + + if (device_file) + { + asprintf(&opt, "--interface=\"%s\"", device_file); + argz_add (argz, argz_len, opt); + free (opt); + } + + if (snd_string) + { + asprintf(&opt, "--send-filter=\"%s\"", snd_string); + argz_add (argz, argz_len, opt); + free (opt); + } + + if (rcv_string) + { + asprintf(&opt, "--receive-filter=\"%s\"", rcv_string); + argz_add (argz, argz_len, opt); + free (opt); + } + + return 0; +} + +error_t +trivfs_set_options (struct trivfs_control *fsys, char *argz, size_t argz_len) +{ + error_t err; + + err = fshelp_set_options (&argp, 0, argz, argz_len, fsys); + if (err) + return err; + + err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) snd_filter, + snd_filter_length); + if (err) + return err; + + err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) rcv_filter, + rcv_filter_length); + if (err) + return err; +} + +int +main (int argc, char *argv[]) +{ + error_t err; + mach_port_t bootstrap; + struct trivfs_control *fsys; + + port_bucket = ports_create_bucket (); + user_portclass = ports_create_class (clean_proxy_user, 0); + device_portclass = ports_create_class (clean_proxy_device, 0); + other_portclass = ports_create_class (0, 0); + trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0); + trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0); + + argp_parse (&argp, argc, argv, 0, 0, 0); + + /* Prepare the filter. */ + if (snd_filter == NULL) + { + snd_filter = default_snd_filter; + snd_filter_length = sizeof (default_snd_filter) / sizeof (short); + } + if (rcv_filter == NULL) + { + rcv_filter = default_rcv_filter; + rcv_filter_length = sizeof (default_rcv_filter) / sizeof (short); + } + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "must be started as a translator"); + + err = ports_create_port (other_portclass, port_bucket, + sizeof (struct port_info), ¬ify_pi); + if (err) + error (1, err, "ports_create_port for notification"); + + /* Reply to our parent. */ + err = trivfs_startup (bootstrap, 0, + trivfs_cntl_portclasses[0], port_bucket, + trivfs_protid_portclasses[0], port_bucket, &fsys); + mach_port_deallocate (mach_task_self (), bootstrap); + if (err) + error (1, err, "Contacting parent"); + + /* Initialize the bpf, and set the filter for outgoing packets. + * MACH_PORT_DEAD is used because we don't need a receiving port. */ + bpf_init (); + err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) snd_filter, + snd_filter_length); + if (err) + error (1, err, "set the sending filter"); + /* Set the filter translator's own rule. */ + err = net_set_filter (MACH_PORT_DEAD, 0, (filter_t *) rcv_filter, + rcv_filter_length); + if (err) + error (1, err, "set the receiving filter"); + + /* Launch. */ + do + { + ports_manage_port_operations_one_thread (port_bucket, + filter_demuxer, 0); + } while (trivfs_goaway (fsys, 0)); + return 0; +} + +int +deliver_msg (struct net_rcv_msg *msg, queue_head_t *port_list, + mach_port_t dest) +{ + int ret_count = 0; + int has_filter = 0; + mach_msg_return_t err; + net_rcv_port_t infp, nextfp; + + msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0); + msg->msg_hdr.msgh_local_port = MACH_PORT_NULL; + msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL; + + /* Deliver the packet to the right pfinet, + * actually there should be only one filter in the list. */ + FILTER_ITERATE (port_list, infp, nextfp, &infp->chain) + { + net_hash_entry_t entp, *hash_headp; + + entp = (net_hash_entry_t) 0; + ret_count = mach_bpf_do_filter (infp, + msg->packet + sizeof (struct packet_header), + msg->net_rcv_msg_packet_count, + msg->header, + sizeof (struct ethhdr), + &hash_headp, &entp); + + if (ret_count) + break; + } + FILTER_ITERATE_END + + if (ret_count || !has_filter) + { + msg->msg_hdr.msgh_remote_port = dest; + debug ("before delivering the message\n"); + err = mach_msg ((mach_msg_header_t *)msg, + MACH_SEND_MSG|MACH_SEND_TIMEOUT, + msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL, + 0, MACH_PORT_NULL); + if (err != MACH_MSG_SUCCESS) + { + fprintf (stderr, "deliver msg: mach_msg: %s\n", + strerror (err)); + return -1; + } + debug ("after delivering the message\n"); + } + + return 0; +} diff --git a/eth-filter/pcap_filter.c b/eth-filter/pcap_filter.c new file mode 100644 index 00000000..67ee9bb8 --- /dev/null +++ b/eth-filter/pcap_filter.c @@ -0,0 +1,77 @@ +/* + Copyright (C) 2008 Free Software Foundation, Inc. + Written by Zheng Da. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + The GNU Hurd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + * This file translates a string into a bpf program. + * The BPF structures are defined in both of bpf.h and pcap-bpf.h + * Hopefully, there is no conflict between them. + * This file uses the BPF definition in pcap-bpf.h. + */ + +#include <pcap.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" + +#define NETF_IN 0x1 +#define NETF_OUT 0x2 +#define NETF_NBPA 10 +#define NETF_BPF (1 << NETF_NBPA) + +/* This function translates the bpf program + * from the string into the instructions. */ +struct bpf_insn *trans_filter_program (char *str, int send, int *filter_len) +{ + struct bpf_program program; + struct bpf_insn *insn; + pcap_t *pcap; + int err; + + debug ("Compiling the bpf program: %s.\n", str); + pcap = pcap_open_dead (DLT_EN10MB, 1500); + if (pcap == NULL) + return NULL; + err = pcap_compile (pcap, &program, str, 1, 0); + if (err < 0) + { + debug ("pcap_compile: %s\n", pcap_geterr (pcap)); + pcap_close (pcap); + return NULL; + } + + debug ("Finish compiling the bpf program, get %d bpf instructions.\n", + program.bf_len); + insn = (struct bpf_insn *) malloc ((program.bf_len + 1) * sizeof (*insn)); + /* Clear the first instruction. */ + memset (insn, 0, sizeof (*insn)); + if (send) + insn->code = NETF_OUT | NETF_BPF; + else + insn->code = NETF_IN | NETF_BPF; + memcpy (insn + 1, program.bf_insns, program.bf_len * sizeof (*insn)); + *filter_len = ((program.bf_len + 1) * sizeof (*insn)) / sizeof (short); + debug ("%d bpf instructions, the length of filters is %d words\n", + program.bf_len, *filter_len); + pcap_freecode (&program); + pcap_close (pcap); + + return insn; +} diff --git a/eth-filter/queue.c b/eth-filter/queue.c new file mode 100644 index 00000000..a43a21b0 --- /dev/null +++ b/eth-filter/queue.c @@ -0,0 +1,131 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * Routines to implement queue package. + */ + +#include "queue.h" + + + +/* + * Insert element at head of queue. + */ +void enqueue_head( + register queue_t que, + register queue_entry_t elt) +{ + elt->next = que->next; + elt->prev = que; + elt->next->prev = elt; + que->next = elt; +} + +/* + * Insert element at tail of queue. + */ +void enqueue_tail( + register queue_t que, + register queue_entry_t elt) +{ + elt->next = que; + elt->prev = que->prev; + elt->prev->next = elt; + que->prev = elt; +} + +/* + * Remove and return element at head of queue. + */ +queue_entry_t dequeue_head( + register queue_t que) +{ + register queue_entry_t elt; + + if (que->next == que) + return((queue_entry_t)0); + + elt = que->next; + elt->next->prev = que; + que->next = elt->next; + return(elt); +} + +/* + * Remove and return element at tail of queue. + */ +queue_entry_t dequeue_tail( + register queue_t que) +{ + register queue_entry_t elt; + + if (que->prev == que) + return((queue_entry_t)0); + + elt = que->prev; + elt->prev->next = que; + que->prev = elt->prev; + return(elt); +} + +/* + * Remove arbitrary element from queue. + * Does not check whether element is on queue - the world + * will go haywire if it isn't. + */ + +/*ARGSUSED*/ +void remqueue( + queue_t que, + register queue_entry_t elt) +{ + elt->next->prev = elt->prev; + elt->prev->next = elt->next; +} + +/* + * Routines to directly imitate the VAX hardware queue + * package. + */ +void insque( + register struct queue_entry *entry, + register struct queue_entry *pred) +{ + entry->next = pred->next; + entry->prev = pred; + (pred->next)->prev = entry; + pred->next = entry; +} + +struct queue_entry +*remque( + register struct queue_entry *elt) +{ + (elt->next)->prev = elt->prev; + (elt->prev)->next = elt->next; + return(elt); +} + diff --git a/eth-filter/queue.h b/eth-filter/queue.h new file mode 100644 index 00000000..f067f557 --- /dev/null +++ b/eth-filter/queue.h @@ -0,0 +1,329 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon rights + * to redistribute these changes. + */ +/* + * File: queue.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * Type definitions for generic queues. + * + */ + +#ifndef _KERN_QUEUE_H_ +#define _KERN_QUEUE_H_ + +/* + * Queue of abstract objects. Queue is maintained + * within that object. + * + * Supports fast removal from within the queue. + * + * How to declare a queue of elements of type "foo_t": + * In the "*foo_t" type, you must have a field of + * type "queue_chain_t" to hold together this queue. + * There may be more than one chain through a + * "foo_t", for use by different queues. + * + * Declare the queue as a "queue_t" type. + * + * Elements of the queue (of type "foo_t", that is) + * are referred to by reference, and cast to type + * "queue_entry_t" within this module. + */ + +/* + * A generic doubly-linked list (queue). + */ + +struct queue_entry { + struct queue_entry *next; /* next element */ + struct queue_entry *prev; /* previous element */ +}; + +typedef struct queue_entry *queue_t; +typedef struct queue_entry queue_head_t; +typedef struct queue_entry queue_chain_t; +typedef struct queue_entry *queue_entry_t; + +/* + * enqueue puts "elt" on the "queue". + * dequeue returns the first element in the "queue". + * remqueue removes the specified "elt" from the specified "queue". + */ + +#define enqueue(queue,elt) enqueue_tail(queue, elt) +#define dequeue(queue) dequeue_head(queue) + +void enqueue_head(queue_t, queue_entry_t); +void enqueue_tail(queue_t, queue_entry_t); +queue_entry_t dequeue_head(queue_t); +queue_entry_t dequeue_tail(queue_t); +void remqueue(queue_t, queue_entry_t); + +/* + * Macro: queue_init + * Function: + * Initialize the given queue. + * Header: + * void queue_init(q) + * queue_t q; *MODIFIED* + */ +#define queue_init(q) ((q)->next = (q)->prev = q) + +/* + * Macro: queue_first + * Function: + * Returns the first entry in the queue, + * Header: + * queue_entry_t queue_first(q) + * queue_t q; *IN* + */ +#define queue_first(q) ((q)->next) + +/* + * Macro: queue_next + * Function: + * Returns the entry after an item in the queue. + * Header: + * queue_entry_t queue_next(qc) + * queue_t qc; + */ +#define queue_next(qc) ((qc)->next) + +/* + * Macro: queue_last + * Function: + * Returns the last entry in the queue. + * Header: + * queue_entry_t queue_last(q) + * queue_t q; *IN* + */ +#define queue_last(q) ((q)->prev) + +/* + * Macro: queue_prev + * Function: + * Returns the entry before an item in the queue. + * Header: + * queue_entry_t queue_prev(qc) + * queue_t qc; + */ +#define queue_prev(qc) ((qc)->prev) + +/* + * Macro: queue_end + * Function: + * Tests whether a new entry is really the end of + * the queue. + * Header: + * boolean_t queue_end(q, qe) + * queue_t q; + * queue_entry_t qe; + */ +#define queue_end(q, qe) ((q) == (qe)) + +/* + * Macro: queue_empty + * Function: + * Tests whether a queue is empty. + * Header: + * boolean_t queue_empty(q) + * queue_t q; + */ +#define queue_empty(q) queue_end((q), queue_first(q)) + + +/*----------------------------------------------------------------*/ +/* + * Macros that operate on generic structures. The queue + * chain may be at any location within the structure, and there + * may be more than one chain. + */ + +/* + * Macro: queue_enter + * Function: + * Insert a new element at the tail of the queue. + * Header: + * void queue_enter(q, elt, type, field) + * queue_t q; + * <type> elt; + * <type> is what's in our queue + * <field> is the chain field in (*<type>) + */ +#define queue_enter(head, elt, type, field) \ +{ \ + register queue_entry_t prev; \ + \ + prev = (head)->prev; \ + if ((head) == prev) { \ + (head)->next = (queue_entry_t) (elt); \ + } \ + else { \ + ((type)prev)->field.next = (queue_entry_t)(elt);\ + } \ + (elt)->field.prev = prev; \ + (elt)->field.next = head; \ + (head)->prev = (queue_entry_t) elt; \ +} + +/* + * Macro: queue_enter_first + * Function: + * Insert a new element at the head of the queue. + * Header: + * void queue_enter_first(q, elt, type, field) + * queue_t q; + * <type> elt; + * <type> is what's in our queue + * <field> is the chain field in (*<type>) + */ +#define queue_enter_first(head, elt, type, field) \ +{ \ + register queue_entry_t next; \ + \ + next = (head)->next; \ + if ((head) == next) { \ + (head)->prev = (queue_entry_t) (elt); \ + } \ + else { \ + ((type)next)->field.prev = (queue_entry_t)(elt);\ + } \ + (elt)->field.next = next; \ + (elt)->field.prev = head; \ + (head)->next = (queue_entry_t) elt; \ +} + +/* + * Macro: queue_field [internal use only] + * Function: + * Find the queue_chain_t (or queue_t) for the + * given element (thing) in the given queue (head) + */ +#define queue_field(head, thing, type, field) \ + (((head) == (thing)) ? (head) : &((type)(thing))->field) + +/* + * Macro: queue_remove + * Function: + * Remove an arbitrary item from the queue. + * Header: + * void queue_remove(q, qe, type, field) + * arguments as in queue_enter + */ +#define queue_remove(head, elt, type, field) \ +{ \ + register queue_entry_t next, prev; \ + \ + next = (elt)->field.next; \ + prev = (elt)->field.prev; \ + \ + if ((head) == next) \ + (head)->prev = prev; \ + else \ + ((type)next)->field.prev = prev; \ + \ + if ((head) == prev) \ + (head)->next = next; \ + else \ + ((type)prev)->field.next = next; \ +} + +/* + * Macro: queue_remove_first + * Function: + * Remove and return the entry at the head of + * the queue. + * Header: + * queue_remove_first(head, entry, type, field) + * entry is returned by reference + */ +#define queue_remove_first(head, entry, type, field) \ +{ \ + register queue_entry_t next; \ + \ + (entry) = (type) ((head)->next); \ + next = (entry)->field.next; \ + \ + if ((head) == next) \ + (head)->prev = (head); \ + else \ + ((type)(next))->field.prev = (head); \ + (head)->next = next; \ +} + +/* + * Macro: queue_remove_last + * Function: + * Remove and return the entry at the tail of + * the queue. + * Header: + * queue_remove_last(head, entry, type, field) + * entry is returned by reference + */ +#define queue_remove_last(head, entry, type, field) \ +{ \ + register queue_entry_t prev; \ + \ + (entry) = (type) ((head)->prev); \ + prev = (entry)->field.prev; \ + \ + if ((head) == prev) \ + (head)->next = (head); \ + else \ + ((type)(prev))->field.next = (head); \ + (head)->prev = prev; \ +} + +/* + * Macro: queue_assign + */ +#define queue_assign(to, from, type, field) \ +{ \ + ((type)((from)->prev))->field.next = (to); \ + ((type)((from)->next))->field.prev = (to); \ + *to = *from; \ +} + +/* + * Macro: queue_iterate + * Function: + * iterate over each item in the queue. + * Generates a 'for' loop, setting elt to + * each item in turn (by reference). + * Header: + * queue_iterate(q, elt, type, field) + * queue_t q; + * <type> elt; + * <type> is what's in our queue + * <field> is the chain field in (*<type>) + */ +#define queue_iterate(head, elt, type, field) \ + for ((elt) = (type) queue_first(head); \ + !queue_end((head), (queue_entry_t)(elt)); \ + (elt) = (type) queue_next(&(elt)->field)) + +#endif /* _KERN_QUEUE_H_ */ diff --git a/eth-filter/util.h b/eth-filter/util.h new file mode 100644 index 00000000..de23710a --- /dev/null +++ b/eth-filter/util.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2008 Free Software Foundation, Inc. + Written by Zheng Da. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + The GNU Hurd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the GNU Hurd; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef UTIL_H +#define UTIL_H + +#include <stdio.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/ip.h> + +#ifdef DEBUG + +#define debug(format, ...) do \ +{ \ + char buf[1024]; \ + snprintf (buf, 1024, "filter: %s", format); \ + fprintf (stderr , buf, ## __VA_ARGS__); \ + fflush (stderr); \ +} while (0) + +#else + +#define debug(format, ...) do {} while (0) + +#endif + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ + +struct ethhdr +{ + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +}; + +static inline void +print_pack (char *packet, int len) +{ +#ifdef DEBUG + struct ethhdr *ethh = (struct ethhdr *) packet; + struct iphdr *iph = (struct iphdr *)(ethh + 1); + char src_str[INET_ADDRSTRLEN]; + char dst_str[INET_ADDRSTRLEN]; + if (ntohs (ethh->h_proto) == ETH_P_IP + && len >= sizeof (struct ethhdr) + sizeof (struct iphdr)) + { + debug ("pack: get a IP packet from %s to %s\n", + inet_ntop (AF_INET, &iph->saddr, src_str, INET_ADDRSTRLEN), + inet_ntop (AF_INET, &iph->daddr, dst_str, INET_ADDRSTRLEN)); + } + else + { + debug ("pack: get a non-IP packet: %x\n", ntohs (ethh->h_proto)); + } +#endif +} + +#endif |