diff options
46 files changed, 8568 insertions, 5 deletions
diff --git a/eth-filter.multi-thread/ChangeLog b/eth-filter.multi-thread/ChangeLog new file mode 100644 index 00000000..cd920067 --- /dev/null +++ b/eth-filter.multi-thread/ChangeLog @@ -0,0 +1,105 @@ +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.multi-thread/Makefile b/eth-filter.multi-thread/Makefile new file mode 100644 index 00000000..216664f1 --- /dev/null +++ b/eth-filter.multi-thread/Makefile @@ -0,0 +1,36 @@ +# 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 msg_queue.c +LCLHDRS = bpf_impl.h queue.h util.h filter.h msg_queue.h +DIST_FILES = ourdevice.defs notify.defs device_reply.defs +HURDLIBS = ports trivfs fshelp ihash shouldbeinlibc +OTHERLIBS = -lpthread +target = eth-filter +MIGSTUBS = ourdeviceServer.o notifyServer.o device_replyUser.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.multi-thread/README b/eth-filter.multi-thread/README new file mode 100644 index 00000000..2ef16eda --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/bpf_impl.c b/eth-filter.multi-thread/bpf_impl.c new file mode 100644 index 00000000..e9202a5f --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/bpf_impl.h b/eth-filter.multi-thread/bpf_impl.h new file mode 100644 index 00000000..de61df12 --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/filter.c b/eth-filter.multi-thread/filter.c new file mode 100644 index 00000000..ec3efe95 --- /dev/null +++ b/eth-filter.multi-thread/filter.c @@ -0,0 +1,877 @@ +/* + 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 <unistd.h> + +#include <hurd.h> +#include <mach.h> +#include <pthread.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 "device_reply_U.h" +#include "bpf_impl.h" +#include "util.h" +#include "filter.h" +#include "msg_queue.h" + +#ifdef DEBUG + +FILE *logfile; + +#endif + +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 struct bpf_insn *snd_filter = NULL; +static int snd_filter_length; +static struct bpf_insn *rcv_filter = NULL; +static int rcv_filter_length; + +static pthread_t send_thread; +static int send_thread_running = 1; + +/* 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 = 0; + +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; + +/* Write the data from the client to the device. */ +int +filter_device_write (struct filter_msg *msg) +{ + error_t err = 0; + int ret_count = 0; + int has_filter = 0; + net_hash_entry_t entp, *hash_headp; + net_rcv_port_t infp, nextfp; + int bytes_written; + struct filter_write_msg *write_msg = (struct filter_write_msg *) msg; + + /* 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, + write_msg->data + sizeof (struct ethhdr), + write_msg->datalen - sizeof (struct ethhdr), + write_msg->data, sizeof (struct ethhdr), + &hash_headp, &entp); + if (ret_count) + break; + } + FILTER_ITERATE_END + + if (ret_count || !has_filter) + err = device_write (msg->proxy->device_port, write_msg->mode, + write_msg->recnum, write_msg->data, + write_msg->datalen, &bytes_written); + ds_device_write_reply (write_msg->reply_port, write_msg->reply_type, + err, bytes_written); + return 0; +} + +/* Deliver the data from the device to the client. */ +int +filter_deliver (struct filter_msg *msg) +{ + deliver_msg (((struct filter_deliver_msg *)msg)->net_msg, + &rcv_port_list, msg->proxy->deliver_port); + return 0; +} + +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; + + 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) + queue_deliver (msg, proxy); +// deliver_msg (msg, &rcv_port_list, proxy->deliver_port); + + 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; + struct proxy *proxy; + + if (device_file == NULL) + return D_NO_SUCH_DEVICE; + + master_device = file_name_lookup (device_file, 0, 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) +{ + kern_return_t err = 0; + struct proxy_user *user; + struct proxy *proxy; + int ret_count = 0; + int has_filter = 0; + net_hash_entry_t entp, *hash_headp; + net_rcv_port_t infp, nextfp; + + user = ports_lookup_port (port_bucket, device, user_portclass); + if (user == NULL) + return D_INVALID_OPERATION; + proxy = user->proxy; + + /* 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 + + if (ret_count || !has_filter) + err = device_write (proxy->device_port, mode, recnum, data, + datalen, bytes_written); + ports_port_deref (user); + 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; + } + + queue_flush (); + send_thread_running = 0; +// pthread_join (send_thread); + +#ifdef DEBUG + fclose (logfile); +#endif + 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) + free (snd_filter); + snd_filter = trans_filter_program (arg, 1, &snd_filter_length); + break; + case 'r': + if (rcv_filter) + free (rcv_filter); + rcv_filter = trans_filter_program (arg, 0, &rcv_filter_length); + 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) + free (snd_filter); + snd_filter = trans_filter_program (buf, 1, &snd_filter_length); + 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) + free (rcv_filter); + rcv_filter = trans_filter_program (buf, 0, &rcv_filter_length); + break; + case ARGP_KEY_ERROR: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_INIT: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +void *send_thread_func (void *arg) +{ + struct filter_msg *msg; + + while (send_thread_running) + { + msg = dequeue_msg (); + if (msg == NULL) + { +// queue_empty_wait (); + continue; + } + msg->forward (msg); + msg->destroy (msg); + free (msg); + } + return NULL; +} + +int +main (int argc, char *argv[]) +{ + error_t err; + mach_port_t bootstrap; + struct trivfs_control *fsys; + const struct argp argp = { options, parse_opt, 0, doc }; + +#ifdef DEBUG + logfile = fopen ("/root/filter.log", "a+"); + if (logfile == NULL) + error (1, errno, "fopen"); +#endif + + 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"); + + pthread_create (&send_thread, NULL, send_thread_func, NULL); + /* TODO if the main thread exits, + * does the created thread exit if pthread_detach is called */ + pthread_detach (send_thread); + + /* 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) +{ + static int count = 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; + int ret_count; + + 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) + { + msg->msg_hdr.msgh_remote_port = dest; + debug ("before delivering the packet to port %d, count: %d\n", + dest, ++count); + err = mach_msg ((mach_msg_header_t *)msg, + MACH_SEND_MSG|MACH_SEND_TIMEOUT, + msg->msg_hdr.msgh_size, 0, MACH_PORT_NULL, + /*MACH_MSG_TIMEOUT_NONE*/500, MACH_PORT_NULL); + if (err != MACH_MSG_SUCCESS) + { + mach_port_deallocate(mach_task_self (), + ((mach_msg_header_t *)msg)->msgh_remote_port); + error (0, err, "mach_msg"); + return -1; + } + debug ("after delivering the packet\n"); + } + } + FILTER_ITERATE_END + + return 0; +} diff --git a/eth-filter.multi-thread/filter.h b/eth-filter.multi-thread/filter.h new file mode 100644 index 00000000..e1c4c8bf --- /dev/null +++ b/eth-filter.multi-thread/filter.h @@ -0,0 +1,57 @@ +/* + 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 FILTER_H +#define FILTER_H + +#include <hurd.h> +#include <mach.h> +#include <hurd/ports.h> + +#include "bpf_impl.h" + +struct filter_msg; + +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); +int filter_device_write (struct filter_msg *msg); +int filter_deliver (struct filter_msg *msg); + +#endif diff --git a/eth-filter.multi-thread/msg_queue.c b/eth-filter.multi-thread/msg_queue.c new file mode 100644 index 00000000..602b7fd1 --- /dev/null +++ b/eth-filter.multi-thread/msg_queue.c @@ -0,0 +1,224 @@ +/* + 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. */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "msg_queue.h" +#include "filter.h" +#include "util.h" + +static struct filter_msg *queue_head; +static struct filter_msg *queue_tail; +static int queue_len = 0; + +static pthread_mutex_t queuelock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t condition_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t condition_cond = PTHREAD_COND_INITIALIZER; + +void +queue_msg (struct filter_msg *msg) +{ + msg->next = NULL; + // TODO what kind of lock do I need? + pthread_mutex_lock (&queuelock); + if (queue_head == NULL) + { + assert (queue_tail == NULL); + assert (queue_len == 0); + /* When the queue is empty. */ + queue_head = msg; + queue_tail = msg; + } + else + { + queue_tail->next = msg; + queue_tail = msg; + } + queue_len++; + debug ("queue a message, queue length: %d.\n", queue_len); + pthread_mutex_unlock (&queuelock); +// queue_wakeup (); +} + +struct filter_msg * +dequeue_msg () +{ + struct filter_msg *msg; + + pthread_mutex_lock (&queuelock); + if (queue_head == NULL) + { + assert (queue_tail == NULL); + assert (queue_len == 0); + msg = NULL; + } + else + { + msg = queue_head; + queue_head = msg->next; + if (queue_head == NULL) + queue_tail = NULL; + queue_len--; + } + debug ("dequeue a message, the queue length: %d.\n", queue_len); + pthread_mutex_unlock (&queuelock); + + return msg; +} + +/* It can be called when the translator exits. */ +void +queue_flush () +{ + struct filter_msg *msg; + struct filter_msg *tmp; + + pthread_mutex_lock (&queuelock); + msg = queue_head; + queue_head = queue_tail = NULL; + queue_len = 0; + pthread_mutex_unlock (&queuelock); + + while (msg) + { + tmp = msg; + msg = msg->next; + if (tmp->destroy) + tmp->destroy (tmp); + free (tmp); + } +} + +void +filter_write_destroy (struct filter_msg *msg) +{ + struct filter_write_msg *write_msg = (struct filter_write_msg *) msg; + + free (write_msg->data); +} + +void filter_deliver_destroy (struct filter_msg *msg) +{ + struct filter_deliver_msg *deliver_msg = (struct filter_deliver_msg *) msg; + + free (deliver_msg->net_msg); +} + +kern_return_t +queue_write (char *data, size_t datalen, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, struct proxy *proxy) +{ + /* Make a copy of the data */ + char *new_data; + struct filter_msg *msg; + struct filter_write_msg *write_msg; + + if (queue_len >= MAX_QUEUE) + return D_NO_MEMORY; + + new_data = (char *) malloc (datalen); + if (new_data == NULL) + return D_NO_MEMORY; + + memcpy (new_data, data, datalen); + + write_msg = (struct filter_write_msg *) malloc (sizeof (*write_msg)); + if (write_msg == NULL) + { + free (new_data); + return D_NO_MEMORY; + } + msg = (struct filter_msg *) write_msg; + + msg->proxy = proxy; + msg->forward = filter_device_write; + msg->destroy = filter_write_destroy; + + write_msg->data = new_data; + write_msg->datalen = datalen; + write_msg->reply_port = reply_port; + write_msg->reply_type = reply_type; + write_msg->mode = mode; + write_msg->recnum = recnum; + + queue_msg (msg); + return D_SUCCESS; +} + +int +queue_deliver (struct net_rcv_msg *msg, struct proxy *proxy) +{ + struct net_rcv_msg *msg_copy; + struct filter_deliver_msg *deliver_msg; + + if (queue_len >= MAX_QUEUE) + return D_NO_MEMORY; + + /* Make a copy of the data */ + msg_copy = (struct net_rcv_msg *) malloc (sizeof (*msg_copy)); + if (msg_copy == NULL) + return D_NO_MEMORY; + + deliver_msg = (struct filter_deliver_msg *) malloc (sizeof (*deliver_msg)); + if (deliver_msg == NULL) + { + free (msg_copy); + return D_NO_MEMORY; + } + + *msg_copy = *msg; + + deliver_msg->msg.proxy = proxy; + deliver_msg->msg.forward = filter_deliver; + deliver_msg->msg.destroy = filter_deliver_destroy; + + deliver_msg->net_msg = msg_copy; + + queue_msg ((struct filter_msg *)deliver_msg); + return D_SUCCESS; +} + +void +queue_empty_wait () +{ + pthread_mutex_lock (&condition_mutex); +// debug ("queue length is %d\n", queue_len); + while (queue_head == NULL) + { + debug ("thread waits for a signal.\n"); + pthread_cond_wait (&condition_cond, &condition_mutex); + } + pthread_mutex_unlock (&condition_mutex); +} + +void +queue_wakeup () +{ + if (queue_head) + { + debug ("wake up a thread.\n"); + pthread_cond_signal (&condition_cond); + } +} + diff --git a/eth-filter.multi-thread/msg_queue.h b/eth-filter.multi-thread/msg_queue.h new file mode 100644 index 00000000..bc7800fb --- /dev/null +++ b/eth-filter.multi-thread/msg_queue.h @@ -0,0 +1,69 @@ +/* + 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 MSG_QUEUE_H +#define MSG_QUEUE_H + +#include <hurd.h> +#include <mach.h> + +#define MAX_QUEUE 1000 + +struct proxy; + +struct filter_msg +{ + struct proxy *proxy; + struct filter_msg *next; + + int (*forward) (struct filter_msg *); + void (*destroy) (struct filter_msg *); +}; + +struct filter_write_msg +{ + struct filter_msg msg; + + char *data; + size_t datalen; + dev_mode_t mode; + recnum_t recnum; + mach_port_t reply_port; + mach_msg_type_name_t reply_type; +}; + +struct filter_deliver_msg +{ + struct filter_msg msg; + + struct net_rcv_msg *net_msg; +}; + +void queue_msg (struct filter_msg *msg); +struct filter_msg * dequeue_msg (); +void queue_flush (); +kern_return_t queue_write (char *data, size_t datalen, mach_port_t reply_port, + mach_msg_type_name_t reply_type, dev_mode_t mode, + recnum_t recnum, struct proxy *proxy); +int queue_deliver (struct net_rcv_msg *msg, struct proxy *proxy); +void queue_wakeup (); +void queue_empty_wait (); + +#endif diff --git a/eth-filter.multi-thread/pcap_filter.c b/eth-filter.multi-thread/pcap_filter.c new file mode 100644 index 00000000..67ee9bb8 --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/queue.c b/eth-filter.multi-thread/queue.c new file mode 100644 index 00000000..a43a21b0 --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/queue.h b/eth-filter.multi-thread/queue.h new file mode 100644 index 00000000..f067f557 --- /dev/null +++ b/eth-filter.multi-thread/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.multi-thread/util.h b/eth-filter.multi-thread/util.h new file mode 100644 index 00000000..fa7189eb --- /dev/null +++ b/eth-filter.multi-thread/util.h @@ -0,0 +1,73 @@ +/* + 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 <linux/if_ether.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/ip.h> + +#define DEBUG + +#ifdef DEBUG + +#define debug(format, ...) do \ +{ \ + extern FILE *logfile; \ + char buf[1024]; \ + snprintf (buf, 1024, "filter: %s", format); \ + fprintf (stderr , buf, ## __VA_ARGS__); \ + fflush (logfile); \ +} while (0) + +#else + +#define debug(format, ...) do {} while (0) + +#endif + +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 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 diff --git a/eth-multiplexer/ChangeLog b/eth-multiplexer/ChangeLog new file mode 100644 index 00000000..37371185 --- /dev/null +++ b/eth-multiplexer/ChangeLog @@ -0,0 +1,341 @@ +2009-04-18 Zheng Da <zhengda1936@gmail.com> + + * device_impl.c (ds_device_open): Create a virtual device if it + doesn't exist. + + * netfs_impl.c (new_node): Test if the lnode structure exists + before setting its field. + (lookup): Copy the device name and don't create the virtual device. + (netfs_validate_stat): Set the status with the one of the underlying + node if the node has no lnode structure. + (netfs_attempt_chmod): chmod isn't supported if the node has no lnode + structure. + (netfs_node_norefs): Free the name in netnode. + + * netfs_impl.h (net_node): Add a new field 'name'. + +2009-04-18 Zheng Da <zhengda1936@gmail.com> + + * device_impl.c (ds_device_open): Check the mode for opening the file. + + * multiplexer.c + (underlying_node_stat): New variable. + (main): Get the mapped time from Mach and set the time of the underlying + node of the translator. + + * netfs_impl.c (lookup): Set the new created node with the same permission + as the underlying node of the translator and its time. + (netfs_check_open_permissions): Check the open permission of a node + in the same way. + (netfs_attempt_unlink): Change the return value. + (netfs_attempt_rename): Likewise. + (netfs_attempt_mkdir): Likewise. + (netfs_attempt_rmdir): Likewise. + (netfs_attempt_chown): Likewise. + (netfs_attempt_chauthor): Likewise. + (netfs_attempt_mksymlink): Likewise. + (netfs_attempt_mkdev): Likewise. + (netfs_set_translator): Likewise. + (netfs_attempt_chflags): Likewise. + (netfs_attempt_set_size): Likewise. + (netfs_attempt_link): Likewise. + (netfs_attempt_mkfile): Likewise. + (netfs_attempt_write): Likewise. + (netfs_attempt_chmod): Write the code to support the change of the mode. + + * netfs_impl.h (multiplexer_maptime): Add the declaration. + +2009-01-03 Zheng Da <zhengda1936@gmail.com> + + * device_impl.c (ds_device_write): Deallocate the out-of-line data. + +2008-12-12 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (main): Initialize the file status of the root node. + + * netfs_impl.c (netfs_validate_stat): Set the file status of the node + with the one in the light node. + + * vdev.h (dev_act_func): Define a new type. + (foreach_dev_do): Declare the function. + +2008-11-18 Zheng Da <zhengda1936@gmail.com> + + * netfs_impl.c (netfs_get_dirents): Use foreach_dev_do. + + * vdev.c (dev_head, dev_num): Hide in the file. + (dev_list_lock): New variable. + (get_dev_num): New function. + (lookup_dev_by_name): Use lock. + (foreach_dev_do): New function. + (remove_dead_port_from_dev): Use lock. + (broadcast_pack, broadcast_msg): Use foreach_dev_do. + + * vdev.h (dev_num): Remove declaration. + (get_dev_num): Add declaration. + +2008-11-13 Zheng Da <zhengda1936@gmail.com> + + * device_impl.c (ds_device_open): Use dev_port, dereference pi. + + * util.h (print_backtrace): New macro. + + * vdev.c (add_vdev): Set dev_port. + + * vdev.h (vether_device): Add dev_port. + +2008-11-12 Zheng Da <zhengda1936@gmail.com> + + * Makefile (SRCS): Updated. + + * demuxer.c: New file. + + * device_impl.c (ds_device_open): Use netfs_port_bucket. + + * make-protid.c: Deleted. + +2008-11-02 Zheng Da <zhengda1936@gmail.com> + + * Makefile (CFLAGS): Add a macro. + (SRCS): Add new C files. + (LCLHDRS): Add new H files. + (HURDLIBS): Change libraries. + + * demuxer.c: New file. + + * device_impl.c: New file. + + * make-protid.c: New file. + + * netfs_impl.c: New file. + + * netfs_impl.h: New file. + + * notify_impl.c: New file. + + * multiplexer.c: Remove the trivfs variables. Move the implementation of + notify interface. Move the implementation of device interface. + (multiplexer_thread): New functions. + (main): Run the libnetfs translator. + + * util.h (debug): Update. + + * vdev.c (lookup_dev_by_name): Use strncmp. + (add_vdev): Change its interface. + +2008-10-27 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + + * bpf_impl.c (destroy_filters): New function. + + * multiplexer.c (nb_dev): Deleted. + (options): Remove the option '-v'. + (do_mach_notify_no_senders): Remove all port_info in the same way. + (ds_device_open): Create new devices if they don't exist, and decrease + their reference counts. + (ds_device_set_filter): Fix a bug. + (trivfs_goaway): Use has_vdev() to test. + (parse_opt): Remove the code of handling '-v'. + (main): Remove the code of creating virtual devices. + + * util.h (ETH_P_IP): New macro. + + * vdev.c (all_dev_close): Deleted. + (add_vdev): Link virtual device. + (destroy_vdev): New function. + + * vdev.h (vether_device): Changed. + + +2008-10-03 Zheng Da <zhengda1936@gmail.com> + + * Makefile (CFLAGS): Remove the include paths from pfinet. + + * util.h: Remove the line of including linux/if_ether.h. + (ETH_ALEN): New macro. + (ethhdr): New structure. + + * vdev.c (ETH_HLEN): New macro. + + * vdev.h: Remove the line of including linux/etherdevice.h and include util.h + +2008-10-03 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (parse_opt): Don't create the virtual devices in case 'v'. + (main): Create the virtual devices. + + * README: Update. + +2008-10-03 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (ds_device_write): Don't call device_write when ether_port is NULL. + (ds_device_get_status): Call dev_getstat when ether_port is NULL. + (main): If device_file isn't specified, don't open the underlying device. + +2008-09-26 Zheng Da <zhengda1936@gmail.com> + + * vdev.c (deliver_msg): Use non-block send. + +2008-09-21 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + +2008-09-02 Zheng Da <zhengda1936@gmail.com> + + * ethernet.c (ether_filter): Use the original NPF filter. + +2008-9-01 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (ds_device_write): Reverse the calling of functions. + (ds_device_get_status): Call device_get_status. + + * vdev.c (broadcast_pack): Change its function prototype. Broadcast to + all other interface. + (deliver_pack): Don't set the message header. + (broadcast_msg): Save the original message header and restore it. + (deliver_msg): Deallocate the port if mach_msg fails. + + * vdev.h (broadcast_pack): Change its function prototype. + +2008-8-29 Zheng Da <zhengda1936@gmail.com> + + * ethernet.c (ethernet_open): Use error instead of assert_perror. + + * multiplexer.c (ds_device_set_filter): Return the error. + +2008-8-28 Zheng Da <zhengda1936@gmail.com> + + * ethernet.c (NET_FLAGS): New macro. + +2008-8-22 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + + * Makefile: Remove list.h. + + * multiplexer.c (do_mach_notify_no_senders): Get vether_device object + with ports_lookup_port(). + (ds_xxx_device_set_status): Likewise. + (ds_xxx_device_get_status): Likewise. + (ds_xxx_device_set_filter): 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. + (do_mach_notify_dead_name): Deallocate the port. + (ds_device_open): Get the name directly from the vether_device object. + (ds_device_close): Return 0 immediately. + + * vdev.c (dev_head): Point to the head of the device list. + (print_eth_addr): Removed. + (lookup_dev_by_devport): Likewise. + (lookup_dev_by_name): Use the for loop to replace list_for_each_entry. + (remove_dead_port_from_dev): Likewise. + (all_dev_close): Likewise. + (broadcast_pack): Likewise. + (broadcast_msg): Likewise. + (add_vdev): Create the vether_device object with ports_create_port. + (has_vdev): Test if the device list is empty. + + * vdev.h: Don't include list.h. + (vether_device): Include the port_info object instead of its pointer. + (next): Replace dev_list. + + * list.h: Removed. + +2008-8-20 Zheng Da <zhengda1936@gmail.com> + + * README: Update. + + * multiplexer.c (options): Update. + +2008-8-20 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (main): Test device_file before using it. + +2008-8-20 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (device_file): Rename a variable. + (main): Use device_file directly. + +2008-8-19 Zheng Da <zhengda1936@gmail.com> + + * multiplexer.c (main): Generate the device file name, and use it + to open the device. + +2008-8-18 Zheng Da <zhengda1936@gmail.com> + + * README: New file. + + * multiplexer.c (options): Change the meaning of '-v' option. + (parse_opt): Change the way of handling '-v' option. + + * vdev.c (has_vdev): New function. + + * vdev.h (has_vdev): New declaration. + +2008-8-17 Zheng Da <zhengda1936@gmail.com> + + * ethernet.c (ethernet_open): Use a hard coded string for the device name. + + * multiplexer.c: Remove the option '-M'. + (parse_opt): Remove the code of handling '-M' option. + (main): Get the master device port from the device file. + +2008-8-14 Zheng Da <zhengda1936@gmail.com> + + * ChangeLog: New file. + + * multiplexer.c (vdev_portclass, other_portclass): + New variables. + (do_mach_notify_no_senders): Mark the device unused when there are no + senders for the device. + (do_mach_notify_dead_name): Return 0. + (ds_device_open): Mark the device used. + (ds_device_close): Remove the code of decreasing the count of the device. + (trivfs_goaway): Only test if all devices aren't used, and delete the code + of closing the device. + (parse_opt): Use vdev_portclass to create the virtual device. + (main): Create vdev_portclass and other_portclass, open the ethernet + device with other_portclass and create notify_pi with other_portclass. + + * vdev.c (all_dev_close): Change the way of testing if all devices are + closed. + + * vdev.h (vether_device): Replace count field with used. + +2008-8-13 Zheng Da <zhengda1936@gmail.com> + + * bpf_impl.c: New file. + + * bpf_impl.h: New file. + + * dev_stat.c: New file. + + * ethernet.c: New file. + + * ethernet.h: New file. + + * list.h: New file. + + * Makefile: New file. + + * multiplexer.c: New file. + + * queue.c: New file. + + * queue.h: New file. + + * test.c: New file. + + * util.h: New file. + + * vdev.c: New file. + + * vdev.h: New file. + diff --git a/eth-multiplexer/Makefile b/eth-multiplexer/Makefile new file mode 100644 index 00000000..a96619cf --- /dev/null +++ b/eth-multiplexer/Makefile @@ -0,0 +1,37 @@ +# 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-multiplexer +makemode := server +target = eth-multiplexer + +#CFLAGS += -DDEBUG +SRCS = ethernet.c vdev.c multiplexer.c dev_stat.c netfs_impl.c notify_impl.c device_impl.c demuxer.c +MIGSTUBS = ourdeviceServer.o notifyServer.o +OBJS = $(SRCS:.c=.o) $(MIGSTUBS) +LCLHDRS = ethernet.h util.h vdev.h netfs_impl.h +DIST_FILES = ourdevice.defs notify.defs +HURDLIBS=ports fshelp shouldbeinlibc netfs bpf +OTHERLIBS = -lpthread + +CFLAGS += -I$(top_srcdir)/libbpf + +include ../Makeconf + +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-multiplexer/README b/eth-multiplexer/README new file mode 100644 index 00000000..0024a937 --- /dev/null +++ b/eth-multiplexer/README @@ -0,0 +1,27 @@ +[Introduction] + +eth-multiplexer is a network multiplexer. It creates virtual ethernet interface and dispatches the packet to the right user program that opens its virtual interface. It also works as a bridge to connect the real ethernet interface and the virtual ones. + + +[Usage] + +Usage: eth-multiplexer [OPTION...] +Hurd multiplexer server. + + -i, --interface=DEVICE Network interface to use + -?, --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-multiplexer can only connect to one network interface and the '-i' option should be only used once. DEVICE is a device file that is created by the devnode translator. + + +[Internal] + +eth-multiplexer implements the server side functions in device.defs, so other programs can access the virtual device as other devices. All information about the virtual interface is kept in the vether_device structure. +When eth-multiplexer gets a packet from a virtual interface (which happens in ds_device_write) or from the real interface (which happens in ethernet_demuxer), it sends the packet to all other interfaces. eth-multipexer has BPF filters for each client. The BPF filter decides whether to deliver the packet. The packet delivery is done by deliver_pack(). There is no filter for the real network interface in eth-multiplexer, so every packet from the virtual interface will be sent to the real interface whose filter will decide the destination of the packet. +eth-multiplexer sets the real interface into the promiscuous mode, so eth-multiplexer can receive the packet with the virtual interface's hardware address from the real interface. diff --git a/eth-multiplexer/demuxer.c b/eth-multiplexer/demuxer.c new file mode 100644 index 00000000..1f671b20 --- /dev/null +++ b/eth-multiplexer/demuxer.c @@ -0,0 +1,41 @@ +/* + Copyright (C) 1996 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <hurd/netfs.h> + +int +netfs_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + int netfs_fs_server (mach_msg_header_t *, mach_msg_header_t *); + int netfs_io_server (mach_msg_header_t *, mach_msg_header_t *); + int netfs_fsys_server (mach_msg_header_t *, mach_msg_header_t *); + int netfs_ifsock_server (mach_msg_header_t *, mach_msg_header_t *); + int device_server (mach_msg_header_t *, mach_msg_header_t *); + + return (netfs_io_server (inp, outp) + || netfs_fs_server (inp, outp) + || ports_notify_server (inp, outp) + || netfs_fsys_server (inp, outp) + || ports_interrupt_server (inp, outp) + || netfs_ifsock_server (inp, outp) + || device_server (inp, outp)); +} + diff --git a/eth-multiplexer/dev_stat.c b/eth-multiplexer/dev_stat.c new file mode 100644 index 00000000..43c68d6a --- /dev/null +++ b/eth-multiplexer/dev_stat.c @@ -0,0 +1,101 @@ + /* + * 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 <string.h> +#include <arpa/inet.h> + +#include <mach.h> + +#include "vdev.h" + +io_return_t +dev_getstat(struct vether_device *ifp, dev_flavor_t flavor, + dev_status_t status, natural_t *count) +{ + switch (flavor) { + case NET_STATUS: + { + register struct net_status *ns = (struct net_status *)status; + + if (*count < NET_STATUS_COUNT) + return (D_INVALID_OPERATION); + + ns->min_packet_size = ifp->if_header_size; + ns->max_packet_size = ifp->if_header_size + ifp->if_mtu; + ns->header_format = ifp->if_header_format; + ns->header_size = ifp->if_header_size; + ns->address_size = ifp->if_address_size; + ns->flags = ifp->if_flags; + ns->mapped_size = 0; + + *count = NET_STATUS_COUNT; + break; + } + case NET_ADDRESS: + { + register int addr_byte_count; + register int addr_int_count; + register int i; + + addr_byte_count = ifp->if_address_size; + addr_int_count = (addr_byte_count + (sizeof(int)-1)) + / sizeof(int); + + if (*count < addr_int_count) { + return (D_INVALID_OPERATION); + } + + memcpy(status, ifp->if_address, addr_byte_count); + if (addr_byte_count < addr_int_count * sizeof(int)) + memset((char *)status + addr_byte_count, 0, + (addr_int_count * sizeof(int) + - addr_byte_count)); + + for (i = 0; i < addr_int_count; i++) { + register int word; + + word = status[i]; + status[i] = htonl(word); + } + *count = addr_int_count; + break; + } + default: + return (D_INVALID_OPERATION); + } + return (D_SUCCESS); +} diff --git a/eth-multiplexer/device.h b/eth-multiplexer/device.h new file mode 100644 index 00000000..db0798d6 --- /dev/null +++ b/eth-multiplexer/device.h @@ -0,0 +1,336 @@ +#ifndef _device_user_ +#define _device_user_ + +/* Module device */ + +#include <mach/kern_return.h> +#include <mach/port.h> +#include <mach/message.h> + +#include <mach/std_types.h> +#include <mach/mach_types.h> +#include <device/device_types.h> +#include <device/net_status.h> + +/* Routine device_open */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_open +#if defined(LINTLIBRARY) + (master_port, mode, name, device) + mach_port_t master_port; + dev_mode_t mode; + dev_name_t name; + mach_port_t *device; +{ return device_open(master_port, mode, name, device); } +#else +( + mach_port_t master_port, + dev_mode_t mode, + dev_name_t name, + mach_port_t *device +); +#endif + +/* Routine device_close */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_close +#if defined(LINTLIBRARY) + (device) + mach_port_t device; +{ return device_close(device); } +#else +( + mach_port_t device +); +#endif + +/* Routine device_write */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_write +#if defined(LINTLIBRARY) + (device, mode, recnum, data, dataCnt, bytes_written) + mach_port_t device; + dev_mode_t mode; + recnum_t recnum; + io_buf_ptr_t data; + mach_msg_type_number_t dataCnt; + int *bytes_written; +{ return device_write(device, mode, recnum, data, dataCnt, bytes_written); } +#else +( + mach_port_t device, + dev_mode_t mode, + recnum_t recnum, + io_buf_ptr_t data, + mach_msg_type_number_t dataCnt, + int *bytes_written +); +#endif + +/* Routine device_write_inband */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_write_inband +#if defined(LINTLIBRARY) + (device, mode, recnum, data, dataCnt, bytes_written) + mach_port_t device; + dev_mode_t mode; + recnum_t recnum; + io_buf_ptr_inband_t data; + mach_msg_type_number_t dataCnt; + int *bytes_written; +{ return device_write_inband(device, mode, recnum, data, dataCnt, bytes_written); } +#else +( + mach_port_t device, + dev_mode_t mode, + recnum_t recnum, + io_buf_ptr_inband_t data, + mach_msg_type_number_t dataCnt, + int *bytes_written +); +#endif + +/* Routine device_read */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_read +#if defined(LINTLIBRARY) + (device, mode, recnum, bytes_wanted, data, dataCnt) + mach_port_t device; + dev_mode_t mode; + recnum_t recnum; + int bytes_wanted; + io_buf_ptr_t *data; + mach_msg_type_number_t *dataCnt; +{ return device_read(device, mode, recnum, bytes_wanted, data, dataCnt); } +#else +( + mach_port_t device, + dev_mode_t mode, + recnum_t recnum, + int bytes_wanted, + io_buf_ptr_t *data, + mach_msg_type_number_t *dataCnt +); +#endif + +/* Routine device_read_inband */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_read_inband +#if defined(LINTLIBRARY) + (device, mode, recnum, bytes_wanted, data, dataCnt) + mach_port_t device; + dev_mode_t mode; + recnum_t recnum; + int bytes_wanted; + io_buf_ptr_inband_t data; + mach_msg_type_number_t *dataCnt; +{ return device_read_inband(device, mode, recnum, bytes_wanted, data, dataCnt); } +#else +( + mach_port_t device, + dev_mode_t mode, + recnum_t recnum, + int bytes_wanted, + io_buf_ptr_inband_t data, + mach_msg_type_number_t *dataCnt +); +#endif + +/* Routine xxx_device_set_status */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t xxx_device_set_status +#if defined(LINTLIBRARY) + (device, flavor, status, statusCnt) + mach_port_t device; + dev_flavor_t flavor; + dev_status_t status; + mach_msg_type_number_t statusCnt; +{ return xxx_device_set_status(device, flavor, status, statusCnt); } +#else +( + mach_port_t device, + dev_flavor_t flavor, + dev_status_t status, + mach_msg_type_number_t statusCnt +); +#endif + +/* Routine xxx_device_get_status */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t xxx_device_get_status +#if defined(LINTLIBRARY) + (device, flavor, status, statusCnt) + mach_port_t device; + dev_flavor_t flavor; + dev_status_t status; + mach_msg_type_number_t *statusCnt; +{ return xxx_device_get_status(device, flavor, status, statusCnt); } +#else +( + mach_port_t device, + dev_flavor_t flavor, + dev_status_t status, + mach_msg_type_number_t *statusCnt +); +#endif + +/* Routine xxx_device_set_filter */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t xxx_device_set_filter +#if defined(LINTLIBRARY) + (device, receive_port, receive_portPoly, priority, filter, filterCnt) + mach_port_t device; + mach_port_t receive_port; + mach_msg_type_name_t receive_portPoly; + int priority; + filter_array_t filter; + mach_msg_type_number_t filterCnt; +{ return xxx_device_set_filter(device, receive_port, receive_portPoly, priority, filter, filterCnt); } +#else +( + mach_port_t device, + mach_port_t receive_port, + mach_msg_type_name_t receive_portPoly, + int priority, + filter_array_t filter, + mach_msg_type_number_t filterCnt +); +#endif + +/* Routine device_map */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_map +#if defined(LINTLIBRARY) + (device, prot, offset, size, pager, unmap) + mach_port_t device; + vm_prot_t prot; + vm_offset_t offset; + vm_size_t size; + mach_port_t *pager; + int unmap; +{ return device_map(device, prot, offset, size, pager, unmap); } +#else +( + mach_port_t device, + vm_prot_t prot, + vm_offset_t offset, + vm_size_t size, + mach_port_t *pager, + int unmap +); +#endif + +/* Routine device_set_status */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_set_status +#if defined(LINTLIBRARY) + (device, flavor, status, statusCnt) + mach_port_t device; + dev_flavor_t flavor; + dev_status_t status; + mach_msg_type_number_t statusCnt; +{ return device_set_status(device, flavor, status, statusCnt); } +#else +( + mach_port_t device, + dev_flavor_t flavor, + dev_status_t status, + mach_msg_type_number_t statusCnt +); +#endif + +/* Routine device_get_status */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_get_status +#if defined(LINTLIBRARY) + (device, flavor, status, statusCnt) + mach_port_t device; + dev_flavor_t flavor; + dev_status_t status; + mach_msg_type_number_t *statusCnt; +{ return device_get_status(device, flavor, status, statusCnt); } +#else +( + mach_port_t device, + dev_flavor_t flavor, + dev_status_t status, + mach_msg_type_number_t *statusCnt +); +#endif + +/* Routine device_set_filter */ +#ifdef mig_external +mig_external +#else +extern +#endif +kern_return_t device_set_filter +#if defined(LINTLIBRARY) + (device, receive_port, receive_portPoly, priority, filter, filterCnt) + mach_port_t device; + mach_port_t receive_port; + mach_msg_type_name_t receive_portPoly; + int priority; + filter_array_t filter; + mach_msg_type_number_t filterCnt; +{ return device_set_filter(device, receive_port, receive_portPoly, priority, filter, filterCnt); } +#else +( + mach_port_t device, + mach_port_t receive_port, + mach_msg_type_name_t receive_portPoly, + int priority, + filter_array_t filter, + mach_msg_type_number_t filterCnt +); +#endif + +#endif /* not defined(_device_user_) */ diff --git a/eth-multiplexer/device_impl.c b/eth-multiplexer/device_impl.c new file mode 100644 index 00000000..953fca70 --- /dev/null +++ b/eth-multiplexer/device_impl.c @@ -0,0 +1,284 @@ +/* + 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. */ + +#include <fcntl.h> +#include <mach.h> +#include <hurd.h> +#include <hurd/ports.h> +#include <hurd/netfs.h> +#include <device/device.h> + +#include "ethernet.h" +#include "vdev.h" +#include "ourdevice_S.h" +#include "notify_S.h" +#include "bpf_impl.h" +#include "netfs_impl.h" +#include "util.h" + +extern struct port_bucket *port_bucket; +extern struct port_class *vdev_portclass; +extern struct port_class *other_portclass; +extern struct port_info *notify_pi; + +/* 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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + 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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + 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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +/* + * This function is currently running in the multithread environment, + * it should be protected by locks. + */ +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) +{ + struct vether_device *dev; + int openstat; + int right_mode = 1; + struct protid *pi = ports_lookup_port (netfs_port_bucket, master_port, 0); + if (pi == NULL) + return D_NO_SUCH_DEVICE; + + /* If the virtual device hasn't been created yet, + * create it now. */ + if (pi->po->np->nn->ln == NULL) + { + extern struct port_bucket *port_bucket; + extern struct port_class *vdev_portclass; + extern struct stat underlying_node_stat; + static int ino_count = 0; + /* Create a new light node (virtual device). */ + struct lnode *ln = (struct lnode *) add_vdev (pi->po->np->nn->name, + sizeof (*ln), + vdev_portclass, + port_bucket); + if (ln == NULL) + { + ports_port_deref (pi); + return D_NO_MEMORY; + } + memset (&ln->st, 0, sizeof (ln->st)); + ln->st.st_ino = ++ino_count; + ln->st.st_mode = S_IFCHR | (underlying_node_stat.st_mode & ~S_IFMT); + ln->st.st_ctime = ln->st.st_mtime = ln->st.st_atime = time (NULL); + fshelp_touch (&ln->st, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + multiplexer_maptime); + pi->po->np->nn->ln = ln; + } + + dev = (struct vether_device *) pi->po->np->nn->ln; + /* check the mode */ + openstat = pi->po->openstat; + if (mode & D_READ && !(openstat & O_READ)) + right_mode = 0; + if (mode & D_WRITE && !(openstat & O_WRITE)) + right_mode = 0; + ports_port_deref (pi); + + if (dev) + { + if (!right_mode) + return EBADF; + *device = dev->dev_port; + *devicetype = MACH_MSG_TYPE_MAKE_SEND; + return 0; + } + return D_NO_SUCH_DEVICE; +} + +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) +{ + kern_return_t ret = 0; + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + { + vm_deallocate (mach_task_self (), data, datalen); + return D_NO_SUCH_DEVICE; + } + /* The packet is forwarded to all virtual interfaces and + * the interface which the multiplexer connects to. */ + broadcast_pack (data, datalen, vdev); + *bytes_written = datalen; + if(ether_port != MACH_PORT_NULL) + ret = device_write (ether_port, mode , recnum , + data, datalen, bytes_written); + /* The data in device_write() is transmifered out of line, + * so the server-side function has to deallocate it. */ + vm_deallocate (mach_task_self (), data, datalen); + ports_port_deref (vdev); + return ret; +} + +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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +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) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +kern_return_t +ds_device_set_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t statuslen) +{ + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + ports_port_deref (vdev); + return D_INVALID_OPERATION; +} + +kern_return_t +ds_device_get_status (device_t device, dev_flavor_t flavor, + dev_status_t status, size_t *statuslen) +{ + extern io_return_t dev_getstat (struct vether_device *, dev_flavor_t, + dev_status_t, natural_t *); + kern_return_t ret = 0; + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + if(ether_port != MACH_PORT_NULL) + ret = device_get_status (ether_port, flavor, status, statuslen); + else + ret = dev_getstat (vdev, flavor, status, statuslen); + ports_port_deref (vdev); + 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; + struct vether_device *vdev = ports_lookup_port (port_bucket, device, + vdev_portclass); + if (vdev == NULL) + return D_NO_SUCH_DEVICE; + 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 (err != KERN_SUCCESS) + goto out; + if (tmp != MACH_PORT_NULL) + mach_port_deallocate (mach_task_self (), tmp); + err = net_set_filter (&vdev->port_list, receive_port, + priority, filter, filterlen); +out: + ports_port_deref (vdev); + return err; +} diff --git a/eth-multiplexer/ethernet.c b/eth-multiplexer/ethernet.c new file mode 100644 index 00000000..32c5589f --- /dev/null +++ b/eth-multiplexer/ethernet.c @@ -0,0 +1,144 @@ +/* + Copyright (C) 1995, 1996, 1998, 1999, 2000, 2002, 2007, 2008 + Free Software Foundation, Inc. + + Written by Zheng Da + + Based on pfinet/ethernet.c, written by Michael I. Bushnell, p/BSG. + + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <string.h> +#include <error.h> +#include <assert.h> +#include <net/if.h> +#include <sys/ioctl.h> + +#include <hurd/ports.h> +#include <device/device.h> +#include <device/net_status.h> + +#include "ethernet.h" +#include "vdev.h" +#include "util.h" + +#define ETH_HLEN 14 + +static struct port_info *readpt; + +/* Port for writing message to the real network interface. */ +mach_port_t ether_port; +/* Port for receiving messages from the interface. */ +static mach_port_t readptname; + +/* The BPF instruction allows IP and ARP packets */ +static struct bpf_insn ether_filter[] = +{ + {NETF_IN|NETF_BPF, /* Header. */ 0, 0, 0}, + {40, 0, 0, 12}, + {21, 1, 0, 2054}, + {21, 0, 1, 2048}, + {6, 0, 0, 1500}, + {6, 0, 0, 0} +}; +static int ether_filter_len = sizeof (ether_filter) / sizeof (short); + +int ethernet_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + struct net_rcv_msg *msg = (struct net_rcv_msg *) inp; + + if (inp->msgh_id != NET_RCV_MSG_ID) + return 0; + + broadcast_msg (msg); + /* The data from the underlying network is inside the message, + * so we don't need to deallocate the data. */ + return 1; +} + +int set_promisc (char *dev_name, mach_port_t ether_port, int is_promisc) +{ +#ifndef NET_FLAGS +#define NET_FLAGS (('n'<<16) + 4) +#endif + short flags; + int ret; + size_t count; + + debug ("set_promisc is called, is_promisc: %d\n", is_promisc); + count = sizeof (flags); + ret = device_get_status (ether_port, NET_FLAGS, (dev_status_t) &flags, + &count); + if (ret) + { + error (0, ret, "device_get_status"); + return -1; + } + if (is_promisc) + flags |= IFF_PROMISC; + else + flags &= ~IFF_PROMISC; + ret = device_set_status(ether_port, NET_FLAGS, (dev_status_t) &flags, + sizeof (flags)); + if (ret) + { + error (0, ret, "device_set_status"); + return -1; + } + return 0; +} + +int ethernet_open (char *dev_name, device_t master_device, + struct port_bucket *etherport_bucket, + struct port_class *etherreadclass) +{ + error_t err; + + assert (ether_port == MACH_PORT_NULL); + + err = ports_create_port (etherreadclass, etherport_bucket, + sizeof (struct port_info), &readpt); + if (err) + error (2, err, "ports_create_port"); + readptname = ports_get_right (readpt); + mach_port_insert_right (mach_task_self (), readptname, readptname, + MACH_MSG_TYPE_MAKE_SEND); + + mach_port_set_qlimit (mach_task_self (), readptname, MACH_PORT_QLIMIT_MAX); + + err = device_open (master_device, D_WRITE | D_READ, "eth", ðer_port); + mach_port_deallocate (mach_task_self (), master_device); + if (err) + error (2, err, "device_open: %s", dev_name); + + err = device_set_filter (ether_port, ports_get_right (readpt), + MACH_MSG_TYPE_MAKE_SEND, 0, + (unsigned short *)ether_filter, ether_filter_len); + if (err) + error (2, err, "device_set_filter: %s", dev_name); + + set_promisc (dev_name, ether_port, 1); + return 0; +} + +int ethernet_close (char *dev_name) +{ + set_promisc (dev_name, ether_port, 0); + return 0; +} + diff --git a/eth-multiplexer/ethernet.h b/eth-multiplexer/ethernet.h new file mode 100644 index 00000000..04b41e38 --- /dev/null +++ b/eth-multiplexer/ethernet.h @@ -0,0 +1,39 @@ +/* + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef ETHERNET_H +#define ETHERNET_H + +#include <netinet/in.h> +#include <stdlib.h> + +extern mach_port_t ether_port; + +int ethernet_open (char *dev_name, device_t master_device, + struct port_bucket *etherport_bucket, + struct port_class *etherreadclass); +int ethernet_close (char *dev_name); +int ethernet_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp); + +#endif + diff --git a/eth-multiplexer/multiplexer.c b/eth-multiplexer/multiplexer.c new file mode 100644 index 00000000..87578ec5 --- /dev/null +++ b/eth-multiplexer/multiplexer.c @@ -0,0 +1,208 @@ +/* + 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. */ + +/* + * The multiplexer server provides the virtual network interface. + * When it gets a packet, it forwards it to every other network interface, + * the ones that are created by itself or that it connects to. + * BPF is ported to the multiplexer to help deliver packets + * to the right pfinet. + */ + +#include <argz.h> +#include <argp.h> +#include <errno.h> +#include <error.h> +#include <stdlib.h> +#include <fcntl.h> + +#include <hurd.h> +#include <mach.h> +#include <version.h> +#include <device/device.h> +#include <hurd/ports.h> +#include <hurd/netfs.h> + +#include "ethernet.h" +#include "vdev.h" +#include "ourdevice_S.h" +#include "notify_S.h" +#include "bpf_impl.h" +#include "netfs_impl.h" +#include "util.h" + +/* The device which the multiplexer connects to */ +static char *device_file; + +const char *argp_program_version = "eth-multiplexer 0.1"; +const char *argp_program_bug_address = "<bug-hurd@gnu.org>"; +static const char doc[] = "Hurd multiplexer server."; +static const struct argp_option options[] = +{ + {"interface", 'i', "DEVICE", 0, + "Network interface to use", 2}, + {0} +}; + +/* Port bucket we service requests on. */ +struct port_bucket *port_bucket; +struct port_class *other_portclass; +struct port_class *vdev_portclass; +struct port_info *notify_pi; + +int netfs_maxsymlinks = 12; +char *netfs_server_name = "multiplexer"; +char *netfs_server_version = HURD_VERSION; +file_t root_file; +struct lnode root; +struct stat underlying_node_stat; + +static int +multiplexer_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + int device_server (mach_msg_header_t *, mach_msg_header_t *); + int notify_server (mach_msg_header_t *, mach_msg_header_t *); + + return (device_server (inp, outp) + || notify_server (inp, outp) + || ethernet_demuxer (inp, outp)); +} + +static void * +multiplexer_thread (void *arg) +{ + ports_manage_port_operations_one_thread (port_bucket, + multiplexer_demuxer, + 0); + return 0; +} + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + switch (opt) + { + case 'i': + device_file = arg; + break; + case ARGP_KEY_ERROR: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_INIT: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +int +main (int argc, char *argv[]) +{ + error_t err; + mach_port_t bootstrap; + mach_port_t master_device; + const struct argp argp = { options, parse_opt, 0, doc }; + pthread_t t; + + port_bucket = ports_create_bucket (); + other_portclass = ports_create_class (0, 0); + vdev_portclass = ports_create_class (destroy_vdev, 0); + + argp_parse (&argp, argc, argv, 0, 0, 0); + + /* Open the network interface. */ + if (device_file) + { + master_device = file_name_lookup (device_file, 0, 0); + if (master_device == MACH_PORT_NULL) + error (1, errno, "file_name_lookup"); + + ethernet_open (device_file, master_device, port_bucket, + other_portclass); + } + + /* Prepare for the notification. */ + err = ports_create_port (other_portclass, port_bucket, + sizeof (struct port_info), ¬ify_pi); + if (err) + error (1, err, "ports_create_port for notification"); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "must be started as a translator"); + + /* Run the multiplexer server in another thread. */ + pthread_create (&t, NULL, multiplexer_thread, NULL); + pthread_detach (t); + + err = maptime_map (0, 0, &multiplexer_maptime); + if (err) + error (4, err, "Cannot map time"); + + /* Initialize netfs and start the translator. */ + netfs_init (); + + root_file = netfs_startup (bootstrap, O_READ); + err = new_node (&root, &netfs_root_node); + if (err) + error (5, err, "Cannot create root node"); + + err = io_stat (root_file, &underlying_node_stat); + if (err) + error (6, err, "Cannot stat underlying node"); + + struct stat stat = underlying_node_stat; + /* If the underlying node is not a directory, increase its permissions */ + if(!S_ISDIR(stat.st_mode)) + { + if(stat.st_mode & S_IRUSR) + stat.st_mode |= S_IXUSR; + if(stat.st_mode & S_IRGRP) + stat.st_mode |= S_IXGRP; + if(stat.st_mode & S_IROTH) + stat.st_mode |= S_IXOTH; + } + + stat.st_mode &= ~(S_ITRANS | S_IFMT); + stat.st_mode |= S_IFDIR; + netfs_root_node->nn->ln->st = stat; + fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + multiplexer_maptime); + + netfs_server_loop (); /* Never returns. */ + return 0; +} + +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + error_t err = 0; + +#define ADD_OPT(fmt, args...) \ + do { char buf[100]; \ + if (! err) { \ + snprintf (buf, sizeof buf, fmt , ##args); \ + err = argz_add (argz, argz_len, buf); } } while (0) + if (device_file) + ADD_OPT ("--interface=%s", device_file); +#undef ADD_OPT + return err; +} diff --git a/eth-multiplexer/netfs_impl.c b/eth-multiplexer/netfs_impl.c new file mode 100644 index 00000000..29ae072c --- /dev/null +++ b/eth-multiplexer/netfs_impl.c @@ -0,0 +1,508 @@ +/* + Copyright (C) 2008, 2009 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. */ + +#include <fcntl.h> +#include <dirent.h> +#include <stddef.h> +#include <sys/mman.h> +#include <stdlib.h> +#include <ctype.h> + +#include <hurd/netfs.h> + +#include "netfs_impl.h" +#include "vdev.h" +#include "util.h" + +#define DIRENTS_CHUNK_SIZE (8*1024) +/* Returned directory entries are aligned to blocks this many bytes long. + * Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) + +/* Length is structure before the name + the name + '\0', all + * padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + +extern struct stat underlying_node_stat; + +int +is_num (char *str) +{ + for (; *str; str++) + { + if (!isdigit (*str)) + return 0; + } + return 1; +} + +/* Make a new virtual node. Always consumes the ports. */ +error_t +new_node (struct lnode *ln, struct node **np) +{ + error_t err = 0; + struct netnode *nn = calloc (1, sizeof *nn); + struct node *node; + + if (nn == 0) + return ENOMEM; + node = netfs_make_node (nn); + if (node == 0) + { + free (nn); + *np = NULL; + return ENOMEM; + } + if (ln) + ln->n = node; + nn->ln = ln; + *np = node; + return err; +} + +struct node * +lookup (char *name) +{ + struct lnode *ln = (struct lnode *) lookup_dev_by_name (name); + + char *copied_name = malloc (strlen (name) + 1); + strcpy (copied_name, name); + if (ln) + { + new_node (ln, &ln->n); + ln->n->nn->name = copied_name; + return ln->n; + } + else + { + struct node *n; + new_node (ln, &n); + n->nn->name = copied_name; + return n; + } +} + +/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE + to the new node upon return. On any error, clear *NODE. *NODE should be + locked on success; no matter what, unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser *user, struct node *dir, + char *name, mode_t mode, struct node **node) +{ + debug(""); + *node = 0; + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we + just created this node. Return an error if we should not permit the open + to complete because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *node, + int flags, int newnode) +{ + error_t err = 0; + + /*Cheks user's permissions*/ + if(flags & O_READ) + err = fshelp_access(&node->nn_stat, S_IREAD, user); + if(!err && (flags & O_WRITE)) + err = fshelp_access(&node->nn_stat, S_IWRITE, user); + if(!err && (flags & O_EXEC)) + err = fshelp_access(&node->nn_stat, S_IEXEC, user); + + debug("the mode of node: %o, return result: %d", + (node->nn_stat.st_mode & ~S_IFMT), err); + /*Return the result of the check*/ + return err; +} + +/* This should attempt a utimes call for the user specified by CRED on node + NODE, to change the atime to ATIME and the mtime to MTIME. */ +error_t +netfs_attempt_utimes (struct iouser *cred, struct node *node, + struct timespec *atime, struct timespec *mtime) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) + in *TYPES for file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser *cred, struct node *node, int *types) +{ + debug(""); + *types = 0; + return 0; +} + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node *node, struct iouser *cred) +{ + struct stat st; + + if (node->nn->ln) + st = node->nn->ln->st; + else + st = underlying_node_stat; + + debug("node: %p", node); + node->nn_translated = S_ISLNK (st.st_mode) ? S_IFLNK : 0; + node->nn_stat = st; + return 0; +} + +/* This should sync the file NODE completely to disk, for the user CRED. If + WAIT is set, return only after sync is completely finished. */ +error_t +netfs_attempt_sync (struct iouser *cred, struct node *node, int wait) +{ + debug(""); + return 0; +} + +error_t +netfs_get_dirents (struct iouser *cred, struct node *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err; + int count = 0; + char *data_p; + size_t size = (max_data_len == 0 || max_data_len > DIRENTS_CHUNK_SIZE + ? DIRENTS_CHUNK_SIZE : max_data_len); + debug (""); + int + add_dirent (const char * name, ino_t ino, int type) + { + /*If the required number of dirents has not been listed yet*/ + if((max_entries == -1) || (count < max_entries)) + { + struct dirent hdr; + size_t name_len = strlen(name); + size_t sz = DIRENT_LEN(name_len); + + /*If there is no room for this dirent*/ + if ((data_p - *data) + sz > size) + { + if (max_data_len > 0) + return 1; + else + /* Try to grow our return buffer. */ + { + error_t err; + vm_address_t extension = (vm_address_t)(*data + size); + err = vm_allocate (mach_task_self (), &extension, + DIRENTS_CHUNK_SIZE, 0); + if (err) + { + munmap (*data, size); + return 1; + } + size += DIRENTS_CHUNK_SIZE; + } + } + + /*setup the dirent*/ + hdr.d_ino = ino; + hdr.d_reclen = sz; + hdr.d_type = type; + hdr.d_namlen = name_len; + memcpy(data_p, &hdr, DIRENT_NAME_OFFS); + strcpy(data_p + DIRENT_NAME_OFFS, name); + data_p += sz; + + /*count the new dirent*/ + ++count; + } + return 0; + } + int add_each_dev (struct vether_device *dev) + { + struct lnode *ln = (struct lnode *) dev; + add_dirent (ln->vdev.name, ln->st.st_ino, DT_CHR); + return 0; + } + if (dir != netfs_root_node) + return ENOTDIR; + + *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + err = ((void *) *data == (void *) -1) ? errno : 0; + if (!err) + { + data_p = *data; + if (first_entry < 2 + get_dev_num ()) + { + add_dirent (".", 2, DT_DIR); + add_dirent ("..", 2, DT_DIR); + foreach_dev_do (add_each_dev); + } + + vm_address_t alloc_end = (vm_address_t)(*data + size); + vm_address_t real_end = round_page (data_p); + if (alloc_end > real_end) + munmap ((caddr_t) real_end, alloc_end - real_end); + *data_entries = count; + debug ("first_entry is %d, count is %d", first_entry, count); + *data_len = data_p - *data; + } + return err; +} + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, this call should unlock DIR no matter + what.) */ +error_t netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err = 0; + + debug ("dir: %p, file name: %s", dir, name); + + if (strcmp(name, ".") == 0) + { + netfs_nref(dir); + *node = dir; + return 0; + } + else if (strcmp(name, "..") == 0) + { + /*The supplied node is always root*/ + err = ENOENT; + *node = NULL; + + /*unlock the directory*/ + pthread_mutex_unlock (&dir->lock); + + /*stop here*/ + return err; + } + + *node = lookup (name); + pthread_mutex_lock (&(*node)->lock); + pthread_mutex_unlock (&dir->lock); + return 0; +} + +/* Delete NAME in DIR for USER. */ +error_t netfs_attempt_unlink (struct iouser *user, struct node *dir, + char *name) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Note that in this one call, neither of the specific nodes are locked. */ +error_t netfs_attempt_rename (struct iouser *user, struct node *fromdir, + char *fromname, struct node *todir, + char *toname, int excl) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Attempt to create a new directory named NAME in DIR for USER with mode + MODE. */ +error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir, + char *name, mode_t mode) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Attempt to remove directory named NAME in DIR for USER. */ +error_t netfs_attempt_rmdir (struct iouser *user, + struct node *dir, char *name) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t netfs_attempt_chown (struct iouser *cred, struct node *node, + uid_t uid, uid_t gid) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node, + uid_t author) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t netfs_attempt_chmod (struct iouser *cred, struct node *node, + mode_t mode) +{ + error_t err = 0; + debug(""); + if (node->nn->ln == NULL) + return EOPNOTSUPP; + + mode &= ~S_ITRANS; + err = fshelp_isowner (&node->nn->ln->st, cred); + if (err) + return err; + mode |= node->nn->ln->st.st_mode & S_IFMT; + node->nn->ln->st.st_mode = mode; + fshelp_touch (&node->nn_stat, TOUCH_CTIME, multiplexer_maptime); + return err; +} + +/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */ +error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node, + char *name) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or + S_IFCHR. */ +error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node, + mode_t type, dev_t indexes) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Attempt to set the passive translator record for FILE to ARGZ (of length + ARGZLEN) for user CRED. */ +error_t netfs_set_translator (struct iouser *cred, struct node *node, + char *argz, size_t argzlen) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should attempt a chflags call for the user specified by CRED on node + NODE, to change the flags to FLAGS. */ +error_t netfs_attempt_chflags (struct iouser *cred, struct node *node, + int flags) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should attempt to set the size of the file NODE (for user CRED) to + SIZE bytes long. */ +error_t netfs_attempt_set_size (struct iouser *cred, struct node *node, + off_t size) +{ + debug(""); + return EOPNOTSUPP; +} + +/*Fetches the filesystem status information*/ +error_t +netfs_attempt_statfs (struct iouser *cred, struct node *node, + struct statfs *st) +{ + debug(""); + return EOPNOTSUPP; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + debug(""); + return 0; +} + +/* Create a link in DIR with name NAME to FILE for USER. Note that neither + DIR nor FILE are locked. If EXCL is set, do not delete the target, but + return EEXIST if NAME is already found in DIR. */ +error_t netfs_attempt_link (struct iouser *user, struct node *dir, + struct node *file, char *name, int excl) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Attempt to create an anonymous file related to DIR for USER with MODE. + Set *NODE to the returned file upon success. No matter what, unlock DIR. */ +error_t netfs_attempt_mkfile (struct iouser *user, struct node *dir, + mode_t mode, struct node **node) +{ + debug(""); + *node = 0; + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* Read the contents of NODE (a symlink), for USER, into BUF. */ +error_t netfs_attempt_readlink (struct iouser *user, struct node *node, char *buf) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Read from the file NODE for user CRED starting at OFFSET and continuing for + up to *LEN bytes. Put the data at DATA. Set *LEN to the amount + successfully read upon return. */ +error_t netfs_attempt_read (struct iouser *cred, struct node *node, + off_t offset, size_t *len, void *data) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Write to the file NODE for user CRED starting at OFSET and continuing for up + to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon + return. */ +error_t netfs_attempt_write (struct iouser *cred, struct node *node, + off_t offset, size_t *len, void *data) +{ + debug(""); + return EOPNOTSUPP; +} + +/* Node NP is all done; free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + debug("node: %p", node); + if (node->nn->ln) + node->nn->ln->n = NULL; + free (node->nn->name); + free (node->nn); + free (node); +} + diff --git a/eth-multiplexer/netfs_impl.h b/eth-multiplexer/netfs_impl.h new file mode 100644 index 00000000..17c66f63 --- /dev/null +++ b/eth-multiplexer/netfs_impl.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2008, 2009 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 NETFS_IMPL +#define NETFS_IMPL + +#include <hurd.h> +#include <mach.h> + +#include "vdev.h" + +struct netnode +{ + struct lnode *ln; + char *name; +}; + +struct lnode +{ + struct vether_device vdev; + struct stat st; + struct node *n; +}; + +extern file_t root_file; +volatile struct mapped_time_value *multiplexer_maptime; + +error_t new_node (struct lnode *ln, struct node **np); + +#endif diff --git a/eth-multiplexer/notify_impl.c b/eth-multiplexer/notify_impl.c new file mode 100644 index 00000000..33725bb6 --- /dev/null +++ b/eth-multiplexer/notify_impl.c @@ -0,0 +1,69 @@ +/* + 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. */ + +#include <hurd.h> +#include <mach.h> + +#include "vdev.h" + +/* 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) +{ + 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) +{ + debug ("do_mach_notify_dead_name is called\n"); + mach_port_deallocate (mach_task_self (), name); + remove_dead_port_from_dev (name); + return 0; +} diff --git a/eth-multiplexer/test.c b/eth-multiplexer/test.c new file mode 100644 index 00000000..bf80583f --- /dev/null +++ b/eth-multiplexer/test.c @@ -0,0 +1,53 @@ +/* + 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. */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <error.h> + +#include <hurd.h> +#include <mach.h> +#include <device/device.h> + +int +main(int argc , char *argv[]) +{ + mach_port_t device; + mach_port_t master_device; + error_t err; + + err = get_privileged_ports (0, &master_device); + if (err) + error (2, err, "cannot get device master port"); + + err = device_open (master_device, D_READ | D_WRITE, "eth0", &device); + if (err) + error (1, err, "device_open"); + printf ("the device port is %d\n", device); + + err = device_open (master_device, D_READ | D_WRITE, "eth0", &device); + if (err) + error (1, err, "device_open"); + printf ("the device port is %d\n", device); + + return 0; +} diff --git a/eth-multiplexer/util.h b/eth-multiplexer/util.h new file mode 100644 index 00000000..c90b0f85 --- /dev/null +++ b/eth-multiplexer/util.h @@ -0,0 +1,91 @@ +/* + 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 <execinfo.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/ip.h> + +#include <mach.h> + +#ifdef DEBUG + +#define debug(format, ...) do \ +{ \ + char buf[1024]; \ + snprintf (buf, 1024, "multiplexer: %s: %s\n", __func__, format); \ + fprintf (stderr , buf, ## __VA_ARGS__); \ + fflush (stderr); \ +} while (0) + +#else + +#define debug(format, ...) do {} while (0) + +#endif + +#define print_backtrace() do \ +{ \ + size_t size; \ + void *array[30]; \ + size = backtrace (array, sizeof (array)); \ + debug ("the depth of the stack: %d", size); \ + backtrace_symbols_fd(array, size, fileno (stderr)); \ +} while (0) + +#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 +#define ETH_P_IP 0x0800 + 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 ("multiplexer: 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 ("multiplexer: get a non-IP packet\n"); + } +#endif +} + +#endif diff --git a/eth-multiplexer/vdev.c b/eth-multiplexer/vdev.c new file mode 100644 index 00000000..fd886615 --- /dev/null +++ b/eth-multiplexer/vdev.c @@ -0,0 +1,309 @@ +/* + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +/* This file implement the virtual network interface */ + +#include <string.h> +#include <stdio.h> +#include <netinet/ip.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <error.h> + +#include <pthread.h> + +#include "vdev.h" +#include "queue.h" +#include "bpf_impl.h" +#include "util.h" + +#define ETH_HLEN sizeof (struct ethhdr) + +static struct vether_device *dev_head; +static int dev_num; + +/* This lock is only used to protected the virtual device list. + * TODO every device structure should has its own lock to protect itself. */ +static pthread_mutex_t dev_list_lock = PTHREAD_MUTEX_INITIALIZER; + +mach_msg_type_t header_type = +{ + MACH_MSG_TYPE_BYTE, + 8, + NET_HDW_HDR_MAX, + TRUE, + FALSE, + FALSE, + 0 +}; + +mach_msg_type_t packet_type = +{ + MACH_MSG_TYPE_BYTE, /* name */ + 8, /* size */ + 0, /* number */ + TRUE, /* inline */ + FALSE, /* longform */ + FALSE /* deallocate */ +}; + +int +get_dev_num () +{ + return dev_num; +} + +struct vether_device * +lookup_dev_by_name (char *name) +{ + struct vether_device *vdev; + pthread_mutex_lock (&dev_list_lock); + for (vdev = dev_head; vdev; vdev = vdev->next) + { + if (strncmp (vdev->name, name, IFNAMSIZ) == 0) + break; + } + pthread_mutex_unlock (&dev_list_lock); + return vdev; +} + +int +foreach_dev_do (int (func) (struct vether_device *)) +{ + struct vether_device *vdev; + int rval = 0; + pthread_mutex_lock (&dev_list_lock); + for (vdev = dev_head; vdev; vdev = vdev->next) + { + pthread_mutex_unlock (&dev_list_lock); + /* func() can stop the loop by returning <> 0 */ + rval = func (vdev); + pthread_mutex_lock (&dev_list_lock); + if (rval) + break; + } + pthread_mutex_unlock (&dev_list_lock); + return rval; +} + +/* Remove all filters with the dead name. */ +int +remove_dead_port_from_dev (mach_port_t dead_port) +{ + struct vether_device *vdev; + pthread_mutex_lock (&dev_list_lock); + for (vdev = dev_head; vdev; vdev = vdev->next) + { + remove_dead_filter (&vdev->port_list, + &vdev->port_list.if_rcv_port_list, dead_port); + remove_dead_filter (&vdev->port_list, + &vdev->port_list.if_snd_port_list, dead_port); + } + pthread_mutex_unlock (&dev_list_lock); + return 0; +} + +/* Add a new virtual interface to the multiplexer. */ +struct vether_device * +add_vdev (char *name, int size, + struct port_class *class, struct port_bucket *bucket) +{ + error_t err; + struct vether_device *vdev; + + if (size < sizeof (*vdev)) + size = sizeof (*vdev); + err = ports_create_port (class, bucket, size, &vdev); + if (err) + return NULL; + + vdev->dev_port = ports_get_right (vdev); + ports_port_deref (vdev); + strncpy (vdev->name, name, IFNAMSIZ); + vdev->if_header_size = ETH_HLEN; + vdev->if_mtu = ETH_MTU; + vdev->if_header_format = HDR_ETHERNET; + vdev->if_address_size = ETH_ALEN; + vdev->if_flags = 0; + vdev->if_address[0] = 0x52; + vdev->if_address[1] = 0x54; + *(int *)(vdev->if_address + 2) = random (); + + queue_init (&vdev->port_list.if_rcv_port_list); + queue_init (&vdev->port_list.if_snd_port_list); + + pthread_mutex_lock (&dev_list_lock); + vdev->next = dev_head; + dev_head = vdev; + vdev->pprev = &dev_head; + if (vdev->next) + vdev->next->pprev = &vdev->next; + dev_num++; + pthread_mutex_unlock (&dev_list_lock); + + debug ("initialize the virtual device\n"); + return vdev; +} + +void +destroy_vdev (void *port) +{ + struct vether_device *vdev = (struct vether_device *)port; + + debug ("device %s is going to be destroyed\n", vdev->name); + /* Delete it from the virtual device list */ + pthread_mutex_lock (&dev_list_lock); + *vdev->pprev = vdev->next; + if (vdev->next) + vdev->next->pprev = vdev->pprev; + dev_num--; + pthread_mutex_unlock (&dev_list_lock); + + /* TODO Delete all filters in the interface, + * there shouldn't be any filters left */ + destroy_filters (&vdev->port_list); +} + +/* Test if there are devices existing in the list */ +int +has_vdev () +{ + return dev_head != NULL; +} + +/* Broadcast the packet to all virtual interfaces + * except the one the packet is from */ +int +broadcast_pack (char *data, int datalen, struct vether_device *from_vdev) +{ + int internal_deliver_pack (struct vether_device *vdev) + { + if (from_vdev == vdev) + return 0; + return deliver_pack (data, datalen, vdev); + } + + return foreach_dev_do (internal_deliver_pack); +} + +/* Create a message, and deliver it. */ +int +deliver_pack (char *data, int datalen, struct vether_device *vdev) +{ + struct net_rcv_msg msg; + int pack_size; + struct ethhdr *header; + struct packet_header *packet; + + pack_size = datalen - sizeof (struct ethhdr); + /* remember message sizes must be rounded up */ + msg.msg_hdr.msgh_size = (((mach_msg_size_t) (sizeof(struct net_rcv_msg) + - NET_RCV_MAX + pack_size)) + 3) & ~3; + + header = (struct ethhdr *) msg.header; + packet = (struct packet_header *) msg.packet; + msg.header_type = header_type; + memcpy (header, data, sizeof (struct ethhdr)); + msg.packet_type = packet_type; + memcpy (packet + 1, data + sizeof (struct ethhdr), pack_size); + packet->type = header->h_proto; + packet->length = pack_size + sizeof (struct packet_header); + msg.packet_type.msgt_number = packet->length; + + return deliver_msg (&msg, vdev); +} + +/* Broadcast the message to all virtual interfaces. */ +int +broadcast_msg (struct net_rcv_msg *msg) +{ + int rval = 0; + mach_msg_header_t header; + + int internal_deliver_msg (struct vether_device *vdev) + { + return deliver_msg (msg, vdev); + } + + /* Save the message header because deliver_msg will change it. */ + header = msg->msg_hdr; + rval = foreach_dev_do (internal_deliver_msg); + msg->msg_hdr = header; + return rval; +} + +/* + * Deliver the message to all right pfinet servers that + * connects to the virtual network interface. + */ +int +deliver_msg(struct net_rcv_msg *msg, struct vether_device *vdev) +{ + mach_msg_return_t err; + queue_head_t *if_port_list; + net_rcv_port_t infp, nextfp; + + msg->msg_hdr.msgh_bits = MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND, 0); + /* remember message sizes must be rounded up */ + msg->msg_hdr.msgh_local_port = MACH_PORT_NULL; + msg->msg_hdr.msgh_kind = MACH_MSGH_KIND_NORMAL; + msg->msg_hdr.msgh_id = NET_RCV_MSG_ID; + + if_port_list = &vdev->port_list.if_rcv_port_list; + FILTER_ITERATE (if_port_list, infp, nextfp, &infp->input) + { + mach_port_t dest; + net_hash_entry_t entp, *hash_headp; + int ret_count; + + entp = (net_hash_entry_t) 0; + ret_count = 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 (entp == (net_hash_entry_t) 0) + dest = infp->rcv_port; + else + dest = entp->rcv_port; + + if (ret_count) + { + debug ("before delivering the packet\n"); + msg->msg_hdr.msgh_remote_port = dest; + 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) + { + mach_port_deallocate(mach_task_self (), + ((mach_msg_header_t *)msg)->msgh_remote_port); + error (0, err, "mach_msg"); + return -1; + } + debug ("after delivering the packet\n"); + } + } + FILTER_ITERATE_END + + return 0; +} + diff --git a/eth-multiplexer/vdev.h b/eth-multiplexer/vdev.h new file mode 100644 index 00000000..c8696785 --- /dev/null +++ b/eth-multiplexer/vdev.h @@ -0,0 +1,79 @@ +/* + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef VDEV_H +#define VDEV_H + +#include <net/if.h> + +#include <hurd.h> +#include <mach.h> +#include <hurd/ports.h> +#include <device/net_status.h> + +#include <bpf_impl.h> + +#include "queue.h" +#include "util.h" + +#define MAX_SERVERS 10 +#define ETH_MTU 1500 + +struct vether_device +{ + /* The ports used by the socket server to send packets to the interface. */ + struct port_info dev_pi; + mach_port_t dev_port; + + char name[IFNAMSIZ]; + + short if_header_size; + short if_mtu; + short if_header_format; + short if_address_size; + short if_flags; + char if_address[ETH_ALEN]; + + struct vether_device *next; + struct vether_device **pprev; + + if_filter_list_t port_list; +}; + +typedef int (*dev_act_func) (struct vether_device *); + +int serv_connect (mach_port_t port); +int serv_disconnect (); +struct vether_device *lookup_dev_by_name (char *name); +int remove_dead_port_from_dev (mach_port_t dead_port); +struct vether_device *add_vdev (char *name, int size, + struct port_class *class, + struct port_bucket *bucket); +void destroy_vdev (void *port); +int has_vdev (); +int deliver_msg (struct net_rcv_msg *msg, struct vether_device *vdev); +int deliver_pack (char *data, int datalen, struct vether_device *vdev); +boolean_t all_dev_close (); +int broadcast_pack (char *data, int datalen, struct vether_device *from_vdev); +int broadcast_msg (struct net_rcv_msg *msg); +int get_dev_num (); +int foreach_dev_do (dev_act_func func); + +#endif diff --git a/procfs/Makefile b/procfs/Makefile index c69cb206..5c51c1d2 100644 --- a/procfs/Makefile +++ b/procfs/Makefile @@ -1,7 +1,7 @@ TARGET = procfs OBJS = procfs.o netfs.o procfs_dir.o \ process.o proclist.o rootdir.o dircat.o main.o -LIBS = -lnetfs -lps -lfshelp +LIBS = -lnetfs -lps -lfshelp -lpthread CC = gcc CFLAGS = -Wall -g @@ -12,7 +12,7 @@ ifdef PROFILE CFLAGS= -g -pg CPPFLAGS= -DPROFILE LDFLAGS= -static -LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lthreads -lihash -lshouldbeinlibc +LIBS= -lnetfs -lfshelp -liohelp -lps -lports -lpthread -lihash -lshouldbeinlibc endif CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 diff --git a/procfs/main.c b/procfs/main.c index 1b19c013..90b3e92c 100644 --- a/procfs/main.c +++ b/procfs/main.c @@ -37,6 +37,10 @@ pid_t opt_fake_self; pid_t opt_kernel_pid; uid_t opt_anon_owner; +#define NODEV_KEY -1 /* <= 0, so no short option. */ +#define NOEXEC_KEY -2 /* Likewise. */ +#define NOSUID_KEY -3 /* Likewise. */ + static error_t argp_parser (int key, char *arg, struct argp_state *state) { @@ -104,6 +108,18 @@ argp_parser (int key, char *arg, struct argp_state *state) else opt_anon_owner = v; break; + + case NODEV_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOEXEC_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; + + case NOSUID_KEY: + /* Ignored for compatibility with Linux' procfs. */ + ;; } return 0; @@ -136,6 +152,12 @@ struct argp argp = { "Be aware that USER will be granted access to the environment and " "other sensitive information about the processes in question. " "(default: use uid 0)" }, + { "nodev", NODEV_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "noexec", NOEXEC_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, + { "nosuid", NOSUID_KEY, NULL, 0, + "Ignored for compatibility with Linux' procfs." }, {} }, .parser = argp_parser, diff --git a/procfs/netfs.c b/procfs/netfs.c index c139d11a..276c57cc 100644 --- a/procfs/netfs.c +++ b/procfs/netfs.c @@ -230,6 +230,17 @@ void netfs_node_norefs (struct node *np) pthread_spin_lock (&netfs_node_refcnt_lock); } +/* The user may define this function (but should define it together + with netfs_set_translator). For locked node NODE with S_IPTRANS + set in its mode, look up the name of its translator. Store the + name into newly malloced storage, and return it in *ARGZ; set + *ARGZ_LEN to the total length. */ +error_t netfs_get_translator (struct node *np, char **argz, + size_t *argz_len) +{ + return procfs_get_translator (np, argz, argz_len); +} + /* Libnetfs callbacks managed with libfshelp. */ diff --git a/procfs/procfs.c b/procfs/procfs.c index ae5a6769..cae4a519 100644 --- a/procfs/procfs.c +++ b/procfs/procfs.c @@ -19,6 +19,7 @@ #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <fcntl.h> #include <mach.h> #include <hurd/netfs.h> @@ -76,6 +77,9 @@ struct node *procfs_make_node (const struct procfs_node_ops *ops, void *hook) else np->nn_stat.st_mode = S_IFREG | 0444; + np->nn_stat.st_uid = getuid (); + np->nn_stat.st_gid = getgid (); + return np; fail: @@ -93,7 +97,7 @@ void procfs_node_chown (struct node *np, uid_t owner) void procfs_node_chmod (struct node *np, mode_t mode) { - np->nn_stat.st_mode = (np->nn_stat.st_mode & S_IFMT) | mode; + np->nn_stat.st_mode = (np->nn_stat.st_mode & ~ALLPERMS) | mode; np->nn_translated = np->nn_stat.st_mode; } @@ -201,3 +205,15 @@ void procfs_cleanup (struct node *np) free (np->nn); } + +error_t procfs_get_translator (struct node *np, + char **argz, + size_t *argz_len) +{ + if (np->nn->ops->get_translator) + return np->nn->ops->get_translator (np->nn->hook, argz, argz_len); + + *argz = NULL; + *argz_len = 0; + return 0; +} diff --git a/procfs/procfs.h b/procfs/procfs.h index 64782ec4..d04bbad7 100644 --- a/procfs/procfs.h +++ b/procfs/procfs.h @@ -51,6 +51,9 @@ struct procfs_node_ops /* Destroy this node. */ void (*cleanup) (void *hook); + + /* Get the passive translator record. */ + error_t (*get_translator) (void *hook, char **argz, size_t *argz_len); }; /* These helper functions can be used as procfs_node_ops.cleanup_contents. */ @@ -91,3 +94,6 @@ error_t procfs_get_contents (struct node *np, char **data, ssize_t *data_len); error_t procfs_lookup (struct node *np, const char *name, struct node **npp); void procfs_cleanup (struct node *np); +/* Get the passive translator record if any. */ +error_t procfs_get_translator (struct node *np, char **argz, size_t *argz_len); + diff --git a/procfs/rootdir.c b/procfs/rootdir.c index 31e2d8c6..f234dd03 100644 --- a/procfs/rootdir.c +++ b/procfs/rootdir.c @@ -300,7 +300,7 @@ rootdir_gc_meminfo (void *hook, char **contents, ssize_t *contents_len) , (long unsigned) hbi.memory_size / 1024, (long unsigned) vmstats.free_count * PAGE_SIZE / 1024, - 0, + 0UL, (long unsigned) cache_stats.cache_count * PAGE_SIZE / 1024, (long unsigned) vmstats.active_count * PAGE_SIZE / 1024, (long unsigned) vmstats.inactive_count * PAGE_SIZE / 1024, @@ -392,7 +392,7 @@ out: } static int -rootdir_fakeself_exists () +rootdir_fakeself_exists (void *dir_hook, const void *entry_hook) { return opt_fake_self >= 0; } |