summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
committer <ralf@linux-mips.org>1997-04-29 21:13:14 +0000
commit19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch)
tree40b1cb534496a7f1ca0f5c314a523c69f1fee464 /net
parent7206675c40394c78a90e74812bbdbf8cf3cca1be (diff)
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'net')
-rw-r--r--net/802/Makefile15
-rw-r--r--net/802/TODO29
-rw-r--r--net/802/cl2llc.c615
-rw-r--r--net/802/cl2llc.pre615
-rw-r--r--net/802/fddi.c115
-rw-r--r--net/802/llc.c412
-rw-r--r--net/802/llc_macinit.c225
-rw-r--r--net/802/llc_sendpdu.c363
-rw-r--r--net/802/llc_utility.c253
-rw-r--r--net/802/p8022.c54
-rw-r--r--net/802/p8022tr.c50
-rw-r--r--net/802/p8023.c39
-rw-r--r--net/802/pseudo/Makefile13
-rw-r--r--net/802/pseudo/actionnm.awk27
-rw-r--r--net/802/pseudo/actionnm.h51
-rw-r--r--net/802/pseudo/compile.awk57
-rw-r--r--net/802/pseudo/opcd2num.sed72
-rw-r--r--net/802/pseudo/opcodes72
-rw-r--r--net/802/pseudo/opcodesnm.h23
-rw-r--r--net/802/pseudo/pseudocode780
-rw-r--r--net/802/pseudo/pseudocode.h287
-rw-r--r--net/802/psnap.c37
-rw-r--r--net/802/sysctl_net_802.c20
-rw-r--r--net/802/tr.c370
-rw-r--r--net/802/transit/Makefile13
-rw-r--r--net/802/transit/compile.awk81
-rw-r--r--net/802/transit/pdutr.h309
-rw-r--r--net/802/transit/pdutr.pre1121
-rw-r--r--net/802/transit/timertr.h157
-rw-r--r--net/802/transit/timertr.pre527
-rw-r--r--net/Config.in24
-rw-r--r--net/Makefile52
-rw-r--r--net/README7
-rw-r--r--net/appletalk/Makefile6
-rw-r--r--net/appletalk/aarp.c51
-rw-r--r--net/appletalk/ddp.c1014
-rw-r--r--net/ax25/Makefile12
-rw-r--r--net/ax25/af_ax25.c1643
-rw-r--r--net/ax25/ax25_addr.c311
-rw-r--r--net/ax25/ax25_dev.c238
-rw-r--r--net/ax25/ax25_ds_in.c562
-rw-r--r--net/ax25/ax25_ds_subr.c225
-rw-r--r--net/ax25/ax25_ds_timer.c313
-rw-r--r--net/ax25/ax25_iface.c282
-rw-r--r--net/ax25/ax25_in.c879
-rw-r--r--net/ax25/ax25_ip.c178
-rw-r--r--net/ax25/ax25_out.c352
-rw-r--r--net/ax25/ax25_route.c282
-rw-r--r--net/ax25/ax25_std_in.c542
-rw-r--r--net/ax25/ax25_std_subr.c105
-rw-r--r--net/ax25/ax25_std_timer.c218
-rw-r--r--net/ax25/ax25_subr.c388
-rw-r--r--net/ax25/ax25_timer.c486
-rw-r--r--net/ax25/ax25_uid.c189
-rw-r--r--net/ax25/sysctl_net_ax25.c147
-rw-r--r--net/bridge/Makefile6
-rw-r--r--net/bridge/br.c396
-rw-r--r--net/bridge/br_tree.c31
-rw-r--r--net/core/Makefile7
-rw-r--r--net/core/datagram.c110
-rw-r--r--net/core/dev.c686
-rw-r--r--net/core/dev_mcast.c44
-rw-r--r--net/core/dst.c110
-rw-r--r--net/core/firewall.c79
-rw-r--r--net/core/iovec.c55
-rw-r--r--net/core/neighbour.c287
-rw-r--r--net/core/net_alias.c1919
-rw-r--r--net/core/scm.c308
-rw-r--r--net/core/skbuff.c808
-rw-r--r--net/core/sock.c571
-rw-r--r--net/ethernet/Makefile6
-rw-r--r--net/ethernet/eth.c101
-rw-r--r--net/ipv4/Config.in27
-rw-r--r--net/ipv4/Makefile8
-rw-r--r--net/ipv4/af_inet.c1444
-rw-r--r--net/ipv4/arp.c1647
-rw-r--r--net/ipv4/devinet.c356
-rw-r--r--net/ipv4/fib.c2076
-rw-r--r--net/ipv4/icmp.c557
-rw-r--r--net/ipv4/igmp.c257
-rw-r--r--net/ipv4/ip_alias.c4
-rw-r--r--net/ipv4/ip_forward.c579
-rw-r--r--net/ipv4/ip_fragment.c265
-rw-r--r--net/ipv4/ip_fw.c32
-rw-r--r--net/ipv4/ip_input.c706
-rw-r--r--net/ipv4/ip_masq.c138
-rw-r--r--net/ipv4/ip_masq_app.c138
-rw-r--r--net/ipv4/ip_masq_ftp.c27
-rw-r--r--net/ipv4/ip_masq_irc.c63
-rw-r--r--net/ipv4/ip_masq_quake.c316
-rw-r--r--net/ipv4/ip_masq_raudio.c25
-rw-r--r--net/ipv4/ip_nat_dumb.c77
-rw-r--r--net/ipv4/ip_options.c414
-rw-r--r--net/ipv4/ip_output.c1273
-rw-r--r--net/ipv4/ip_sockglue.c640
-rw-r--r--net/ipv4/ipip.c66
-rw-r--r--net/ipv4/ipmr.c610
-rw-r--r--net/ipv4/packet.c65
-rw-r--r--net/ipv4/proc.c138
-rw-r--r--net/ipv4/protocol.c10
-rw-r--r--net/ipv4/rarp.c13
-rw-r--r--net/ipv4/raw.c435
-rw-r--r--net/ipv4/route.c2465
-rw-r--r--net/ipv4/sysctl_net_ipv4.c102
-rw-r--r--net/ipv4/tcp.c856
-rw-r--r--net/ipv4/tcp_input.c1706
-rw-r--r--net/ipv4/tcp_ipv4.c1595
-rw-r--r--net/ipv4/tcp_output.c820
-rw-r--r--net/ipv4/tcp_timer.c263
-rw-r--r--net/ipv4/timer.c6
-rw-r--r--net/ipv4/udp.c1043
-rw-r--r--net/ipv6/Makefile14
-rw-r--r--net/ipv6/addrconf.c1293
-rw-r--r--net/ipv6/af_inet6.c757
-rw-r--r--net/ipv6/datagram.c138
-rw-r--r--net/ipv6/exthdrs.c39
-rw-r--r--net/ipv6/icmp.c233
-rw-r--r--net/ipv6/ip6_fib.c927
-rw-r--r--net/ipv6/ip6_fw.c378
-rw-r--r--net/ipv6/ip6_input.c408
-rw-r--r--net/ipv6/ip6_output.c629
-rw-r--r--net/ipv6/ipv6_input.c437
-rw-r--r--net/ipv6/ipv6_output.c1003
-rw-r--r--net/ipv6/ipv6_route.c2056
-rw-r--r--net/ipv6/ipv6_sockglue.c120
-rw-r--r--net/ipv6/mcast.c409
-rw-r--r--net/ipv6/ndisc.c1789
-rw-r--r--net/ipv6/proc.c143
-rw-r--r--net/ipv6/protocol.c43
-rw-r--r--net/ipv6/raw.c336
-rw-r--r--net/ipv6/reassembly.c85
-rw-r--r--net/ipv6/route.c1599
-rw-r--r--net/ipv6/sit.c255
-rw-r--r--net/ipv6/sysctl_net_ipv6.c96
-rw-r--r--net/ipv6/tcp_ipv6.c989
-rw-r--r--net/ipv6/udp.c494
-rw-r--r--net/ipx/Makefile6
-rw-r--r--net/ipx/af_ipx.c1167
-rw-r--r--net/lapb/Makefile20
-rw-r--r--net/lapb/lapb_iface.c418
-rw-r--r--net/lapb/lapb_in.c631
-rw-r--r--net/lapb/lapb_out.c233
-rw-r--r--net/lapb/lapb_subr.c291
-rw-r--r--net/lapb/lapb_timer.c208
-rw-r--r--net/netbeui/README19
-rw-r--r--net/netbeui/af_netbeui.c658
-rw-r--r--net/netbeui/netbeui_llc.c265
-rw-r--r--net/netbeui/netbeui_name.c159
-rw-r--r--net/netlink.c259
-rw-r--r--net/netrom/Makefile6
-rw-r--r--net/netrom/af_netrom.c641
-rw-r--r--net/netrom/nr_dev.c153
-rw-r--r--net/netrom/nr_in.c43
-rw-r--r--net/netrom/nr_out.c86
-rw-r--r--net/netrom/nr_route.c141
-rw-r--r--net/netrom/nr_subr.c105
-rw-r--r--net/netrom/nr_timer.c48
-rw-r--r--net/netrom/sysctl_net_netrom.c24
-rw-r--r--net/netsyms.c422
-rw-r--r--net/protocols.c53
-rw-r--r--net/rose/Makefile6
-rw-r--r--net/rose/af_rose.c765
-rw-r--r--net/rose/rose_dev.c128
-rw-r--r--net/rose/rose_in.c73
-rw-r--r--net/rose/rose_link.c131
-rw-r--r--net/rose/rose_out.c78
-rw-r--r--net/rose/rose_route.c194
-rw-r--r--net/rose/rose_subr.c121
-rw-r--r--net/rose/rose_timer.c45
-rw-r--r--net/rose/sysctl_net_rose.c19
-rw-r--r--net/socket.c1216
-rw-r--r--net/sunrpc/Makefile23
-rw-r--r--net/sunrpc/auth.c295
-rw-r--r--net/sunrpc/auth_null.c131
-rw-r--r--net/sunrpc/auth_unix.c238
-rw-r--r--net/sunrpc/clnt.c761
-rw-r--r--net/sunrpc/pmap_clnt.c268
-rw-r--r--net/sunrpc/sched.c805
-rw-r--r--net/sunrpc/stats.c175
-rw-r--r--net/sunrpc/sunrpc_syms.c101
-rw-r--r--net/sunrpc/svc.c357
-rw-r--r--net/sunrpc/svcauth.c163
-rw-r--r--net/sunrpc/svcauth_des.c215
-rw-r--r--net/sunrpc/svcsock.c967
-rw-r--r--net/sunrpc/sysctl.c134
-rw-r--r--net/sunrpc/xdr.c118
-rw-r--r--net/sunrpc/xprt.c1367
-rw-r--r--net/sysctl_net.c7
-rw-r--r--net/unix/Makefile6
-rw-r--r--net/unix/af_unix.c1556
-rw-r--r--net/unix/garbage.c19
-rw-r--r--net/unix/sysctl_net_unix.c14
-rw-r--r--net/wanrouter/Makefile17
-rw-r--r--net/wanrouter/patchlevel1
-rw-r--r--net/wanrouter/wanmain.c676
-rw-r--r--net/wanrouter/wanproc.c460
-rw-r--r--net/x25/Makefile21
-rw-r--r--net/x25/af_x25.c1356
-rw-r--r--net/x25/sysctl_net_x25.c57
-rw-r--r--net/x25/x25_dev.c246
-rw-r--r--net/x25/x25_in.c376
-rw-r--r--net/x25/x25_link.c482
-rw-r--r--net/x25/x25_out.c205
-rw-r--r--net/x25/x25_route.c273
-rw-r--r--net/x25/x25_subr.c386
-rw-r--r--net/x25/x25_timer.c145
206 files changed, 50529 insertions, 27488 deletions
diff --git a/net/802/Makefile b/net/802/Makefile
index cb8d33a61..a8d25f77e 100644
--- a/net/802/Makefile
+++ b/net/802/Makefile
@@ -8,7 +8,17 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := 802.o
-O_OBJS = p8023.o sysctl_net_802.o
+O_OBJS = p8023.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_802.o
+endif
+
+ifeq ($(CONFIG_LLC),y)
+SUB_DIRS += transit
+O_OBJS += llc_sendpdu.o llc_utility.o cl2llc.o
+OX_OBJS += llc_macinit.o
+endif
ifdef CONFIG_TR
O_OBJS += tr.o
@@ -30,5 +40,8 @@ endif
include $(TOPDIR)/Rules.make
+cl2llc.c: cl2llc.pre
+ sed -f ./pseudo/opcd2num.sed cl2llc.pre >cl2llc.c
+
tar:
tar -cvf /dev/f1 .
diff --git a/net/802/TODO b/net/802/TODO
new file mode 100644
index 000000000..639b10caf
--- /dev/null
+++ b/net/802/TODO
@@ -0,0 +1,29 @@
+Remaining Problems:
+
+1. Serialization of access to variables in the llc structure
+by mac_data_indicate(), timer expired functions, and data_request() .
+There is not serialization of any kind right now.
+While testing, I have not seen any problems that stem from this lack of
+serialization, but it wories me...
+
+2. The code is currently able to handle one connection only,
+there is more work in register_cl2llc_client() to make a chain
+of llc structures and in mac_data_indicate() to find back
+the llc structure addressed by an incomming frame.
+According to IEEE, connections are identified by (remote mac + local mac
++ dsap + ssap). dsap and ssap do not seem important: existing applications
+always use the same dsap/ssap. Its probably sufficient to index on
+the remote mac only.
+
+3. There is no test to see if the transmit window is full in data_request()
+as described in the doc p73, "7.5.1 Sending I PDUs" 3th alinea.
+The pdus presented to data_request() could probably go on the
+awaiting-transmit-queue (atq). The real difficulty is coding a test
+to see if the transmit window is used up and to send the queue
+when space in the window becomes available.
+As I have no network layer that can generate a continous flow of pdus it is
+difficult to simulate a remote busy condition and hence to test the code
+to handle it.
+
+4. A simple flow control algorithm, steering the size of the transmit
+window would be nice to have.
diff --git a/net/802/cl2llc.c b/net/802/cl2llc.c
new file mode 100644
index 000000000..5e1d12837
--- /dev/null
+++ b/net/802/cl2llc.c
@@ -0,0 +1,615 @@
+/*
+ * NET An implementation of the IEEE 802.2 LLC protocol for the
+ * LINUX operating system. LLC is implemented as a set of
+ * state machines and callbacks for higher networking layers.
+ *
+ * Class 2 llc algorithm.
+ * Pseudocode interpreter, transition table lookup,
+ * data_request & indicate primitives...
+ *
+ * Code for initialization, termination, registration and
+ * MAC layer glue.
+ *
+ * Copyright Tim Alpaerts,
+ * <Tim_Alpaerts@toyota-motor-europe.com>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : Chainsawed into Linux format
+ * Modified to use llc_ names
+ * Changed callbacks
+ *
+ * This file must be processed by sed before it can be compiled.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+
+#include "pseudo/pseudocode.h"
+#include "transit/pdutr.h"
+#include "transit/timertr.h"
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ * Data_request() is called by the client to present a data unit
+ * to the llc for transmission.
+ * In the future this function should also check if the transmit window
+ * allows the sending of another pdu, and if not put the skb on the atq
+ * for deferred sending.
+ */
+
+int llc_data_request(llcptr lp, struct sk_buff *skb)
+{
+ if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
+ printk("cl2llc: data_request() not enough headroom in skb\n");
+ return -1;
+ };
+
+ skb_push(skb, 4);
+
+ if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
+ {
+ printk("cl2llc: data_request() while no llc connection\n");
+ return -1;
+ }
+
+ if (lp->remote_busy)
+ { /* if the remote llc is BUSY, */
+ ADD_TO_ATQ(skb); /* save skb in the await transmit queue */
+ return 0;
+ }
+ else
+ {
+ /*
+ * Else proceed with xmit
+ */
+
+ switch(lp->state)
+ {
+ case NORMAL:
+ if(lp->p_flag)
+ llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
+ break;
+ case BUSY:
+ if (lp->p_flag)
+ llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
+ break;
+ case REJECT:
+ if (lp->p_flag)
+ llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
+ break;
+ default:
+ }
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ return 0;
+ }
+}
+
+
+
+/*
+ * Disconnect_request() requests that the llc to terminate a connection
+ */
+
+void disconnect_request(llcptr lp)
+{
+ if ((lp->state == NORMAL) ||
+ (lp->state == BUSY) ||
+ (lp->state == REJECT) ||
+ (lp->state == AWAIT) ||
+ (lp->state == AWAIT_BUSY) ||
+ (lp->state == AWAIT_REJECT))
+ {
+ lp->state = D_CONN;
+ llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may be invalid after the callback
+ */
+ }
+}
+
+
+/*
+ * Connect_request() requests that the llc to start a connection
+ */
+
+void connect_request(llcptr lp)
+{
+ if (lp->state == ADM)
+ {
+ lp->state = SETUP;
+ llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may be invalid after the callback
+ */
+ }
+}
+
+
+/*
+ * Interpret_pseudo_code() executes the actions in the connection component
+ * state transition table. Table 4 in document on p88.
+ *
+ * If this function is called to handle an incomming pdu, skb will point
+ * to the buffer with the pdu and type will contain the decoded pdu type.
+ *
+ * If called by data_request skb points to an skb that was skb_alloc-ed by
+ * the llc client to hold the information unit to be transmitted, there is
+ * no valid type in this case.
+ *
+ * If called because a timer expired no skb is passed, and there is no
+ * type.
+ */
+
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb,
+ char type)
+{
+ short int pc; /* program counter in pseudo code array */
+ char p_flag_received;
+ frameptr fr;
+ int resend_count; /* number of pdus resend by llc_resend_ipdu() */
+ int ack_count; /* number of pdus acknowledged */
+ struct sk_buff *skb2;
+
+ if (skb != NULL)
+ {
+ fr = (frameptr) skb->data;
+ }
+ else
+ fr = NULL;
+
+ pc = pseudo_code_idx[pc_label];
+ while(pseudo_code[pc])
+ {
+ switch(pseudo_code[pc])
+ {
+ case 9:
+ if ((type != I_CMD) || (fr->i_hdr.i_pflag =0))
+ break;
+ case 1:
+ lp->remote_busy = 0;
+ llc_stop_timer(lp, BUSY_TIMER);
+ if ((lp->state == NORMAL) ||
+ (lp->state == REJECT) ||
+ (lp->state == BUSY))
+ {
+ skb2 = llc_pull_from_atq(lp);
+ if (skb2 != NULL)
+ llc_start_timer(lp, ACK_TIMER);
+ while (skb2 != NULL)
+ {
+ llc_sendipdu( lp, I_CMD, 0, skb2);
+ skb2 = llc_pull_from_atq(lp);
+ }
+ }
+ break;
+ case 2:
+ lp->state = NORMAL; /* needed to eliminate connect_response() */
+ lp->llc_mode = MODE_ABM;
+ lp->llc_callbacks|=LLC_CONN_INDICATION;
+ break;
+ case 3:
+ lp->llc_mode = MODE_ABM;
+ lp->llc_callbacks|=LLC_CONN_CONFIRM;
+ break;
+ case 4:
+ skb_pull(skb, 4);
+ lp->inc_skb=skb;
+ lp->llc_callbacks|=LLC_DATA_INDIC;
+ break;
+ case 5:
+ lp->llc_mode = MODE_ADM;
+ lp->llc_callbacks|=LLC_DISC_INDICATION;
+ break;
+ case 70:
+ lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
+ break;
+ case 71:
+ lp->llc_callbacks|=LLC_RESET_INDIC_REM;
+ break;
+ case 7:
+ lp->llc_callbacks|=LLC_RST_CONFIRM;
+ break;
+ case 66:
+ lp->llc_callbacks|=LLC_FRMR_RECV;
+ break;
+ case 67:
+ lp->llc_callbacks|=LLC_FRMR_SENT;
+ break;
+ case 68:
+ lp->llc_callbacks|=LLC_REMOTE_BUSY;
+ break;
+ case 69:
+ lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
+ break;
+ case 11:
+ llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
+ break;
+ case 12:
+ llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
+ break;
+ case 13:
+ lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
+ lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
+ lp->frmr_info_fld.vs = lp->vs;
+ lp->frmr_info_fld.vr_cr = lp->vr;
+ llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+ break;
+ case 14:
+ llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+ break;
+ case 15:
+ llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
+ 5, (char *) &lp->frmr_info_fld);
+ break;
+ case 16:
+ llc_sendipdu(lp, I_CMD, 1, skb);
+ break;
+ case 17:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+ break;
+ case 18:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+ if (resend_count == 0)
+ {
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ }
+ break;
+ case 19:
+ llc_sendipdu(lp, I_CMD, 0, skb);
+ break;
+ case 20:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+ break;
+ case 21:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+ if (resend_count == 0)
+ {
+ llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+ }
+ break;
+ case 22:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
+ break;
+ case 23:
+ llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
+ break;
+ case 24:
+ llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
+ break;
+ case 25:
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
+ break;
+ case 26:
+ llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
+ break;
+ case 27:
+ llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
+ break;
+ case 28:
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+ break;
+ case 29:
+ if (lp->remote_busy == 0)
+ {
+ lp->remote_busy = 1;
+ llc_start_timer(lp, BUSY_TIMER);
+ lp->llc_callbacks|=LLC_REMOTE_BUSY;
+ }
+ else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
+ {
+ llc_start_timer(lp, BUSY_TIMER);
+ }
+ break;
+ case 30:
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+ break;
+ case 31:
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ break;
+ case 32:
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ break;
+ case 33:
+ llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+ break;
+ case 34:
+ llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+ break;
+ case 35:
+ llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+ break;
+ case 36:
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+ break;
+ case 37:
+ llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
+ lp->f_flag = 0;
+ break;
+ case 38:
+ llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
+ break;
+ case 39:
+ lp->s_flag = 0;
+ break;
+ case 40:
+ lp->s_flag = 1;
+ break;
+ case 41:
+ if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
+ llc_stop_timer(lp, P_TIMER);
+ llc_start_timer(lp, P_TIMER);
+ if (lp->p_flag == 0)
+ {
+ lp->retry_count = 0;
+ lp->p_flag = 1;
+ }
+ break;
+ case 44:
+ if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
+ llc_start_timer(lp, ACK_TIMER);
+ break;
+ case 42:
+ llc_start_timer(lp, ACK_TIMER);
+ break;
+ case 43:
+ llc_start_timer(lp, REJ_TIMER);
+ break;
+ case 45:
+ llc_stop_timer(lp, ACK_TIMER);
+ break;
+ case 46:
+ llc_stop_timer(lp, ACK_TIMER);
+ lp->p_flag = 0;
+ break;
+ case 10:
+ if (lp->data_flag == 2)
+ llc_stop_timer(lp, REJ_TIMER);
+ break;
+ case 47:
+ llc_stop_timer(lp, REJ_TIMER);
+ break;
+ case 48:
+ llc_stop_timer(lp, ACK_TIMER);
+ llc_stop_timer(lp, P_TIMER);
+ llc_stop_timer(lp, REJ_TIMER);
+ llc_stop_timer(lp, BUSY_TIMER);
+ break;
+ case 49:
+ llc_stop_timer(lp, P_TIMER);
+ llc_stop_timer(lp, REJ_TIMER);
+ llc_stop_timer(lp, BUSY_TIMER);
+ break;
+ case 50:
+ ack_count = llc_free_acknowledged_skbs(lp,
+ (unsigned char) fr->s_hdr.nr);
+ if (ack_count > 0)
+ {
+ lp->retry_count = 0;
+ llc_stop_timer(lp, ACK_TIMER);
+ if (skb_peek(&lp->rtq) != NULL)
+ {
+ /*
+ * Re-transmit queue not empty
+ */
+ llc_start_timer(lp, ACK_TIMER);
+ }
+ }
+ break;
+ case 51:
+ if (IS_UFRAME(fr))
+ p_flag_received = fr->u_hdr.u_pflag;
+ else
+ p_flag_received = fr->i_hdr.i_pflag;
+ if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
+ {
+ lp->p_flag = 0;
+ llc_stop_timer(lp, P_TIMER);
+ }
+ break;
+ case 52:
+ lp->data_flag = 2;
+ break;
+ case 53:
+ lp->data_flag = 0;
+ break;
+ case 54:
+ lp->data_flag = 1;
+ break;
+ case 55:
+ if (lp->data_flag == 0)
+ lp->data_flag = 1;
+ break;
+ case 56:
+ lp->p_flag = 0;
+ break;
+ case 57:
+ lp->p_flag = lp->f_flag;
+ break;
+ case 58:
+ lp->remote_busy = 0;
+ break;
+ case 59:
+ lp->retry_count = 0;
+ break;
+ case 60:
+ lp->retry_count++;
+ break;
+ case 61:
+ lp->vr = 0;
+ break;
+ case 62:
+ lp->vr++;
+ break;
+ case 63:
+ lp->vs = 0;
+ break;
+ case 64:
+ lp->vs = fr->i_hdr.nr;
+ break;
+ case 65:
+ if (IS_UFRAME(fr))
+ lp->f_flag = fr->u_hdr.u_pflag;
+ else
+ lp->f_flag = fr->i_hdr.i_pflag;
+ break;
+ default:
+ }
+ pc++;
+ }
+}
+
+
+/*
+ * Process_otype2_frame will handle incoming frames
+ * for 802.2 Type 2 Procedure.
+ */
+
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
+{
+ int idx; /* index in transition table */
+ int pc_label; /* action to perform, from tr tbl */
+ int validation; /* result of validate_seq_nos */
+ int p_flag_received; /* p_flag in received frame */
+ frameptr fr;
+
+ fr = (frameptr) skb->data;
+
+ if (IS_UFRAME(fr))
+ p_flag_received = fr->u_hdr.u_pflag;
+ else
+ p_flag_received = fr->i_hdr.i_pflag;
+
+ switch(lp->state)
+ {
+ /* Compute index in transition table: */
+ case ADM:
+ idx = type;
+ idx = (idx << 1) + p_flag_received;
+ break;
+ case CONN:
+ case RESET_WAIT:
+ case RESET_CHECK:
+ case ERROR:
+ idx = type;
+ break;
+ case SETUP:
+ case RESET:
+ case D_CONN:
+ idx = type;
+ idx = (idx << 1) + lp->p_flag;
+ break;
+ case NORMAL:
+ case BUSY:
+ case REJECT:
+ case AWAIT:
+ case AWAIT_BUSY:
+ case AWAIT_REJECT:
+ validation = llc_validate_seq_nos(lp, fr);
+ if (validation > 3)
+ type = BAD_FRAME;
+ idx = type;
+ idx = (idx << 1);
+ if (validation & 1)
+ idx = idx +1;
+ idx = (idx << 1) + p_flag_received;
+ idx = (idx << 1) + lp->p_flag;
+ default:
+ printk("llc_proc: bad state\n");
+ return;
+ }
+ idx = (idx << 1) + pdutr_offset[lp->state];
+ lp->state = pdutr_entry[idx +1];
+ pc_label = pdutr_entry[idx];
+ if (pc_label != 0)
+ {
+ llc_interpret_pseudo_code(lp, pc_label, skb, type);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may no longer be valid after this point. Be
+ * careful what is added!
+ */
+ }
+}
+
+
+void llc_timer_expired(llcptr lp, int t)
+{
+ int idx; /* index in transition table */
+ int pc_label; /* action to perform, from tr tbl */
+
+ lp->timer_state[t] = TIMER_EXPIRED;
+ idx = lp->state; /* Compute index in transition table: */
+ idx = (idx << 2) + t;
+ idx = idx << 1;
+ if (lp->retry_count >= lp->n2)
+ idx = idx + 1;
+ idx = (idx << 1) + lp->s_flag;
+ idx = (idx << 1) + lp->p_flag;
+ idx = idx << 1; /* 2 bytes per entry: action & newstate */
+
+ pc_label = timertr_entry[idx];
+ if (pc_label != 0)
+ {
+ llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
+ lp->state = timertr_entry[idx +1];
+ }
+ lp->timer_state[t] = TIMER_IDLE;
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * And lp may have vanished in the event callback
+ */
+}
+
diff --git a/net/802/cl2llc.pre b/net/802/cl2llc.pre
new file mode 100644
index 000000000..8e10cf32a
--- /dev/null
+++ b/net/802/cl2llc.pre
@@ -0,0 +1,615 @@
+/*
+ * NET An implementation of the IEEE 802.2 LLC protocol for the
+ * LINUX operating system. LLC is implemented as a set of
+ * state machines and callbacks for higher networking layers.
+ *
+ * Class 2 llc algorithm.
+ * Pseudocode interpreter, transition table lookup,
+ * data_request & indicate primitives...
+ *
+ * Code for initialization, termination, registration and
+ * MAC layer glue.
+ *
+ * Copyright Tim Alpaerts,
+ * <Tim_Alpaerts@toyota-motor-europe.com>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : Chainsawed into Linux format
+ * Modified to use llc_ names
+ * Changed callbacks
+ *
+ * This file must be processed by sed before it can be compiled.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+
+#include "pseudo/pseudocode.h"
+#include "transit/pdutr.h"
+#include "transit/timertr.h"
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ * Data_request() is called by the client to present a data unit
+ * to the llc for transmission.
+ * In the future this function should also check if the transmit window
+ * allows the sending of another pdu, and if not put the skb on the atq
+ * for deferred sending.
+ */
+
+int llc_data_request(llcptr lp, struct sk_buff *skb)
+{
+ if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
+ printk("cl2llc: data_request() not enough headroom in skb\n");
+ return -1;
+ };
+
+ skb_push(skb, 4);
+
+ if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
+ {
+ printk("cl2llc: data_request() while no llc connection\n");
+ return -1;
+ }
+
+ if (lp->remote_busy)
+ { /* if the remote llc is BUSY, */
+ ADD_TO_ATQ(skb); /* save skb in the await transmit queue */
+ return 0;
+ }
+ else
+ {
+ /*
+ * Else proceed with xmit
+ */
+
+ switch(lp->state)
+ {
+ case NORMAL:
+ if(lp->p_flag)
+ llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
+ break;
+ case BUSY:
+ if (lp->p_flag)
+ llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
+ break;
+ case REJECT:
+ if (lp->p_flag)
+ llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
+ else
+ llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
+ break;
+ default:
+ }
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ return 0;
+ }
+}
+
+
+
+/*
+ * Disconnect_request() requests that the llc to terminate a connection
+ */
+
+void disconnect_request(llcptr lp)
+{
+ if ((lp->state == NORMAL) ||
+ (lp->state == BUSY) ||
+ (lp->state == REJECT) ||
+ (lp->state == AWAIT) ||
+ (lp->state == AWAIT_BUSY) ||
+ (lp->state == AWAIT_REJECT))
+ {
+ lp->state = D_CONN;
+ llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may be invalid after the callback
+ */
+ }
+}
+
+
+/*
+ * Connect_request() requests that the llc to start a connection
+ */
+
+void connect_request(llcptr lp)
+{
+ if (lp->state == ADM)
+ {
+ lp->state = SETUP;
+ llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may be invalid after the callback
+ */
+ }
+}
+
+
+/*
+ * Interpret_pseudo_code() executes the actions in the connection component
+ * state transition table. Table 4 in document on p88.
+ *
+ * If this function is called to handle an incomming pdu, skb will point
+ * to the buffer with the pdu and type will contain the decoded pdu type.
+ *
+ * If called by data_request skb points to an skb that was skb_alloc-ed by
+ * the llc client to hold the information unit to be transmitted, there is
+ * no valid type in this case.
+ *
+ * If called because a timer expired no skb is passed, and there is no
+ * type.
+ */
+
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb,
+ char type)
+{
+ short int pc; /* program counter in pseudo code array */
+ char p_flag_received;
+ frameptr fr;
+ int resend_count; /* number of pdus resend by llc_resend_ipdu() */
+ int ack_count; /* number of pdus acknowledged */
+ struct sk_buff *skb2;
+
+ if (skb != NULL)
+ {
+ fr = (frameptr) skb->data;
+ }
+ else
+ fr = NULL;
+
+ pc = pseudo_code_idx[pc_label];
+ while(pseudo_code[pc])
+ {
+ switch(pseudo_code[pc])
+ {
+ case IF_F=1_CLEAR_REMOTE_BUSY:
+ if ((type != I_CMD) || (fr->i_hdr.i_pflag =0))
+ break;
+ case CLEAR_REMOTE_BUSY:
+ lp->remote_busy = 0;
+ llc_stop_timer(lp, BUSY_TIMER);
+ if ((lp->state == NORMAL) ||
+ (lp->state == REJECT) ||
+ (lp->state == BUSY))
+ {
+ skb2 = llc_pull_from_atq(lp);
+ if (skb2 != NULL)
+ llc_start_timer(lp, ACK_TIMER);
+ while (skb2 != NULL)
+ {
+ llc_sendipdu( lp, I_CMD, 0, skb2);
+ skb2 = llc_pull_from_atq(lp);
+ }
+ }
+ break;
+ case CONNECT_INDICATION:
+ lp->state = NORMAL; /* needed to eliminate connect_response() */
+ lp->llc_mode = MODE_ABM;
+ lp->llc_callbacks|=LLC_CONN_INDICATION;
+ break;
+ case CONNECT_CONFIRM:
+ lp->llc_mode = MODE_ABM;
+ lp->llc_callbacks|=LLC_CONN_CONFIRM;
+ break;
+ case DATA_INDICATION:
+ skb_pull(skb, 4);
+ lp->inc_skb=skb;
+ lp->llc_callbacks|=LLC_DATA_INDIC;
+ break;
+ case DISCONNECT_INDICATION:
+ lp->llc_mode = MODE_ADM;
+ lp->llc_callbacks|=LLC_DISC_INDICATION;
+ break;
+ case RESET_INDICATION(LOCAL):
+ lp->llc_callbacks|=LLC_RESET_INDIC_LOC;
+ break;
+ case RESET_INDICATION(REMOTE):
+ lp->llc_callbacks|=LLC_RESET_INDIC_REM;
+ break;
+ case RESET_CONFIRM:
+ lp->llc_callbacks|=LLC_RST_CONFIRM;
+ break;
+ case REPORT_STATUS(FRMR_RECEIVED):
+ lp->llc_callbacks|=LLC_FRMR_RECV;
+ break;
+ case REPORT_STATUS(FRMR_SENT):
+ lp->llc_callbacks|=LLC_FRMR_SENT;
+ break;
+ case REPORT_STATUS(REMOTE_BUSY):
+ lp->llc_callbacks|=LLC_REMOTE_BUSY;
+ break;
+ case REPORT_STATUS(REMOTE_NOT_BUSY):
+ lp->llc_callbacks|=LLC_REMOTE_NOTBUSY;
+ break;
+ case SEND_DISC_CMD(P=X):
+ llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
+ break;
+ case SEND_DM_RSP(F=X):
+ llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
+ break;
+ case SEND_FRMR_RSP(F=X):
+ lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
+ lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
+ lp->frmr_info_fld.vs = lp->vs;
+ lp->frmr_info_fld.vr_cr = lp->vr;
+ llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+ break;
+ case RE-SEND_FRMR_RSP(F=0):
+ llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+ break;
+ case RE-SEND_FRMR_RSP(F=P):
+ llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
+ 5, (char *) &lp->frmr_info_fld);
+ break;
+ case SEND_I_CMD(P=1):
+ llc_sendipdu(lp, I_CMD, 1, skb);
+ break;
+ case RE-SEND_I_CMD(P=1):
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+ break;
+ case RE-SEND_I_CMD(P=1)_OR_SEND_RR:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+ if (resend_count == 0)
+ {
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ }
+ break;
+ case SEND_I_XXX(X=0):
+ llc_sendipdu(lp, I_CMD, 0, skb);
+ break;
+ case RE-SEND_I_XXX(X=0):
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+ break;
+ case RE-SEND_I_XXX(X=0)_OR_SEND_RR:
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+ if (resend_count == 0)
+ {
+ llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+ }
+ break;
+ case RE-SEND_I_RSP(F=1):
+ resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
+ break;
+ case SEND_REJ_CMD(P=1):
+ llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
+ break;
+ case SEND_REJ_RSP(F=1):
+ llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
+ break;
+ case SEND_REJ_XXX(X=0):
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
+ break;
+ case SEND_RNR_CMD(F=1):
+ llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
+ break;
+ case SEND_RNR_RSP(F=1):
+ llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
+ break;
+ case SEND_RNR_XXX(X=0):
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+ break;
+ case SET_REMOTE_BUSY:
+ if (lp->remote_busy == 0)
+ {
+ lp->remote_busy = 1;
+ llc_start_timer(lp, BUSY_TIMER);
+ lp->llc_callbacks|=LLC_REMOTE_BUSY;
+ }
+ else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
+ {
+ llc_start_timer(lp, BUSY_TIMER);
+ }
+ break;
+ case OPTIONAL_SEND_RNR_XXX(X=0):
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+ break;
+ case SEND_RR_CMD(P=1):
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ break;
+ case SEND_ACKNOWLEDGE_CMD(P=1):
+ llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+ break;
+ case SEND_RR_RSP(F=1):
+ llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+ break;
+ case SEND_ACKNOWLEDGE_RSP(F=1):
+ llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+ break;
+ case SEND_RR_XXX(X=0):
+ llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+ break;
+ case SEND_ACKNOWLEDGE_XXX(X=0):
+ if (IS_RSP(fr))
+ llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+ else
+ llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+ break;
+ case SEND_SABME_CMD(P=X):
+ llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
+ lp->f_flag = 0;
+ break;
+ case SEND_UA_RSP(F=X):
+ llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
+ break;
+ case S_FLAG:=0:
+ lp->s_flag = 0;
+ break;
+ case S_FLAG:=1:
+ lp->s_flag = 1;
+ break;
+ case START_P_TIMER:
+ if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
+ llc_stop_timer(lp, P_TIMER);
+ llc_start_timer(lp, P_TIMER);
+ if (lp->p_flag == 0)
+ {
+ lp->retry_count = 0;
+ lp->p_flag = 1;
+ }
+ break;
+ case START_ACK_TIMER_IF_NOT_RUNNING:
+ if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
+ llc_start_timer(lp, ACK_TIMER);
+ break;
+ case START_ACK_TIMER:
+ llc_start_timer(lp, ACK_TIMER);
+ break;
+ case START_REJ_TIMER:
+ llc_start_timer(lp, REJ_TIMER);
+ break;
+ case STOP_ACK_TIMER:
+ llc_stop_timer(lp, ACK_TIMER);
+ break;
+ case STOP_P_TIMER:
+ llc_stop_timer(lp, ACK_TIMER);
+ lp->p_flag = 0;
+ break;
+ case IF_DATA_FLAG=2_STOP_REJ_TIMER:
+ if (lp->data_flag == 2)
+ llc_stop_timer(lp, REJ_TIMER);
+ break;
+ case STOP_REJ_TIMER:
+ llc_stop_timer(lp, REJ_TIMER);
+ break;
+ case STOP_ALL_TIMERS:
+ llc_stop_timer(lp, ACK_TIMER);
+ llc_stop_timer(lp, P_TIMER);
+ llc_stop_timer(lp, REJ_TIMER);
+ llc_stop_timer(lp, BUSY_TIMER);
+ break;
+ case STOP_OTHER_TIMERS:
+ llc_stop_timer(lp, P_TIMER);
+ llc_stop_timer(lp, REJ_TIMER);
+ llc_stop_timer(lp, BUSY_TIMER);
+ break;
+ case UPDATE_N(R)_RECEIVED:
+ ack_count = llc_free_acknowledged_skbs(lp,
+ (unsigned char) fr->s_hdr.nr);
+ if (ack_count > 0)
+ {
+ lp->retry_count = 0;
+ llc_stop_timer(lp, ACK_TIMER);
+ if (skb_peek(&lp->rtq) != NULL)
+ {
+ /*
+ * Re-transmit queue not empty
+ */
+ llc_start_timer(lp, ACK_TIMER);
+ }
+ }
+ break;
+ case UPDATE_P_FLAG:
+ if (IS_UFRAME(fr))
+ p_flag_received = fr->u_hdr.u_pflag;
+ else
+ p_flag_received = fr->i_hdr.i_pflag;
+ if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
+ {
+ lp->p_flag = 0;
+ llc_stop_timer(lp, P_TIMER);
+ }
+ break;
+ case DATA_FLAG:=2:
+ lp->data_flag = 2;
+ break;
+ case DATA_FLAG:=0:
+ lp->data_flag = 0;
+ break;
+ case DATA_FLAG:=1:
+ lp->data_flag = 1;
+ break;
+ case IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1:
+ if (lp->data_flag == 0)
+ lp->data_flag = 1;
+ break;
+ case P_FLAG:=0:
+ lp->p_flag = 0;
+ break;
+ case P_FLAG:=P:
+ lp->p_flag = lp->f_flag;
+ break;
+ case REMOTE_BUSY:=0:
+ lp->remote_busy = 0;
+ break;
+ case RETRY_COUNT:=0:
+ lp->retry_count = 0;
+ break;
+ case RETRY_COUNT:=RETRY_COUNT+1:
+ lp->retry_count++;
+ break;
+ case V(R):=0:
+ lp->vr = 0;
+ break;
+ case V(R):=V(R)+1:
+ lp->vr++;
+ break;
+ case V(S):=0:
+ lp->vs = 0;
+ break;
+ case V(S):=N(R):
+ lp->vs = fr->i_hdr.nr;
+ break;
+ case F_FLAG:=P:
+ if (IS_UFRAME(fr))
+ lp->f_flag = fr->u_hdr.u_pflag;
+ else
+ lp->f_flag = fr->i_hdr.i_pflag;
+ break;
+ default:
+ }
+ pc++;
+ }
+}
+
+
+/*
+ * Process_otype2_frame will handle incoming frames
+ * for 802.2 Type 2 Procedure.
+ */
+
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
+{
+ int idx; /* index in transition table */
+ int pc_label; /* action to perform, from tr tbl */
+ int validation; /* result of validate_seq_nos */
+ int p_flag_received; /* p_flag in received frame */
+ frameptr fr;
+
+ fr = (frameptr) skb->data;
+
+ if (IS_UFRAME(fr))
+ p_flag_received = fr->u_hdr.u_pflag;
+ else
+ p_flag_received = fr->i_hdr.i_pflag;
+
+ switch(lp->state)
+ {
+ /* Compute index in transition table: */
+ case ADM:
+ idx = type;
+ idx = (idx << 1) + p_flag_received;
+ break;
+ case CONN:
+ case RESET_WAIT:
+ case RESET_CHECK:
+ case ERROR:
+ idx = type;
+ break;
+ case SETUP:
+ case RESET:
+ case D_CONN:
+ idx = type;
+ idx = (idx << 1) + lp->p_flag;
+ break;
+ case NORMAL:
+ case BUSY:
+ case REJECT:
+ case AWAIT:
+ case AWAIT_BUSY:
+ case AWAIT_REJECT:
+ validation = llc_validate_seq_nos(lp, fr);
+ if (validation > 3)
+ type = BAD_FRAME;
+ idx = type;
+ idx = (idx << 1);
+ if (validation & 1)
+ idx = idx +1;
+ idx = (idx << 1) + p_flag_received;
+ idx = (idx << 1) + lp->p_flag;
+ default:
+ printk("llc_proc: bad state\n");
+ return;
+ }
+ idx = (idx << 1) + pdutr_offset[lp->state];
+ lp->state = pdutr_entry[idx +1];
+ pc_label = pdutr_entry[idx];
+ if (pc_label != NOP)
+ {
+ llc_interpret_pseudo_code(lp, pc_label, skb, type);
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * lp may no longer be valid after this point. Be
+ * careful what is added!
+ */
+ }
+}
+
+
+void llc_timer_expired(llcptr lp, int t)
+{
+ int idx; /* index in transition table */
+ int pc_label; /* action to perform, from tr tbl */
+
+ lp->timer_state[t] = TIMER_EXPIRED;
+ idx = lp->state; /* Compute index in transition table: */
+ idx = (idx << 2) + t;
+ idx = idx << 1;
+ if (lp->retry_count >= lp->n2)
+ idx = idx + 1;
+ idx = (idx << 1) + lp->s_flag;
+ idx = (idx << 1) + lp->p_flag;
+ idx = idx << 1; /* 2 bytes per entry: action & newstate */
+
+ pc_label = timertr_entry[idx];
+ if (pc_label != NOP)
+ {
+ llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
+ lp->state = timertr_entry[idx +1];
+ }
+ lp->timer_state[t] = TIMER_IDLE;
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ /*
+ * And lp may have vanished in the event callback
+ */
+}
+
diff --git a/net/802/fddi.c b/net/802/fddi.c
index 24fcff127..c57a967d1 100644
--- a/net/802/fddi.c
+++ b/net/802/fddi.c
@@ -20,7 +20,11 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : New arp/rebuild header
*/
+
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/types.h>
@@ -33,6 +37,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/fddidevice.h>
+#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <net/arp.h>
@@ -45,28 +50,26 @@
* daddr=NULL means leave destination address (eg unresolved arp)
*/
-int fddi_header(
- struct sk_buff *skb,
- struct device *dev,
- unsigned short type,
- void *daddr,
- void *saddr,
- unsigned len
- )
-
+int fddi_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ int hl = FDDI_K_SNAP_HLEN;
+ struct fddihdr *fddi;
+
+ if(type!=htons(ETH_P_IP))
+ hl=FDDI_K_8022_HLEN-3;
+ fddi = (struct fddihdr *)skb_push(skb, hl);
+ fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF;
+ if(type==htons(ETH_P_IP))
{
- struct fddihdr *fddi = (struct fddihdr *)skb_push(skb, FDDI_K_SNAP_HLEN);
-
- /* Fill in frame header - assume 802.2 SNAP frames for now */
-
- fddi->fc = FDDI_FC_K_ASYNC_LLC_DEF;
- fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP;
- fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP;
- fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD;
- fddi->hdr.llc_snap.oui[0] = 0x00;
- fddi->hdr.llc_snap.oui[1] = 0x00;
- fddi->hdr.llc_snap.oui[2] = 0x00;
- fddi->hdr.llc_snap.ethertype = htons(type);
+ fddi->hdr.llc_snap.dsap = FDDI_EXTENDED_SAP;
+ fddi->hdr.llc_snap.ssap = FDDI_EXTENDED_SAP;
+ fddi->hdr.llc_snap.ctrl = FDDI_UI_CMD;
+ fddi->hdr.llc_snap.oui[0] = 0x00;
+ fddi->hdr.llc_snap.oui[1] = 0x00;
+ fddi->hdr.llc_snap.oui[2] = 0x00;
+ fddi->hdr.llc_snap.ethertype = htons(type);
+ }
/* Set the source and destination hardware addresses */
@@ -76,12 +79,12 @@ int fddi_header(
memcpy(fddi->saddr, dev->dev_addr, dev->addr_len);
if (daddr != NULL)
- {
+ {
memcpy(fddi->daddr, daddr, dev->addr_len);
- return(FDDI_K_SNAP_HLEN);
- }
- return(-FDDI_K_SNAP_HLEN);
+ return(hl);
}
+ return(-hl);
+}
/*
@@ -90,32 +93,22 @@ int fddi_header(
* this sk_buff. We now let ARP fill in the other fields.
*/
-int fddi_rebuild_header(
- void *buff,
- struct device *dev,
- unsigned long dest,
- struct sk_buff *skb
- )
-
- {
- struct fddihdr *fddi = (struct fddihdr *)buff;
+int fddi_rebuild_header(struct sk_buff *skb)
+{
+ struct fddihdr *fddi = (struct fddihdr *)skb->data;
/* Only ARP/IP is currently supported */
if (fddi->hdr.llc_snap.ethertype != htons(ETH_P_IP))
- {
+ {
printk("fddi_rebuild_header: Don't know how to resolve type %04X addresses?\n", (unsigned int)htons(fddi->hdr.llc_snap.ethertype));
return(0);
- }
+ }
/* Try to get ARP to resolve the header and fill destination address */
- if (arp_find(fddi->daddr, dest, dev, dev->pa_addr, skb))
- return(1);
- else
- return(0);
- }
-
+ return arp_find(fddi->daddr, skb) ? 1 : 0;
+}
/*
* Determine the packet's protocol ID and fill in skb fields.
@@ -124,39 +117,47 @@ int fddi_rebuild_header(
* the proper pointer to the start of packet data (skb->data).
*/
-unsigned short fddi_type_trans(
- struct sk_buff *skb,
- struct device *dev
- )
-
- {
+unsigned short fddi_type_trans(struct sk_buff *skb, struct device *dev)
+{
struct fddihdr *fddi = (struct fddihdr *)skb->data;
-
+ unsigned short type;
+
/*
* Set mac.raw field to point to FC byte, set data field to point
* to start of packet data. Assume 802.2 SNAP frames for now.
*/
skb->mac.raw = skb->data; /* point to frame control (FC) */
- skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */
-
+
+ if(fddi->hdr.llc_8022_1.dsap==0xe0)
+ {
+ skb_pull(skb, FDDI_K_8022_HLEN-3);
+ type=htons(ETH_P_802_2);
+ }
+ else
+ {
+ skb_pull(skb, FDDI_K_SNAP_HLEN); /* adjust for 21 byte header */
+ type=fddi->hdr.llc_snap.ethertype;
+ }
+
/* Set packet type based on destination address and flag settings */
if (*fddi->daddr & 0x01)
- {
+ {
if (memcmp(fddi->daddr, dev->broadcast, FDDI_K_ALEN) == 0)
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
- }
+ }
else if (dev->flags & IFF_PROMISC)
- {
+ {
if (memcmp(fddi->daddr, dev->dev_addr, FDDI_K_ALEN))
skb->pkt_type = PACKET_OTHERHOST;
- }
+ }
/* Assume 802.2 SNAP frames, for now */
- return(fddi->hdr.llc_snap.ethertype);
- }
+ return(type);
+
+}
diff --git a/net/802/llc.c b/net/802/llc.c
deleted file mode 100644
index d280fb38f..000000000
--- a/net/802/llc.c
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * 802.2 Class 2 LLC service.
- */
-
-
-int llc_rx_adm(struct sock *sk,struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==DISC)
- send_response(sk,DM|pf);
- else if(cmd==SABM)
- {
- if(sk->state!=TCP_LISTEN)
- send_response(sk. DM|pf);
- else
- {
- sk=ll_rx_accept(sk);
- if(sk!=NULL)
- {
- send_response(sk, UA|pf);
- sk->llc.vs=0;
- sk->llc.vr=0;
- sk->llc.p_flag=0;
- sk->llc.remote_busy=0;
- llc_state(sk,LLC_NORMAL);
- }
- }
- }
- else if(pf)
- send_response(sk, DM|PF);
- }
- return 0;
-}
-
-int llc_rx_setup(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==SABM)
- {
- sk->llc.vs=0;
- sk->llc.vr=0;
- send_response(sk, UA|pf);
- }
- if(cmd==DISC)
- {
- send_response(sk, DM|pf);
- llc_error(sk,ECONNRESET);
- llc_state(sk, LLC_ADM);
- }
- }
- else
- {
- if(cmd==UA && pf==sk->llc.p_flag)
- {
- del_timer(&sk->llc.t1);
- sk->llc.vs=0;
- llc_update_p_flag(sk,pf);
- llc_state(sk,LLC_NORMAL);
- }
- if(cmd==DM)
- {
- llc_error(sk, ECONNRESET);
- llc_state(sk, LLC_ADM);
- }
- }
-}
-
-int llc_rx_reset(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==SABM)
- {
- sk->llc.vr=0;
- sk->llc.vs=0;
- send_response(sk, UA|pf);
- }
- else if(cmd==DISC)
- {
- if(sk->llc.cause_flag==1)
- llc_shutdown(sk,SHUTDOWN_MASK);
- else
- llc_eror(sk, ECONNREFUSED);
- send_response(sk, DM|pf);
- llc_state(sk, LLC_ADM);
- }
- }
- else
- {
- if(cmd==UA)
- {
- if(sk->llc.p_flag==pf)
- {
- del_timer(&sk->llc.t1);
- sk->llc.vs=0;
- sk->llc.vr=0;
- llc_update_p_flag(sk,pf);
- llc_confirm_reset(sk, sk->llc.cause_flag);
- sk->llc.remote_busy=0;
- llc_state(sk, LLC_NORMAL);
- }
- }
- if(cmd==DM)
- { /* Should check cause_flag */
- llc_shutdown(sk, SHUTDOWN_MASK);
- llc_state(sk, LLC_ADM);
- }
- }
- return 0;
-}
-
-int llc_rx_d_conn(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==SABM)
- {
- llc_error(sk, ECONNRESET);
- llc_state(sk, ADM);
- }
- else if(cmd==DISC)
- {
- send_response(UA|pf);
- llc_state(sk, LLC_D_CONN);
- }
- else if(pf)
- send_response(sk, DM|PF);
- }
- else
- {
- if(cmd==UA && pf==sk->llc.p_flag)
- {
- del_timer(&sk->llc.t1);
- llc_state(sk, ADM);
- llc_confirm_reset(sk, sk->llc.cause_flag);
- }
- if(cmd==DM)
- {
- del_timer(&sk->llc.t1);
- /*if(sk->llc.cause_flag)*/
- llc_shutdown(sk, SHUTDOWN_MASK);
- }
-
- }
- return 0;
-}
-
-int llc_rx_error(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==SABM)
- {
- sk->llc.vs=0;
- sk->llc.vr=0;
- send_response(sk, UA|pf);
- llc_error(sk,ECONNRESET);
- sk->llc.p_flag=0;
- sk->llc.remote_busy=0;
- llc_state(sk, LLC_NORMAL);
- }
- else if(cmd==DISC)
- {
- send_response(sk, UA|pf);
- llc_shutdown(sk, SHUTDOWN_MASK);
- llc_state(sk, LLC_ADM);
- }
- else
- llc_resend_frmr_rsp(sk,pf);
- }
- else
- {
- if(cmd==DM)
- {
- llc_error(sk, ECONNRESET);
- del_timer(&sk->llc.t1);
- llc_state(sk, LLC_ADM);
- }
- if(cmd==FRMR)
- {
- send_command(sk, SABM);
- sk->llc.p_flag=pf;
- llc_start_t1();
- sk->llc.retry_count=0;
- sk->llc.cause_flag=0;
- llc_error(sk, EPROTO);
- llc_state(sk, LLC_RESET);
- }
- }
-}
-
-
-/*
- * Subroutine for handling the shared cases of the data modes.
- */
-
-int llc_rx_nr_shared(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(type==CMD)
- {
- if(cmd==SABM)
- {
- /*
- * Optional reset processing. We decline resets.
- */
- send_response(sk,DM|pf);
- llc_error(sk, ECONNRESET);
- llc_state(sk, LLC_ADM);
- }
- else if(cmd==DISC)
- {
- send_response(sk,UA|pf);
- llc_state(sk, LLC_ADM);
- llc_shutdown(sk, SHUTDOWN_MASK);
- }
- /*
- * We only ever use windows of 7, so there is no illegal NR/NS value
- * otherwise we would FRMR here and go to ERROR state
- */
- else if(cmd==ILLEGAL)
- {
- llc_send_frmr_response(sk, ILLEGAL_TYPE,pf);
- llc_state(sk, LLC_ERROR);
- llc_error(sk, EPROTO);
- }
- else
- /*
- * Not covered by general rule
- */
- return 0;
- }
- else
- {
- /*
- * We close on errors
- */
- if(cmd==FRMR)
- {
- send_command(sk, DM|pf);
- sk->llc.p_flag=pf;
- llc_start_t1(sk);
- llc_error(sk, EPROTO);
- sk->llc.cause_flag=0;
- llc_state(sk, LLC_D_CONN):
- }
- else if(cmd==DM)
- {
- llc_state(sk, LLC_ADM);
- llc_error(sk, ECONNREFUSED);
- }
- /*
- * We always use a window of 7 so can't get I resp
- * with invalid NS, or any resp with invalid NR. If
- * we add this they do the same as..
- */
- else if(cmd==UA)
- {
- llc_send_frmr_response(sk, UNEXPECTED_CONTROL, pf);
- llc_state(sk, LLC_ERROR);
- llc_error(sk, EPROTO);
- }
- else if(pf==1 && sk->llc.p_flag==0)
- {
- llc_send_frmr_response(sk, UNEXPECTED_RESPONSE, pf);
- llc_state(sk, LLC_ERROR);
- llc_error(sk, EPROTO);
- }
- else if(cmd==ILLEGAL)
- {
- llc_send_frmr_response(sk, ILLEGAL_TYPE,pf);
- llc_state(sk, LLC_ERROR);
- llc_error(sk, EPROTO);
- }
- else
- /*
- * Not covered by general rule
- */
- return 0
- }
- /*
- * Processed.
- */
- return 1;
-}
-
-int llc_rx_normal(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
- if(llc_rx_nr_shared(sk, skb, type, cmd, pf, nr, ns))
- return 0;
- if(cmd==I)
- {
- if(llc_invalid_ns(sk,ns))
- {
- if((type==RESP && sk->llc.p_flag==pf)||(type==CMD && pf==0 && sk->llc.p_flag==0))
- {
- llc_command(sk, REJ|PF);
- llc_ack_frames(sk,nr); /* Ack frames and update N(R) */
- sk->llc.p_flag=PF;
- llc_state(sk, LLC_REJECT);
- sk->llc.retry_count=0;
- llc_start_t1(sk);
- sk->llc.remote_busy=0;
- }
- else if((type==CMD && !pf && sk->llc.p_flag==1) || (type==RESP && !pf && sk->llc.p_flag==1))
- {
- if(type==CMD)
- llc_response(sk, REJ);
- else
- llc_command(sk, REJ);
- llc_ack_frames(sk,nr);
- sk->llc.retry_count=0;
- llc_state(sk, LLC_REJECT);
- llc_start_t1(sk);
- }
- else if(pf && type==CMD)
- {
- llc_response(sk, REJ|PF);
- llc_ack_frames(sk,nr);
- sk->llc.retry_count=0;
- llc_start_t1(sk);
- }
- }
- else
- {
- /*
- * Valid I frame cases
- */
-
- if(sk->llc.p_flag==pf && !(type==CMD && pf))
- {
- sk->llc.vr=(sk->llc.vr+1)&7;
- llc_queue_rr_cmd(sk, PF);
- sk->llc.retry_count=0;
- llc_start_t1(sk);
- sk->llc.p_flag=1;
- llc_ack_frames(sk,nr);
- sk->llc.remote_busy=0;
- }
- else if(sk->ppc.p_flag!=pf)
- {
- sk->llc.vr=(sk->llc.vr+1)&7;
- if(type==CMD)
- llc_queue_rr_resp(sk, 0);
- else
- llc_queue_rr_cmd(sk, 0);
- if(sk->llc.nr!=nr)
- {
- llc_ack_frames(sk,nr);
- llc_reset_t1(sk);
- }
- }
- else if(pf)
- {
- sk->llc.vr=(sk->llc.vr+1)&7;
- llc_queue_rr_resp(sk,PF);
- if(sk->llc.nr!=nr)
- {
- llc_ack_frames(sk,nr);
- llc_reset_t1(sk);
- }
- }
- llc_queue_data(sk,skb);
- return 1;
- }
- }
- else if(cmd==RR||cmd==RNR)
- {
- if(type==CMD || (type==RESP && (!pf || pf==1 && sk->llc.p_flag==1)))
- {
- llc_update_p_flag(sk,pf);
- if(sk->llc.nr!=nr)
- {
- llc_ack_frames(sk,nr);
- llc_reset_t1(sk);
- }
- if(cmd==RR)
- sk->llc.remote_busy=0;
- else
- { sk->llc.remote_busy=1;
- if(!llc_t1_running(sk))
- llc_start_t1(sk);
- }
- }
- else if(type==cmd && pf)
- {
- if(cmd==RR)
- llc_queue_rr_resp(sk,PF);
- else
- {
- send_response(sk, RR|PF);
- if(!llc_t1_running(sk))
- llc_start_t1(sk);
- }
- if(sk->llc.nr!=nr)
- {
- llc_ack_frames(sk,nr);
- llc_reset_t1(sk);
- }
- if(cmd==RR)
- sk->llc.remote_busy=0;
- else
- sk->llc.remote_busy=1;
- }
- }
- else if(cmd==REJ)
- {
-
- }
-}
-
diff --git a/net/802/llc_macinit.c b/net/802/llc_macinit.c
new file mode 100644
index 000000000..1ee0a9699
--- /dev/null
+++ b/net/802/llc_macinit.c
@@ -0,0 +1,225 @@
+/*
+ * NET An implementation of the IEEE 802.2 LLC protocol for the
+ * LINUX operating system. LLC is implemented as a set of
+ * state machines and callbacks for higher networking layers.
+ *
+ * Code for initialization, termination, registration and
+ * MAC layer glue.
+ *
+ * Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : Chainsawed to Linux format
+ * Added llc_ to names
+ * Started restructuring handlers
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <linux/netdevice.h>
+#include <net/p8022.h>
+
+#include <asm/byteorder.h>
+
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ * All incoming frames pass thru mac_data_indicate().
+ * Here an llc structure is associated with an skb depending on the source
+ * MAC address in the pdu.
+ * The received sk_buffs with pdus other than I_CMD and I_RSP
+ * are freed by mac_data_indicate() after processing,
+ * the I pdu buffers are freed by the cl2llc client when it no longer needs
+ * the skb.
+*/
+
+int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ int ll; /* logical length == 802.3 length field */
+ unsigned char p_flag;
+ unsigned char type;
+ frameptr fr;
+ int free=1;
+
+ lp->inc_skb=NULL;
+
+ /*
+ * Truncate buffer to true 802.3 length
+ * [FIXME: move to 802.2 demux]
+ */
+
+ ll = *(skb->data -2) * 256 + *(skb->data -1);
+ skb_trim( skb, ll );
+
+ fr = (frameptr) skb->data;
+ type = llc_decode_frametype( fr );
+
+
+ if (type <= FRMR_RSP)
+ {
+ /*
+ * PDU is of the type 2 set
+ */
+ if ((lp->llc_mode == MODE_ABM)||(type == SABME_CMD))
+ llc_process_otype2_frame(lp, skb, type);
+
+ }
+ else
+ {
+ /*
+ * PDU belongs to type 1 set
+ */
+ p_flag = fr->u_hdr.u_pflag;
+ switch(type)
+ {
+ case TEST_CMD:
+ llc_sendpdu(lp, TEST_RSP, 0,ll -3,
+ fr->u_hdr.u_info);
+ break;
+ case TEST_RSP:
+ lp->llc_callbacks|=LLC_TEST_INDICATION;
+ lp->inc_skb=skb;
+ free=0;
+ break;
+ case XID_CMD:
+ /*
+ * Basic format XID is handled by LLC itself
+ * Doc 5.4.1.1.2 p 48/49
+ */
+
+ if ((ll == 6)&&(fr->u_hdr.u_info[0] == 0x81))
+ {
+ lp->k = fr->u_hdr.u_info[2];
+ llc_sendpdu(lp, XID_RSP,
+ fr->u_hdr.u_pflag, ll -3,
+ fr->u_hdr.u_info);
+ }
+ break;
+
+ case XID_RSP:
+ if( ll == 6 && fr->u_hdr.u_info[0] == 0x81 )
+ {
+ lp->k = fr->u_hdr.u_info[2];
+ }
+ lp->llc_callbacks|=LLC_XID_INDICATION;
+ lp->inc_skb=skb;
+ free=0;
+ break;
+
+ case UI_CMD:
+ lp->llc_callbacks|=LLC_UI_DATA;
+ skb_pull(skb,3);
+ lp->inc_skb=skb;
+ free=0;
+ break;
+
+ default:
+ /*
+ * All other type 1 pdus ignored for now
+ */
+ }
+ }
+
+ if (free&&(!(IS_IFRAME(fr))))
+ {
+ /*
+ * No auto free for I pdus
+ */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ }
+
+ if(lp->llc_callbacks)
+ {
+ lp->llc_event(lp);
+ lp->llc_callbacks=0;
+ }
+ return 0;
+}
+
+
+/*
+ * Create an LLC client. As it is the job of the caller to clean up
+ * LLC's on device down, the device list must be locked before this call.
+ */
+
+int register_cl2llc_client(llcptr lp, const char *device, void (*event)(llcptr), u8 *rmac, u8 ssap, u8 dsap)
+{
+ char eye_init[] = "LLC\0";
+
+ memset(lp, 0, sizeof(*lp));
+ lp->dev = dev_get(device);
+ if(lp->dev == NULL)
+ return -ENODEV;
+ memcpy(lp->eye, eye_init, sizeof(lp->eye));
+ lp->rw = 1;
+ lp->k = 127;
+ lp->n1 = 1490;
+ lp->n2 = 10;
+ lp->timer_interval[P_TIMER] = HZ; /* 1 sec */
+ lp->timer_interval[REJ_TIMER] = HZ/8;
+ lp->timer_interval[ACK_TIMER] = HZ/8;
+ lp->timer_interval[BUSY_TIMER] = HZ*2;
+ lp->local_sap = ssap;
+ lp->llc_event = event;
+ lp->remote_mac_len = lp->dev->addr_len;
+ memcpy(lp->remote_mac, rmac, lp->remote_mac_len);
+ lp->state = 0;
+ lp->llc_mode = MODE_ADM;
+ lp->remote_sap = dsap;
+ skb_queue_head_init(&lp->atq);
+ skb_queue_head_init(&lp->rtq);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+void unregister_cl2llc_client(llcptr lp)
+{
+ llc_cancel_timers(lp);
+ MOD_DEC_USE_COUNT;
+ kfree(lp);
+}
+
+
+EXPORT_SYMBOL(register_cl2llc_client);
+EXPORT_SYMBOL(unregister_cl2llc_client);
+EXPORT_SYMBOL(llc_data_request);
+EXPORT_SYMBOL(llc_unit_data_request);
+EXPORT_SYMBOL(llc_test_request);
+EXPORT_SYMBOL(llc_xid_request);
+
+
+#define ALL_TYPES_8022 0
+
+void llc_init(struct net_proto *proto)
+{
+ printk(KERN_NOTICE "IEEE 802.2 LLC for Linux 2.1 (c) 1996 Tim Alpaerts\n");
+ return;
+}
+
+#ifdef MODULE
+
+
+int init_module(void)
+{
+ llc_init(NULL);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+
+}
+
+#endif
diff --git a/net/802/llc_sendpdu.c b/net/802/llc_sendpdu.c
new file mode 100644
index 000000000..436ddb9c7
--- /dev/null
+++ b/net/802/llc_sendpdu.c
@@ -0,0 +1,363 @@
+/*
+ * NET An implementation of the IEEE 802.2 LLC protocol for the
+ * LINUX operating system. LLC is implemented as a set of
+ * state machines and callbacks for higher networking layers.
+ *
+ * llc_sendpdu(), llc_sendipdu(), resend() + queue handling code
+ *
+ * Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : Chainsawed into Linux format, style
+ * Added llc_ to function names
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+static unsigned char cntl_byte_encode[] =
+{
+ 0x00, /* I_CMD */
+ 0x01, /* RR_CMD */
+ 0x05, /* RNR_CMD */
+ 0x09, /* REJ_CMD */
+ 0x43, /* DISC_CMD */
+ 0x7F, /* SABME_CMD */
+ 0x00, /* I_RSP */
+ 0x01, /* RR_RSP */
+ 0x05, /* RNR_RSP */
+ 0x09, /* REJ_RSP */
+ 0x63, /* UA_RSP */
+ 0x0F, /* DM_RSP */
+ 0x87, /* FRMR_RSP */
+ 0xFF, /* BAD_FRAME */
+ 0x03, /* UI_CMD */
+ 0xBF, /* XID_CMD */
+ 0xE3, /* TEST_CMD */
+ 0xBF, /* XID_RSP */
+ 0xE3 /* TEST_RSP */
+};
+
+static unsigned char fr_length_encode[] =
+{
+ 0x04, /* I_CMD */
+ 0x04, /* RR_CMD */
+ 0x04, /* RNR_CMD */
+ 0x04, /* REJ_CMD */
+ 0x03, /* DISC_CMD */
+ 0x03, /* SABME_CMD */
+ 0x04, /* I_RSP */
+ 0x04, /* RR_RSP */
+ 0x04, /* RNR_RSP */
+ 0x04, /* REJ_RSP */
+ 0x03, /* UA_RSP */
+ 0x03, /* DM_RSP */
+ 0x03, /* FRMR_RSP */
+ 0x00, /* BAD_FRAME */
+ 0x03, /* UI_CMD */
+ 0x03, /* XID_CMD */
+ 0x03, /* TEST_CMD */
+ 0x03, /* XID_RSP */
+ 0x03 /* TEST_RSP */
+};
+
+static unsigned char cr_bit_encode[] = {
+ 0x00, /* I_CMD */
+ 0x00, /* RR_CMD */
+ 0x00, /* RNR_CMD */
+ 0x00, /* REJ_CMD */
+ 0x00, /* DISC_CMD */
+ 0x00, /* SABME_CMD */
+ 0x01, /* I_RSP */
+ 0x01, /* RR_RSP */
+ 0x01, /* RNR_RSP */
+ 0x01, /* REJ_RSP */
+ 0x01, /* UA_RSP */
+ 0x01, /* DM_RSP */
+ 0x01, /* FRMR_RSP */
+ 0x00, /* BAD_FRAME */
+ 0x00, /* UI_CMD */
+ 0x00, /* XID_CMD */
+ 0x00, /* TEST_CMD */
+ 0x01, /* XID_RSP */
+ 0x01 /* TEST_RSP */
+};
+
+/*
+ * Sendpdu() constructs an output frame in a new skb and
+ * gives it to the MAC layer for transmision.
+ * This function is not used to send I pdus.
+ * No queues are updated here, nothing is saved for retransmission.
+ *
+ * Parameter pf controls both the poll/final bit and dsap
+ * fields in the output pdu.
+ * The dsap trick was needed to implement XID_CMD send with
+ * zero dsap field as described in doc 6.6 item 1 of enum.
+ */
+
+void llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data)
+{
+ frameptr fr; /* ptr to output pdu buffer */
+ unsigned short int fl; /* frame length == 802.3 "length" value */
+ struct sk_buff *skb;
+
+ fl = data_len + fr_length_encode[(int)type];
+ skb = alloc_skb(16 + fl, GFP_ATOMIC);
+ if (skb != NULL)
+ {
+ skb->dev = lp->dev;
+ skb_reserve(skb, 16);
+ fr = (frameptr) skb_put(skb, fl);
+ memset(fr, 0, fl);
+ /*
+ * Construct 802.2 header
+ */
+ if (pf & 0x02)
+ fr->pdu_hdr.dsap = 0;
+ else
+ fr->pdu_hdr.dsap = lp->remote_sap;
+ fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
+ fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
+ /*
+ * Fill in pflag and seq nbrs:
+ */
+ if (IS_SFRAME(fr))
+ {
+ /* case S-frames */
+ if (pf & 0x01)
+ fr->i_hdr.i_pflag = 1;
+ fr->i_hdr.nr = lp->vr;
+ }
+ else
+ {
+ /* case U frames */
+ if (pf & 0x01)
+ fr->u_hdr.u_pflag = 1;
+ }
+
+ if (data_len > 0)
+ { /* append data if any */
+ if (IS_UFRAME(fr))
+ {
+ memcpy(fr->u_hdr.u_info, pdu_data, data_len);
+ }
+ else
+ {
+ memcpy(fr->i_hdr.is_info, pdu_data, data_len);
+ }
+ }
+ lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
+ lp->remote_mac, NULL, fl);
+ skb->arp = 1;
+ skb->priority=SOPRI_NORMAL;
+ skb->dev=lp->dev;
+ dev_queue_xmit(skb);
+ }
+ else
+ printk(KERN_DEBUG "cl2llc: skb_alloc() in llc_sendpdu() failed\n");
+}
+
+void llc_xid_request(llcptr lp, char opt, int ll, char * data)
+{
+ llc_sendpdu(lp, XID_CMD, opt, ll, data);
+}
+
+void llc_test_request(llcptr lp, int ll, char * data)
+{
+ llc_sendpdu(lp, TEST_CMD, 0, ll, data);
+}
+
+void llc_unit_data_request(llcptr lp, int ll, char * data)
+{
+ llc_sendpdu(lp, UI_CMD, 0, ll, data);
+}
+
+
+/*
+ * llc_sendipdu() Completes an I pdu in an existing skb and gives it
+ * to the MAC layer for transmision.
+ * Parameter "type" must be either I_CMD or I_RSP.
+ * The skb is not freed after xmit, it is kept in case a retransmission
+ * is requested. If needed it can be picked up again from the rtq.
+ */
+
+void llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb)
+{
+ frameptr fr; /* ptr to output pdu buffer */
+ struct sk_buff *tmp;
+
+ fr = (frameptr) skb->data;
+
+ fr->pdu_hdr.dsap = lp->remote_sap;
+ fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
+ fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
+
+ if (pf)
+ fr->i_hdr.i_pflag = 1; /* p/f and seq numbers */
+ fr->i_hdr.nr = lp->vr;
+ fr->i_hdr.ns = lp->vs;
+ lp->vs++;
+ if (lp->vs > 127)
+ lp->vs = 0;
+ lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
+ lp->remote_mac, NULL, skb->len);
+ skb->arp = 1;
+ ADD_TO_RTQ(skb); /* add skb to the retransmit queue */
+ tmp=skb_clone(skb, GFP_ATOMIC);
+ if(tmp!=NULL)
+ {
+ tmp->dev=lp->dev;
+ tmp->priority=SOPRI_NORMAL;
+ dev_queue_xmit(tmp);
+ }
+}
+
+
+/*
+ * Resend_ipdu() will resend the pdus in the retransmit queue (rtq)
+ * the return value is the number of pdus resend.
+ * ack_nr is N(R) of 1st pdu to resent.
+ * Type is I_CMD or I_RSP for 1st pdu resent.
+ * p is p/f flag 0 or 1 for 1st pdu resent.
+ * All subsequent pdus will be sent as I_CMDs with p/f set to 0
+ */
+
+int llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p)
+{
+ struct sk_buff *skb,*tmp;
+ int resend_count;
+ frameptr fr;
+ unsigned long flags;
+
+
+ resend_count = 0;
+
+ save_flags(flags);
+ cli();
+
+ skb = skb_peek(&lp->rtq);
+
+ while(skb && skb != (struct sk_buff *)&lp->rtq)
+ {
+ fr = (frameptr) (skb->data + lp->dev->hard_header_len);
+ if (resend_count == 0)
+ {
+ /*
+ * Resending 1st pdu:
+ */
+
+ if (p)
+ fr->i_hdr.i_pflag = 1;
+ else
+ fr->i_hdr.i_pflag = 0;
+
+ if (type == I_CMD)
+ fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
+ else
+ fr->pdu_hdr.ssap = fr->pdu_hdr.ssap | 0x01;
+ }
+ else
+ {
+ /*
+ * Resending pdu 2...n
+ */
+
+ fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
+ fr->i_hdr.i_pflag = 0;
+ }
+ fr->i_hdr.nr = lp->vr;
+ fr->i_hdr.ns = lp->vs;
+ lp->vs++;
+ if (lp->vs > 127)
+ lp->vs = 0;
+ tmp=skb_clone(skb, GFP_ATOMIC);
+ if(tmp!=NULL)
+ {
+ tmp->arp = 1;
+ tmp->dev = lp->dev;
+ tmp->priority = SOPRI_NORMAL;
+ dev_queue_xmit(skb);
+ }
+ resend_count++;
+ skb = skb->next;
+ }
+ restore_flags(flags);
+ return resend_count;
+}
+
+/* ************** internal queue management code ****************** */
+
+
+/*
+ * Remove one skb from the front of the awaiting transmit queue
+ * (this is the skb longest on the queue) and return a pointer to
+ * that skb.
+ */
+
+struct sk_buff *llc_pull_from_atq(llcptr lp)
+{
+ return skb_dequeue(&lp->atq);
+}
+
+/*
+ * Free_acknowledged_skbs(), remove from retransmit queue (rtq)
+ * and free all skbs with an N(S) chronologicaly before 'pdu_ack'.
+ * The return value is the number of pdus acknowledged.
+ */
+
+int llc_free_acknowledged_skbs(llcptr lp, unsigned char pdu_ack)
+{
+ struct sk_buff *pp;
+ frameptr fr;
+ int ack_count;
+ unsigned char ack; /* N(S) of most recently ack'ed pdu */
+ unsigned char ns_save;
+ unsigned long flags;
+
+ if (pdu_ack > 0)
+ ack = pdu_ack -1;
+ else
+ ack = 127;
+
+ ack_count = 0;
+
+ save_flags(flags);
+ cli();
+
+ pp = skb_dequeue(&lp->rtq);
+ while (pp != NULL)
+ {
+ /*
+ * Locate skb with N(S) == ack
+ */
+
+ /*
+ * BUG: FIXME - use skb->h.*
+ */
+ fr = (frameptr) (pp->data + lp->dev->hard_header_len);
+ ns_save = fr->i_hdr.ns;
+
+ kfree_skb(pp, FREE_WRITE);
+ ack_count++;
+
+ if (ns_save == ack)
+ break;
+ pp = skb_dequeue(&lp->rtq);
+ }
+ restore_flags(flags);
+ return ack_count;
+}
+
diff --git a/net/802/llc_utility.c b/net/802/llc_utility.c
new file mode 100644
index 000000000..d0a58018f
--- /dev/null
+++ b/net/802/llc_utility.c
@@ -0,0 +1,253 @@
+/*
+ * NET An implementation of the IEEE 802.2 LLC protocol for the
+ * LINUX operating system. LLC is implemented as a set of
+ * state machines and callbacks for higher networking layers.
+ *
+ * Small utilities, Linux timer handling.
+ *
+ * Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.com
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Changes
+ * Alan Cox : Chainsawed into Linux form.
+ * Added llc_ function name prefixes.
+ * Fixed bug in stop/start timer.
+ * Added llc_cancel_timers for closing
+ * down an llc
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+int llc_decode_frametype(frameptr fr)
+{
+ if (IS_UFRAME(fr))
+ { /* unnumbered cmd/rsp */
+ switch(fr->u_mm.mm & 0x3B)
+ {
+ case 0x1B:
+ return(SABME_CMD);
+ break;
+ case 0x10:
+ return(DISC_CMD);
+ break;
+ case 0x18:
+ return(UA_RSP);
+ break;
+ case 0x03:
+ return(DM_RSP);
+ break;
+ case 0x21:
+ return(FRMR_RSP);
+ break;
+ case 0x00:
+ return(UI_CMD);
+ break;
+ case 0x2B:
+ if (IS_RSP(fr))
+ return(XID_RSP);
+ else
+ return(XID_CMD);
+ break;
+ case 0x38:
+ if (IS_RSP(fr))
+ return(TEST_RSP);
+ else
+ return(TEST_CMD);
+ break;
+ default:
+ return(BAD_FRAME);
+ }
+ }
+ else if (IS_SFRAME(fr))
+ { /* supervisory cmd/rsp */
+ switch(fr->s_hdr.ss)
+ {
+ case 0x00:
+ if (IS_RSP(fr))
+ return(RR_RSP);
+ else
+ return(RR_CMD);
+ break;
+ case 0x02:
+ if (IS_RSP(fr))
+ return(REJ_RSP);
+ else
+ return(REJ_CMD);
+ break;
+ case 0x01:
+ if (IS_RSP(fr))
+ return(RNR_RSP);
+ else
+ return(RNR_CMD);
+ break;
+ default:
+ return(BAD_FRAME);
+ }
+ }
+ else
+ { /* information xfer */
+ if (IS_RSP(fr))
+ return(I_RSP);
+ else
+ return(I_CMD);
+ }
+}
+
+
+/*
+ * Validate_seq_nos will check N(S) and N(R) to see if they are
+ * invalid or unexpected.
+ * "unexpected" is explained on p44 Send State Variable.
+ * The return value is:
+ * 4 * invalid N(R) +
+ * 2 * invalid N(S) +
+ * 1 * unexpected N(S)
+ */
+
+int llc_validate_seq_nos(llcptr lp, frameptr fr)
+{
+ int res;
+
+ /*
+ * A U-frame is always good
+ */
+
+ if (IS_UFRAME(fr))
+ return(0);
+
+ /*
+ * For S- and I-frames check N(R):
+ */
+
+ if (fr->i_hdr.nr == lp->vs)
+ { /* if N(R) = V(S) */
+ res = 0; /* N(R) is good */
+ }
+ else
+ { /* lp->k = transmit window size */
+ if (lp->vs >= lp->k)
+ { /* if window not wrapped around 127 */
+ if ((fr->i_hdr.nr < lp->vs) &&
+ (fr->i_hdr.nr > (lp->vs - lp->k)))
+ res = 0;
+ else
+ res = 4; /* N(R) invalid */
+ }
+ else
+ { /* window wraps around 127 */
+ if ((fr->i_hdr.nr < lp->vs) ||
+ (fr->i_hdr.nr > (128 + lp->vs - lp->k)))
+ res = 0;
+ else
+ res = 4; /* N(R) invalid */
+ }
+ }
+
+ /*
+ * For an I-frame, must check N(S) also:
+ */
+
+ if (IS_IFRAME(fr))
+ {
+ if (fr->i_hdr.ns == lp->vr)
+ return res; /* N(S) good */
+ if (lp->vr >= lp->rw)
+ {
+ /* if receive window not wrapped */
+
+ if ((fr->i_hdr.ns < lp->vr) &&
+ (fr->i_hdr.ns > (lp->vr - lp->k)))
+ res = res +1; /* N(S) unexpected */
+ else
+ res = res +2; /* N(S) invalid */
+ }
+ else
+ {
+ /* Window wraps around 127 */
+
+ if ((fr->i_hdr.ns < lp->vr) ||
+ (fr->i_hdr.ns > (128 + lp->vr - lp->k)))
+ res = res +1; /* N(S) unexpected */
+ else
+ res = res +2; /* N(S) invalid */
+ }
+ }
+ return(res);
+}
+
+/* **************** timer management routines ********************* */
+
+static void llc_p_timer_expired(unsigned long ulp)
+{
+ llc_timer_expired((llcptr) ulp, P_TIMER);
+}
+
+static void llc_rej_timer_expired(unsigned long ulp)
+{
+ llc_timer_expired((llcptr) ulp, REJ_TIMER);
+}
+
+static void llc_ack_timer_expired(unsigned long ulp)
+{
+ llc_timer_expired((llcptr) ulp, ACK_TIMER);
+}
+
+static void llc_busy_timer_expired(unsigned long ulp)
+{
+ llc_timer_expired((llcptr) ulp, BUSY_TIMER);
+}
+
+/* exp_fcn is an array holding the 4 entry points of the
+ timer expiry routines above.
+ It is required to keep start_timer() generic.
+ Thank you cdecl.
+ */
+
+static void (* exp_fcn[])(unsigned long) =
+{
+ llc_p_timer_expired,
+ llc_rej_timer_expired,
+ llc_ack_timer_expired,
+ llc_busy_timer_expired
+};
+
+void llc_start_timer(llcptr lp, int t)
+{
+ if (lp->timer_state[t] == TIMER_IDLE)
+ {
+ lp->tl[t].expires = jiffies + lp->timer_interval[t];
+ lp->tl[t].data = (unsigned long) lp;
+ lp->tl[t].function = exp_fcn[t];
+ add_timer(&lp->tl[t]);
+ lp->timer_state[t] = TIMER_RUNNING;
+ }
+}
+
+void llc_stop_timer(llcptr lp, int t)
+{
+ if (lp->timer_state[t] == TIMER_RUNNING)
+ {
+ del_timer(&lp->tl[t]);
+ lp->timer_state[t] = TIMER_IDLE;
+ }
+}
+
+void llc_cancel_timers(llcptr lp)
+{
+ llc_stop_timer(lp, P_TIMER);
+ llc_stop_timer(lp, REJ_TIMER);
+ llc_stop_timer(lp, ACK_TIMER);
+ llc_stop_timer(lp, BUSY_TIMER);
+}
+
diff --git a/net/802/p8022.c b/net/802/p8022.c
index f8754e5c0..23e6f2fad 100644
--- a/net/802/p8022.c
+++ b/net/802/p8022.c
@@ -1,3 +1,22 @@
+/*
+ * NET3: Support for 802.2 demultiplexing off ethernet (Token ring
+ * is kept seperate see p8022tr.c)
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Demultiplex 802.2 encoded protocols. We match the entry by the
+ * SSAP/DSAP pair and then deliver to the registered datalink that
+ * matches. The control byte is ignored and handling of such items
+ * is up to the routine passed the frame.
+ *
+ * Unlike the 802.3 datalink we have a list of 802.2 entries as there
+ * are multiple protocols to demux. The list is currently short (3 or
+ * 4 entries at most). The current demux assumes this.
+ */
+
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
@@ -12,11 +31,11 @@ static struct datalink_proto *p8022_list = NULL;
* We don't handle the loopback SAP stuff, the extended
* 802.2 command set, multicast SAP identifiers and non UI
* frames. We have the absolute minimum needed for IPX,
- * IP and Appletalk phase 2.
+ * IP and Appletalk phase 2. See the llc_* routines for
+ * support libraries if your protocol needs these.
*/
-
-static struct datalink_proto *
-find_8022_client(unsigned char type)
+
+static struct datalink_proto *find_8022_client(unsigned char type)
{
struct datalink_proto *proto;
@@ -28,14 +47,15 @@ find_8022_client(unsigned char type)
return proto;
}
-int
-p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
struct datalink_proto *proto;
proto = find_8022_client(*(skb->h.raw));
- if (proto != NULL) {
+ if (proto != NULL)
+ {
skb->h.raw += 3;
+ skb->nh.raw += 3;
skb_pull(skb,3);
return proto->rcvfunc(skb, dev, pt);
}
@@ -45,8 +65,7 @@ p8022_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return 0;
}
-static void
-p8022_datalink_header(struct datalink_proto *dl,
+static void p8022_datalink_header(struct datalink_proto *dl,
struct sk_buff *skb, unsigned char *dest_node)
{
struct device *dev = skb->dev;
@@ -59,7 +78,7 @@ p8022_datalink_header(struct datalink_proto *dl,
dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
}
-static struct packet_type p8022_packet_type =
+static struct packet_type p8022_packet_type =
{
0, /* MUTTER ntohs(ETH_P_8022),*/
NULL, /* All devices */
@@ -68,23 +87,16 @@ static struct packet_type p8022_packet_type =
NULL,
};
-static struct symbol_table p8022_proto_syms = {
-#include <linux/symtab_begin.h>
- X(register_8022_client),
- X(unregister_8022_client),
-#include <linux/symtab_end.h>
-};
-
+EXPORT_SYMBOL(register_8022_client);
+EXPORT_SYMBOL(unregister_8022_client);
void p8022_proto_init(struct net_proto *pro)
{
p8022_packet_type.type=htons(ETH_P_802_2);
dev_add_pack(&p8022_packet_type);
- register_symtab(&p8022_proto_syms);
}
-
-struct datalink_proto *
-register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+
+struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
{
struct datalink_proto *proto;
diff --git a/net/802/p8022tr.c b/net/802/p8022tr.c
index d1fcc5c46..6a5864d54 100644
--- a/net/802/p8022tr.c
+++ b/net/802/p8022tr.c
@@ -1,3 +1,14 @@
+/*
+ * NET3: Handling for token ring frames that are not IP. IP is hooked
+ * early in the token ring support code.
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
@@ -14,11 +25,14 @@ static struct datalink_proto *p8022tr_list = NULL;
* We don't handle the loopback SAP stuff, the extended
* 802.2 command set, multicast SAP identifiers and non UI
* frames. We have the absolute minimum needed for IPX,
- * IP and Appletalk phase 2.
+ * IP and Appletalk phase 2. See the llc_* routines for support
+ * to handle the fun stuff.
+ *
+ * We assume the list will be very short (at the moment its normally
+ * one or two entries).
*/
-
-static struct datalink_proto *
-find_8022tr_client(unsigned char type)
+
+static struct datalink_proto *find_8022tr_client(unsigned char type)
{
struct datalink_proto *proto;
@@ -30,8 +44,7 @@ find_8022tr_client(unsigned char type)
return proto;
}
-int
-p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
struct datalink_proto *proto;
@@ -47,8 +60,7 @@ p8022tr_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return 0;
}
-static void
-p8022tr_datalink_header(struct datalink_proto *dl,
+static void p8022tr_datalink_header(struct datalink_proto *dl,
struct sk_buff *skb, unsigned char *dest_node)
{
struct device *dev = skb->dev;
@@ -66,32 +78,27 @@ p8022tr_datalink_header(struct datalink_proto *dl,
memmove(newdata, olddata, dev->hard_header_len - SNAP_HEADER_LEN);
}
-static struct packet_type p8022tr_packet_type =
+static struct packet_type p8022tr_packet_type =
{
- 0,
+ 0,
NULL, /* All devices */
p8022tr_rcv,
NULL,
NULL,
};
-
-static struct symbol_table p8022tr_proto_syms = {
-#include <linux/symtab_begin.h>
- X(register_8022tr_client),
- X(unregister_8022tr_client),
-#include <linux/symtab_end.h>
-};
+
+EXPORT_SYMBOL(register_8022tr_client);
+EXPORT_SYMBOL(unregister_8022tr_client);
void p8022tr_proto_init(struct net_proto *pro)
{
p8022tr_packet_type.type=htons(ETH_P_TR_802_2);
dev_add_pack(&p8022tr_packet_type);
- register_symtab(&p8022tr_proto_syms);
}
-
-struct datalink_proto *
-register_8022tr_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
+
+struct datalink_proto *register_8022tr_client(unsigned char type,
+ int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
{
struct datalink_proto *proto;
@@ -134,4 +141,3 @@ void unregister_8022tr_client(unsigned char type)
restore_flags(flags);
}
-
diff --git a/net/802/p8023.c b/net/802/p8023.c
index 57bd6a74a..82a80c3c2 100644
--- a/net/802/p8023.c
+++ b/net/802/p8023.c
@@ -1,34 +1,59 @@
+/*
+ * NET3: 802.3 data link hooks used for IPX 802.3
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * 802.3 isn't really a protocol data link layer. Some old IPX stuff
+ * uses it however. Note that there is only one 802.3 protocol layer
+ * in the system. We don't currently support different protocols
+ * running raw 802.3 on different devices. Thankfully nobody else
+ * has done anything like the old IPX.
+ */
+
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/datalink.h>
#include <linux/mm.h>
#include <linux/in.h>
-static void
-p8023_datalink_header(struct datalink_proto *dl,
+/*
+ * Place an 802.3 header on a packet. The driver will do the mac
+ * addresses, we just need to give it the buffer length.
+ */
+
+static void p8023_datalink_header(struct datalink_proto *dl,
struct sk_buff *skb, unsigned char *dest_node)
{
struct device *dev = skb->dev;
-
dev->hard_header(skb, dev, ETH_P_802_3, dest_node, NULL, skb->len);
}
-struct datalink_proto *
-make_8023_client(void)
+/*
+ * Create an 802.3 client. Note there can be only one 802.3 client
+ */
+
+struct datalink_proto *make_8023_client(void)
{
struct datalink_proto *proto;
proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
- if (proto != NULL) {
+ if (proto != NULL)
+ {
proto->type_len = 0;
proto->header_length = 0;
proto->datalink_header = p8023_datalink_header;
proto->string_name = "802.3";
}
-
return proto;
}
+/*
+ * Destroy the 802.3 client.
+ */
+
void destroy_8023_client(struct datalink_proto *dl)
{
if (dl)
diff --git a/net/802/pseudo/Makefile b/net/802/pseudo/Makefile
new file mode 100644
index 000000000..d9f416029
--- /dev/null
+++ b/net/802/pseudo/Makefile
@@ -0,0 +1,13 @@
+all: pseudocode.h actionnm.h
+
+clean:
+ touch pseudocode.h actionnm.h
+ rm pseudocode.h actionnm.h
+
+pseudocode.h: pseudocode opcd2num.sed compile.awk
+ sed -f opcd2num.sed pseudocode | awk -f compile.awk >pseudocode.h
+
+actionnm.h: pseudocode.h actionnm.awk
+ awk -f actionnm.awk pseudocode.h>actionnm.h
+
+
diff --git a/net/802/pseudo/actionnm.awk b/net/802/pseudo/actionnm.awk
new file mode 100644
index 000000000..b5ca78289
--- /dev/null
+++ b/net/802/pseudo/actionnm.awk
@@ -0,0 +1,27 @@
+# usage: awk -f actionnm.awk pseudocode.h
+#
+BEGIN { "date" | getline
+ today = $0
+ printf("\n/* this file generated on %s */\n", today )
+ printf("\nstatic char *action_names[] = { \n " )
+ opl = 0
+}
+
+/^#define/ {
+ if ( opl > 3 ) {
+ printf("\n ")
+ opl = 0
+ }
+ opl = opl +1
+ t = sprintf("\"%s\"", $2 )
+ printf("%-15s ,", t )
+# printf("%-10s", $2 )
+}
+
+END {
+ if ( opl > 3 ) {
+ printf("\n ")
+ }
+ printf("\t 0\n};\n\n")
+}
+
diff --git a/net/802/pseudo/actionnm.h b/net/802/pseudo/actionnm.h
new file mode 100644
index 000000000..924cf9972
--- /dev/null
+++ b/net/802/pseudo/actionnm.h
@@ -0,0 +1,51 @@
+
+/* this file generated on Thu Oct 24 11:42:37 GMT 1996 */
+
+static char *action_names[] = {
+ "NOP" ,"ADM1" ,"ADM2" ,"ADM3" ,
+ "ADM4" ,"ADM5" ,"CONN2" ,"CONN3" ,
+ "CONN4" ,"CONN5" ,"RESWAIT1" ,"RESWAIT2" ,
+ "RESWAIT3" ,"RESWAIT4" ,"RESWAIT5" ,"RESWAIT6" ,
+ "RESWAIT7" ,"RESWAIT8" ,"RESCHK1" ,"RESCHK2" ,
+ "RESCHK3" ,"RESCHK4" ,"RESCHK5" ,"RESCHK6" ,
+ "SETUP1" ,"SETUP2" ,"SETUP3" ,"SETUP4" ,
+ "SETUP5" ,"SETUP6" ,"SETUP7" ,"SETUP8" ,
+ "RESET1" ,"RESET2" ,"RESET3" ,"RESET4" ,
+ "RESET5" ,"RESET6" ,"RESET7" ,"RESET8" ,
+ "D_CONN1" ,"D_CONN2" ,"D_CONN3" ,"D_CONN4" ,
+ "D_CONN5" ,"D_CONN6" ,"D_CONN7" ,"ERR1" ,
+ "ERR2" ,"ERR3" ,"ERR4" ,"ERR5" ,
+ "ERR6" ,"ERR7" ,"ERR8" ,"SH1" ,
+ "SH2" ,"SH3" ,"SH4" ,"SH5" ,
+ "SH6" ,"SH7" ,"SH8" ,"SH9" ,
+ "SH10" ,"SH11" ,"NORMAL1" ,"NORMAL2" ,
+ "NORMAL3" ,"NORMAL4" ,"NORMAL5" ,"NORMAL6" ,
+ "NORMAL7" ,"NORMAL8A" ,"NORMAL8B" ,"NORMAL9" ,
+ "NORMAL10" ,"NORMAL11" ,"NORMAL12" ,"NORMAL13" ,
+ "NORMAL14" ,"NORMAL15" ,"NORMAL16" ,"NORMAL17" ,
+ "NORMAL18" ,"NORMAL19" ,"NORMAL20" ,"BUSY1" ,
+ "BUSY2" ,"BUSY3" ,"BUSY4" ,"BUSY5" ,
+ "BUSY6" ,"BUSY7" ,"BUSY8" ,"BUSY9" ,
+ "BUSY10" ,"BUSY11" ,"BUSY12" ,"BUSY13" ,
+ "BUSY14" ,"BUSY15" ,"BUSY16" ,"BUSY17" ,
+ "BUSY18" ,"BUSY19" ,"BUSY20" ,"BUSY21" ,
+ "BUSY22" ,"BUSY23" ,"BUSY24" ,"BUSY25" ,
+ "BUSY26" ,"REJECT1" ,"REJECT2" ,"REJECT3" ,
+ "REJECT4" ,"REJECT5" ,"REJECT6" ,"REJECT7" ,
+ "REJECT8" ,"REJECT9" ,"REJECT10" ,"REJECT11" ,
+ "REJECT12" ,"REJECT13" ,"REJECT14" ,"REJECT15" ,
+ "REJECT16" ,"REJECT17" ,"REJECT18" ,"REJECT19" ,
+ "REJECT20" ,"AWAIT1" ,"AWAIT2" ,"AWAIT3" ,
+ "AWAIT4" ,"AWAIT5" ,"AWAIT6" ,"AWAIT7" ,
+ "AWAIT8" ,"AWAIT9" ,"AWAIT10" ,"AWAIT11" ,
+ "AWAIT12" ,"AWAIT13" ,"AWAIT14" ,"AWAIT_BUSY1" ,
+ "AWAIT_BUSY2" ,"AWAIT_BUSY3" ,"AWAIT_BUSY4" ,"AWAIT_BUSY5" ,
+ "AWAIT_BUSY6" ,"AWAIT_BUSY7" ,"AWAIT_BUSY8" ,"AWAIT_BUSY9" ,
+ "AWAIT_BUSY10" ,"AWAIT_BUSY11" ,"AWAIT_BUSY12" ,"AWAIT_BUSY13" ,
+ "AWAIT_BUSY14" ,"AWAIT_BUSY15" ,"AWAIT_BUSY16" ,"AWAIT_REJECT1" ,
+ "AWAIT_REJECT2" ,"AWAIT_REJECT3" ,"AWAIT_REJECT4" ,"AWAIT_REJECT5" ,
+ "AWAIT_REJECT6" ,"AWAIT_REJECT7" ,"AWAIT_REJECT8" ,"AWAIT_REJECT9" ,
+ "AWAIT_REJECT10" ,"AWAIT_REJECT11" ,"AWAIT_REJECT12" ,"AWAIT_REJECT13" ,
+ 0
+};
+
diff --git a/net/802/pseudo/compile.awk b/net/802/pseudo/compile.awk
new file mode 100644
index 000000000..ca901fa35
--- /dev/null
+++ b/net/802/pseudo/compile.awk
@@ -0,0 +1,57 @@
+# usage: cat pseudocode | sed -f act2num | awk -f compile.awk
+#
+#
+BEGIN { "date" | getline
+ today = $0
+ printf("\n/* this file generated on %s */\n", today )
+ printf("\nstatic char pseudo_code [ ] = { \n" )
+ opl = 0 # op codes on the current line
+
+ opc = 0 # opcode counter
+ fpi = 0 # fill pointer for idx array
+}
+
+/^;/ { } # line starting with semicolon is comment
+
+/^[A-Z]/ { # start of a new action
+ emit( 0 )
+ idx[ ++fpi ] = opc
+ name[ fpi ] = $1
+ emit( $2 )
+}
+
+/^[\t ]/ {
+ emit( $1 )
+}
+
+END {
+ if ( opl > 8 ) {
+ printf("\n")
+ }
+ printf("\t 0\n};\n\n")
+ printf("static short int pseudo_code_idx [ ] ={\n")
+ opl = 0
+ emit( 0 )
+ for( ii = 1; ii <= fpi; ii++ )
+ emit( idx[ ii ] )
+ if ( opl > 8 ) {
+ printf("\n")
+ }
+ printf("\t 0\n};\n\n")
+
+ printf("#define %-10s \t %3d \n", "NOP", 0 )
+ for( ii = 1; ii <= fpi; ii++ )
+ printf("#define %-10s \t %3d \n", name[ ii ], ii )
+ printf("\n")
+}
+
+function emit( opcode ){ # Niclaus Wirth
+ if ( opl > 8 ) {
+ printf("\n")
+ opl = 0
+ }
+ opl = opl +1
+ printf("\t%4d,", opcode )
+ opc++
+}
+
diff --git a/net/802/pseudo/opcd2num.sed b/net/802/pseudo/opcd2num.sed
new file mode 100644
index 000000000..c66f85a65
--- /dev/null
+++ b/net/802/pseudo/opcd2num.sed
@@ -0,0 +1,72 @@
+s/NOP/0/
+s/DUMMY_6/6/
+s/DUMMY_8/8/
+s/IF_F=1_CLEAR_REMOTE_BUSY/9/
+s/CLEAR_REMOTE_BUSY/1/
+s/CONNECT_CONFIRM/3/
+s/DISCONNECT_INDICATION/5/
+s/CONNECT_INDICATION/2/
+s/IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1/55/
+s/DATA_FLAG:=0/53/
+s/DATA_FLAG:=1/54/
+s/DATA_FLAG:=2/52/
+s/DATA_INDICATION/4/
+s/F_FLAG:=P/65/
+s/IF_DATA_FLAG=2_STOP_REJ_TIMER/10/
+s/OPTIONAL_SEND_RNR_XXX(X=0)/30/
+s/P_FLAG:=0/56/
+s/P_FLAG:=P/57/
+s/RE-SEND_FRMR_RSP(F=0)/14/
+s/RE-SEND_FRMR_RSP(F=P)/15/
+s/RE-SEND_I_CMD(P=1)_OR_SEND_RR/18/
+s/RE-SEND_I_CMD(P=1)/17/
+s/RE-SEND_I_RSP(F=1)/22/
+s/RE-SEND_I_XXX(X=0)_OR_SEND_RR/21/
+s/RE-SEND_I_XXX(X=0)/20/
+s/REMOTE_BUSY:=0/58/
+s/REPORT_STATUS(FRMR_RECEIVED)/66/
+s/REPORT_STATUS(FRMR_SENT)/67/
+s/REPORT_STATUS(REMOTE_BUSY)/68/
+s/REPORT_STATUS(REMOTE_NOT_BUSY)/69/
+s/RESET_CONFIRM/7/
+s/RESET_INDICATION(LOCAL)/70/
+s/RESET_INDICATION(REMOTE)/71/
+s/RETRY_COUNT:=RETRY_COUNT+1/60/
+s/RETRY_COUNT:=0/59/
+s/SEND_ACKNOWLEDGE_CMD(P=1)/32/
+s/SEND_ACKNOWLEDGE_RSP(F=1)/34/
+s/SEND_ACKNOWLEDGE_XXX(X=0)/36/
+s/SEND_DISC_CMD(P=X)/11/
+s/SEND_DM_RSP(F=X)/12/
+s/SEND_FRMR_RSP(F=X)/13/
+s/SEND_I_CMD(P=1)/16/
+s/SEND_I_XXX(X=0)/19/
+s/SEND_REJ_CMD(P=1)/23/
+s/SEND_REJ_RSP(F=1)/24/
+s/SEND_REJ_XXX(X=0)/25/
+s/SEND_RNR_CMD(F=1)/26/
+s/SEND_RNR_RSP(F=1)/27/
+s/SEND_RNR_XXX(X=0)/28/
+s/SEND_RR_CMD(P=1)/31/
+s/SEND_RR_RSP(F=1)/33/
+s/SEND_RR_XXX(X=0)/35/
+s/SEND_SABME_CMD(P=X)/37/
+s/SEND_UA_RSP(F=X)/38/
+s/SET_REMOTE_BUSY/29/
+s/START_ACK_TIMER_IF_NOT_RUNNING/44/
+s/START_ACK_TIMER/42/
+s/START_P_TIMER/41/
+s/START_REJ_TIMER/43/
+s/STOP_ACK_TIMER/45/
+s/STOP_ALL_TIMERS/48/
+s/STOP_OTHER_TIMERS/49/
+s/STOP_P_TIMER/46/
+s/STOP_REJ_TIMER/47/
+s/S_FLAG:=0/39/
+s/S_FLAG:=1/40/
+s/UPDATE_N(R)_RECEIVED/50/
+s/UPDATE_P_FLAG/51/
+s/V(R):=0/61/
+s/V(R):=V(R)+1/62/
+s/V(S):=0/63/
+s/V(S):=N(R)/64/
diff --git a/net/802/pseudo/opcodes b/net/802/pseudo/opcodes
new file mode 100644
index 000000000..1bd7e7240
--- /dev/null
+++ b/net/802/pseudo/opcodes
@@ -0,0 +1,72 @@
+ 0 NOP
+ 1 CLEAR_REMOTE_BUSY
+ 2 CONNECT_INDICATION
+ 3 CONNECT_CONFIRM
+ 4 DATA_INDICATION
+ 5 DISCONNECT_INDICATION
+ 6 DUMMY_6
+ 7 RESET_CONFIRM
+ 8 DUMMY_8
+ 9 IF_F=1_CLEAR_REMOTE_BUSY
+ 10 IF_DATA_FLAG=2_STOP_REJ_TIMER
+ 11 SEND_DISC_CMD(P=X)
+ 12 SEND_DM_RSP(F=X)
+ 13 SEND_FRMR_RSP(F=X)
+ 14 RE-SEND_FRMR_RSP(F=0)
+ 15 RE-SEND_FRMR_RSP(F=P)
+ 16 SEND_I_CMD(P=1)
+ 17 RE-SEND_I_CMD(P=1)
+ 18 RE-SEND_I_CMD(P=1)_OR_SEND_RR
+ 19 SEND_I_XXX(X=0)
+ 20 RE-SEND_I_XXX(X=0)
+ 21 RE-SEND_I_XXX(X=0)_OR_SEND_RR
+ 22 RE-SEND_I_RSP(F=1)
+ 23 SEND_REJ_CMD(P=1)
+ 24 SEND_REJ_RSP(F=1)
+ 25 SEND_REJ_XXX(X=0)
+ 26 SEND_RNR_CMD(F=1)
+ 27 SEND_RNR_RSP(F=1)
+ 28 SEND_RNR_XXX(X=0)
+ 29 SET_REMOTE_BUSY
+ 30 OPTIONAL_SEND_RNR_XXX(X=0)
+ 31 SEND_RR_CMD(P=1)
+ 32 SEND_ACKNOWLEDGE_CMD(P=1)
+ 33 SEND_RR_RSP(F=1)
+ 34 SEND_ACKNOWLEDGE_RSP(F=1)
+ 35 SEND_RR_XXX(X=0)
+ 36 SEND_ACKNOWLEDGE_XXX(X=0)
+ 37 SEND_SABME_CMD(P=X)
+ 38 SEND_UA_RSP(F=X)
+ 39 S_FLAG:=0
+ 40 S_FLAG:=1
+ 41 START_P_TIMER
+ 42 START_ACK_TIMER
+ 43 START_REJ_TIMER
+ 44 START_ACK_TIMER_IF_NOT_RUNNING
+ 45 STOP_ACK_TIMER
+ 46 STOP_P_TIMER
+ 47 STOP_REJ_TIMER
+ 48 STOP_ALL_TIMERS
+ 49 STOP_OTHER_TIMERS
+ 50 UPDATE_N(R)_RECEIVED
+ 51 UPDATE_P_FLAG
+ 52 DATA_FLAG:=2
+ 53 DATA_FLAG:=0
+ 54 DATA_FLAG:=1
+ 55 IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+ 56 P_FLAG:=0
+ 57 P_FLAG:=P
+ 58 REMOTE_BUSY:=0
+ 59 RETRY_COUNT:=0
+ 60 RETRY_COUNT:=RETRY_COUNT+1
+ 61 V(R):=0
+ 62 V(R):=V(R)+1
+ 63 V(S):=0
+ 64 V(S):=N(R)
+ 65 F_FLAG:=P
+ 66 REPORT_STATUS(FRMR_RECEIVED)
+ 67 REPORT_STATUS(FRMR_SENT)
+ 68 REPORT_STATUS(REMOTE_BUSY)
+ 69 REPORT_STATUS(REMOTE_NOT_BUSY)
+ 70 RESET_INDICATION(LOCAL)
+ 71 RESET_INDICATION(REMOTE)
diff --git a/net/802/pseudo/opcodesnm.h b/net/802/pseudo/opcodesnm.h
new file mode 100644
index 000000000..d24f2c320
--- /dev/null
+++ b/net/802/pseudo/opcodesnm.h
@@ -0,0 +1,23 @@
+static char *opcode_names[] = {
+"NOP", "CLEAR_REMOTE_BUSY", "CONNECT_INDICATION", "CONNECT_CONFIRM", "DATA_INDICATION",
+"DISCONNECT_INDICATION", "DUMMY_6", "RESET_CONFIRM", "DUMMY_8",
+"IF_F=1_CLEAR_REMOTE_BUSY", "IF_DATA_FLAG=2_STOP_REJ_TIMER", "SEND_DISC_CMD(P=X)",
+"SEND_DM_RSP(F=X)", "SEND_FRMR_RSP(F=X)", "RE-SEND_FRMR_RSP(F=0)",
+"RE-SEND_FRMR_RSP(F=P)", "SEND_I_CMD(P=1)", "RE-SEND_I_CMD(P=1)",
+"RE-SEND_I_CMD(P=1)_OR_SEND_RR", "SEND_I_XXX(X=0)", "RE-SEND_I_XXX(X=0)",
+"RE-SEND_I_XXX(X=0)_OR_SEND_RR", "RE-SEND_I_RSP(F=1)", "SEND_REJ_CMD(P=1)",
+"SEND_REJ_RSP(F=1)", "SEND_REJ_XXX(X=0)", "SEND_RNR_CMD(F=1)", "SEND_RNR_RSP(F=1)",
+"SEND_RNR_XXX(X=0)", "SET_REMOTE_BUSY", "OPTIONAL_SEND_RNR_XXX(X=0)",
+"SEND_RR_CMD(P=1)", "SEND_ACKNOWLEDGE_CMD(P=1)", "SEND_RR_RSP(F=1)",
+"SEND_ACKNOWLEDGE_RSP(F=1)", "SEND_RR_XXX(X=0)", "SEND_ACKNOWLEDGE_XXX(X=0)",
+"SEND_SABME_CMD(P=X)", "SEND_UA_RSP(F=X)", "S_FLAG:=0", "S_FLAG:=1", "START_P_TIMER",
+"START_ACK_TIMER", "START_REJ_TIMER", "START_ACK_TIMER_IF_NOT_RUNNING",
+"STOP_ACK_TIMER", "STOP_P_TIMER", "STOP_REJ_TIMER", "STOP_ALL_TIMERS",
+"STOP_OTHER_TIMERS", "UPDATE_N(R)_RECEIVED", "UPDATE_P_FLAG", "DATA_FLAG:=2",
+"DATA_FLAG:=0", "DATA_FLAG:=1", "IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1", "P_FLAG:=0",
+"P_FLAG:=P", "REMOTE_BUSY:=0", "RETRY_COUNT:=0", "RETRY_COUNT:=RETRY_COUNT+1",
+"V(R):=0", "V(R):=V(R)+1", "V(S):=0", "V(S):=N(R)", "F_FLAG:=P",
+"REPORT_STATUS(FRMR_RECEIVED)", "REPORT_STATUS(FRMR_SENT)",
+"REPORT_STATUS(REMOTE_BUSY)", "REPORT_STATUS(REMOTE_NOT_BUSY)",
+"RESET_INDICATION(LOCAL)", "RESET_INDICATION(REMOTE)"
+};
diff --git a/net/802/pseudo/pseudocode b/net/802/pseudo/pseudocode
new file mode 100644
index 000000000..f91978ef7
--- /dev/null
+++ b/net/802/pseudo/pseudocode
@@ -0,0 +1,780 @@
+;============================================================================
+;
+; translate this with
+; cat pseudocode | sed -f act2num | awk -f compile.awk >pseudocode.h
+;
+; actionname pseudocode
+;
+;============================================================================
+ADM1 SEND_SABME_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=0
+ S_FLAG:=0
+;
+; instructions in ADM2 have been changed:
+; 1. P_FLAG:=P is probably wrong in doc...
+; I think it should be F_FLAG:=P the way it is in CONN3
+; 2. CONNECT_RESPONSE has been wired in here,
+; CONN1 is no longer referenced
+;
+ADM2 F_FLAG:=P
+ SEND_UA_RSP(F=X)
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ P_FLAG:=0
+ REMOTE_BUSY:=0
+ CONNECT_INDICATION
+ADM3 SEND_DM_RSP(F=X)
+ADM4 SEND_DM_RSP(F=X)
+ADM5 NOP
+;============================================================================
+;CONN1 SEND_UA_RSP(F=X)
+; V(S):=0
+; V(R):=0
+; RETRY_COUNT:=0
+; P_FLAG:=0
+; REMOTE_BUSY:=0
+CONN2 SEND_DM_RSP(F=X)
+CONN3 F_FLAG:=P
+CONN4 DISCONNECT_INDICATION
+CONN5 NOP
+;============================================================================
+RESWAIT1 SEND_SABME_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=0
+RESWAIT2 SEND_UA_RSP(F=X)
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ P_FLAG:=0
+ REMOTE_BUSY:=0
+ RESET_CONFIRM
+RESWAIT3 SEND_DISC_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=0
+RESWAIT4 SEND_DM_RSP(F=X)
+RESWAIT5 DISCONNECT_INDICATION
+RESWAIT6 S_FLAG:=1
+ F_FLAG:=P
+RESWAIT7 SEND_DM_RSP(F=X)
+ DISCONNECT_INDICATION
+RESWAIT8 NOP
+;============================================================================
+RESCHK1 SEND_UA_RSP(F=X)
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ P_FLAG:=0
+ REMOTE_BUSY:=0
+RESCHK2 SEND_DM_RSP(F=X)
+RESCHK3 DISCONNECT_INDICATION
+RESCHK4 F_FLAG:=P
+RESCHK5 SEND_DM_RSP(F=X)
+ DISCONNECT_INDICATION
+RESCHK6 NOP
+;============================================================================
+SETUP1 SEND_UA_RSP(F=X)
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ S_FLAG:=1
+SETUP2 STOP_ACK_TIMER
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ UPDATE_P_FLAG
+ CONNECT_CONFIRM
+ REMOTE_BUSY:=0
+SETUP3 P_FLAG:=0
+ CONNECT_CONFIRM
+ REMOTE_BUSY:=0
+SETUP4 SEND_DM_RSP(F=X)
+ DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+SETUP5 DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+SETUP6 NOP
+SETUP7 SEND_SABME_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+SETUP8 DISCONNECT_INDICATION
+;============================================================================
+RESET1 SEND_UA_RSP(F=X)
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ S_FLAG:=1
+RESET2 STOP_ACK_TIMER
+ V(S):=0
+ V(R):=0
+ RETRY_COUNT:=0
+ UPDATE_P_FLAG
+ RESET_CONFIRM
+ REMOTE_BUSY:=0
+RESET3 P_FLAG:=0
+ RESET_CONFIRM
+ REMOTE_BUSY:=0
+RESET4 SEND_DM_RSP(F=X)
+ DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+RESET5 DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+RESET6 NOP
+RESET7 SEND_SABME_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+RESET8 DISCONNECT_INDICATION
+;============================================================================
+D_CONN1 SEND_DM_RSP(F=X)
+ STOP_ACK_TIMER
+D_CONN2 STOP_ACK_TIMER
+D_CONN3 SEND_UA_RSP(F=X)
+D_CONN4 STOP_ACK_TIMER
+D_CONN5 NOP
+D_CONN6 SEND_DISC_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+D_CONN7 NOP
+;============================================================================
+ERR1 RESET_INDICATION(REMOTE)
+ STOP_ACK_TIMER
+ F_FLAG:=P
+ERR2 SEND_UA_RSP(F=X)
+ DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+ERR3 DISCONNECT_INDICATION
+ STOP_ACK_TIMER
+ERR4 RESET_INDICATION(LOCAL)
+ STOP_ACK_TIMER
+ REPORT_STATUS(FRMR_RECEIVED)
+ S_FLAG:=0
+ERR5 RE-SEND_FRMR_RSP(F=P)
+ START_ACK_TIMER
+ERR6 NOP
+ERR7 RE-SEND_FRMR_RSP(F=0)
+ START_ACK_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+ERR8 S_FLAG:=0
+ RESET_INDICATION(LOCAL)
+;============================================================================
+; the shared actions are common to states NORMAL, BUSY, REJECT,
+; AWAIT, AWAIT_BUSY and AWAIT_REJECT.
+;============================================================================
+SH1 SEND_DISC_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+SH2 SEND_SABME_CMD(P=X)
+ P_FLAG:=P
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+ S_FLAG:=0
+SH3 RESET_INDICATION(REMOTE)
+ F_FLAG:=P
+ STOP_ALL_TIMERS
+SH4 SEND_UA_RSP(F=X)
+ DISCONNECT_INDICATION
+ STOP_ALL_TIMERS
+SH5 STOP_ALL_TIMERS
+ RESET_INDICATION(LOCAL)
+ REPORT_STATUS(FRMR_RECEIVED)
+ S_FLAG:=0
+SH6 DISCONNECT_INDICATION
+ STOP_ALL_TIMERS
+SH7 SEND_FRMR_RSP(F=X)
+ REPORT_STATUS(FRMR_SENT)
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+SH8 SEND_FRMR_RSP(F=0)
+ REPORT_STATUS(FRMR_SENT)
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+SH9 SEND_FRMR_RSP(F=0)
+ REPORT_STATUS(FRMR_SENT)
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+SH10 SEND_FRMR_RSP(F=X)
+ REPORT_STATUS(FRMR_SENT)
+ START_ACK_TIMER
+ STOP_OTHER_TIMERS
+ RETRY_COUNT:=0
+SH11 STOP_ALL_TIMERS
+ RESET_INDICATION(LOCAL)
+ S_FLAG:=0
+;============================================================================
+NORMAL1 SEND_I_CMD(P=1)
+ START_P_TIMER
+ START_ACK_TIMER_IF_NOT_RUNNING
+; SEND_I_XXX(X=0)
+; START_ACK_TIMER_IF_NOT_RUNNING
+NORMAL2 SEND_I_XXX(X=0)
+ START_ACK_TIMER_IF_NOT_RUNNING
+NORMAL3 SEND_RNR_CMD(F=1)
+ START_P_TIMER
+ DATA_FLAG:=0
+; SEND_RNR_XXX(X=0)
+; DATA_FLAG:=0
+NORMAL4 SEND_RNR_XXX(X=0)
+ DATA_FLAG:=0
+NORMAL5 SEND_REJ_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ UPDATE_P_FLAG
+ START_REJ_TIMER
+ IF_F=1_CLEAR_REMOTE_BUSY
+; SEND_REJ_CMD(P=1)
+; UPDATE_N(R)_RECEIVED
+; START_P_TIMER
+; START_REJ_TIMER
+; IF_F=1_CLEAR_REMOTE_BUSY
+NORMAL6 SEND_REJ_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ START_REJ_TIMER
+NORMAL7 SEND_REJ_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ START_REJ_TIMER
+;
+; the order of opcodes in NORMAL8 is changed.
+; the transition table will execute NORMAL8A for incomming pdus
+; with p/f 1, pdus with pf 0 are treated in NORMAL8B.
+;
+NORMAL8A V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_CMD(P=1)
+ START_P_TIMER
+ UPDATE_N(R)_RECEIVED
+ IF_F=1_CLEAR_REMOTE_BUSY
+ DATA_INDICATION
+;
+NORMAL8B V(R):=V(R)+1
+ UPDATE_P_FLAG
+ SEND_ACKNOWLEDGE_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ IF_F=1_CLEAR_REMOTE_BUSY
+ DATA_INDICATION
+;
+; the order of opcodes in NORMAL9 is changed
+NORMAL9 V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+;
+; the order of opcodes in NORMAL10 is changed
+NORMAL10 V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+NORMAL11 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+NORMAL12 SEND_ACKNOWLEDGE_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+NORMAL13 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+NORMAL14 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+NORMAL15 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ UPDATE_P_FLAG
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; V(S):=N(R)
+; UPDATE_N(R)_RECEIVED
+; START_P_TIMER
+; RE-SEND_I_CMD(P=1)
+; CLEAR_REMOTE_BUSY
+NORMAL16 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+NORMAL17 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ RE-SEND_I_RSP(F=1)
+ CLEAR_REMOTE_BUSY
+NORMAL18 SEND_RR_CMD(P=1)
+ START_P_TIMER
+NORMAL19 P_FLAG:=0
+; SEND_RR_CMD(P=1)
+; START_P_TIMER
+; RETRY_COUNT:=RETRY_COUNT+1
+NORMAL20 SEND_RR_CMD(P=1)
+ START_P_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+BUSY1 SEND_I_CMD(P=1)
+ START_P_TIMER
+ START_ACK_TIMER_IF_NOT_RUNNING
+; SEND_I_XXX(X=0)
+; START_ACK_TIMER_IF_NOT_RUNNING
+BUSY2 SEND_I_XXX(X=0)
+ START_ACK_TIMER_IF_NOT_RUNNING
+BUSY3 SEND_REJ_CMD(P=1)
+ START_REJ_TIMER
+ START_P_TIMER
+; SEND_REJ_XXX(X=0)
+; START_REJ_TIMER
+BUSY4 SEND_REJ_XXX(X=0)
+ START_REJ_TIMER
+BUSY5 SEND_RR_CMD(P=1)
+ START_P_TIMER
+ SEND_RR_XXX(X=0)
+BUSY6 SEND_RR_XXX(X=0)
+BUSY7 SEND_RR_CMD(P=1)
+ START_P_TIMER
+ SEND_RR_XXX(X=0)
+BUSY8 SEND_RR_XXX(X=0)
+BUSY9 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+ IF_F=1_CLEAR_REMOTE_BUSY
+; SEND_RNR_CMD(P=1)
+; START_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+; IF_F=1_CLEAR_REMOTE_BUSY
+BUSY10 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+BUSY11 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+BUSY12 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG=2_STOP_REJ_TIMER
+ DATA_FLAG:=1
+; V(R):=V(R)+1
+; DATA_INDICATION
+; SEND_RNR_RSP(F=1)
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG=2_STOP_REJ_TIMER
+; DATA_FLAG:=0
+BUSY13 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG=2_STOP_REJ_TIMER
+ DATA_FLAG:=1
+ IF_F=1_CLEAR_REMOTE_BUSY
+; SEND_RNR_CMD(F=1)
+; START_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG=2_STOP_REJ_TIMER
+; DATA_FLAG:=1
+; IF_F=1_CLEAR_REMOTE_BUSY
+; V(R):=V(R)+1
+; DATA_INDICATION
+; SEND_RNR_CMD(F=1)
+; START_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG=2_STOP_REJ_TIMER
+; DATA_FLAG:=0
+; IF_F=1_CLEAR_REMOTE_BUSY
+; V(R):=V(R)+1
+; DATA_INDICATION
+; UPDATE_P_FLAG
+; OPTIONAL_SEND_RNR_XXX(X=0)
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG=2_STOP_REJ_TIMER
+; DATA_FLAG:=0
+; IF_F=1_CLEAR_REMOTE_BUSY
+BUSY14 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ IF_DATA_FLAG=2_STOP_REJ_TIMER
+ DATA_FLAG:=1
+; V(R):=V(R)+1
+; DATA_INDICATION
+; OPTIONAL_SEND_RNR_XXX(X=0)
+; UPDATE_N(R)_RECEIVED
+; IF_DATA_FLAG=2_STOP_REJ_TIMER
+; DATA_FLAG:=0
+BUSY15 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+BUSY16 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+BUSY17 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+BUSY18 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+BUSY19 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ UPDATE_P_FLAG
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; V(S):=N(R)
+; UPDATE_N(R)_RECEIVED
+; RE-SEND_I_CMD(P=1)
+; CLEAR_REMOTE_BUSY
+BUSY20 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+BUSY21 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ SEND_RNR_RSP(F=1)
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+BUSY22 SEND_RNR_CMD(F=1)
+ START_P_TIMER
+BUSY23 P_FLAG:=0
+; SEND_RNR_CMD(F=1)
+; START_P_TIMER
+; RETRY_COUNT:=RETRY_COUNT+1
+BUSY24 SEND_RNR_CMD(F=1)
+ START_P_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+BUSY25 DATA_FLAG:=1
+; SEND_RNR_CMD(F=1)
+; START_P_TIMER
+; RETRY_COUNT:=RETRY_COUNT+1
+; DATA_FLAG:=1
+BUSY26 DATA_FLAG:=1
+;============================================================================
+REJECT1 SEND_I_CMD(P=1)
+ START_P_TIMER
+ START_ACK_TIMER_IF_NOT_RUNNING
+; SEND_I_XXX(X=0)
+; START_ACK_TIMER_IF_NOT_RUNNING
+REJECT2 SEND_I_XXX(X=0)
+ START_ACK_TIMER_IF_NOT_RUNNING
+REJECT3 SEND_RNR_CMD(F=1)
+ START_P_TIMER
+ DATA_FLAG:=2
+; SEND_RNR_XXX(X=0)
+; DATA_FLAG:=2
+REJECT4 SEND_RNR_XXX(X=0)
+ DATA_FLAG:=2
+REJECT5 UPDATE_N(R)_RECEIVED
+ UPDATE_P_FLAG
+ IF_F=1_CLEAR_REMOTE_BUSY
+REJECT6 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+;
+; order of opcodes in REJECT7 is changed
+REJECT7 V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_CMD(P=1)
+ START_P_TIMER
+ UPDATE_N(R)_RECEIVED
+ IF_F=1_CLEAR_REMOTE_BUSY
+ STOP_REJ_TIMER
+ DATA_INDICATION
+; V(R):=V(R)+1
+; DATA_INDICATION
+; UPDATE_P_FLAG
+; SEND_ACKNOWLEDGE_XXX(X=0)
+; UPDATE_N(R)_RECEIVED
+; IF_F=1_CLEAR_REMOTE_BUSY
+; STOP_REJ_TIMER
+;
+; order of opcodes in REJECT8 is changed
+REJECT8 V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ STOP_REJ_TIMER
+ DATA_INDICATION
+;
+; order of opcodes in REJECT9 is changed
+REJECT9 V(R):=V(R)+1
+ SEND_ACKNOWLEDGE_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ STOP_REJ_TIMER
+ DATA_INDICATION
+REJECT10 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+REJECT11 SEND_ACKNOWLEDGE_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+REJECT12 UPDATE_P_FLAG
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+REJECT13 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+REJECT14 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ UPDATE_P_FLAG
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; V(S):=N(R)
+; UPDATE_N(R)_RECEIVED
+; RE-SEND_I_CMD(P=1)
+; START_P_TIMER
+; CLEAR_REMOTE_BUSY
+REJECT15 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+REJECT16 V(S):=N(R)
+ UPDATE_N(R)_RECEIVED
+ RE-SEND_I_RSP(F=1)
+ CLEAR_REMOTE_BUSY
+REJECT17 SEND_RR_CMD(P=1)
+ START_P_TIMER
+REJECT18 SEND_REJ_CMD(P=1)
+ START_P_TIMER
+ START_REJ_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+REJECT19 P_FLAG:=0
+; SEND_RR_CMD(P=1)
+; START_P_TIMER
+; START_REJ_TIMER
+; RETRY_COUNT:=RETRY_COUNT+1
+REJECT20 SEND_RR_CMD(P=1)
+ START_P_TIMER
+ START_REJ_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT1 SEND_RNR_XXX(X=0)
+ DATA_FLAG:=0
+AWAIT2 SEND_REJ_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ RE-SEND_I_XXX(X=0)
+ START_REJ_TIMER
+ CLEAR_REMOTE_BUSY
+; SEND_REJ_CMD(P=1)
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_XXX(X=0)
+; START_P_TIMER
+; START_REJ_TIMER
+; CLEAR_REMOTE_BUSY
+AWAIT3 SEND_REJ_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ START_REJ_TIMER
+AWAIT4 SEND_REJ_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ START_REJ_TIMER
+;
+; order of opcode in AWAIT5 changed
+AWAIT5 V(R):=V(R)+1
+ UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ RE-SEND_I_CMD(P=1)_OR_SEND_RR
+ START_P_TIMER
+ CLEAR_REMOTE_BUSY
+ DATA_INDICATION
+; V(R):=V(R)+1
+; DATA_INDICATION
+; STOP_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_XXX(X=0)_OR_SEND_RR
+; CLEAR_REMOTE_BUSY
+;
+; order of opcode in AWAIT6 changed
+AWAIT6 V(R):=V(R)+1
+ SEND_RR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+;
+; order of opcode in AWAIT7 changed
+AWAIT7 V(R):=V(R)+1
+ SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+AWAIT8 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_CMD(P=1)
+; START_P_TIMER
+; CLEAR_REMOTE_BUSY
+AWAIT9 UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT10 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT11 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ SET_REMOTE_BUSY
+AWAIT12 UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT13 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT14 SEND_RR_CMD(P=1)
+ START_P_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT_BUSY1 SEND_REJ_XXX(X=0)
+ START_REJ_TIMER
+AWAIT_BUSY2 SEND_RR_XXX(X=0)
+AWAIT_BUSY3 SEND_RR_XXX(X=0)
+AWAIT_BUSY4 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ DATA_FLAG:=1
+ CLEAR_REMOTE_BUSY
+ RE-SEND_I_XXX(X=0)
+; SEND_RNR_CMD(F=1)
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; START_P_TIMER
+; DATA_FLAG:=1
+; CLEAR_REMOTE_BUSY
+; RE-SEND_I_XXX(X=0)
+AWAIT_BUSY5 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ DATA_FLAG:=1
+AWAIT_BUSY6 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ DATA_FLAG:=1
+AWAIT_BUSY7 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ DATA_FLAG:=1
+ STOP_P_TIMER
+ CLEAR_REMOTE_BUSY
+ RE-SEND_I_XXX(X=0)
+; SEND_RNR_CMD(F=1)
+; V(R):=V(R)+1
+; DATA_INDICATION
+; START_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; DATA_FLAG:=0
+; CLEAR_REMOTE_BUSY
+; RE-SEND_I_XXX(X=0)
+; OPTIONAL_SEND_RNR_XXX(X=0)
+; V(R):=V(R)+1
+; DATA_INDICATION
+; STOP_P_TIMER
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; DATA_FLAG:=0
+; CLEAR_REMOTE_BUSY
+; RE-SEND_I_XXX(X=0)
+AWAIT_BUSY8 OPTIONAL_SEND_RNR_XXX(X=0)
+ UPDATE_N(R)_RECEIVED
+ DATA_FLAG:=1
+; OPTIONAL_SEND_RNR_XXX(X=0)
+; V(R):=V(R)+1
+; DATA_INDICATION
+; UPDATE_N(R)_RECEIVED
+; DATA_FLAG:=0
+AWAIT_BUSY9 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ DATA_FLAG:=1
+; SEND_RNR_RSP(F=1)
+; V(R):=V(R)+1
+; DATA_INDICATION
+; UPDATE_N(R)_RECEIVED
+; DATA_FLAG:=0
+AWAIT_BUSY10 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_CMD(P=1)
+; START_P_TIMER
+; CLEAR_REMOTE_BUSY
+AWAIT_BUSY11 UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT_BUSY12 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT_BUSY13 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ SET_REMOTE_BUSY
+AWAIT_BUSY14 UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT_BUSY15 SEND_RNR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT_BUSY16 SEND_RNR_CMD(F=1)
+ START_P_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT_REJECT1 SEND_RNR_XXX(X=0)
+ DATA_FLAG:=2
+AWAIT_REJECT2 UPDATE_N(R)_RECEIVED
+AWAIT_REJECT3 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+;
+; order of opcodes in AWAIT_REJECT4 changed
+AWAIT_REJECT4 V(R):=V(R)+1
+ UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ RE-SEND_I_CMD(P=1)_OR_SEND_RR
+ START_P_TIMER
+ STOP_REJ_TIMER
+ CLEAR_REMOTE_BUSY
+ DATA_INDICATION
+; V(R):=V(R)+1
+; DATA_INDICATION
+; STOP_P_TIMER
+; STOP_REJ_TIMER
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_CMD(P=1)_OR_SEND_RR
+; CLEAR_REMOTE_BUSY
+;
+; order of opcodes in AWAIT_REJECT5 changed
+AWAIT_REJECT5 V(R):=V(R)+1
+ SEND_RR_XXX(X=0)
+ STOP_REJ_TIMER
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+;
+; order of opcodes in AWAIT_REJECT6 changed
+AWAIT_REJECT6 V(R):=V(R)+1
+ SEND_RR_RSP(F=1)
+ STOP_REJ_TIMER
+ UPDATE_N(R)_RECEIVED
+ DATA_INDICATION
+AWAIT_REJECT7 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ RE-SEND_I_XXX(X=0)
+ CLEAR_REMOTE_BUSY
+; UPDATE_N(R)_RECEIVED
+; V(S):=N(R)
+; RE-SEND_I_CMD(P=1)
+; START_P_TIMER
+; CLEAR_REMOTE_BUSY
+AWAIT_REJECT8 UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT_REJECT9 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ CLEAR_REMOTE_BUSY
+AWAIT_REJECT10 UPDATE_N(R)_RECEIVED
+ V(S):=N(R)
+ STOP_P_TIMER
+ SET_REMOTE_BUSY
+AWAIT_REJECT11 UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT_REJECT12 SEND_RR_RSP(F=1)
+ UPDATE_N(R)_RECEIVED
+ SET_REMOTE_BUSY
+AWAIT_REJECT13 SEND_REJ_CMD(P=1)
+ START_P_TIMER
+ RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+
diff --git a/net/802/pseudo/pseudocode.h b/net/802/pseudo/pseudocode.h
new file mode 100644
index 000000000..f32bfeb4d
--- /dev/null
+++ b/net/802/pseudo/pseudocode.h
@@ -0,0 +1,287 @@
+
+/* this file generated on Thu Oct 24 11:42:35 GMT 1996 */
+
+static char pseudo_code [ ] = {
+ 0, 37, 57, 42, 59, 39, 0, 65, 38,
+ 63, 61, 59, 56, 58, 2, 0, 12, 0,
+ 12, 0, 0, 0, 12, 0, 65, 0, 5,
+ 0, 0, 0, 37, 57, 42, 59, 0, 38,
+ 63, 61, 59, 56, 58, 7, 0, 11, 57,
+ 42, 59, 0, 12, 0, 5, 0, 40, 65,
+ 0, 12, 5, 0, 0, 0, 38, 63, 61,
+ 59, 56, 58, 0, 12, 0, 5, 0, 65,
+ 0, 12, 5, 0, 0, 0, 38, 63, 61,
+ 59, 40, 0, 45, 63, 61, 59, 51, 3,
+ 58, 0, 56, 3, 58, 0, 12, 5, 45,
+ 0, 5, 45, 0, 0, 0, 37, 57, 42,
+ 60, 0, 5, 0, 38, 63, 61, 59, 40,
+ 0, 45, 63, 61, 59, 51, 7, 58, 0,
+ 56, 7, 58, 0, 12, 5, 45, 0, 5,
+ 45, 0, 0, 0, 37, 57, 42, 60, 0,
+ 5, 0, 12, 45, 0, 45, 0, 38, 0,
+ 45, 0, 0, 0, 11, 57, 42, 60, 0,
+ 0, 0, 71, 45, 65, 0, 38, 5, 45,
+ 0, 5, 45, 0, 70, 45, 66, 39, 0,
+ 15, 42, 0, 0, 0, 14, 42, 60, 0,
+ 39, 70, 0, 11, 57, 42, 49, 59, 0,
+ 37, 57, 42, 49, 59, 39, 0, 71, 65,
+ 48, 0, 38, 5, 48, 0, 48, 70, 66,
+ 39, 0, 5, 48, 0, 13, 67, 42, 49,
+ 59, 0, 0, 67, 42, 49, 59, 0, 0,
+ 67, 42, 49, 59, 0, 13, 67, 42, 49,
+ 59, 0, 48, 70, 39, 0, 16, 41, 44,
+ 0, 19, 44, 0, 26, 41, 53, 0, 28,
+ 53, 0, 25, 50, 51, 43, 9, 0, 25,
+ 50, 43, 0, 24, 50, 43, 0, 62, 32,
+ 41, 50, 9, 4, 0, 62, 51, 36, 50,
+ 9, 4, 0, 62, 36, 50, 4, 0, 62,
+ 34, 50, 4, 0, 51, 50, 1, 0, 34,
+ 50, 1, 0, 51, 50, 29, 0, 33, 50,
+ 29, 0, 64, 50, 51, 20, 1, 0, 64,
+ 50, 20, 1, 0, 64, 50, 22, 1, 0,
+ 31, 41, 0, 56, 0, 31, 41, 60, 0,
+ 16, 41, 44, 0, 19, 44, 0, 23, 43,
+ 41, 0, 25, 43, 0, 31, 41, 35, 0,
+ 35, 0, 31, 41, 35, 0, 35, 0, 30,
+ 51, 50, 55, 9, 0, 30, 50, 55, 0,
+ 27, 50, 55, 0, 27, 50, 10, 54, 0,
+ 30, 51, 50, 10, 54, 9, 0, 30, 50,
+ 10, 54, 0, 51, 50, 1, 0, 27, 50,
+ 1, 0, 51, 50, 29, 0, 27, 50, 29,
+ 0, 64, 50, 51, 20, 1, 0, 64, 50,
+ 20, 1, 0, 64, 50, 27, 20, 1, 0,
+ 26, 41, 0, 56, 0, 26, 41, 60, 0,
+ 54, 0, 54, 0, 16, 41, 44, 0, 19,
+ 44, 0, 26, 41, 52, 0, 28, 52, 0,
+ 50, 51, 9, 0, 33, 50, 0, 62, 32,
+ 41, 50, 9, 47, 4, 0, 62, 36, 50,
+ 47, 4, 0, 62, 34, 50, 47, 4, 0,
+ 51, 50, 1, 0, 34, 50, 1, 0, 51,
+ 50, 29, 0, 33, 50, 29, 0, 64, 50,
+ 51, 20, 1, 0, 64, 50, 20, 1, 0,
+ 64, 50, 22, 1, 0, 31, 41, 0, 23,
+ 41, 43, 60, 0, 56, 0, 31, 41, 43,
+ 60, 0, 28, 53, 0, 25, 50, 64, 46,
+ 20, 43, 1, 0, 25, 50, 43, 0, 24,
+ 50, 43, 0, 62, 50, 64, 18, 41, 1,
+ 4, 0, 62, 35, 50, 4, 0, 62, 33,
+ 50, 4, 0, 50, 64, 46, 20, 1, 0,
+ 50, 1, 0, 33, 50, 1, 0, 50, 64,
+ 46, 29, 0, 50, 29, 0, 33, 50, 29,
+ 0, 31, 41, 60, 0, 25, 43, 0, 35,
+ 0, 35, 0, 30, 50, 64, 46, 54, 1,
+ 20, 0, 30, 50, 54, 0, 27, 50, 54,
+ 0, 30, 50, 64, 54, 46, 1, 20, 0,
+ 30, 50, 54, 0, 27, 50, 54, 0, 50,
+ 64, 46, 20, 1, 0, 50, 1, 0, 27,
+ 50, 1, 0, 50, 64, 46, 29, 0, 50,
+ 29, 0, 27, 50, 29, 0, 26, 41, 60,
+ 0, 28, 52, 0, 50, 0, 33, 50, 0,
+ 62, 50, 64, 18, 41, 47, 1, 4, 0,
+ 62, 35, 47, 50, 4, 0, 62, 33, 47,
+ 50, 4, 0, 50, 64, 46, 20, 1, 0,
+ 50, 1, 0, 33, 50, 1, 0, 50, 64,
+ 46, 29, 0, 50, 29, 0, 33, 50, 29,
+ 0, 23, 41, 60, 0
+};
+
+static short int pseudo_code_idx [ ] ={
+ 0, 1, 7, 16, 18, 20, 22, 24, 26,
+ 28, 30, 35, 43, 48, 50, 52, 55, 58,
+ 60, 67, 69, 71, 73, 76, 78, 84, 92,
+ 96, 100, 103, 105, 110, 112, 118, 126, 130,
+ 134, 137, 139, 144, 146, 149, 151, 153, 155,
+ 157, 162, 164, 168, 172, 175, 180, 183, 185,
+ 189, 192, 198, 205, 209, 213, 218, 221, 227,
+ 233, 239, 245, 249, 253, 256, 260, 263, 269,
+ 273, 277, 284, 291, 296, 301, 305, 309, 313,
+ 317, 323, 328, 333, 336, 338, 342, 346, 349,
+ 353, 356, 360, 362, 366, 368, 374, 378, 382,
+ 387, 394, 399, 403, 407, 411, 415, 421, 426,
+ 432, 435, 437, 441, 443, 445, 449, 452, 456,
+ 459, 463, 466, 474, 480, 486, 490, 494, 498,
+ 502, 508, 513, 518, 521, 526, 528, 533, 536,
+ 544, 548, 552, 560, 565, 570, 576, 579, 583,
+ 588, 591, 595, 599, 602, 604, 606, 614, 618,
+ 622, 630, 634, 638, 644, 647, 651, 656, 659,
+ 663, 667, 670, 672, 675, 684, 690, 696, 702,
+ 705, 709, 714, 717, 721, 0
+};
+
+#define NOP 0
+#define ADM1 1
+#define ADM2 2
+#define ADM3 3
+#define ADM4 4
+#define ADM5 5
+#define CONN2 6
+#define CONN3 7
+#define CONN4 8
+#define CONN5 9
+#define RESWAIT1 10
+#define RESWAIT2 11
+#define RESWAIT3 12
+#define RESWAIT4 13
+#define RESWAIT5 14
+#define RESWAIT6 15
+#define RESWAIT7 16
+#define RESWAIT8 17
+#define RESCHK1 18
+#define RESCHK2 19
+#define RESCHK3 20
+#define RESCHK4 21
+#define RESCHK5 22
+#define RESCHK6 23
+#define SETUP1 24
+#define SETUP2 25
+#define SETUP3 26
+#define SETUP4 27
+#define SETUP5 28
+#define SETUP6 29
+#define SETUP7 30
+#define SETUP8 31
+#define RESET1 32
+#define RESET2 33
+#define RESET3 34
+#define RESET4 35
+#define RESET5 36
+#define RESET6 37
+#define RESET7 38
+#define RESET8 39
+#define D_CONN1 40
+#define D_CONN2 41
+#define D_CONN3 42
+#define D_CONN4 43
+#define D_CONN5 44
+#define D_CONN6 45
+#define D_CONN7 46
+#define ERR1 47
+#define ERR2 48
+#define ERR3 49
+#define ERR4 50
+#define ERR5 51
+#define ERR6 52
+#define ERR7 53
+#define ERR8 54
+#define SH1 55
+#define SH2 56
+#define SH3 57
+#define SH4 58
+#define SH5 59
+#define SH6 60
+#define SH7 61
+#define SH8 62
+#define SH9 63
+#define SH10 64
+#define SH11 65
+#define NORMAL1 66
+#define NORMAL2 67
+#define NORMAL3 68
+#define NORMAL4 69
+#define NORMAL5 70
+#define NORMAL6 71
+#define NORMAL7 72
+#define NORMAL8A 73
+#define NORMAL8B 74
+#define NORMAL9 75
+#define NORMAL10 76
+#define NORMAL11 77
+#define NORMAL12 78
+#define NORMAL13 79
+#define NORMAL14 80
+#define NORMAL15 81
+#define NORMAL16 82
+#define NORMAL17 83
+#define NORMAL18 84
+#define NORMAL19 85
+#define NORMAL20 86
+#define BUSY1 87
+#define BUSY2 88
+#define BUSY3 89
+#define BUSY4 90
+#define BUSY5 91
+#define BUSY6 92
+#define BUSY7 93
+#define BUSY8 94
+#define BUSY9 95
+#define BUSY10 96
+#define BUSY11 97
+#define BUSY12 98
+#define BUSY13 99
+#define BUSY14 100
+#define BUSY15 101
+#define BUSY16 102
+#define BUSY17 103
+#define BUSY18 104
+#define BUSY19 105
+#define BUSY20 106
+#define BUSY21 107
+#define BUSY22 108
+#define BUSY23 109
+#define BUSY24 110
+#define BUSY25 111
+#define BUSY26 112
+#define REJECT1 113
+#define REJECT2 114
+#define REJECT3 115
+#define REJECT4 116
+#define REJECT5 117
+#define REJECT6 118
+#define REJECT7 119
+#define REJECT8 120
+#define REJECT9 121
+#define REJECT10 122
+#define REJECT11 123
+#define REJECT12 124
+#define REJECT13 125
+#define REJECT14 126
+#define REJECT15 127
+#define REJECT16 128
+#define REJECT17 129
+#define REJECT18 130
+#define REJECT19 131
+#define REJECT20 132
+#define AWAIT1 133
+#define AWAIT2 134
+#define AWAIT3 135
+#define AWAIT4 136
+#define AWAIT5 137
+#define AWAIT6 138
+#define AWAIT7 139
+#define AWAIT8 140
+#define AWAIT9 141
+#define AWAIT10 142
+#define AWAIT11 143
+#define AWAIT12 144
+#define AWAIT13 145
+#define AWAIT14 146
+#define AWAIT_BUSY1 147
+#define AWAIT_BUSY2 148
+#define AWAIT_BUSY3 149
+#define AWAIT_BUSY4 150
+#define AWAIT_BUSY5 151
+#define AWAIT_BUSY6 152
+#define AWAIT_BUSY7 153
+#define AWAIT_BUSY8 154
+#define AWAIT_BUSY9 155
+#define AWAIT_BUSY10 156
+#define AWAIT_BUSY11 157
+#define AWAIT_BUSY12 158
+#define AWAIT_BUSY13 159
+#define AWAIT_BUSY14 160
+#define AWAIT_BUSY15 161
+#define AWAIT_BUSY16 162
+#define AWAIT_REJECT1 163
+#define AWAIT_REJECT2 164
+#define AWAIT_REJECT3 165
+#define AWAIT_REJECT4 166
+#define AWAIT_REJECT5 167
+#define AWAIT_REJECT6 168
+#define AWAIT_REJECT7 169
+#define AWAIT_REJECT8 170
+#define AWAIT_REJECT9 171
+#define AWAIT_REJECT10 172
+#define AWAIT_REJECT11 173
+#define AWAIT_REJECT12 174
+#define AWAIT_REJECT13 175
+
diff --git a/net/802/psnap.c b/net/802/psnap.c
index 4f17352ab..bdcb5efd2 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -9,7 +9,8 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-
+
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
@@ -25,7 +26,7 @@ static struct datalink_proto *snap_dl = NULL; /* 802.2 DL for SNAP */
/*
* Find a snap client by matching the 5 bytes.
*/
-
+
static struct datalink_proto *find_snap_client(unsigned char *desc)
{
struct datalink_proto *proto;
@@ -37,27 +38,27 @@ static struct datalink_proto *find_snap_client(unsigned char *desc)
/*
* A SNAP packet has arrived
*/
-
+
int snap_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
- static struct packet_type psnap_packet_type =
+ static struct packet_type psnap_packet_type =
{
- 0,
+ 0,
NULL, /* All Devices */
snap_rcv,
NULL,
NULL,
};
-
+
struct datalink_proto *proto;
proto = find_snap_client(skb->h.raw);
- if (proto != NULL)
+ if (proto != NULL)
{
/*
* Pass the frame on.
*/
-
+
skb->h.raw += 5;
skb_pull(skb,5);
if (psnap_packet_type.type == 0)
@@ -72,7 +73,7 @@ int snap_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
/*
* Put a SNAP header on a frame and pass to 802.2
*/
-
+
static void snap_datalink_header(struct datalink_proto *dl, struct sk_buff *skb, unsigned char *dest_node)
{
memcpy(skb_push(skb,5),dl->type,5);
@@ -83,25 +84,20 @@ static void snap_datalink_header(struct datalink_proto *dl, struct sk_buff *skb,
* Set up the SNAP layer
*/
-static struct symbol_table snap_proto_syms = {
-#include <linux/symtab_begin.h>
- X(register_snap_client),
- X(unregister_snap_client),
-#include <linux/symtab_end.h>
-};
-
+EXPORT_SYMBOL(register_snap_client);
+EXPORT_SYMBOL(unregister_snap_client);
+
void snap_proto_init(struct net_proto *pro)
{
snap_dl=register_8022_client(0xAA, snap_rcv);
if(snap_dl==NULL)
printk("SNAP - unable to register with 802.2\n");
- register_symtab(&snap_proto_syms);
}
-
+
/*
* Register SNAP clients. We don't yet use this for IP or IPX.
*/
-
+
struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *))
{
struct datalink_proto *proto;
@@ -110,7 +106,7 @@ struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(
return NULL;
proto = (struct datalink_proto *) kmalloc(sizeof(*proto), GFP_ATOMIC);
- if (proto != NULL)
+ if (proto != NULL)
{
memcpy(proto->type, desc,5);
proto->type_len = 5;
@@ -152,4 +148,3 @@ void unregister_snap_client(unsigned char *desc)
restore_flags(flags);
}
-
diff --git a/net/802/sysctl_net_802.c b/net/802/sysctl_net_802.c
index 96f51588c..f97141d3c 100644
--- a/net/802/sysctl_net_802.c
+++ b/net/802/sysctl_net_802.c
@@ -1,13 +1,27 @@
/* -*- linux-c -*-
- * sysctl_net_802.c: sysctl interface to net 802 subsystem.
+ * sysctl_net_802.c: sysctl interface to net 802 subsystem.
*
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/802 directory entry (empty =) ). [MS]
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/802 directory entry (empty =) ). [MS]
+ *
+ * This program 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 of the License, or (at your option) any later version.
*/
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
ctl_table e802_table[] = {
{0}
};
+
+#ifdef CONFIG_TR
+extern int sysctl_tr_rif_timeout;
+ctl_table tr_table[] = {
+ {NET_TR_RIF_TIMEOUT, "rif_timeout", &sysctl_tr_rif_timeout, sizeof(int),
+ 0644, NULL, &proc_dointvec},
+};
+#endif
diff --git a/net/802/tr.c b/net/802/tr.c
index c12a66d83..e903924f7 100644
--- a/net/802/tr.c
+++ b/net/802/tr.c
@@ -1,5 +1,20 @@
+/*
+ * NET3: Token ring device handling subroutines
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ * Fixes: 3 Feb 97 Paul Norton <pnorton@cts.com> Minor routing fixes.
+ * Added rif table to /proc/net/tr_rif and rif timeout to
+ * /proc/sys/net/token-ring/rif_timeout.
+ *
+ */
+
#include <asm/uaccess.h>
#include <asm/system.h>
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
@@ -15,29 +30,56 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/net.h>
+#include <linux/proc_fs.h>
#include <net/arp.h>
-static void tr_source_route(struct trh_hdr *trh,struct device *dev);
-static void tr_add_rif_info(struct trh_hdr *trh);
+static void tr_source_route(struct trh_hdr *trh, struct device *dev);
+static void tr_add_rif_info(struct trh_hdr *trh, struct device *dev);
static void rif_check_expire(unsigned long dummy);
+#define TR_SR_DEBUG 0
+
typedef struct rif_cache_s *rif_cache;
+/*
+ * Each RIF entry we learn is kept this way
+ */
+
struct rif_cache_s {
- unsigned char addr[TR_ALEN];
- unsigned short rcf;
- unsigned short rseg[8];
- rif_cache next;
- unsigned long last_used;
+ unsigned char addr[TR_ALEN];
+ unsigned char iface[5];
+ __u16 rcf;
+ __u8 rseg[8];
+ rif_cache next;
+ unsigned long last_used;
+ unsigned char local_ring;
};
-#define RIF_TABLE_SIZE 16
+#define RIF_TABLE_SIZE 32
+
+/*
+ * We hash the RIF cache 32 ways. We do after all have to look it
+ * up a lot.
+ */
+
rif_cache rif_table[RIF_TABLE_SIZE]={ NULL, };
#define RIF_TIMEOUT 60*10*HZ
#define RIF_CHECK_INTERVAL 60*HZ
-static struct timer_list rif_timer={ NULL,NULL,RIF_CHECK_INTERVAL,0L,rif_check_expire };
+/*
+ * Garbage disposal timer.
+ */
+
+static struct timer_list rif_timer;
+
+int sysctl_tr_rif_timeout = RIF_TIMEOUT;
+
+/*
+ * Put the headers on a token ring packet. Token ring source routing
+ * makes this a little more exciting than on ethernet.
+ */
+
int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
void *daddr, void *saddr, unsigned len)
{
@@ -53,13 +95,23 @@ int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
else
memset(trh->saddr,0,dev->addr_len); /* Adapter fills in address */
+ /*
+ * This is the stuff needed for IP encoding - IP over 802.2
+ * with SNAP.
+ */
+
trllc->dsap=trllc->ssap=EXTENDED_SAP;
trllc->llc=UI_CMD;
trllc->protid[0]=trllc->protid[1]=trllc->protid[2]=0x00;
trllc->ethertype=htons(type);
- if(daddr) {
+ /*
+ * Build the destination and then source route the frame
+ */
+
+ if(daddr)
+ {
memcpy(trh->daddr,daddr,dev->addr_len);
tr_source_route(trh,dev);
return(dev->hard_header_len);
@@ -68,27 +120,44 @@ int tr_header(struct sk_buff *skb, struct device *dev, unsigned short type,
}
-int tr_rebuild_header(void *buff, struct device *dev, unsigned long dest,
- struct sk_buff *skb) {
-
- struct trh_hdr *trh=(struct trh_hdr *)buff;
- struct trllc *trllc=(struct trllc *)(buff+sizeof(struct trh_hdr));
+/*
+ * A neighbour discovery of some species (eg arp) has completed. We
+ * can now send the packet.
+ */
+
+int tr_rebuild_header(struct sk_buff *skb)
+{
+ struct trh_hdr *trh=(struct trh_hdr *)skb->data;
+ struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
+ struct device *dev = skb->dev;
+ /*
+ * FIXME: We don't yet support IPv6 over token rings
+ */
+
if(trllc->ethertype != htons(ETH_P_IP)) {
- printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons( trllc->ethertype));
+ printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(trllc->ethertype));
return 0;
}
- if(arp_find(trh->daddr, dest, dev, dev->pa_addr, skb)) {
+ if(arp_find(trh->daddr, skb)) {
return 1;
}
- else {
+ else
+ {
tr_source_route(trh,dev);
return 0;
}
}
-unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) {
+/*
+ * Some of this is a bit hackish. We intercept RIF information
+ * used for source routing. We also grab IP directly and don't feed
+ * it via SNAP.
+ */
+
+unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev)
+{
struct trh_hdr *trh=(struct trh_hdr *)skb->data;
struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
@@ -97,8 +166,7 @@ unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) {
skb_pull(skb,dev->hard_header_len);
- if(trh->saddr[0] & TR_RII)
- tr_add_rif_info(trh);
+ tr_add_rif_info(trh, dev);
if(*trh->daddr & 1)
{
@@ -117,174 +185,296 @@ unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev) {
return trllc->ethertype;
}
-/* We try to do source routing... */
+/*
+ * We try to do source routing...
+ */
-static void tr_source_route(struct trh_hdr *trh,struct device *dev) {
+static void tr_source_route(struct trh_hdr *trh,struct device *dev)
+{
int i;
unsigned int hash;
rif_cache entry;
- /* Broadcasts are single route as stated in RFC 1042 */
- if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN)) {
+ /*
+ * Broadcasts are single route as stated in RFC 1042
+ */
+ if(!memcmp(&(trh->daddr[0]),&(dev->broadcast[0]),TR_ALEN))
+ {
trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK)
| TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
trh->saddr[0]|=TR_RII;
}
- else {
+ else
+ {
for(i=0,hash=0;i<TR_ALEN;hash+=trh->daddr[i++]);
hash&=RIF_TABLE_SIZE-1;
+ /*
+ * Walk the hash table and look for an entry
+ */
for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->daddr[0]),TR_ALEN);entry=entry->next);
- if(entry) {
-#if 0
+ /*
+ * If we found an entry we can route the frame.
+ */
+ if(entry)
+ {
+#if TR_SR_DEBUG
printk("source routing for %02X %02X %02X %02X %02X %02X\n",trh->daddr[0],
trh->daddr[1],trh->daddr[2],trh->daddr[3],trh->daddr[4],trh->daddr[5]);
#endif
- if((ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8) {
+ if(!entry->local_ring && (ntohs(entry->rcf) & TR_RCF_LEN_MASK) >> 8)
+ {
trh->rcf=entry->rcf;
memcpy(&trh->rseg[0],&entry->rseg[0],8*sizeof(unsigned short));
trh->rcf^=htons(TR_RCF_DIR_BIT);
trh->rcf&=htons(0x1fff); /* Issam Chehab <ichehab@madge1.demon.co.uk> */
trh->saddr[0]|=TR_RII;
- entry->last_used=jiffies;
+#if TR_SR_DEBUG
+ printk("entry found with rcf %04x\n", entry->rcf);
+ }
+ else
+ {
+ printk("entry found but without rcf length, local=%02x\n", entry->local_ring);
+#endif
}
+ entry->last_used=jiffies;
}
- else {
+ else
+ {
+ /*
+ * Without the information we simply have to shout
+ * on the wire. The replies should rapidly clean this
+ * situation up.
+ */
trh->rcf=htons((((sizeof(trh->rcf)) << 8) & TR_RCF_LEN_MASK)
| TR_RCF_FRAME2K | TR_RCF_LIMITED_BROADCAST);
trh->saddr[0]|=TR_RII;
+ printk("no entry in rif table found - broadcasting frame\n");
}
}
-
}
-static void tr_add_rif_info(struct trh_hdr *trh) {
-
+/*
+ * We have learned some new RIF information for our source
+ * routing.
+ */
+
+static void tr_add_rif_info(struct trh_hdr *trh, struct device *dev)
+{
int i;
- unsigned int hash;
+ unsigned int hash, rii_p = 0;
rif_cache entry;
+ /*
+ * Firstly see if the entry exists
+ */
+
+ if(trh->saddr[0] & TR_RII)
+ {
+ trh->saddr[0]&=0x7f;
+ if (((ntohs(trh->rcf) & TR_RCF_LEN_MASK) >> 8) > 2)
+ {
+ rii_p = 1;
+ }
+ }
- trh->saddr[0]&=0x7f;
for(i=0,hash=0;i<TR_ALEN;hash+=trh->saddr[i++]);
hash&=RIF_TABLE_SIZE-1;
-#if 0
- printk("hash: %d\n",hash);
-#endif
for(entry=rif_table[hash];entry && memcmp(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);entry=entry->next);
- if(entry==NULL) {
-#if 0
+ if(entry==NULL)
+ {
+#if TR_SR_DEBUG
printk("adding rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n",
trh->saddr[0],trh->saddr[1],trh->saddr[2],
trh->saddr[3],trh->saddr[4],trh->saddr[5],
- trh->rcf);
+ ntohs(trh->rcf));
#endif
+ /*
+ * Allocate our new entry. A failure to allocate loses
+ * use the information. This is harmless.
+ *
+ * FIXME: We ought to keep some kind of cache size
+ * limiting and adjust the timers to suit.
+ */
entry=kmalloc(sizeof(struct rif_cache_s),GFP_ATOMIC);
- if(!entry) {
- printk("tr.c: Couldn't malloc rif cache entry !\n");
+
+ if(!entry)
+ {
+ printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
return;
}
- entry->rcf=trh->rcf;
- memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
+
+ if (rii_p)
+ {
+ entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK);
+ memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
+ entry->local_ring = 0;
+ }
+ else
+ {
+ entry->local_ring = 1;
+ }
+
memcpy(&(entry->addr[0]),&(trh->saddr[0]),TR_ALEN);
+ memcpy(&(entry->iface[0]),dev->name,5);
entry->next=rif_table[hash];
entry->last_used=jiffies;
rif_table[hash]=entry;
- }
-/* Y. Tahara added */
- else {
- if ( entry->rcf != trh->rcf ) {
- if (!(trh->rcf & htons(TR_RCF_BROADCAST_MASK))) {
-#if 0
+ }
+ else /* Y. Tahara added */
+ {
+ /*
+ * Update existing entries
+ */
+ if (!entry->local_ring)
+ if (entry->rcf != (trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK)) &&
+ !(trh->rcf & htons(TR_RCF_BROADCAST_MASK)))
+ {
+#if TR_SR_DEBUG
printk("updating rif_entry: addr:%02X:%02X:%02X:%02X:%02X:%02X rcf:%04X\n",
trh->saddr[0],trh->saddr[1],trh->saddr[2],
trh->saddr[3],trh->saddr[4],trh->saddr[5],
- trh->rcf);
+ ntohs(trh->rcf));
#endif
- entry->rcf = trh->rcf;
+ entry->rcf = trh->rcf & htons((unsigned short)~TR_RCF_BROADCAST_MASK);
memcpy(&(entry->rseg[0]),&(trh->rseg[0]),8*sizeof(unsigned short));
- entry->last_used=jiffies;
- }
- }
+ }
+ entry->last_used=jiffies;
}
-
}
-static void rif_check_expire(unsigned long dummy) {
+/*
+ * Scan the cache with a timer and see what we need to throw out.
+ */
+static void rif_check_expire(unsigned long dummy)
+{
int i;
unsigned long now=jiffies,flags;
save_flags(flags);
cli();
- for(i=0; i < RIF_TABLE_SIZE;i++) {
-
- rif_cache entry, *pentry=rif_table+i;
-
+ for(i=0; i < RIF_TABLE_SIZE;i++)
+ {
+ rif_cache entry, *pentry=rif_table+i;
while((entry=*pentry))
- if((now-entry->last_used) > RIF_TIMEOUT) {
+ {
+ /*
+ * Out it goes
+ */
+ if((now-entry->last_used) > sysctl_tr_rif_timeout)
+ {
*pentry=entry->next;
kfree_s(entry,sizeof(struct rif_cache_s));
}
else
- pentry=&entry->next;
+ pentry=&entry->next;
+ }
}
restore_flags(flags);
+ /*
+ * Reset the timer
+ */
+
del_timer(&rif_timer);
- rif_timer.expires=jiffies+RIF_CHECK_INTERVAL;
+ rif_timer.expires = jiffies + sysctl_tr_rif_timeout;
add_timer(&rif_timer);
}
-int rif_get_info(char *buffer,char **start, off_t offset, int length) {
-
- int len=0;
- off_t begin=0;
- off_t pos=0;
- int size,i;
+/*
+ * Generate the /proc/net information for the token ring RIF
+ * routing.
+ */
+
+#ifdef CONFIG_PROC_FS
+int rif_get_info(char *buffer,char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size,i,j,rcf_len;
+ unsigned long now=jiffies;
- rif_cache entry;
+ rif_cache entry;
size=sprintf(buffer,
-" TR address rcf routing segments TTL\n\n");
- pos+=size;
- len+=size;
+ "if TR address TTL rcf routing segments\n\n");
+ pos+=size;
+ len+=size;
- for(i=0;i < RIF_TABLE_SIZE;i++) {
+ for(i=0;i < RIF_TABLE_SIZE;i++)
+ {
for(entry=rif_table[i];entry;entry=entry->next) {
- size=sprintf(buffer+len,"%02X:%02X:%02X:%02X:%02X:%02X %04X %04X %04X %04X %04X %04X %04X %04X %04X %lu\n",
- entry->addr[0],entry->addr[1],entry->addr[2],entry->addr[3],entry->addr[4],entry->addr[5],
- entry->rcf,entry->rseg[0],entry->rseg[1],entry->rseg[2],entry->rseg[3],
- entry->rseg[4],entry->rseg[5],entry->rseg[6],entry->rseg[7],jiffies-entry->last_used);
+ size=sprintf(buffer+len,"%s %02X:%02X:%02X:%02X:%02X:%02X %7li ",
+ entry->iface,entry->addr[0],entry->addr[1],entry->addr[2],entry->addr[3],entry->addr[4],entry->addr[5],
+ sysctl_tr_rif_timeout-(now-entry->last_used));
+ len+=size;
+ pos=begin+len;
+ if (entry->local_ring)
+ size=sprintf(buffer+len,"local\n");
+ else {
+ size=sprintf(buffer+len,"%04X", ntohs(entry->rcf));
+ rcf_len = ((ntohs(entry->rcf) & TR_RCF_LEN_MASK)>>8)-2;
+ if (rcf_len)
+ rcf_len >>= 1;
+ for(j = 0; j < rcf_len; j++) {
+ len+=size;
+ pos=begin+len;
+ size=sprintf(buffer+len," %04X",ntohs(entry->rseg[j]));
+ }
+ len+=size;
+ pos=begin+len;
+ size=sprintf(buffer+len,"\n");
+ }
len+=size;
pos=begin+len;
- if(pos<offset) {
+ if(pos<offset)
+ {
len=0;
begin=pos;
}
if(pos>offset+length)
break;
- }
+ }
if(pos>offset+length)
break;
}
- *start=buffer+(offset-begin); /* Start of wanted data */
- len-=(offset-begin); /* Start slop */
- if(len>length)
- len=length; /* Ending slop */
- return len;
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len=length; /* Ending slop */
+ return len;
}
+#endif
-void rif_init(struct net_proto *unused) {
+/*
+ * Called during bootup. We don't actually have to initialise
+ * too much for this.
+ */
+
+void rif_init(struct net_proto *unused)
+{
+ rif_timer.expires = RIF_TIMEOUT;
+ rif_timer.data = 0L;
+ rif_timer.function = rif_check_expire;
+ init_timer(&rif_timer);
add_timer(&rif_timer);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_TR_RIF, 6, "tr_rif",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rif_get_info
+ });
+#endif
}
-
diff --git a/net/802/transit/Makefile b/net/802/transit/Makefile
new file mode 100644
index 000000000..d2e2e5de4
--- /dev/null
+++ b/net/802/transit/Makefile
@@ -0,0 +1,13 @@
+all: pdutr.h timertr.h
+
+pdutr.h: pdutr.pre compile.awk
+ awk -f ./compile.awk pdutr.pre > pdutr.h
+
+timertr.h: timertr.pre compile.awk
+ awk -f ./compile.awk timertr.pre > timertr.h
+
+clean:
+ touch pdutr.h timertr.h
+ rm pdutr.h timertr.h
+
+
diff --git a/net/802/transit/compile.awk b/net/802/transit/compile.awk
new file mode 100644
index 000000000..1b3b56c37
--- /dev/null
+++ b/net/802/transit/compile.awk
@@ -0,0 +1,81 @@
+# to run: awk -f transit.awk transit.p0
+#
+BEGIN { "date" | getline
+ enable_index = 1
+ today = $0
+ printf("\n/* this file was generated on %s */\n", today )
+ not_firstone = 0 # flag to avoid empty entry in 1st table
+ fpe = 0 # entry tbl array fill pointer
+ fpeo = 0 # entry tbl offset list fill pointer
+ fpdef = 0 # define list fill pointer
+}
+
+### /^;/ { } # line starting with a semicolon is comment
+
+/^[A-Z]/ { # table name
+ if ( $1 == "TABLE" ) {
+ tbl = $2 # get table name
+ newtbl( tbl )
+ }
+ else if ( $1 == "COMPILE" ) {
+ array_name = $2
+ if ( $3 == "NOINDEX" ) { enable_index = 0 }
+ }
+ else { # table entry
+ ec = ec +1
+ n = split( $0, fld, " " )
+ action = fld[ n-1 ]
+ newstate = fld[ n ]
+ store( action, newstate )
+ ecct = ecct +1
+ }
+}
+
+END { store( action, newstate )
+
+ if ( enable_index ) {
+ printf( "\n/* index name #defines: */\n\n",
+ ec, ecct )
+
+ for( ii = 1; ii <= fpeo; ii++ ){
+ printf( "#define %-12s %3d\n", define[ ii ], ii -1 )
+ }
+ }
+
+ printf( "\n\n/* size of transition table is %d bytes */\n",
+ fpe )
+
+ if ( enable_index ) {
+ printf( "\nstatic short int %s_offset [ ] ={", array_name )
+ for( ii = 1; ii <= fpeo; ii++ ){
+ if ( (ii % 10) == 1 ) printf("\n ")
+ printf( " %4d", entry_offset[ ii ] )
+ if ( ii < fpeo ) printf( "," )
+ }
+ printf(" };\n")
+ }
+
+ printf( "\nstatic char %s_entry [ ] = {", array_name )
+ for( ii = 1; ii <= fpe; ii++ ){
+ if ( (ii % 6) == 1 ) printf("\n ")
+ printf( " %-14s", entry[ ii ] )
+ if ( ii < fpe ) printf( "," )
+ }
+ printf(" };\n")
+
+}
+
+function store( act, ns ){
+# printf( "%s %s\n", act, ns )
+ entry[ ++fpe ] = act
+ entry[ ++fpe ] = ns
+}
+
+function newtbl( tbl ){
+ if ( not_firstone ) {
+ store( action, newstate )
+ }
+ not_firstone = 1
+ entry_offset[ ++fpeo ] = fpe # entry tbl offset list
+ define[ ++fpdef ] = tbl # state name to define
+}
diff --git a/net/802/transit/pdutr.h b/net/802/transit/pdutr.h
new file mode 100644
index 000000000..55a65001d
--- /dev/null
+++ b/net/802/transit/pdutr.h
@@ -0,0 +1,309 @@
+
+/* this file was generated on Thu Dec 5 13:58:11 GMT 1996 */
+
+/* index name #defines: */
+
+#define ADM 0
+#define CONN 1
+#define RESET_WAIT 2
+#define RESET_CHECK 3
+#define SETUP 4
+#define RESET 5
+#define D_CONN 6
+#define ERROR 7
+#define NORMAL 8
+#define BUSY 9
+#define REJECT 10
+#define AWAIT 11
+#define AWAIT_BUSY 12
+#define AWAIT_REJECT 13
+
+
+/* size of transition table is 1684 bytes */
+
+static short int pdutr_offset [ ] ={
+ 0, 54, 82, 110, 138, 192, 246, 300, 328, 554,
+ 780, 1006, 1232, 1458 };
+
+static char pdutr_entry [ ] = {
+ ADM5 , ADM , ADM4 , ADM , ADM5 , ADM ,
+ ADM4 , ADM , ADM5 , ADM , ADM4 , ADM ,
+ ADM5 , ADM , ADM4 , ADM , ADM3 , ADM ,
+ ADM3 , ADM , ADM2 , CONN , ADM2 , CONN ,
+ ADM5 , ADM , ADM5 , ADM , ADM5 , ADM ,
+ ADM5 , ADM , ADM5 , ADM , ADM5 , ADM ,
+ ADM5 , ADM , ADM5 , ADM , ADM5 , ADM ,
+ ADM5 , ADM , ADM5 , ADM , ADM5 , ADM ,
+ ADM5 , ADM , ADM5 , ADM , ADM5 , ADM ,
+ CONN5 , CONN , CONN5 , CONN , CONN5 , CONN ,
+ CONN5 , CONN , CONN5 , CONN , CONN3 , CONN ,
+ CONN5 , CONN , CONN5 , CONN , CONN5 , CONN ,
+ CONN5 , CONN , CONN5 , CONN , CONN4 , ADM ,
+ CONN5 , CONN , CONN5 , CONN , RESWAIT8 , RESET_WAIT ,
+ RESWAIT8 , RESET_WAIT , RESWAIT8 , RESET_WAIT , RESWAIT8 , RESET_WAIT ,
+ RESWAIT7 , RESET_WAIT , RESWAIT6 , RESET_WAIT , RESWAIT8 , RESET_WAIT ,
+ RESWAIT8 , RESET_WAIT , RESWAIT8 , RESET_WAIT , RESWAIT8 , RESET_WAIT ,
+ RESWAIT8 , RESET_WAIT , RESWAIT5 , ADM , RESWAIT8 , RESET_WAIT ,
+ RESWAIT8 , RESET_WAIT , RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK ,
+ RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK , RESCHK5 , ADM ,
+ RESCHK4 , RESET_CHECK , RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK ,
+ RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK ,
+ RESCHK3 , ADM , RESCHK6 , RESET_CHECK , RESCHK6 , RESET_CHECK ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP4 , ADM ,
+ SETUP4 , ADM , SETUP1 , SETUP , SETUP1 , SETUP ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ SETUP2 , NORMAL , SETUP5 , ADM , SETUP5 , ADM ,
+ SETUP6 , SETUP , SETUP6 , SETUP , SETUP6 , SETUP ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ RESET6 , RESET , RESET6 , RESET , RESET4 , ADM ,
+ RESET4 , ADM , RESET1 , RESET , RESET1 , RESET ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ RESET2 , NORMAL , RESET5 , ADM , RESET5 , ADM ,
+ RESET6 , RESET , RESET6 , RESET , RESET6 , RESET ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN3 , D_CONN ,
+ D_CONN3 , D_CONN , D_CONN1 , ADM , D_CONN1 , ADM ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ D_CONN4 , ADM , D_CONN4 , ADM , D_CONN5 , ADM ,
+ D_CONN5 , D_CONN , D_CONN5 , D_CONN , D_CONN5 , D_CONN ,
+ ERR5 , ERROR , ERR5 , ERROR , ERR5 , ERROR ,
+ ERR5 , ERROR , ERR2 , ADM , ERR1 , RESET_CHECK ,
+ ERR6 , ERROR , ERR6 , ERROR , ERR6 , ERROR ,
+ ERR6 , ERROR , ERR6 , ERROR , ERR3 , ADM ,
+ ERR4 , RESET_WAIT , ERR4 , RESET_WAIT , NORMAL8B , NORMAL ,
+ NORMAL9 , NORMAL , NORMAL10 , NORMAL , NORMAL10 , NORMAL ,
+ NORMAL5 , REJECT , NORMAL6 , REJECT , NORMAL7 , REJECT ,
+ NORMAL7 , REJECT , NORMAL11 , NORMAL , NORMAL11 , NORMAL ,
+ NORMAL12 , NORMAL , NORMAL12 , NORMAL , NORMAL11 , NORMAL ,
+ NORMAL11 , NORMAL , NORMAL12 , NORMAL , NORMAL12 , NORMAL ,
+ NORMAL13 , NORMAL , NORMAL13 , NORMAL , NORMAL14 , NORMAL ,
+ NORMAL14 , NORMAL , NORMAL13 , NORMAL , NORMAL13 , NORMAL ,
+ NORMAL14 , NORMAL , NORMAL14 , NORMAL , NORMAL15 , NORMAL ,
+ NORMAL16 , NORMAL , NORMAL17 , NORMAL , NORMAL17 , NORMAL ,
+ NORMAL15 , NORMAL , NORMAL16 , NORMAL , NORMAL17 , NORMAL ,
+ NORMAL17 , NORMAL , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , NORMAL8B , NORMAL ,
+ NORMAL9 , NORMAL , SH10 , ERROR , NORMAL8A , NORMAL ,
+ NORMAL5 , REJECT , NORMAL6 , REJECT , SH10 , ERROR ,
+ NORMAL5 , REJECT , NORMAL11 , NORMAL , NORMAL11 , NORMAL ,
+ SH10 , ERROR , NORMAL11 , NORMAL , NORMAL11 , NORMAL ,
+ NORMAL11 , NORMAL , SH10 , ERROR , NORMAL11 , NORMAL ,
+ NORMAL13 , NORMAL , NORMAL13 , NORMAL , SH10 , ERROR ,
+ NORMAL13 , NORMAL , NORMAL13 , NORMAL , NORMAL13 , NORMAL ,
+ SH10 , ERROR , NORMAL13 , NORMAL , NORMAL15 , NORMAL ,
+ NORMAL16 , NORMAL , SH10 , ERROR , NORMAL15 , NORMAL ,
+ NORMAL15 , NORMAL , NORMAL16 , NORMAL , SH10 , ERROR ,
+ NORMAL15 , NORMAL , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , BUSY13 , BUSY , BUSY14 , BUSY ,
+ BUSY12 , BUSY , BUSY12 , BUSY , BUSY9 , BUSY ,
+ BUSY10 , BUSY , BUSY11 , BUSY , BUSY11 , BUSY ,
+ BUSY15 , BUSY , BUSY15 , BUSY , BUSY16 , BUSY ,
+ BUSY16 , BUSY , BUSY15 , BUSY , BUSY15 , BUSY ,
+ BUSY16 , BUSY , BUSY16 , BUSY , BUSY17 , BUSY ,
+ BUSY17 , BUSY , BUSY18 , BUSY , BUSY18 , BUSY ,
+ BUSY17 , BUSY , BUSY17 , BUSY , BUSY18 , BUSY ,
+ BUSY18 , BUSY , BUSY19 , BUSY , BUSY20 , BUSY ,
+ BUSY21 , BUSY , BUSY21 , BUSY , BUSY19 , BUSY ,
+ BUSY20 , BUSY , BUSY21 , BUSY , BUSY21 , BUSY ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , BUSY13 , BUSY , BUSY14 , BUSY ,
+ SH10 , ERROR , BUSY13 , BUSY , BUSY9 , BUSY ,
+ BUSY10 , BUSY , SH10 , ERROR , BUSY9 , BUSY ,
+ BUSY15 , BUSY , BUSY15 , BUSY , SH10 , ERROR ,
+ BUSY15 , BUSY , BUSY15 , BUSY , BUSY15 , BUSY ,
+ SH10 , ERROR , BUSY15 , BUSY , BUSY17 , BUSY ,
+ BUSY17 , BUSY , SH10 , ERROR , BUSY17 , BUSY ,
+ BUSY17 , BUSY , BUSY17 , BUSY , SH10 , ERROR ,
+ BUSY17 , BUSY , BUSY19 , BUSY , BUSY20 , BUSY ,
+ SH10 , ERROR , BUSY19 , BUSY , BUSY19 , BUSY ,
+ BUSY20 , BUSY , SH10 , ERROR , BUSY19 , BUSY ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ REJECT7 , REJECT , REJECT8 , REJECT , REJECT9 , REJECT ,
+ REJECT9 , REJECT , REJECT5 , REJECT , REJECT5 , REJECT ,
+ REJECT6 , REJECT , REJECT6 , REJECT , REJECT10 , REJECT ,
+ REJECT10 , REJECT , REJECT11 , REJECT , REJECT11 , REJECT ,
+ REJECT10 , REJECT , REJECT10 , REJECT , REJECT11 , REJECT ,
+ REJECT11 , REJECT , REJECT12 , REJECT , REJECT12 , REJECT ,
+ REJECT13 , REJECT , REJECT13 , REJECT , REJECT12 , REJECT ,
+ REJECT12 , REJECT , REJECT13 , REJECT , REJECT13 , REJECT ,
+ REJECT14 , REJECT , REJECT15 , REJECT , REJECT16 , REJECT ,
+ REJECT16 , REJECT , REJECT14 , REJECT , REJECT15 , REJECT ,
+ REJECT16 , REJECT , REJECT16 , REJECT , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ REJECT7 , REJECT , REJECT8 , REJECT , SH10 , ERROR ,
+ REJECT7 , REJECT , REJECT5 , REJECT , REJECT5 , REJECT ,
+ SH10 , ERROR , REJECT5 , REJECT , REJECT10 , REJECT ,
+ REJECT10 , REJECT , SH10 , ERROR , REJECT10 , REJECT ,
+ REJECT10 , REJECT , REJECT10 , REJECT , SH10 , ERROR ,
+ REJECT10 , REJECT , REJECT12 , REJECT , REJECT12 , REJECT ,
+ SH10 , ERROR , REJECT12 , REJECT , REJECT12 , REJECT ,
+ REJECT12 , REJECT , SH10 , ERROR , REJECT12 , REJECT ,
+ REJECT14 , REJECT , REJECT15 , REJECT , SH10 , ERROR ,
+ REJECT14 , REJECT , REJECT14 , REJECT , REJECT15 , REJECT ,
+ SH10 , ERROR , REJECT14 , REJECT , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , AWAIT6 , AWAIT ,
+ AWAIT6 , AWAIT , AWAIT7 , AWAIT , AWAIT7 , AWAIT ,
+ AWAIT3 , AWAIT_REJECT , AWAIT3 , AWAIT_REJECT , AWAIT4 , AWAIT_REJECT ,
+ AWAIT4 , AWAIT_REJECT , AWAIT9 , AWAIT , AWAIT9 , AWAIT ,
+ AWAIT10 , AWAIT , AWAIT10 , AWAIT , AWAIT9 , AWAIT ,
+ AWAIT9 , AWAIT , AWAIT10 , AWAIT , AWAIT10 , AWAIT ,
+ AWAIT12 , AWAIT , AWAIT12 , AWAIT , AWAIT13 , AWAIT ,
+ AWAIT13 , AWAIT , AWAIT12 , AWAIT , AWAIT12 , AWAIT ,
+ AWAIT13 , AWAIT , AWAIT13 , AWAIT , AWAIT9 , AWAIT ,
+ AWAIT9 , AWAIT , AWAIT10 , AWAIT , AWAIT10 , AWAIT ,
+ AWAIT9 , AWAIT , AWAIT9 , AWAIT , AWAIT10 , AWAIT ,
+ AWAIT10 , AWAIT , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , AWAIT6 , AWAIT ,
+ AWAIT6 , AWAIT , SH10 , ERROR , AWAIT5 , NORMAL ,
+ AWAIT3 , AWAIT_REJECT , AWAIT3 , AWAIT_REJECT , SH10 , ERROR ,
+ AWAIT2 , REJECT , AWAIT9 , AWAIT , AWAIT9 , AWAIT ,
+ SH10 , ERROR , AWAIT8 , AWAIT , AWAIT9 , AWAIT ,
+ AWAIT9 , AWAIT , SH10 , ERROR , AWAIT8 , AWAIT ,
+ AWAIT12 , AWAIT , AWAIT12 , AWAIT , SH10 , ERROR ,
+ AWAIT11 , AWAIT , AWAIT12 , AWAIT , AWAIT12 , AWAIT ,
+ SH10 , ERROR , AWAIT11 , AWAIT , AWAIT9 , AWAIT ,
+ AWAIT9 , AWAIT , SH10 , ERROR , AWAIT8 , AWAIT ,
+ AWAIT9 , AWAIT , AWAIT9 , AWAIT , SH10 , ERROR ,
+ AWAIT8 , AWAIT , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , AWAIT_BUSY8 , AWAIT_BUSY , AWAIT_BUSY8 , AWAIT_BUSY ,
+ AWAIT_BUSY9 , AWAIT_BUSY , AWAIT_BUSY9 , AWAIT_BUSY , AWAIT_BUSY5 , AWAIT_BUSY ,
+ AWAIT_BUSY5 , AWAIT_BUSY , AWAIT_BUSY6 , AWAIT_BUSY , AWAIT_BUSY6 , AWAIT_BUSY ,
+ AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY12 , AWAIT_BUSY ,
+ AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY14 , AWAIT_BUSY ,
+ AWAIT_BUSY14 , AWAIT_BUSY , AWAIT_BUSY15 , AWAIT_BUSY , AWAIT_BUSY15 , AWAIT_BUSY ,
+ AWAIT_BUSY14 , AWAIT_BUSY , AWAIT_BUSY14 , AWAIT_BUSY , AWAIT_BUSY15 , AWAIT_BUSY ,
+ AWAIT_BUSY15 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY12 , AWAIT_BUSY , AWAIT_BUSY12 , AWAIT_BUSY ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , AWAIT_BUSY8 , AWAIT_BUSY , AWAIT_BUSY8 , AWAIT_BUSY ,
+ SH10 , ERROR , AWAIT_BUSY7 , BUSY , AWAIT_BUSY5 , AWAIT_BUSY ,
+ AWAIT_BUSY5 , AWAIT_BUSY , SH10 , ERROR , AWAIT_BUSY4 , BUSY ,
+ AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY , SH10 , ERROR ,
+ AWAIT_BUSY10 , BUSY , AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ SH10 , ERROR , AWAIT_BUSY10 , BUSY , AWAIT_BUSY14 , AWAIT_BUSY ,
+ AWAIT_BUSY14 , AWAIT_BUSY , SH10 , ERROR , AWAIT_BUSY13 , BUSY ,
+ AWAIT_BUSY14 , AWAIT_BUSY , AWAIT_BUSY14 , AWAIT_BUSY , SH10 , ERROR ,
+ AWAIT_BUSY13 , BUSY , AWAIT_BUSY11 , AWAIT_BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ SH10 , ERROR , AWAIT_BUSY10 , BUSY , AWAIT_BUSY11 , AWAIT_BUSY ,
+ AWAIT_BUSY11 , AWAIT_BUSY , SH10 , ERROR , AWAIT_BUSY10 , BUSY ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ AWAIT_REJECT5 , AWAIT , AWAIT_REJECT5 , AWAIT , AWAIT_REJECT6 , AWAIT ,
+ AWAIT_REJECT6 , AWAIT , AWAIT_REJECT2 , AWAIT_REJECT , AWAIT_REJECT2 , AWAIT_REJECT ,
+ AWAIT_REJECT3 , AWAIT_REJECT , AWAIT_REJECT3 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT9 , AWAIT_REJECT , AWAIT_REJECT9 , AWAIT_REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT9 , AWAIT_REJECT ,
+ AWAIT_REJECT9 , AWAIT_REJECT , AWAIT_REJECT11, AWAIT_REJECT , AWAIT_REJECT11, AWAIT_REJECT ,
+ AWAIT_REJECT12, AWAIT_REJECT , AWAIT_REJECT12, AWAIT_REJECT , AWAIT_REJECT11, AWAIT_REJECT ,
+ AWAIT_REJECT11, AWAIT_REJECT , AWAIT_REJECT12, AWAIT_REJECT , AWAIT_REJECT12, AWAIT_REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT9 , AWAIT_REJECT ,
+ AWAIT_REJECT9 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT ,
+ AWAIT_REJECT9 , AWAIT_REJECT , AWAIT_REJECT9 , AWAIT_REJECT , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH4 , ADM , SH4 , ADM ,
+ SH4 , ADM , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ SH3 , RESET_CHECK , SH3 , RESET_CHECK , SH3 , RESET_CHECK ,
+ AWAIT_REJECT5 , AWAIT , AWAIT_REJECT5 , AWAIT , SH10 , ERROR ,
+ AWAIT_REJECT4 , NORMAL , AWAIT_REJECT2 , AWAIT_REJECT , AWAIT_REJECT2 , AWAIT_REJECT ,
+ SH10 , ERROR , AWAIT_REJECT4 , NORMAL , AWAIT_REJECT8 , AWAIT_REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , SH10 , ERROR , AWAIT_REJECT7 , REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT , SH10 , ERROR ,
+ AWAIT_REJECT7 , REJECT , AWAIT_REJECT11, AWAIT_REJECT , AWAIT_REJECT11, AWAIT_REJECT ,
+ SH10 , ERROR , AWAIT_REJECT10, REJECT , AWAIT_REJECT11, AWAIT_REJECT ,
+ AWAIT_REJECT11, AWAIT_REJECT , SH10 , ERROR , AWAIT_REJECT10, REJECT ,
+ AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT , SH10 , ERROR ,
+ AWAIT_REJECT7 , REJECT , AWAIT_REJECT8 , AWAIT_REJECT , AWAIT_REJECT8 , AWAIT_REJECT ,
+ SH10 , ERROR , AWAIT_REJECT7 , REJECT , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH9 , ERROR , SH9 , ERROR ,
+ SH9 , ERROR , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH6 , ADM , SH6 , ADM , SH6 , ADM ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH5 , RESET_WAIT ,
+ SH5 , RESET_WAIT , SH5 , RESET_WAIT , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR , SH7 , ERROR ,
+ SH7 , ERROR , SH7 , ERROR };
diff --git a/net/802/transit/pdutr.pre b/net/802/transit/pdutr.pre
new file mode 100644
index 000000000..0e74987ac
--- /dev/null
+++ b/net/802/transit/pdutr.pre
@@ -0,0 +1,1121 @@
+COMPILE pdutr INDEX
+;
+; Transition tables for incomming pdu events.
+; translate this thing into C with
+; awk -f ./compile.awk pdu.trans > pdutr.h
+;
+TABLE ADM
+;Transition table for the ADM state:
+;
+;frame type p bit action newstate
+;received in frame
+;
+I_CMD 0 ADM5 ADM
+I_CMD 1 ADM4 ADM
+RR_CMD 0 ADM5 ADM
+RR_CMD 1 ADM4 ADM
+RNR_CMD 0 ADM5 ADM
+RNR_CMD 1 ADM4 ADM
+REJ_CMD 0 ADM5 ADM
+REJ_CMD 1 ADM4 ADM
+DISC_CMD 0 ADM3 ADM
+DISC_CMD 1 ADM3 ADM
+SABME_CMD 0 ADM2 CONN
+SABME_CMD 1 ADM2 CONN
+I_RSP 0 ADM5 ADM
+I_RSP 1 ADM5 ADM
+RR_RSP 0 ADM5 ADM
+RR_RSP 1 ADM5 ADM
+RNR_RSP 0 ADM5 ADM
+RNR_RSP 1 ADM5 ADM
+REJ_RSP 0 ADM5 ADM
+REJ_RSP 1 ADM5 ADM
+UA_RSP 0 ADM5 ADM
+UA_RSP 1 ADM5 ADM
+DM_RSP 0 ADM5 ADM
+DM_RSP 1 ADM5 ADM
+FRMR_RSP 0 ADM5 ADM
+FRMR_RSP 1 ADM5 ADM
+;
+TABLE CONN
+;
+;Transition table for the CONN state:
+;
+;frame type action newstate
+;received
+;
+I_CMD CONN5 CONN
+RR_CMD CONN5 CONN
+RNR_CMD CONN5 CONN
+REJ_CMD CONN5 CONN
+DISC_CMD CONN5 CONN
+SABME_CMD CONN3 CONN
+I_RSP CONN5 CONN
+RR_RSP CONN5 CONN
+RNR_RSP CONN5 CONN
+REJ_RSP CONN5 CONN
+UA_RSP CONN5 CONN
+DM_RSP CONN4 ADM
+FRMR_RSP CONN5 CONN
+;
+TABLE RESET_WAIT
+;Transition table for the RESET_WAIT
+;
+;frame type action newstate
+;received
+;
+I_CMD RESWAIT8 RESET_WAIT
+RR_CMD RESWAIT8 RESET_WAIT
+RNR_CMD RESWAIT8 RESET_WAIT
+REJ_CMD RESWAIT8 RESET_WAIT
+DISC_CMD RESWAIT7 RESET_WAIT
+SABME_CMD RESWAIT6 RESET_WAIT
+I_RSP RESWAIT8 RESET_WAIT
+RR_RSP RESWAIT8 RESET_WAIT
+RNR_RSP RESWAIT8 RESET_WAIT
+REJ_RSP RESWAIT8 RESET_WAIT
+UA_RSP RESWAIT8 RESET_WAIT
+DM_RSP RESWAIT5 ADM
+FRMR_RSP RESWAIT8 RESET_WAIT
+;
+;
+TABLE RESET_CHECK
+;Transition table for the RESET_CHECK state
+;
+;frame type action newstate
+;received
+;
+I_CMD RESCHK6 RESET_CHECK
+RR_CMD RESCHK6 RESET_CHECK
+RNR_CMD RESCHK6 RESET_CHECK
+REJ_CMD RESCHK6 RESET_CHECK
+DISC_CMD RESCHK5 ADM
+SABME_CMD RESCHK4 RESET_CHECK
+I_RSP RESCHK6 RESET_CHECK
+RR_RSP RESCHK6 RESET_CHECK
+RNR_RSP RESCHK6 RESET_CHECK
+REJ_RSP RESCHK6 RESET_CHECK
+UA_RSP RESCHK6 RESET_CHECK
+DM_RSP RESCHK3 ADM
+FRMR_RSP RESCHK6 RESET_CHECK
+;
+;
+TABLE SETUP
+;Transition table for the SETUP state
+;
+;frame type p flag action newstate
+;received = f
+;
+I_CMD 0 SETUP6 SETUP
+I_CMD 1 SETUP6 SETUP
+RR_CMD 0 SETUP6 SETUP
+RR_CMD 1 SETUP6 SETUP
+RNR_CMD 0 SETUP6 SETUP
+RNR_CMD 1 SETUP6 SETUP
+REJ_CMD 0 SETUP6 SETUP
+REJ_CMD 1 SETUP6 SETUP
+DISC_CMD 0 SETUP4 ADM
+DISC_CMD 1 SETUP4 ADM
+SABME_CMD 0 SETUP1 SETUP
+SABME_CMD 1 SETUP1 SETUP
+I_RSP 0 SETUP6 SETUP
+I_RSP 1 SETUP6 SETUP
+RR_RSP 0 SETUP6 SETUP
+RR_RSP 1 SETUP6 SETUP
+RNR_RSP 0 SETUP6 SETUP
+RNR_RSP 1 SETUP6 SETUP
+REJ_RSP 0 SETUP6 SETUP
+REJ_RSP 1 SETUP6 SETUP
+UA_RSP 0 SETUP6 SETUP
+UA_RSP 1 SETUP2 NORMAL
+DM_RSP 0 SETUP5 ADM
+DM_RSP 1 SETUP5 ADM
+FRMR_RSP 0 SETUP6 SETUP
+FRMR_RSP 1 SETUP6 SETUP
+;
+;
+TABLE RESET
+;Transition table for the RESET state:
+;
+;frame type p flag action newstate
+;received = f
+;
+I_CMD 0 RESET6 RESET
+I_CMD 1 RESET6 RESET
+RR_CMD 0 RESET6 RESET
+RR_CMD 1 RESET6 RESET
+RNR_CMD 0 RESET6 RESET
+RNR_CMD 1 RESET6 RESET
+REJ_CMD 0 RESET6 RESET
+REJ_CMD 1 RESET6 RESET
+DISC_CMD 0 RESET4 ADM
+DISC_CMD 1 RESET4 ADM
+SABME_CMD 0 RESET1 RESET
+SABME_CMD 1 RESET1 RESET
+I_RSP 0 RESET6 RESET
+I_RSP 1 RESET6 RESET
+RR_RSP 0 RESET6 RESET
+RR_RSP 1 RESET6 RESET
+RNR_RSP 0 RESET6 RESET
+RNR_RSP 1 RESET6 RESET
+REJ_RSP 0 RESET6 RESET
+REJ_RSP 1 RESET6 RESET
+UA_RSP 0 RESET6 RESET
+UA_RSP 1 RESET2 NORMAL
+DM_RSP 0 RESET5 ADM
+DM_RSP 1 RESET5 ADM
+FRMR_RSP 0 RESET6 RESET
+FRMR_RSP 1 RESET6 RESET
+;
+;
+TABLE D_CONN
+;Transition table for the D_CONN state:
+;
+;frame type p bit action newstate
+;received in frame
+I_CMD 0 D_CONN5 D_CONN
+I_CMD 1 D_CONN5 D_CONN
+RR_CMD 0 D_CONN5 D_CONN
+RR_CMD 1 D_CONN5 D_CONN
+RNR_CMD 0 D_CONN5 D_CONN
+RNR_CMD 1 D_CONN5 D_CONN
+REJ_CMD 0 D_CONN5 D_CONN
+REJ_CMD 1 D_CONN5 D_CONN
+DISC_CMD 0 D_CONN3 D_CONN
+DISC_CMD 1 D_CONN3 D_CONN
+SABME_CMD 0 D_CONN1 ADM
+SABME_CMD 1 D_CONN1 ADM
+I_RSP 0 D_CONN5 D_CONN
+I_RSP 1 D_CONN5 D_CONN
+RR_RSP 0 D_CONN5 D_CONN
+RR_RSP 1 D_CONN5 D_CONN
+RNR_RSP 0 D_CONN5 D_CONN
+RNR_RSP 1 D_CONN5 D_CONN
+REJ_RSP 0 D_CONN5 D_CONN
+REJ_RSP 1 D_CONN5 D_CONN
+UA_RSP 0 D_CONN5 D_CONN
+UA_RSP 1 D_CONN4 ADM
+DM_RSP 0 D_CONN4 ADM
+DM_RSP 1 D_CONN5 ADM
+FRMR_RSP 0 D_CONN5 D_CONN
+FRMR_RSP 1 D_CONN5 D_CONN
+;
+;
+TABLE ERROR
+;Transition table for the ERROR state:
+;
+;frame type action newstate
+;received
+;
+I_CMD ERR5 ERROR
+RR_CMD ERR5 ERROR
+RNR_CMD ERR5 ERROR
+REJ_CMD ERR5 ERROR
+DISC_CMD ERR2 ADM
+SABME_CMD ERR1 RESET_CHECK
+I_RSP ERR6 ERROR
+RR_RSP ERR6 ERROR
+RNR_RSP ERR6 ERROR
+REJ_RSP ERR6 ERROR
+UA_RSP ERR6 ERROR
+DM_RSP ERR3 ADM
+FRMR_RSP ERR4 RESET_WAIT
+;
+TABLE NORMAL
+;Transition table for the NORMAL state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 NORMAL8B NORMAL
+I_CMD 0 0 1 NORMAL9 NORMAL
+I_CMD 0 1 0 NORMAL10 NORMAL
+I_CMD 0 1 1 NORMAL10 NORMAL
+I_CMD 1 0 0 NORMAL5 REJECT
+I_CMD 1 0 1 NORMAL6 REJECT
+I_CMD 1 1 0 NORMAL7 REJECT
+I_CMD 1 1 1 NORMAL7 REJECT
+RR_CMD 0 0 0 NORMAL11 NORMAL
+RR_CMD 0 0 1 NORMAL11 NORMAL
+RR_CMD 0 1 0 NORMAL12 NORMAL
+RR_CMD 0 1 1 NORMAL12 NORMAL
+RR_CMD 1 0 0 NORMAL11 NORMAL
+RR_CMD 1 0 1 NORMAL11 NORMAL
+RR_CMD 1 1 0 NORMAL12 NORMAL
+RR_CMD 1 1 1 NORMAL12 NORMAL
+RNR_CMD 0 0 0 NORMAL13 NORMAL
+RNR_CMD 0 0 1 NORMAL13 NORMAL
+RNR_CMD 0 1 0 NORMAL14 NORMAL
+RNR_CMD 0 1 1 NORMAL14 NORMAL
+RNR_CMD 1 0 0 NORMAL13 NORMAL
+RNR_CMD 1 0 1 NORMAL13 NORMAL
+RNR_CMD 1 1 0 NORMAL14 NORMAL
+RNR_CMD 1 1 1 NORMAL14 NORMAL
+REJ_CMD 0 0 0 NORMAL15 NORMAL
+REJ_CMD 0 0 1 NORMAL16 NORMAL
+REJ_CMD 0 1 0 NORMAL17 NORMAL
+REJ_CMD 0 1 1 NORMAL17 NORMAL
+REJ_CMD 1 0 0 NORMAL15 NORMAL
+REJ_CMD 1 0 1 NORMAL16 NORMAL
+REJ_CMD 1 1 0 NORMAL17 NORMAL
+REJ_CMD 1 1 1 NORMAL17 NORMAL
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 NORMAL8B NORMAL
+I_RSP 0 0 1 NORMAL9 NORMAL
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 NORMAL8A NORMAL
+I_RSP 1 0 0 NORMAL5 REJECT
+I_RSP 1 0 1 NORMAL6 REJECT
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 NORMAL5 REJECT
+RR_RSP 0 0 0 NORMAL11 NORMAL
+RR_RSP 0 0 1 NORMAL11 NORMAL
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 NORMAL11 NORMAL
+RR_RSP 1 0 0 NORMAL11 NORMAL
+RR_RSP 1 0 1 NORMAL11 NORMAL
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 NORMAL11 NORMAL
+RNR_RSP 0 0 0 NORMAL13 NORMAL
+RNR_RSP 0 0 1 NORMAL13 NORMAL
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 NORMAL13 NORMAL
+RNR_RSP 1 0 0 NORMAL13 NORMAL
+RNR_RSP 1 0 1 NORMAL13 NORMAL
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 NORMAL13 NORMAL
+REJ_RSP 0 0 0 NORMAL15 NORMAL
+REJ_RSP 0 0 1 NORMAL16 NORMAL
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 NORMAL15 NORMAL
+REJ_RSP 1 0 0 NORMAL15 NORMAL
+REJ_RSP 1 0 1 NORMAL16 NORMAL
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 NORMAL15 NORMAL
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 0 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 0 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 1 NORMAL6 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 1 NORMAL6 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x NORMAL7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 NORMAL8A NORMAL
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 NORMAL8B NORMAL
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 NORMAL8B NORMAL
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 NORMAL9 NORMAL
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 NORMAL9 NORMAL
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x NORMAL10 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x NORMAL11 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x NORMAL11 NORMAL
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 NORMAL11 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x NORMAL12 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x NORMAL13 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x NORMAL13 NORMAL
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 NORMAL13 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x NORMAL14 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 NORMAL16 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 NORMAL16 NORMAL
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x NORMAL17 NORMAL
+;
+TABLE BUSY
+;Transition table for the BUSY state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 BUSY13 BUSY
+I_CMD 0 0 1 BUSY14 BUSY
+I_CMD 0 1 0 BUSY12 BUSY
+I_CMD 0 1 1 BUSY12 BUSY
+I_CMD 1 0 0 BUSY9 BUSY
+I_CMD 1 0 1 BUSY10 BUSY
+I_CMD 1 1 0 BUSY11 BUSY
+I_CMD 1 1 1 BUSY11 BUSY
+RR_CMD 0 0 0 BUSY15 BUSY
+RR_CMD 0 0 1 BUSY15 BUSY
+RR_CMD 0 1 0 BUSY16 BUSY
+RR_CMD 0 1 1 BUSY16 BUSY
+RR_CMD 1 0 0 BUSY15 BUSY
+RR_CMD 1 0 1 BUSY15 BUSY
+RR_CMD 1 1 0 BUSY16 BUSY
+RR_CMD 1 1 1 BUSY16 BUSY
+RNR_CMD 0 0 0 BUSY17 BUSY
+RNR_CMD 0 0 1 BUSY17 BUSY
+RNR_CMD 0 1 0 BUSY18 BUSY
+RNR_CMD 0 1 1 BUSY18 BUSY
+RNR_CMD 1 0 0 BUSY17 BUSY
+RNR_CMD 1 0 1 BUSY17 BUSY
+RNR_CMD 1 1 0 BUSY18 BUSY
+RNR_CMD 1 1 1 BUSY18 BUSY
+REJ_CMD 0 0 0 BUSY19 BUSY
+REJ_CMD 0 0 1 BUSY20 BUSY
+REJ_CMD 0 1 0 BUSY21 BUSY
+REJ_CMD 0 1 1 BUSY21 BUSY
+REJ_CMD 1 0 0 BUSY19 BUSY
+REJ_CMD 1 0 1 BUSY20 BUSY
+REJ_CMD 1 1 0 BUSY21 BUSY
+REJ_CMD 1 1 1 BUSY21 BUSY
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 BUSY13 BUSY
+I_RSP 0 0 1 BUSY14 BUSY
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 BUSY13 BUSY
+I_RSP 1 0 0 BUSY9 BUSY
+I_RSP 1 0 1 BUSY10 BUSY
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 BUSY9 BUSY
+RR_RSP 0 0 0 BUSY15 BUSY
+RR_RSP 0 0 1 BUSY15 BUSY
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 BUSY15 BUSY
+RR_RSP 1 0 0 BUSY15 BUSY
+RR_RSP 1 0 1 BUSY15 BUSY
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 BUSY15 BUSY
+RNR_RSP 0 0 0 BUSY17 BUSY
+RNR_RSP 0 0 1 BUSY17 BUSY
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 BUSY17 BUSY
+RNR_RSP 1 0 0 BUSY17 BUSY
+RNR_RSP 1 0 1 BUSY17 BUSY
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 BUSY17 BUSY
+REJ_RSP 0 0 0 BUSY19 BUSY
+REJ_RSP 0 0 1 BUSY20 BUSY
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 BUSY19 BUSY
+REJ_RSP 1 0 0 BUSY19 BUSY
+REJ_RSP 1 0 1 BUSY20 BUSY
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 BUSY19 BUSY
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 0 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 0 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 1 BUSY10 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 1 BUSY10 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x BUSY11 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x BUSY12 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 BUSY14 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 BUSY14 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x BUSY15 BUSY
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x BUSY15 BUSY
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 BUSY15 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x BUSY16 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x BUSY17 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x BUSY17 BUSY
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 BUSY17 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x BUSY18 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 BUSY20 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 BUSY20 BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x BUSY21 BUSY
+;
+TABLE REJECT
+;Transition table for the REJECT state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 REJECT7 REJECT
+I_CMD 0 0 1 REJECT8 REJECT
+I_CMD 0 1 0 REJECT9 REJECT
+I_CMD 0 1 1 REJECT9 REJECT
+I_CMD 1 0 0 REJECT5 REJECT
+I_CMD 1 0 1 REJECT5 REJECT
+I_CMD 1 1 0 REJECT6 REJECT
+I_CMD 1 1 1 REJECT6 REJECT
+RR_CMD 0 0 0 REJECT10 REJECT
+RR_CMD 0 0 1 REJECT10 REJECT
+RR_CMD 0 1 0 REJECT11 REJECT
+RR_CMD 0 1 1 REJECT11 REJECT
+RR_CMD 1 0 0 REJECT10 REJECT
+RR_CMD 1 0 1 REJECT10 REJECT
+RR_CMD 1 1 0 REJECT11 REJECT
+RR_CMD 1 1 1 REJECT11 REJECT
+RNR_CMD 0 0 0 REJECT12 REJECT
+RNR_CMD 0 0 1 REJECT12 REJECT
+RNR_CMD 0 1 0 REJECT13 REJECT
+RNR_CMD 0 1 1 REJECT13 REJECT
+RNR_CMD 1 0 0 REJECT12 REJECT
+RNR_CMD 1 0 1 REJECT12 REJECT
+RNR_CMD 1 1 0 REJECT13 REJECT
+RNR_CMD 1 1 1 REJECT13 REJECT
+REJ_CMD 0 0 0 REJECT14 REJECT
+REJ_CMD 0 0 1 REJECT15 REJECT
+REJ_CMD 0 1 0 REJECT16 REJECT
+REJ_CMD 0 1 1 REJECT16 REJECT
+REJ_CMD 1 0 0 REJECT14 REJECT
+REJ_CMD 1 0 1 REJECT15 REJECT
+REJ_CMD 1 1 0 REJECT16 REJECT
+REJ_CMD 1 1 1 REJECT16 REJECT
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 REJECT7 REJECT
+I_RSP 0 0 1 REJECT8 REJECT
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 REJECT7 REJECT
+I_RSP 1 0 0 REJECT5 REJECT
+I_RSP 1 0 1 REJECT5 REJECT
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 REJECT5 REJECT
+RR_RSP 0 0 0 REJECT10 REJECT
+RR_RSP 0 0 1 REJECT10 REJECT
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 REJECT10 REJECT
+RR_RSP 1 0 0 REJECT10 REJECT
+RR_RSP 1 0 1 REJECT10 REJECT
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 REJECT10 REJECT
+RNR_RSP 0 0 0 REJECT12 REJECT
+RNR_RSP 0 0 1 REJECT12 REJECT
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 REJECT12 REJECT
+RNR_RSP 1 0 0 REJECT12 REJECT
+RNR_RSP 1 0 1 REJECT12 REJECT
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 REJECT12 REJECT
+REJ_RSP 0 0 0 REJECT14 REJECT
+REJ_RSP 0 0 1 REJECT15 REJECT
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 REJECT14 REJECT
+REJ_RSP 1 0 0 REJECT14 REJECT
+REJ_RSP 1 0 1 REJECT15 REJECT
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 REJECT14 REJECT
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x REJECT5 REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x REJECT5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 REJECT5 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x REJECT6 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 REJECT8 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 REJECT8 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x REJECT9 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x REJECT10 REJECT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x REJECT11 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x REJECT12 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x REJECT12 REJECT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 REJECT12 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x REJECT13 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 REJECT15 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 REJECT15 REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x REJECT16 REJECT
+;
+TABLE AWAIT
+;Transition table for the AWAIT state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 AWAIT6 AWAIT
+I_CMD 0 0 1 AWAIT6 AWAIT
+I_CMD 0 1 0 AWAIT7 AWAIT
+I_CMD 0 1 1 AWAIT7 AWAIT
+I_CMD 1 0 0 AWAIT3 AWAIT_REJECT
+I_CMD 1 0 1 AWAIT3 AWAIT_REJECT
+I_CMD 1 1 0 AWAIT4 AWAIT_REJECT
+I_CMD 1 1 1 AWAIT4 AWAIT_REJECT
+RR_CMD 0 0 0 AWAIT9 AWAIT
+RR_CMD 0 0 1 AWAIT9 AWAIT
+RR_CMD 0 1 0 AWAIT10 AWAIT
+RR_CMD 0 1 1 AWAIT10 AWAIT
+RR_CMD 1 0 0 AWAIT9 AWAIT
+RR_CMD 1 0 1 AWAIT9 AWAIT
+RR_CMD 1 1 0 AWAIT10 AWAIT
+RR_CMD 1 1 1 AWAIT10 AWAIT
+RNR_CMD 0 0 0 AWAIT12 AWAIT
+RNR_CMD 0 0 1 AWAIT12 AWAIT
+RNR_CMD 0 1 0 AWAIT13 AWAIT
+RNR_CMD 0 1 1 AWAIT13 AWAIT
+RNR_CMD 1 0 0 AWAIT12 AWAIT
+RNR_CMD 1 0 1 AWAIT12 AWAIT
+RNR_CMD 1 1 0 AWAIT13 AWAIT
+RNR_CMD 1 1 1 AWAIT13 AWAIT
+REJ_CMD 0 0 0 AWAIT9 AWAIT
+REJ_CMD 0 0 1 AWAIT9 AWAIT
+REJ_CMD 0 1 0 AWAIT10 AWAIT
+REJ_CMD 0 1 1 AWAIT10 AWAIT
+REJ_CMD 1 0 0 AWAIT9 AWAIT
+REJ_CMD 1 0 1 AWAIT9 AWAIT
+REJ_CMD 1 1 0 AWAIT10 AWAIT
+REJ_CMD 1 1 1 AWAIT10 AWAIT
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 AWAIT6 AWAIT
+I_RSP 0 0 1 AWAIT6 AWAIT
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 AWAIT5 NORMAL
+I_RSP 1 0 0 AWAIT3 AWAIT_REJECT
+I_RSP 1 0 1 AWAIT3 AWAIT_REJECT
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 AWAIT2 REJECT
+RR_RSP 0 0 0 AWAIT9 AWAIT
+RR_RSP 0 0 1 AWAIT9 AWAIT
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 AWAIT8 AWAIT
+RR_RSP 1 0 0 AWAIT9 AWAIT
+RR_RSP 1 0 1 AWAIT9 AWAIT
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 AWAIT8 AWAIT
+RNR_RSP 0 0 0 AWAIT12 AWAIT
+RNR_RSP 0 0 1 AWAIT12 AWAIT
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 AWAIT11 AWAIT
+RNR_RSP 1 0 0 AWAIT12 AWAIT
+RNR_RSP 1 0 1 AWAIT12 AWAIT
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 AWAIT11 AWAIT
+REJ_RSP 0 0 0 AWAIT9 AWAIT
+REJ_RSP 0 0 1 AWAIT9 AWAIT
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 AWAIT8 AWAIT
+REJ_RSP 1 0 0 AWAIT9 AWAIT
+REJ_RSP 1 0 1 AWAIT9 AWAIT
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 AWAIT8 AWAIT
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 x AWAIT2 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT4 AWAIT_REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 x AWAIT5 NORMAL
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT6 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT6 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT7 AWAIT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT8 AWAIT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT8 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT10 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT10 AWAIT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT11 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT12 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT12 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT13 AWAIT
+;
+TABLE AWAIT_BUSY
+;Transition table for the AWAIT_BUSY state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 AWAIT_BUSY8 AWAIT_BUSY
+I_CMD 0 0 1 AWAIT_BUSY8 AWAIT_BUSY
+I_CMD 0 1 0 AWAIT_BUSY9 AWAIT_BUSY
+I_CMD 0 1 1 AWAIT_BUSY9 AWAIT_BUSY
+I_CMD 1 0 0 AWAIT_BUSY5 AWAIT_BUSY
+I_CMD 1 0 1 AWAIT_BUSY5 AWAIT_BUSY
+I_CMD 1 1 0 AWAIT_BUSY6 AWAIT_BUSY
+I_CMD 1 1 1 AWAIT_BUSY6 AWAIT_BUSY
+RR_CMD 0 0 0 AWAIT_BUSY11 AWAIT_BUSY
+RR_CMD 0 0 1 AWAIT_BUSY11 AWAIT_BUSY
+RR_CMD 0 1 0 AWAIT_BUSY12 AWAIT_BUSY
+RR_CMD 0 1 1 AWAIT_BUSY12 AWAIT_BUSY
+RR_CMD 1 0 0 AWAIT_BUSY11 AWAIT_BUSY
+RR_CMD 1 0 1 AWAIT_BUSY11 AWAIT_BUSY
+RR_CMD 1 1 0 AWAIT_BUSY12 AWAIT_BUSY
+RR_CMD 1 1 1 AWAIT_BUSY12 AWAIT_BUSY
+RNR_CMD 0 0 0 AWAIT_BUSY14 AWAIT_BUSY
+RNR_CMD 0 0 1 AWAIT_BUSY14 AWAIT_BUSY
+RNR_CMD 0 1 0 AWAIT_BUSY15 AWAIT_BUSY
+RNR_CMD 0 1 1 AWAIT_BUSY15 AWAIT_BUSY
+RNR_CMD 1 0 0 AWAIT_BUSY14 AWAIT_BUSY
+RNR_CMD 1 0 1 AWAIT_BUSY14 AWAIT_BUSY
+RNR_CMD 1 1 0 AWAIT_BUSY15 AWAIT_BUSY
+RNR_CMD 1 1 1 AWAIT_BUSY15 AWAIT_BUSY
+REJ_CMD 0 0 0 AWAIT_BUSY11 AWAIT_BUSY
+REJ_CMD 0 0 1 AWAIT_BUSY11 AWAIT_BUSY
+REJ_CMD 0 1 0 AWAIT_BUSY12 AWAIT_BUSY
+REJ_CMD 0 1 1 AWAIT_BUSY12 AWAIT_BUSY
+REJ_CMD 1 0 0 AWAIT_BUSY11 AWAIT_BUSY
+REJ_CMD 1 0 1 AWAIT_BUSY11 AWAIT_BUSY
+REJ_CMD 1 1 0 AWAIT_BUSY12 AWAIT_BUSY
+REJ_CMD 1 1 1 AWAIT_BUSY12 AWAIT_BUSY
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 AWAIT_BUSY8 AWAIT_BUSY
+I_RSP 0 0 1 AWAIT_BUSY8 AWAIT_BUSY
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 AWAIT_BUSY7 BUSY
+I_RSP 1 0 0 AWAIT_BUSY5 AWAIT_BUSY
+I_RSP 1 0 1 AWAIT_BUSY5 AWAIT_BUSY
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 AWAIT_BUSY4 BUSY
+RR_RSP 0 0 0 AWAIT_BUSY11 AWAIT_BUSY
+RR_RSP 0 0 1 AWAIT_BUSY11 AWAIT_BUSY
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 AWAIT_BUSY10 BUSY
+RR_RSP 1 0 0 AWAIT_BUSY11 AWAIT_BUSY
+RR_RSP 1 0 1 AWAIT_BUSY11 AWAIT_BUSY
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 AWAIT_BUSY10 BUSY
+RNR_RSP 0 0 0 AWAIT_BUSY14 AWAIT_BUSY
+RNR_RSP 0 0 1 AWAIT_BUSY14 AWAIT_BUSY
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 AWAIT_BUSY13 BUSY
+RNR_RSP 1 0 0 AWAIT_BUSY14 AWAIT_BUSY
+RNR_RSP 1 0 1 AWAIT_BUSY14 AWAIT_BUSY
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 AWAIT_BUSY13 BUSY
+REJ_RSP 0 0 0 AWAIT_BUSY11 AWAIT_BUSY
+REJ_RSP 0 0 1 AWAIT_BUSY11 AWAIT_BUSY
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 AWAIT_BUSY10 BUSY
+REJ_RSP 1 0 0 AWAIT_BUSY11 AWAIT_BUSY
+REJ_RSP 1 0 1 AWAIT_BUSY11 AWAIT_BUSY
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 AWAIT_BUSY10 BUSY
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 x AWAIT_BUSY4 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT_BUSY5 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT_BUSY5 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT_BUSY6 AWAIT_BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 x AWAIT_BUSY7 BUSY
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT_BUSY8 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT_BUSY8 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT_BUSY9 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT_BUSY10 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT_BUSY10 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT_BUSY12 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT_BUSY12 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT_BUSY13 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT_BUSY14 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT_BUSY14 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT_BUSY15 AWAIT_BUSY
+;
+TABLE AWAIT_REJECT
+;Transition table for the AWAIT_REJECT state:
+;
+;frame type uexpect p bit p_flag
+;received N(S) in frame
+;
+I_CMD 0 0 0 AWAIT_REJECT5 AWAIT
+I_CMD 0 0 1 AWAIT_REJECT5 AWAIT
+I_CMD 0 1 0 AWAIT_REJECT6 AWAIT
+I_CMD 0 1 1 AWAIT_REJECT6 AWAIT
+I_CMD 1 0 0 AWAIT_REJECT2 AWAIT_REJECT
+I_CMD 1 0 1 AWAIT_REJECT2 AWAIT_REJECT
+I_CMD 1 1 0 AWAIT_REJECT3 AWAIT_REJECT
+I_CMD 1 1 1 AWAIT_REJECT3 AWAIT_REJECT
+RR_CMD 0 0 0 AWAIT_REJECT8 AWAIT_REJECT
+RR_CMD 0 0 1 AWAIT_REJECT8 AWAIT_REJECT
+RR_CMD 0 1 0 AWAIT_REJECT9 AWAIT_REJECT
+RR_CMD 0 1 1 AWAIT_REJECT9 AWAIT_REJECT
+RR_CMD 1 0 0 AWAIT_REJECT8 AWAIT_REJECT
+RR_CMD 1 0 1 AWAIT_REJECT8 AWAIT_REJECT
+RR_CMD 1 1 0 AWAIT_REJECT9 AWAIT_REJECT
+RR_CMD 1 1 1 AWAIT_REJECT9 AWAIT_REJECT
+RNR_CMD 0 0 0 AWAIT_REJECT11 AWAIT_REJECT
+RNR_CMD 0 0 1 AWAIT_REJECT11 AWAIT_REJECT
+RNR_CMD 0 1 0 AWAIT_REJECT12 AWAIT_REJECT
+RNR_CMD 0 1 1 AWAIT_REJECT12 AWAIT_REJECT
+RNR_CMD 1 0 0 AWAIT_REJECT11 AWAIT_REJECT
+RNR_CMD 1 0 1 AWAIT_REJECT11 AWAIT_REJECT
+RNR_CMD 1 1 0 AWAIT_REJECT12 AWAIT_REJECT
+RNR_CMD 1 1 1 AWAIT_REJECT12 AWAIT_REJECT
+REJ_CMD 0 0 0 AWAIT_REJECT8 AWAIT_REJECT
+REJ_CMD 0 0 1 AWAIT_REJECT8 AWAIT_REJECT
+REJ_CMD 0 1 0 AWAIT_REJECT9 AWAIT_REJECT
+REJ_CMD 0 1 1 AWAIT_REJECT9 AWAIT_REJECT
+REJ_CMD 1 0 0 AWAIT_REJECT8 AWAIT_REJECT
+REJ_CMD 1 0 1 AWAIT_REJECT8 AWAIT_REJECT
+REJ_CMD 1 1 0 AWAIT_REJECT9 AWAIT_REJECT
+REJ_CMD 1 1 1 AWAIT_REJECT9 AWAIT_REJECT
+DISC_CMD 0 0 0 SH4 ADM
+DISC_CMD 0 0 1 SH4 ADM
+DISC_CMD 0 1 0 SH4 ADM
+DISC_CMD 0 1 1 SH4 ADM
+DISC_CMD 1 0 0 SH4 ADM
+DISC_CMD 1 0 1 SH4 ADM
+DISC_CMD 1 1 0 SH4 ADM
+DISC_CMD 1 1 1 SH4 ADM
+SABME_CMD 0 0 0 SH3 RESET_CHECK
+SABME_CMD 0 0 1 SH3 RESET_CHECK
+SABME_CMD 0 1 0 SH3 RESET_CHECK
+SABME_CMD 0 1 1 SH3 RESET_CHECK
+SABME_CMD 1 0 0 SH3 RESET_CHECK
+SABME_CMD 1 0 1 SH3 RESET_CHECK
+SABME_CMD 1 1 0 SH3 RESET_CHECK
+SABME_CMD 1 1 1 SH3 RESET_CHECK
+I_RSP 0 0 0 AWAIT_REJECT5 AWAIT
+I_RSP 0 0 1 AWAIT_REJECT5 AWAIT
+I_RSP 0 1 0 SH10 ERROR
+I_RSP 0 1 1 AWAIT_REJECT4 NORMAL
+I_RSP 1 0 0 AWAIT_REJECT2 AWAIT_REJECT
+I_RSP 1 0 1 AWAIT_REJECT2 AWAIT_REJECT
+I_RSP 1 1 0 SH10 ERROR
+I_RSP 1 1 1 AWAIT_REJECT4 NORMAL
+RR_RSP 0 0 0 AWAIT_REJECT8 AWAIT_REJECT
+RR_RSP 0 0 1 AWAIT_REJECT8 AWAIT_REJECT
+RR_RSP 0 1 0 SH10 ERROR
+RR_RSP 0 1 1 AWAIT_REJECT7 REJECT
+RR_RSP 1 0 0 AWAIT_REJECT8 AWAIT_REJECT
+RR_RSP 1 0 1 AWAIT_REJECT8 AWAIT_REJECT
+RR_RSP 1 1 0 SH10 ERROR
+RR_RSP 1 1 1 AWAIT_REJECT7 REJECT
+RNR_RSP 0 0 0 AWAIT_REJECT11 AWAIT_REJECT
+RNR_RSP 0 0 1 AWAIT_REJECT11 AWAIT_REJECT
+RNR_RSP 0 1 0 SH10 ERROR
+RNR_RSP 0 1 1 AWAIT_REJECT10 REJECT
+RNR_RSP 1 0 0 AWAIT_REJECT11 AWAIT_REJECT
+RNR_RSP 1 0 1 AWAIT_REJECT11 AWAIT_REJECT
+RNR_RSP 1 1 0 SH10 ERROR
+RNR_RSP 1 1 1 AWAIT_REJECT10 REJECT
+REJ_RSP 0 0 0 AWAIT_REJECT8 AWAIT_REJECT
+REJ_RSP 0 0 1 AWAIT_REJECT8 AWAIT_REJECT
+REJ_RSP 0 1 0 SH10 ERROR
+REJ_RSP 0 1 1 AWAIT_REJECT7 REJECT
+REJ_RSP 1 0 0 AWAIT_REJECT8 AWAIT_REJECT
+REJ_RSP 1 0 1 AWAIT_REJECT8 AWAIT_REJECT
+REJ_RSP 1 1 0 SH10 ERROR
+REJ_RSP 1 1 1 AWAIT_REJECT7 REJECT
+UA_RSP 0 0 0 SH9 ERROR
+UA_RSP 0 0 1 SH9 ERROR
+UA_RSP 0 1 0 SH9 ERROR
+UA_RSP 0 1 1 SH9 ERROR
+UA_RSP 1 0 0 SH9 ERROR
+UA_RSP 1 0 1 SH9 ERROR
+UA_RSP 1 1 0 SH9 ERROR
+UA_RSP 1 1 1 SH9 ERROR
+DM_RSP 0 0 0 SH6 ADM
+DM_RSP 0 0 1 SH6 ADM
+DM_RSP 0 1 0 SH6 ADM
+DM_RSP 0 1 1 SH6 ADM
+DM_RSP 1 0 0 SH6 ADM
+DM_RSP 1 0 1 SH6 ADM
+DM_RSP 1 1 0 SH6 ADM
+DM_RSP 1 1 1 SH6 ADM
+FRMR_RSP 0 0 0 SH5 RESET_WAIT
+FRMR_RSP 0 0 1 SH5 RESET_WAIT
+FRMR_RSP 0 1 0 SH5 RESET_WAIT
+FRMR_RSP 0 1 1 SH5 RESET_WAIT
+FRMR_RSP 1 0 0 SH5 RESET_WAIT
+FRMR_RSP 1 0 1 SH5 RESET_WAIT
+FRMR_RSP 1 1 0 SH5 RESET_WAIT
+FRMR_RSP 1 1 1 SH5 RESET_WAIT
+BAD_FRAME 0 0 0 SH7 ERROR
+BAD_FRAME 0 0 1 SH7 ERROR
+BAD_FRAME 0 1 0 SH7 ERROR
+BAD_FRAME 0 1 1 SH7 ERROR
+BAD_FRAME 1 0 0 SH7 ERROR
+BAD_FRAME 1 0 1 SH7 ERROR
+BAD_FRAME 1 1 0 SH7 ERROR
+BAD_FRAME 1 1 1 SH7 ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT_REJECT2 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT_REJECT2 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT_REJECT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP x 1 x AWAIT_REJECT4 NORMAL
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT_REJECT5 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT_REJECT5 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT_REJECT6 AWAIT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 0 modified by tredit4 I_RSP 1 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT_REJECT9 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT_REJECT9 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT_REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT_REJECT11 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT_REJECT11 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT_REJECT12 AWAIT_REJECT
+;112 entries in table, 0 modified by tredit4 RNR_CMD x 1 x AWAIT_REJECT15 AWAIT_BUSY
diff --git a/net/802/transit/timertr.h b/net/802/transit/timertr.h
new file mode 100644
index 000000000..9b9403b5a
--- /dev/null
+++ b/net/802/transit/timertr.h
@@ -0,0 +1,157 @@
+
+/* this file was generated on Mon Mar 10 22:45:36 GMT 1997 */
+
+
+/* size of transition table is 898 bytes */
+
+static char timertr_entry [ ] = {
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , ADM ,
+ NOP , ADM , NOP , ADM , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , CONN , NOP , CONN ,
+ NOP , CONN , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_WAIT , NOP , RESET_WAIT , NOP , RESET_WAIT ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , NOP , RESET_CHECK ,
+ NOP , RESET_CHECK , NOP , RESET_CHECK , SETUP7 , SETUP ,
+ SETUP7 , SETUP , SETUP3 , NORMAL , SETUP3 , NORMAL ,
+ SETUP8 , ADM , SETUP8 , ADM , SETUP3 , NORMAL ,
+ SETUP3 , NORMAL , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , NOP , SETUP , NOP , SETUP ,
+ NOP , SETUP , RESET7 , RESET , RESET7 , RESET ,
+ RESET3 , NORMAL , RESET3 , NORMAL , RESET8 , ADM ,
+ RESET8 , ADM , RESET3 , NORMAL , RESET3 , NORMAL ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ NOP , RESET , NOP , RESET , NOP , RESET ,
+ D_CONN6 , D_CONN , D_CONN6 , D_CONN , D_CONN6 , D_CONN ,
+ D_CONN6 , D_CONN , D_CONN7 , ADM , D_CONN7 , ADM ,
+ D_CONN7 , ADM , D_CONN7 , ADM , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , NOP , D_CONN ,
+ NOP , D_CONN , NOP , D_CONN , ERR7 , ERROR ,
+ ERR7 , ERROR , ERR7 , ERROR , ERR7 , ERROR ,
+ ERR8 , RESET_WAIT , ERR8 , RESET_WAIT , ERR8 , RESET_WAIT ,
+ ERR8 , RESET_WAIT , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NOP , ERROR , NOP , ERROR ,
+ NOP , ERROR , NORMAL20 , AWAIT , NOP , NORMAL ,
+ NORMAL20 , AWAIT , NOP , NORMAL , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NORMAL19 , NORMAL , NORMAL19 , NORMAL , NORMAL19 , NORMAL ,
+ NORMAL19 , NORMAL , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , NORMAL ,
+ NOP , NORMAL , NOP , NORMAL , NOP , NORMAL ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NORMAL20 , AWAIT , NOP , NORMAL ,
+ NORMAL20 , AWAIT , NOP , NORMAL , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ BUSY24 , AWAIT_BUSY , NOP , BUSY , BUSY24 , AWAIT_BUSY ,
+ NOP , BUSY , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , BUSY23 , BUSY ,
+ BUSY23 , BUSY , BUSY23 , BUSY , BUSY23 , BUSY ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , BUSY25 , BUSY , BUSY26 , BUSY ,
+ BUSY25 , BUSY , BUSY26 , BUSY , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , BUSY , NOP , BUSY , NOP , BUSY ,
+ NOP , BUSY , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , REJECT ,
+ NOP , REJECT , NOP , REJECT , NOP , REJECT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NOP , REJECT , NOP , REJECT ,
+ NOP , REJECT , NOP , REJECT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , REJECT , NOP , REJECT , NOP , REJECT ,
+ NOP , REJECT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , REJECT ,
+ NOP , REJECT , NOP , REJECT , NOP , REJECT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NOP , AWAIT , NOP , AWAIT ,
+ NOP , AWAIT , NOP , AWAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , AWAIT , NOP , AWAIT , NOP , AWAIT ,
+ NOP , AWAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , AWAIT ,
+ NOP , AWAIT , NOP , AWAIT , NOP , AWAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NOP , AWAIT , NOP , AWAIT ,
+ NOP , AWAIT , NOP , AWAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , AWAIT_BUSY , NOP , AWAIT_BUSY , NOP , AWAIT_BUSY ,
+ NOP , AWAIT_BUSY , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , AWAIT_BUSY ,
+ NOP , AWAIT_BUSY , NOP , AWAIT_BUSY , NOP , AWAIT_BUSY ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NOP , AWAIT_BUSY , NOP , AWAIT_BUSY ,
+ NOP , AWAIT_BUSY , NOP , AWAIT_BUSY , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , AWAIT_BUSY , NOP , AWAIT_BUSY , NOP , AWAIT_BUSY ,
+ NOP , AWAIT_BUSY , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , AWAIT_REJECT ,
+ NOP , AWAIT_REJECT , NOP , AWAIT_REJECT , NOP , AWAIT_REJECT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , NOP , AWAIT_REJECT , NOP , AWAIT_REJECT ,
+ NOP , AWAIT_REJECT , NOP , AWAIT_REJECT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ NOP , AWAIT_REJECT , NOP , AWAIT_REJECT , NOP , AWAIT_REJECT ,
+ NOP , AWAIT_REJECT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , NOP , AWAIT_REJECT ,
+ NOP , AWAIT_REJECT , NOP , AWAIT_REJECT , NOP , AWAIT_REJECT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT , SH11 , RESET_WAIT ,
+ SH11 , RESET_WAIT , SH11 , RESET_WAIT };
diff --git a/net/802/transit/timertr.pre b/net/802/transit/timertr.pre
new file mode 100644
index 000000000..f082912dd
--- /dev/null
+++ b/net/802/transit/timertr.pre
@@ -0,0 +1,527 @@
+COMPILE timertr NOINDEX
+TABLE XXX
+;
+;Transition table for expiring timers:
+;
+;llc state timer retry_c s_flag p_flag action newstate
+; expired >= N2
+;
+ADM ACK_TIMER 0 0 0 NOP ADM
+ADM ACK_TIMER 0 0 1 NOP ADM
+ADM ACK_TIMER 0 1 0 NOP ADM
+ADM ACK_TIMER 0 1 1 NOP ADM
+ADM ACK_TIMER 1 0 0 NOP ADM
+ADM ACK_TIMER 1 0 1 NOP ADM
+ADM ACK_TIMER 1 1 0 NOP ADM
+ADM ACK_TIMER 1 1 1 NOP ADM
+;;
+ADM P_TIMER 0 0 0 NOP ADM
+ADM P_TIMER 0 0 1 NOP ADM
+ADM P_TIMER 0 1 0 NOP ADM
+ADM P_TIMER 0 1 1 NOP ADM
+ADM P_TIMER 1 0 0 NOP ADM
+ADM P_TIMER 1 0 1 NOP ADM
+ADM P_TIMER 1 1 0 NOP ADM
+ADM P_TIMER 1 1 1 NOP ADM
+;;
+ADM REJ_TIMER 0 0 0 NOP ADM
+ADM REJ_TIMER 0 0 1 NOP ADM
+ADM REJ_TIMER 0 1 0 NOP ADM
+ADM REJ_TIMER 0 1 1 NOP ADM
+ADM REJ_TIMER 1 0 0 NOP ADM
+ADM REJ_TIMER 1 0 1 NOP ADM
+ADM REJ_TIMER 1 1 0 NOP ADM
+ADM REJ_TIMER 1 1 1 NOP ADM
+;;
+ADM BUSY_TIMER 0 0 0 NOP ADM
+ADM BUSY_TIMER 0 0 1 NOP ADM
+ADM BUSY_TIMER 0 1 0 NOP ADM
+ADM BUSY_TIMER 0 1 1 NOP ADM
+ADM BUSY_TIMER 1 0 0 NOP ADM
+ADM BUSY_TIMER 1 0 1 NOP ADM
+ADM BUSY_TIMER 1 1 0 NOP ADM
+ADM BUSY_TIMER 1 1 1 NOP ADM
+;;
+;;
+CONN ACK_TIMER 0 0 0 NOP CONN
+CONN ACK_TIMER 0 0 1 NOP CONN
+CONN ACK_TIMER 0 1 0 NOP CONN
+CONN ACK_TIMER 0 1 1 NOP CONN
+CONN ACK_TIMER 1 0 0 NOP CONN
+CONN ACK_TIMER 1 0 1 NOP CONN
+CONN ACK_TIMER 1 1 0 NOP CONN
+CONN ACK_TIMER 1 1 1 NOP CONN
+;;
+CONN P_TIMER 0 0 0 NOP CONN
+CONN P_TIMER 0 0 1 NOP CONN
+CONN P_TIMER 0 1 0 NOP CONN
+CONN P_TIMER 0 1 1 NOP CONN
+CONN P_TIMER 1 0 0 NOP CONN
+CONN P_TIMER 1 0 1 NOP CONN
+CONN P_TIMER 1 1 0 NOP CONN
+CONN P_TIMER 1 1 1 NOP CONN
+;;
+CONN REJ_TIMER 0 0 0 NOP CONN
+CONN REJ_TIMER 0 0 1 NOP CONN
+CONN REJ_TIMER 0 1 0 NOP CONN
+CONN REJ_TIMER 0 1 1 NOP CONN
+CONN REJ_TIMER 1 0 0 NOP CONN
+CONN REJ_TIMER 1 0 1 NOP CONN
+CONN REJ_TIMER 1 1 0 NOP CONN
+CONN REJ_TIMER 1 1 1 NOP CONN
+;;
+CONN BUSY_TIMER 0 0 0 NOP CONN
+CONN BUSY_TIMER 0 0 1 NOP CONN
+CONN BUSY_TIMER 0 1 0 NOP CONN
+CONN BUSY_TIMER 0 1 1 NOP CONN
+CONN BUSY_TIMER 1 0 0 NOP CONN
+CONN BUSY_TIMER 1 0 1 NOP CONN
+CONN BUSY_TIMER 1 1 0 NOP CONN
+CONN BUSY_TIMER 1 1 1 NOP CONN
+;;
+;;
+RESET_WAIT ACK_TIMER 0 0 0 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 0 0 1 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 0 1 0 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 0 1 1 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 1 0 0 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 1 0 1 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 1 1 0 NOP RESET_WAIT
+RESET_WAIT ACK_TIMER 1 1 1 NOP RESET_WAIT
+;;
+RESET_WAIT P_TIMER 0 0 0 NOP RESET_WAIT
+RESET_WAIT P_TIMER 0 0 1 NOP RESET_WAIT
+RESET_WAIT P_TIMER 0 1 0 NOP RESET_WAIT
+RESET_WAIT P_TIMER 0 1 1 NOP RESET_WAIT
+RESET_WAIT P_TIMER 1 0 0 NOP RESET_WAIT
+RESET_WAIT P_TIMER 1 0 1 NOP RESET_WAIT
+RESET_WAIT P_TIMER 1 1 0 NOP RESET_WAIT
+RESET_WAIT P_TIMER 1 1 1 NOP RESET_WAIT
+;;
+RESET_WAIT REJ_TIMER 0 0 0 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 0 0 1 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 0 1 0 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 0 1 1 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 1 0 0 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 1 0 1 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 1 1 0 NOP RESET_WAIT
+RESET_WAIT REJ_TIMER 1 1 1 NOP RESET_WAIT
+;;
+RESET_WAIT BUSY_TIMER 0 0 0 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 0 0 1 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 0 1 0 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 0 1 1 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 1 0 0 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 1 0 1 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 1 1 0 NOP RESET_WAIT
+RESET_WAIT BUSY_TIMER 1 1 1 NOP RESET_WAIT
+;;
+;;
+RESET_CHECK ACK_TIMER 0 0 0 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 0 0 1 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 0 1 0 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 0 1 1 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 1 0 0 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 1 0 1 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 1 1 0 NOP RESET_CHECK
+RESET_CHECK ACK_TIMER 1 1 1 NOP RESET_CHECK
+;;
+RESET_CHECK P_TIMER 0 0 0 NOP RESET_CHECK
+RESET_CHECK P_TIMER 0 0 1 NOP RESET_CHECK
+RESET_CHECK P_TIMER 0 1 0 NOP RESET_CHECK
+RESET_CHECK P_TIMER 0 1 1 NOP RESET_CHECK
+RESET_CHECK P_TIMER 1 0 0 NOP RESET_CHECK
+RESET_CHECK P_TIMER 1 0 1 NOP RESET_CHECK
+RESET_CHECK P_TIMER 1 1 0 NOP RESET_CHECK
+RESET_CHECK P_TIMER 1 1 1 NOP RESET_CHECK
+;;
+RESET_CHECK REJ_TIMER 0 0 0 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 0 0 1 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 0 1 0 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 0 1 1 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 1 0 0 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 1 0 1 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 1 1 0 NOP RESET_CHECK
+RESET_CHECK REJ_TIMER 1 1 1 NOP RESET_CHECK
+;;
+RESET_CHECK BUSY_TIMER 0 0 0 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 0 0 1 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 0 1 0 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 0 1 1 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 1 0 0 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 1 0 1 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 1 1 0 NOP RESET_CHECK
+RESET_CHECK BUSY_TIMER 1 1 1 NOP RESET_CHECK
+;;
+;;
+;;
+SETUP ACK_TIMER 0 0 0 SETUP7 SETUP
+SETUP ACK_TIMER 0 0 1 SETUP7 SETUP
+SETUP ACK_TIMER 0 1 0 SETUP3 NORMAL
+SETUP ACK_TIMER 0 1 1 SETUP3 NORMAL
+SETUP ACK_TIMER 1 0 0 SETUP8 ADM
+SETUP ACK_TIMER 1 0 1 SETUP8 ADM
+SETUP ACK_TIMER 1 1 0 SETUP3 NORMAL
+SETUP ACK_TIMER 1 1 1 SETUP3 NORMAL
+;;
+SETUP P_TIMER 0 0 0 NOP SETUP
+SETUP P_TIMER 0 0 1 NOP SETUP
+SETUP P_TIMER 0 1 0 NOP SETUP
+SETUP P_TIMER 0 1 1 NOP SETUP
+SETUP P_TIMER 1 0 0 NOP SETUP
+SETUP P_TIMER 1 0 1 NOP SETUP
+SETUP P_TIMER 1 1 0 NOP SETUP
+SETUP P_TIMER 1 1 1 NOP SETUP
+;;
+SETUP REJ_TIMER 0 0 0 NOP SETUP
+SETUP REJ_TIMER 0 0 1 NOP SETUP
+SETUP REJ_TIMER 0 1 0 NOP SETUP
+SETUP REJ_TIMER 0 1 1 NOP SETUP
+SETUP REJ_TIMER 1 0 0 NOP SETUP
+SETUP REJ_TIMER 1 0 1 NOP SETUP
+SETUP REJ_TIMER 1 1 0 NOP SETUP
+SETUP REJ_TIMER 1 1 1 NOP SETUP
+;;
+SETUP BUSY_TIMER 0 0 0 NOP SETUP
+SETUP BUSY_TIMER 0 0 1 NOP SETUP
+SETUP BUSY_TIMER 0 1 0 NOP SETUP
+SETUP BUSY_TIMER 0 1 1 NOP SETUP
+SETUP BUSY_TIMER 1 0 0 NOP SETUP
+SETUP BUSY_TIMER 1 0 1 NOP SETUP
+SETUP BUSY_TIMER 1 1 0 NOP SETUP
+SETUP BUSY_TIMER 1 1 1 NOP SETUP
+;;
+;;
+;;
+RESET ACK_TIMER 0 0 0 RESET7 RESET
+RESET ACK_TIMER 0 0 1 RESET7 RESET
+RESET ACK_TIMER 0 1 0 RESET3 NORMAL
+RESET ACK_TIMER 0 1 1 RESET3 NORMAL
+RESET ACK_TIMER 1 0 0 RESET8 ADM
+RESET ACK_TIMER 1 0 1 RESET8 ADM
+RESET ACK_TIMER 1 1 0 RESET3 NORMAL
+RESET ACK_TIMER 1 1 1 RESET3 NORMAL
+;;
+RESET P_TIMER 0 0 0 NOP RESET
+RESET P_TIMER 0 0 1 NOP RESET
+RESET P_TIMER 0 1 0 NOP RESET
+RESET P_TIMER 0 1 1 NOP RESET
+RESET P_TIMER 1 0 0 NOP RESET
+RESET P_TIMER 1 0 1 NOP RESET
+RESET P_TIMER 1 1 0 NOP RESET
+RESET P_TIMER 1 1 1 NOP RESET
+;;
+RESET REJ_TIMER 0 0 0 NOP RESET
+RESET REJ_TIMER 0 0 1 NOP RESET
+RESET REJ_TIMER 0 1 0 NOP RESET
+RESET REJ_TIMER 0 1 1 NOP RESET
+RESET REJ_TIMER 1 0 0 NOP RESET
+RESET REJ_TIMER 1 0 1 NOP RESET
+RESET REJ_TIMER 1 1 0 NOP RESET
+RESET REJ_TIMER 1 1 1 NOP RESET
+;;
+RESET BUSY_TIMER 0 0 0 NOP RESET
+RESET BUSY_TIMER 0 0 1 NOP RESET
+RESET BUSY_TIMER 0 1 0 NOP RESET
+RESET BUSY_TIMER 0 1 1 NOP RESET
+RESET BUSY_TIMER 1 0 0 NOP RESET
+RESET BUSY_TIMER 1 0 1 NOP RESET
+RESET BUSY_TIMER 1 1 0 NOP RESET
+RESET BUSY_TIMER 1 1 1 NOP RESET
+;;
+;;
+D_CONN ACK_TIMER 0 0 0 D_CONN6 D_CONN
+D_CONN ACK_TIMER 0 0 1 D_CONN6 D_CONN
+D_CONN ACK_TIMER 0 1 0 D_CONN6 D_CONN
+D_CONN ACK_TIMER 0 1 1 D_CONN6 D_CONN
+D_CONN ACK_TIMER 1 0 0 D_CONN7 ADM
+D_CONN ACK_TIMER 1 0 1 D_CONN7 ADM
+D_CONN ACK_TIMER 1 1 0 D_CONN7 ADM
+D_CONN ACK_TIMER 1 1 1 D_CONN7 ADM
+;;
+D_CONN P_TIMER 0 0 0 NOP D_CONN
+D_CONN P_TIMER 0 0 1 NOP D_CONN
+D_CONN P_TIMER 0 1 0 NOP D_CONN
+D_CONN P_TIMER 0 1 1 NOP D_CONN
+D_CONN P_TIMER 1 0 0 NOP D_CONN
+D_CONN P_TIMER 1 0 1 NOP D_CONN
+D_CONN P_TIMER 1 1 0 NOP D_CONN
+D_CONN P_TIMER 1 1 1 NOP D_CONN
+;;
+D_CONN REJ_TIMER 0 0 0 NOP D_CONN
+D_CONN REJ_TIMER 0 0 1 NOP D_CONN
+D_CONN REJ_TIMER 0 1 0 NOP D_CONN
+D_CONN REJ_TIMER 0 1 1 NOP D_CONN
+D_CONN REJ_TIMER 1 0 0 NOP D_CONN
+D_CONN REJ_TIMER 1 0 1 NOP D_CONN
+D_CONN REJ_TIMER 1 1 0 NOP D_CONN
+D_CONN REJ_TIMER 1 1 1 NOP D_CONN
+;;
+D_CONN BUSY_TIMER 0 0 0 NOP D_CONN
+D_CONN BUSY_TIMER 0 0 1 NOP D_CONN
+D_CONN BUSY_TIMER 0 1 0 NOP D_CONN
+D_CONN BUSY_TIMER 0 1 1 NOP D_CONN
+D_CONN BUSY_TIMER 1 0 0 NOP D_CONN
+D_CONN BUSY_TIMER 1 0 1 NOP D_CONN
+D_CONN BUSY_TIMER 1 1 0 NOP D_CONN
+D_CONN BUSY_TIMER 1 1 1 NOP D_CONN
+;;
+;;
+ERROR ACK_TIMER 0 0 0 ERR7 ERROR
+ERROR ACK_TIMER 0 0 1 ERR7 ERROR
+ERROR ACK_TIMER 0 1 0 ERR7 ERROR
+ERROR ACK_TIMER 0 1 1 ERR7 ERROR
+ERROR ACK_TIMER 1 0 0 ERR8 RESET_WAIT
+ERROR ACK_TIMER 1 0 1 ERR8 RESET_WAIT
+ERROR ACK_TIMER 1 1 0 ERR8 RESET_WAIT
+ERROR ACK_TIMER 1 1 1 ERR8 RESET_WAIT
+;;
+ERROR P_TIMER 0 0 0 NOP ERROR
+ERROR P_TIMER 0 0 1 NOP ERROR
+ERROR P_TIMER 0 1 0 NOP ERROR
+ERROR P_TIMER 0 1 1 NOP ERROR
+ERROR P_TIMER 1 0 0 NOP ERROR
+ERROR P_TIMER 1 0 1 NOP ERROR
+ERROR P_TIMER 1 1 0 NOP ERROR
+ERROR P_TIMER 1 1 1 NOP ERROR
+;;
+ERROR REJ_TIMER 0 0 0 NOP ERROR
+ERROR REJ_TIMER 0 0 1 NOP ERROR
+ERROR REJ_TIMER 0 1 0 NOP ERROR
+ERROR REJ_TIMER 0 1 1 NOP ERROR
+ERROR REJ_TIMER 1 0 0 NOP ERROR
+ERROR REJ_TIMER 1 0 1 NOP ERROR
+ERROR REJ_TIMER 1 1 0 NOP ERROR
+ERROR REJ_TIMER 1 1 1 NOP ERROR
+;;
+ERROR BUSY_TIMER 0 0 0 NOP ERROR
+ERROR BUSY_TIMER 0 0 1 NOP ERROR
+ERROR BUSY_TIMER 0 1 0 NOP ERROR
+ERROR BUSY_TIMER 0 1 1 NOP ERROR
+ERROR BUSY_TIMER 1 0 0 NOP ERROR
+ERROR BUSY_TIMER 1 0 1 NOP ERROR
+ERROR BUSY_TIMER 1 1 0 NOP ERROR
+ERROR BUSY_TIMER 1 1 1 NOP ERROR
+;;
+;;
+NORMAL ACK_TIMER 0 0 0 NORMAL20 AWAIT
+NORMAL ACK_TIMER 0 0 1 NOP NORMAL
+NORMAL ACK_TIMER 0 1 0 NORMAL20 AWAIT
+NORMAL ACK_TIMER 0 1 1 NOP NORMAL
+NORMAL ACK_TIMER 1 0 0 SH11 RESET_WAIT
+NORMAL ACK_TIMER 1 0 1 SH11 RESET_WAIT
+NORMAL ACK_TIMER 1 1 0 SH11 RESET_WAIT
+NORMAL ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+NORMAL P_TIMER 0 0 0 NORMAL19 NORMAL
+NORMAL P_TIMER 0 0 1 NORMAL19 NORMAL
+NORMAL P_TIMER 0 1 0 NORMAL19 NORMAL
+NORMAL P_TIMER 0 1 1 NORMAL19 NORMAL
+NORMAL P_TIMER 1 0 0 SH11 RESET_WAIT
+NORMAL P_TIMER 1 0 1 SH11 RESET_WAIT
+NORMAL P_TIMER 1 1 0 SH11 RESET_WAIT
+NORMAL P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+NORMAL REJ_TIMER 0 0 0 NOP NORMAL
+NORMAL REJ_TIMER 0 0 1 NOP NORMAL
+NORMAL REJ_TIMER 0 1 0 NOP NORMAL
+NORMAL REJ_TIMER 0 1 1 NOP NORMAL
+NORMAL REJ_TIMER 1 0 0 SH11 RESET_WAIT
+NORMAL REJ_TIMER 1 0 1 SH11 RESET_WAIT
+NORMAL REJ_TIMER 1 1 0 SH11 RESET_WAIT
+NORMAL REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+NORMAL BUSY_TIMER 0 0 0 NORMAL20 AWAIT
+NORMAL BUSY_TIMER 0 0 1 NOP NORMAL
+NORMAL BUSY_TIMER 0 1 0 NORMAL20 AWAIT
+NORMAL BUSY_TIMER 0 1 1 NOP NORMAL
+NORMAL BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+NORMAL BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+NORMAL BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+NORMAL BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+;;
+BUSY ACK_TIMER 0 0 0 BUSY24 AWAIT_BUSY
+BUSY ACK_TIMER 0 0 1 NOP BUSY
+BUSY ACK_TIMER 0 1 0 BUSY24 AWAIT_BUSY
+BUSY ACK_TIMER 0 1 1 NOP BUSY
+BUSY ACK_TIMER 1 0 0 SH11 RESET_WAIT
+BUSY ACK_TIMER 1 0 1 SH11 RESET_WAIT
+BUSY ACK_TIMER 1 1 0 SH11 RESET_WAIT
+BUSY ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+BUSY P_TIMER 0 0 0 BUSY23 BUSY
+BUSY P_TIMER 0 0 1 BUSY23 BUSY
+BUSY P_TIMER 0 1 0 BUSY23 BUSY
+BUSY P_TIMER 0 1 1 BUSY23 BUSY
+BUSY P_TIMER 1 0 0 SH11 RESET_WAIT
+BUSY P_TIMER 1 0 1 SH11 RESET_WAIT
+BUSY P_TIMER 1 1 0 SH11 RESET_WAIT
+BUSY P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+BUSY REJ_TIMER 0 0 0 BUSY25 BUSY
+BUSY REJ_TIMER 0 0 1 BUSY26 BUSY
+BUSY REJ_TIMER 0 1 0 BUSY25 BUSY
+BUSY REJ_TIMER 0 1 1 BUSY26 BUSY
+BUSY REJ_TIMER 1 0 0 SH11 RESET_WAIT
+BUSY REJ_TIMER 1 0 1 SH11 RESET_WAIT
+BUSY REJ_TIMER 1 1 0 SH11 RESET_WAIT
+BUSY REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+BUSY BUSY_TIMER 0 0 0 NOP BUSY
+BUSY BUSY_TIMER 0 0 1 NOP BUSY
+BUSY BUSY_TIMER 0 1 0 NOP BUSY
+BUSY BUSY_TIMER 0 1 1 NOP BUSY
+BUSY BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+BUSY BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+BUSY BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+BUSY BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+;;
+REJECT ACK_TIMER 0 0 0 NOP REJECT
+REJECT ACK_TIMER 0 0 1 NOP REJECT
+REJECT ACK_TIMER 0 1 0 NOP REJECT
+REJECT ACK_TIMER 0 1 1 NOP REJECT
+REJECT ACK_TIMER 1 0 0 SH11 RESET_WAIT
+REJECT ACK_TIMER 1 0 1 SH11 RESET_WAIT
+REJECT ACK_TIMER 1 1 0 SH11 RESET_WAIT
+REJECT ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+REJECT P_TIMER 0 0 0 NOP REJECT
+REJECT P_TIMER 0 0 1 NOP REJECT
+REJECT P_TIMER 0 1 0 NOP REJECT
+REJECT P_TIMER 0 1 1 NOP REJECT
+REJECT P_TIMER 1 0 0 SH11 RESET_WAIT
+REJECT P_TIMER 1 0 1 SH11 RESET_WAIT
+REJECT P_TIMER 1 1 0 SH11 RESET_WAIT
+REJECT P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+REJECT REJ_TIMER 0 0 0 NOP REJECT
+REJECT REJ_TIMER 0 0 1 NOP REJECT
+REJECT REJ_TIMER 0 1 0 NOP REJECT
+REJECT REJ_TIMER 0 1 1 NOP REJECT
+REJECT REJ_TIMER 1 0 0 SH11 RESET_WAIT
+REJECT REJ_TIMER 1 0 1 SH11 RESET_WAIT
+REJECT REJ_TIMER 1 1 0 SH11 RESET_WAIT
+REJECT REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+REJECT BUSY_TIMER 0 0 0 NOP REJECT
+REJECT BUSY_TIMER 0 0 1 NOP REJECT
+REJECT BUSY_TIMER 0 1 0 NOP REJECT
+REJECT BUSY_TIMER 0 1 1 NOP REJECT
+REJECT BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+REJECT BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+REJECT BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+REJECT BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+;;
+AWAIT ACK_TIMER 0 0 0 NOP AWAIT
+AWAIT ACK_TIMER 0 0 1 NOP AWAIT
+AWAIT ACK_TIMER 0 1 0 NOP AWAIT
+AWAIT ACK_TIMER 0 1 1 NOP AWAIT
+AWAIT ACK_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT ACK_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT ACK_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT P_TIMER 0 0 0 NOP AWAIT
+AWAIT P_TIMER 0 0 1 NOP AWAIT
+AWAIT P_TIMER 0 1 0 NOP AWAIT
+AWAIT P_TIMER 0 1 1 NOP AWAIT
+AWAIT P_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT P_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT P_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT REJ_TIMER 0 0 0 NOP AWAIT
+AWAIT REJ_TIMER 0 0 1 NOP AWAIT
+AWAIT REJ_TIMER 0 1 0 NOP AWAIT
+AWAIT REJ_TIMER 0 1 1 NOP AWAIT
+AWAIT REJ_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT REJ_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT REJ_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT BUSY_TIMER 0 0 0 NOP AWAIT
+AWAIT BUSY_TIMER 0 0 1 NOP AWAIT
+AWAIT BUSY_TIMER 0 1 0 NOP AWAIT
+AWAIT BUSY_TIMER 0 1 1 NOP AWAIT
+AWAIT BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+;;
+AWAIT_BUSY ACK_TIMER 0 0 0 NOP AWAIT_BUSY
+AWAIT_BUSY ACK_TIMER 0 0 1 NOP AWAIT_BUSY
+AWAIT_BUSY ACK_TIMER 0 1 0 NOP AWAIT_BUSY
+AWAIT_BUSY ACK_TIMER 0 1 1 NOP AWAIT_BUSY
+AWAIT_BUSY ACK_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_BUSY ACK_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_BUSY ACK_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_BUSY ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_BUSY P_TIMER 0 0 0 NOP AWAIT_BUSY
+AWAIT_BUSY P_TIMER 0 0 1 NOP AWAIT_BUSY
+AWAIT_BUSY P_TIMER 0 1 0 NOP AWAIT_BUSY
+AWAIT_BUSY P_TIMER 0 1 1 NOP AWAIT_BUSY
+AWAIT_BUSY P_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_BUSY P_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_BUSY P_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_BUSY P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_BUSY REJ_TIMER 0 0 0 NOP AWAIT_BUSY
+AWAIT_BUSY REJ_TIMER 0 0 1 NOP AWAIT_BUSY
+AWAIT_BUSY REJ_TIMER 0 1 0 NOP AWAIT_BUSY
+AWAIT_BUSY REJ_TIMER 0 1 1 NOP AWAIT_BUSY
+AWAIT_BUSY REJ_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_BUSY REJ_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_BUSY REJ_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_BUSY REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_BUSY BUSY_TIMER 0 0 0 NOP AWAIT_BUSY
+AWAIT_BUSY BUSY_TIMER 0 0 1 NOP AWAIT_BUSY
+AWAIT_BUSY BUSY_TIMER 0 1 0 NOP AWAIT_BUSY
+AWAIT_BUSY BUSY_TIMER 0 1 1 NOP AWAIT_BUSY
+AWAIT_BUSY BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_BUSY BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_BUSY BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_BUSY BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+;;
+AWAIT_REJECT ACK_TIMER 0 0 0 NOP AWAIT_REJECT
+AWAIT_REJECT ACK_TIMER 0 0 1 NOP AWAIT_REJECT
+AWAIT_REJECT ACK_TIMER 0 1 0 NOP AWAIT_REJECT
+AWAIT_REJECT ACK_TIMER 0 1 1 NOP AWAIT_REJECT
+AWAIT_REJECT ACK_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_REJECT ACK_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_REJECT ACK_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_REJECT ACK_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_REJECT P_TIMER 0 0 0 NOP AWAIT_REJECT
+AWAIT_REJECT P_TIMER 0 0 1 NOP AWAIT_REJECT
+AWAIT_REJECT P_TIMER 0 1 0 NOP AWAIT_REJECT
+AWAIT_REJECT P_TIMER 0 1 1 NOP AWAIT_REJECT
+AWAIT_REJECT P_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_REJECT P_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_REJECT P_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_REJECT P_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_REJECT REJ_TIMER 0 0 0 NOP AWAIT_REJECT
+AWAIT_REJECT REJ_TIMER 0 0 1 NOP AWAIT_REJECT
+AWAIT_REJECT REJ_TIMER 0 1 0 NOP AWAIT_REJECT
+AWAIT_REJECT REJ_TIMER 0 1 1 NOP AWAIT_REJECT
+AWAIT_REJECT REJ_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_REJECT REJ_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_REJECT REJ_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_REJECT REJ_TIMER 1 1 1 SH11 RESET_WAIT
+;;
+AWAIT_REJECT BUSY_TIMER 0 0 0 NOP AWAIT_REJECT
+AWAIT_REJECT BUSY_TIMER 0 0 1 NOP AWAIT_REJECT
+AWAIT_REJECT BUSY_TIMER 0 1 0 NOP AWAIT_REJECT
+AWAIT_REJECT BUSY_TIMER 0 1 1 NOP AWAIT_REJECT
+AWAIT_REJECT BUSY_TIMER 1 0 0 SH11 RESET_WAIT
+AWAIT_REJECT BUSY_TIMER 1 0 1 SH11 RESET_WAIT
+AWAIT_REJECT BUSY_TIMER 1 1 0 SH11 RESET_WAIT
+AWAIT_REJECT BUSY_TIMER 1 1 1 SH11 RESET_WAIT
+;;
diff --git a/net/Config.in b/net/Config.in
index 834001fdc..97441ad6c 100644
--- a/net/Config.in
+++ b/net/Config.in
@@ -8,13 +8,17 @@ if [ "$CONFIG_NETLINK" = "y" ]; then
bool 'Routing messages' CONFIG_RTNETLINK
fi
bool 'Network firewalls' CONFIG_FIREWALL
+if [ "$CONFIG_FIREWALL" = "y" ]; then
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'Socket Security API Support (EXPERIMENTAL)' CONFIG_NET_SECURITY
+ fi
+fi
bool 'Network aliasing' CONFIG_NET_ALIAS
bool 'TCP/IP networking' CONFIG_INET
if [ "$CONFIG_INET" = "y" ]; then
source net/ipv4/Config.in
-
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- tristate 'The IPv6 protocol' CONFIG_IPV6
+ tristate 'The IPv6 protocol (EXPERIMENTAL)' CONFIG_IPV6
fi
fi
@@ -22,15 +26,29 @@ comment ' '
tristate 'The IPX protocol' CONFIG_IPX
if [ "$CONFIG_IPX" != "n" ]; then
bool 'Full internal IPX network' CONFIG_IPX_INTERN
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ bool 'IPX Type 20 Routing' CONFIG_IPX_PPROP_ROUTING
+ fi
fi
tristate 'Appletalk DDP' CONFIG_ATALK
+if [ "$CONFIG_ATALK" != "n" ]; then
+ bool 'IP-over-DDP support (EXPERIMENTAL)' CONFIG_IPDDP
+fi
tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
if [ "$CONFIG_AX25" != "n" ]; then
+ bool 'AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+# bool 'AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
dep_tristate 'Amateur Radio NET/ROM' CONFIG_NETROM $CONFIG_AX25
dep_tristate 'Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-# tristate 'CCITT X.25 Packet Layer' CONFIG_X25
+ tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
+ tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
+ bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
+# if [ "$CONFIG_LLC" = "y" ]; then
+# bool 'Netbeui (EXPERIMENTAL)' CONFIG_NETBEUI
+# fi
+ tristate 'WAN router' CONFIG_WAN_ROUTER
fi
endmenu
diff --git a/net/Makefile b/net/Makefile
index f86e704e8..9c45939cb 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -9,7 +9,7 @@
MOD_SUB_DIRS := ipv4
ALL_SUB_DIRS := 802 ax25 bridge core ethernet ipv4 ipv6 ipx unix appletalk \
- netrom rose #decnet
+ netrom rose lapb x25 wanrouter sunrpc #decnet
SUB_DIRS := core ethernet unix
MOD_LIST_NAME := NET_MISC_MODULES
@@ -49,6 +49,30 @@ else
endif
endif
+ifeq ($(CONFIG_WAN_ROUTER),y)
+SUB_DIRS += wanrouter
+else
+ ifeq ($(CONFIG_WAN_ROUTER),m)
+ MOD_SUB_DIRS += wanrouter
+ endif
+endif
+
+ifeq ($(CONFIG_X25),y)
+SUB_DIRS += x25
+else
+ ifeq ($(CONFIG_X25),m)
+ MOD_SUB_DIRS += x25
+ endif
+endif
+
+ifeq ($(CONFIG_LAPB),y)
+SUB_DIRS += lapb
+else
+ ifeq ($(CONFIG_LAPB),m)
+ MOD_SUB_DIRS += lapb
+ endif
+endif
+
ifeq ($(CONFIG_NETROM),y)
SUB_DIRS += netrom
else
@@ -73,16 +97,36 @@ else
endif
endif
-L_TARGET := network.a
-L_OBJS := socket.o protocols.o sysctl_net.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+ifeq ($(CONFIG_SUNRPC),y)
+SUB_DIRS += sunrpc
+else
+ ifeq ($(CONFIG_SUNRPC),m)
+ MOD_SUB_DIRS += sunrpc
+ endif
+endif
+
+# We must attach netsyms.o to socket.o, as otherwise there is nothing
+# to pull the object file from the archive.
+
+SOCK := socket.o
ifeq ($(CONFIG_NET),y)
ifeq ($(CONFIG_MODULES),y)
-LX_OBJS = netsyms.o
+O_TARGET := sock_n_syms.o
+O_OBJS := socket.o
+OX_OBJS := netsyms.o
+SOCK := $(O_TARGET)
endif
endif
+L_TARGET := network.a
+L_OBJS := $(SOCK) protocols.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+
M_OBJS :=
+ifeq ($(CONFIG_SYSCTL),y)
+L_OBJS += sysctl_net.o
+endif
+
CONFIG_NETLINK_BUILTIN :=
CONFIG_NETLINK_MODULE :=
diff --git a/net/README b/net/README
index 8685b38f8..09327483c 100644
--- a/net/README
+++ b/net/README
@@ -4,15 +4,18 @@ Maintainers and developers for networking code sections
Code Section Bug Report Contact
-------------------+-------------------------------------------
802 [other ] alan@lxorguk.ukuu.org.uk
- [token ring ] needs a maintainer/debugger
+ [token ring ] pnorton@cts.com
appletalk alan@lxorguk.ukuu.org.uk and netatalk@umich.edu
ax25 jsn@cs.nott.ac.uk
core alan@lxorguk.ukuu.org.uk
ethernet alan@lxorguk.ukuu.org.uk
-ipv4 alan@lxorguk.ukuu.org.uk
+ipv4 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
+ipv6 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
ipx alan@lxorguk.ukuu.org.uk,greg@caldera.com
+lapb jsn@cs.nott.ac.uk
netrom jsn@cs.nott.ac.uk
rose jsn@cs.nott.ac.uk
+wanrouter genek@compuserve.com and dm@sangoma.com
unix alan@lxorguk.ukuu.org.uk
x25 jsn@cs.nott.ac.uk
diff --git a/net/appletalk/Makefile b/net/appletalk/Makefile
index bfe567264..19379a44a 100644
--- a/net/appletalk/Makefile
+++ b/net/appletalk/Makefile
@@ -8,9 +8,13 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := appletalk.o
-O_OBJS := aarp.o ddp.o sysctl_net_atalk.o
+O_OBJS := aarp.o ddp.o
M_OBJS := $(O_TARGET)
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_atalk.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index 4b8d4fb7e..e3e87f9e4 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -3,15 +3,15 @@
* ethernet 'ELAP'.
*
* Alan Cox <Alan.Cox@linux.org>
- * <alan@cymru.net>
*
- * This doesn't fit cleanly with the IP arp. This isn't a problem as
- * the IP arp wants extracting from the device layer in 1.3.x anyway.
- * [see the pre-1.3 test code for details 8)]
+ * This doesn't fit cleanly with the IP arp. Potentially we can use
+ * the generic neighbour discovery code to clean this up.
*
* FIXME:
* We ought to handle the retransmits with a single list and a
* separate fast timer for when it is needed.
+ * Use neighbour discovery code.
+ * Token Ring Support.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -115,8 +115,7 @@ static void aarp_send_query(struct aarp_entry *a)
skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
eah = (struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
skb->arp = 1;
- skb->free = 1;
- skb->dev = a->dev;
+ skb->dev = dev;
/*
* Set up the ARP.
@@ -149,8 +148,9 @@ static void aarp_send_query(struct aarp_entry *a)
/*
* Send it.
*/
-
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+
+ skb->priority = SOPRI_NORMAL;
+ dev_queue_xmit(skb);
/*
* Update the sending count
@@ -175,7 +175,6 @@ static void aarp_send_reply(struct device *dev, struct at_addr *us, struct at_ad
skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
eah = (struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
skb->arp = 1;
- skb->free = 1;
skb->dev = dev;
/*
@@ -212,8 +211,8 @@ static void aarp_send_reply(struct device *dev, struct at_addr *us, struct at_ad
/*
* Send it.
*/
-
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
+ dev_queue_xmit(skb);
}
@@ -239,7 +238,6 @@ void aarp_send_probe(struct device *dev, struct at_addr *us)
eah = (struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
skb->arp = 1;
- skb->free = 1;
skb->dev = dev;
/*
@@ -273,8 +271,8 @@ void aarp_send_probe(struct device *dev, struct at_addr *us)
/*
* Send it.
*/
-
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
+ dev_queue_xmit(skb);
}
@@ -443,9 +441,11 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
* Compressible ?
*
* IFF: src_net==dest_net==device_net
+ * (zero matches anything)
*/
- if(at->s_net==sa->s_net && sa->s_net==ddp->deh_snet)
+ if( ( ddp->deh_snet==0 || at->s_net==ddp->deh_snet)
+ &&( ddp->deh_dnet==0 || at->s_net==ddp->deh_dnet) )
{
skb_pull(skb,sizeof(struct ddpehdr)-4);
/*
@@ -467,9 +467,11 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
skb->data[2]=ft;
if(skb->sk==NULL)
- dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
else
- dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+ skb->priority = skb->sk->priority;
+ skb->dev = dev;
+ dev_queue_xmit(skb);
return 1;
}
@@ -497,9 +499,10 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
{
ddp_dl->datalink_header(ddp_dl, skb, ddp_eth_multicast);
if(skb->sk==NULL)
- dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
else
- dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+ skb->priority = skb->sk->priority;
+ dev_queue_xmit(skb);
restore_flags(flags);
return 1;
}
@@ -513,9 +516,10 @@ int aarp_send_ddp(struct device *dev,struct sk_buff *skb, struct at_addr *sa, vo
a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
ddp_dl->datalink_header(ddp_dl, skb, a->hwaddr);
if(skb->sk==NULL)
- dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
else
- dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+ skb->priority = skb->sk->priority;
+ dev_queue_xmit(skb);
restore_flags(flags);
return 1;
}
@@ -621,9 +625,10 @@ static void aarp_resolved(struct aarp_entry **list, struct aarp_entry *a, int ha
a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
ddp_dl->datalink_header(ddp_dl,skb,a->hwaddr);
if(skb->sk==NULL)
- dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
else
- dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+ skb->priority = skb->sk->priority;
+ dev_queue_xmit(skb);
}
}
else
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 211842144..eba533a23 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -3,10 +3,9 @@
* ethernet 'ELAP'.
*
* Alan Cox <Alan.Cox@linux.org>
- * <iialan@www.linux.org.uk>
*
- * With more than a little assistance from
- *
+ * With more than a little assistance from
+ *
* Wesley Craig <netatalk@umich.edu>
*
* Fixes:
@@ -24,16 +23,16 @@
* Alan Cox : Hooks for PPP (based on the
* localtalk hook).
* Alan Cox : Posix bits
+ * Alan Cox/Mike Freeman : Possible fix to NBP problems
+ * Bradford Johnson : IP-over-DDP (experimental)
*
* This program 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 of the License, or (at your option) any later version.
*
- * TODO
- * ASYNC I/O
*/
-
+
#include <linux/config.h>
#include <linux/module.h>
#include <asm/uaccess.h>
@@ -62,6 +61,8 @@
#include <net/p8022.h>
#include <net/psnap.h>
#include <net/sock.h>
+#include <linux/ip.h>
+#include <net/route.h>
#include <linux/atalk.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
@@ -78,6 +79,7 @@
#endif
struct datalink_proto *ddp_dl, *aarp_dl;
+static struct proto_ops atalk_dgram_ops;
#define min(a,b) (((a)<(b))?(a):(b))
@@ -87,73 +89,46 @@ struct datalink_proto *ddp_dl, *aarp_dl;
* *
\***********************************************************************************************************************/
-static atalk_socket *volatile atalk_socket_list=NULL;
+static struct sock *atalk_socket_list=NULL;
/*
* Note: Sockets may not be removed _during_ an interrupt or inet_bh
* handler using this technique. They can be added although we do not
* use this facility.
*/
-
-static void atalk_remove_socket(atalk_socket *sk)
+
+extern inline void atalk_remove_socket(struct sock *sk)
{
- unsigned long flags;
- atalk_socket *s;
-
- save_flags(flags);
- cli();
-
- s=atalk_socket_list;
- if(s==sk)
- {
- atalk_socket_list=s->next;
- restore_flags(flags);
- return;
- }
- while(s && s->next)
- {
- if(s->next==sk)
- {
- s->next=sk->next;
- restore_flags(flags);
- return;
- }
- s=s->next;
- }
- restore_flags(flags);
+ sklist_remove_socket(&atalk_socket_list,sk);
}
-static void atalk_insert_socket(atalk_socket *sk)
+extern inline void atalk_insert_socket(struct sock *sk)
{
- unsigned long flags;
- save_flags(flags);
- cli();
- sk->next=atalk_socket_list;
- atalk_socket_list=sk;
- restore_flags(flags);
+ sklist_insert_socket(&atalk_socket_list,sk);
}
-static atalk_socket *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif)
+static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif)
{
- atalk_socket *s;
+ struct sock *s;
- for( s = atalk_socket_list; s != NULL; s = s->next )
+ for( s = atalk_socket_list; s != NULL; s = s->next )
{
- if ( to->sat_port != s->protinfo.af_at.src_port )
+ if ( to->sat_port != s->protinfo.af_at.src_port )
{
continue;
}
if ( to->sat_addr.s_net == 0 &&
to->sat_addr.s_node == ATADDR_BCAST &&
- s->protinfo.af_at.src_net == atif->address.s_net )
+ s->protinfo.af_at.src_net == atif->address.s_net )
{
break;
}
if ( to->sat_addr.s_net == s->protinfo.af_at.src_net &&
- (to->sat_addr.s_node == s->protinfo.af_at.src_node
- ||to->sat_addr.s_node == ATADDR_BCAST ))
+ (to->sat_addr.s_node == s->protinfo.af_at.src_node
+ ||to->sat_addr.s_node == ATADDR_BCAST
+ ||to->sat_addr.s_node == ATADDR_ANYNODE ))
{
break;
}
@@ -166,22 +141,22 @@ static atalk_socket *atalk_search_socket(struct sockaddr_at *to, struct atalk_if
/*
* Find a socket in the list.
*/
-
-static atalk_socket *atalk_find_socket(struct sockaddr_at *sat)
+
+static struct sock *atalk_find_socket(struct sockaddr_at *sat)
{
- atalk_socket *s;
+ struct sock *s;
- for ( s = atalk_socket_list; s != NULL; s = s->next )
+ for ( s = atalk_socket_list; s != NULL; s = s->next )
{
- if ( s->protinfo.af_at.src_net != sat->sat_addr.s_net )
+ if ( s->protinfo.af_at.src_net != sat->sat_addr.s_net )
{
continue;
}
- if ( s->protinfo.af_at.src_node != sat->sat_addr.s_node )
+ if ( s->protinfo.af_at.src_node != sat->sat_addr.s_node )
{
continue;
}
- if ( s->protinfo.af_at.src_port != sat->sat_port )
+ if ( s->protinfo.af_at.src_port != sat->sat_port )
{
continue;
}
@@ -190,60 +165,19 @@ static atalk_socket *atalk_find_socket(struct sockaddr_at *sat)
return( s );
}
-/*
- * This is only called from user mode. Thus it protects itself against
- * interrupt users but doesn't worry about being called during work.
- * Once it is removed from the queue no interrupt or bottom half will
- * touch it and we are (fairly 8-) ) safe.
- */
-
-static void atalk_destroy_socket(atalk_socket *sk);
-
-/*
- * Handler for deferred kills.
- */
-
-static void atalk_destroy_timer(unsigned long data)
-{
- atalk_destroy_socket((atalk_socket *)data);
-}
-
-static void atalk_destroy_socket(atalk_socket *sk)
+extern inline void atalk_destroy_socket(struct sock *sk)
{
- struct sk_buff *skb;
- atalk_remove_socket(sk);
-
- while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
- {
- kfree_skb(skb,FREE_READ);
- }
-
- if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
- {
- sk_free(sk);
- MOD_DEC_USE_COUNT;
- }
- else
- {
- /*
- * Someone is using our buffers still.. defer
- */
- init_timer(&sk->timer);
- sk->timer.expires=jiffies+10*HZ;
- sk->timer.function=atalk_destroy_timer;
- sk->timer.data = (unsigned long)sk;
- add_timer(&sk->timer);
- }
+ sklist_destroy_socket(&atalk_socket_list,sk);
+ MOD_DEC_USE_COUNT;
}
-
/*
- * Called from proc fs
+ * Called from proc fs
*/
-
+
int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
- atalk_socket *s;
+ struct sock *s;
int len=0;
off_t pos=0;
off_t begin=0;
@@ -264,12 +198,14 @@ int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dum
ntohs(s->protinfo.af_at.dest_net),
s->protinfo.af_at.dest_node,
s->protinfo.af_at.dest_port);
- len += sprintf (buffer+len,"%08X:%08X ", s->wmem_alloc, s->rmem_alloc);
+ len += sprintf (buffer+len,"%08X:%08X ",
+ atomic_read(&s->wmem_alloc),
+ atomic_read(&s->rmem_alloc));
len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
-
+
/* Are we still dumping unwanted data then discard the record */
pos=begin+len;
-
+
if(pos<offset)
{
len=0; /* Keep dumping into the buffer start */
@@ -278,13 +214,13 @@ int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dum
if(pos>offset+length) /* We have dumped enough */
break;
}
-
+
/* The data in question runs from begin to begin+len */
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Remove unwanted header data from length */
if(len>length)
len=length; /* Remove unwanted tail data from length */
-
+
return len;
}
@@ -302,38 +238,41 @@ static struct atalk_iface *atalk_iface_list=NULL;
/*
* Appletalk interface control
*/
-
+
/*
- * Drop a device. Doesn't drop any of its routes - that is the
- * the callers problem. Called when we down the interface or
+ * Drop a device. Doesn't drop any of its routes - that is the
+ * the callers problem. Called when we down the interface or
* delete the address.
*/
-
+
static void atif_drop_device(struct device *dev)
{
struct atalk_iface **iface = &atalk_iface_list;
struct atalk_iface *tmp;
- while ((tmp = *iface) != NULL)
+ while ((tmp = *iface) != NULL)
{
- if (tmp->dev == dev)
+ if (tmp->dev == dev)
{
*iface = tmp->next;
kfree_s(tmp, sizeof(struct atalk_iface));
+ dev->atalk_ptr=NULL;
}
else
iface = &tmp->next;
}
+ MOD_DEC_USE_COUNT;
}
static struct atalk_iface *atif_add_device(struct device *dev, struct at_addr *sa)
{
struct atalk_iface *iface=(struct atalk_iface *)
kmalloc(sizeof(*iface), GFP_KERNEL);
- unsigned long flags;
+ unsigned long flags;
if(iface==NULL)
return NULL;
iface->dev=dev;
+ dev->atalk_ptr=iface;
iface->address= *sa;
iface->status=0;
save_flags(flags);
@@ -341,13 +280,14 @@ static struct atalk_iface *atif_add_device(struct device *dev, struct at_addr *s
iface->next=atalk_iface_list;
atalk_iface_list=iface;
restore_flags(flags);
+ MOD_INC_USE_COUNT;
return iface;
}
/*
* Perform phase 2 AARP probing on our tentative address.
*/
-
+
static int atif_probe_device(struct atalk_iface *atif)
{
int ct;
@@ -356,19 +296,18 @@ static int atif_probe_device(struct atalk_iface *atif)
int probe_node=atif->address.s_node;
int netct;
int nodect;
-
+
struct ifreq atreq;
struct sockaddr_at *sa;
int err;
/*
* THIS IS A HACK: Farallon cards want to do their own picking of
- * addresses. This needs tidying up post 1.4, but we need it in
- * now for the 1.4 release as is.
- *
+ * addresses. This needs tidying up when someone does localtalk
+ * drivers
*/
if((atif->dev->type == ARPHRD_LOCALTLK || atif->dev->type == ARPHRD_PPP)
- && atif->dev->do_ioctl)
+ && atif->dev->do_ioctl)
{
/* fake up the request and pass it down */
sa = (struct sockaddr_at*)&atreq.ifr_addr;
@@ -388,11 +327,11 @@ static int atif_probe_device(struct atalk_iface *atif)
* properly. We can then also dump the localtalk test.
*/
return err;
- }
+ }
/*
* Offset the network we start probing with.
*/
-
+
if(probe_net==ATADDR_ANYNET)
{
if(!netrange)
@@ -400,15 +339,15 @@ static int atif_probe_device(struct atalk_iface *atif)
else
probe_net=ntohs(atif->nets.nr_firstnet) + (jiffies%netrange);
}
-
+
if(probe_node == ATADDR_ANYNODE)
probe_node = jiffies&0xFF;
-
-
+
+
/*
* Scan the networks.
*/
-
+
for(netct=0;netct<=netrange;netct++)
{
/*
@@ -450,10 +389,9 @@ static int atif_probe_device(struct atalk_iface *atif)
struct at_addr *atalk_find_dev_addr(struct device *dev)
{
- struct atalk_iface *iface;
- for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
- if(iface->dev==dev)
- return &iface->address;
+ struct atalk_iface *iface=dev->atalk_ptr;
+ if(iface)
+ return &iface->address;
return NULL;
}
@@ -463,55 +401,40 @@ static struct at_addr *atalk_find_primary(void)
for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
if(!(iface->dev->flags&IFF_LOOPBACK))
return &iface->address;
- if ( atalk_iface_list != NULL ) {
- return &atalk_iface_list->address;
- } else {
- return NULL;
- }
-}
-
-/*
- * Give a device find its atif control structure
- */
-
-struct atalk_iface *atalk_find_dev(struct device *dev)
-{
- struct atalk_iface *iface;
- for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
- if(iface->dev==dev)
- return iface;
- return NULL;
+ if ( atalk_iface_list != NULL )
+ return &atalk_iface_list->address;
+ else
+ return NULL;
}
/*
* Find a match for 'any network' - ie any of our interfaces with that
* node number will do just nicely.
*/
-
+
static struct atalk_iface *atalk_find_anynet(int node, struct device *dev)
{
- struct atalk_iface *iface;
- for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
- {
- if ( iface->dev != dev || ( iface->status & ATIF_PROBE ))
- continue;
- if ( node == ATADDR_BCAST || iface->address.s_node == node )
- return iface;
- }
+ struct atalk_iface *iface=dev->atalk_ptr;
+ if (iface==NULL || ( iface->status & ATIF_PROBE ))
+ return NULL;
+ if ( node == ATADDR_BCAST || iface->address.s_node == node || node == ATADDR_ANYNODE)
+ return iface;
return NULL;
}
/*
* Find a match for a specific network:node pair
*/
-
+
static struct atalk_iface *atalk_find_interface(int net, int node)
{
struct atalk_iface *iface;
for(iface=atalk_iface_list;iface!=NULL;iface=iface->next)
{
- if((node==ATADDR_BCAST || iface->address.s_node==node)
- && iface->address.s_net==net && !(iface->status&ATIF_PROBE))
+ if((node==ATADDR_BCAST || node==ATADDR_ANYNODE
+ || iface->address.s_node==node)
+ && iface->address.s_net==net
+ && !(iface->status&ATIF_PROBE))
return iface;
}
return NULL;
@@ -523,7 +446,7 @@ static struct atalk_iface *atalk_find_interface(int net, int node)
* the socket (later on...). We know about host routes and the fact
* that a route must be direct to broadcast.
*/
-
+
static struct atalk_route *atrtr_find(struct at_addr *target)
{
struct atalk_route *r;
@@ -542,12 +465,12 @@ static struct atalk_route *atrtr_find(struct at_addr *target)
return NULL;
}
-
+
/*
* Given an appletalk network find the device to use. This can be
- * a simple lookup. Funny stuff like routers can wait 8)
+ * a simple lookup.
*/
-
+
static struct device *atrtr_get_dev(struct at_addr *sa)
{
struct atalk_route *atr=atrtr_find(sa);
@@ -560,7 +483,7 @@ static struct device *atrtr_get_dev(struct at_addr *sa)
/*
* Set up a default router.
*/
-
+
static void atrtr_set_default(struct device *dev)
{
atrtr_default.dev=dev;
@@ -574,7 +497,7 @@ static void atrtr_set_default(struct device *dev)
* entry in the list. While it uses netranges we always set them to one
* entry to work like netatalk.
*/
-
+
static int atrtr_create(struct rtentry *r, struct device *devhint)
{
struct sockaddr_at *ta=(struct sockaddr_at *)&r->rt_dst;
@@ -582,22 +505,22 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
struct atalk_route *rt;
struct atalk_iface *iface, *riface;
unsigned long flags;
-
+
save_flags(flags);
-
+
/*
* Fixme: Raise/Lower a routing change semaphore for these
* operations.
*/
-
+
/*
* Validate the request
- */
+ */
if(ta->sat_family!=AF_APPLETALK)
return -EINVAL;
if(devhint == NULL && ga->sat_family != AF_APPLETALK)
return -EINVAL;
-
+
/*
* Now walk the routing table and make our decisions
*/
@@ -607,7 +530,7 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
if(r->rt_flags != rt->flags)
continue;
- if(ta->sat_addr.s_net == rt->target.s_net)
+ if(ta->sat_addr.s_net == rt->target.s_net)
{
if(!(rt->flags&RTF_HOST))
break;
@@ -616,9 +539,9 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
}
}
- if ( devhint == NULL )
+ if ( devhint == NULL )
{
- for ( riface = NULL, iface = atalk_iface_list; iface; iface = iface->next )
+ for ( riface = NULL, iface = atalk_iface_list; iface; iface = iface->next )
{
if ( riface == NULL && ntohs( ga->sat_addr.s_net ) >= ntohs( iface->nets.nr_firstnet ) &&
ntohs( ga->sat_addr.s_net ) <= ntohs( iface->nets.nr_lastnet ))
@@ -646,11 +569,11 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
/*
* Fill in the entry.
*/
- rt->target=ta->sat_addr;
+ rt->target=ta->sat_addr;
rt->dev=devhint;
rt->flags=r->rt_flags;
rt->gateway=ga->sat_addr;
-
+
restore_flags(flags);
return 0;
}
@@ -659,17 +582,17 @@ static int atrtr_create(struct rtentry *r, struct device *devhint)
/*
* Delete a route. Find it and discard it.
*/
-
+
static int atrtr_delete( struct at_addr *addr )
{
struct atalk_route **r = &atalk_router_list;
struct atalk_route *tmp;
- while ((tmp = *r) != NULL)
+ while ((tmp = *r) != NULL)
{
if (tmp->target.s_net == addr->s_net &&
(!(tmp->flags&RTF_GATEWAY) ||
- tmp->target.s_node == addr->s_node ))
+ tmp->target.s_node == addr->s_node ))
{
*r = tmp->next;
kfree_s(tmp, sizeof(struct atalk_route));
@@ -684,15 +607,15 @@ static int atrtr_delete( struct at_addr *addr )
* Called when a device is downed. Just throw away any routes
* via it.
*/
-
+
void atrtr_device_down(struct device *dev)
{
struct atalk_route **r = &atalk_router_list;
struct atalk_route *tmp;
- while ((tmp = *r) != NULL)
+ while ((tmp = *r) != NULL)
{
- if (tmp->dev == dev)
+ if (tmp->dev == dev)
{
*r = tmp->next;
kfree_s(tmp, sizeof(struct atalk_route));
@@ -727,7 +650,7 @@ static int ddp_device_event(struct notifier_block *this, unsigned long event, vo
/*
* Device configuration ioctl calls.
*/
-
+
int atif_ioctl(int cmd, void *arg)
{
struct ifreq atreq;
@@ -740,17 +663,17 @@ int atif_ioctl(int cmd, void *arg)
int ct;
int limit;
struct rtentry rtdef;
-
+
err = copy_from_user(&atreq,arg,sizeof(atreq));
if (err)
- return -EFAULT;
+ return -EFAULT;
if((dev=dev_get(atreq.ifr_name))==NULL)
return -ENODEV;
-
+
sa=(struct sockaddr_at*)&atreq.ifr_addr;
atif=atalk_find_dev(dev);
-
+
switch(cmd)
{
case SIOCSIFADDR:
@@ -778,7 +701,7 @@ int atif_ioctl(int cmd, void *arg)
*/
if(atif->status&ATIF_PROBE)
return -EBUSY;
-
+
atif->address.s_net=sa->sat_addr.s_net;
atif->address.s_node=sa->sat_addr.s_node;
atrtr_device_down(dev); /* Flush old routes */
@@ -786,14 +709,16 @@ int atif_ioctl(int cmd, void *arg)
else
{
atif=atif_add_device(dev, &sa->sat_addr);
+ if (atif == NULL)
+ return -ENOMEM;
}
atif->nets= *nr;
/*
- * Check if the chosen address is used. If so we
- * error and atalkd will try another.
+ * Check if the chosen address is used. If so we
+ * error and atalkd will try another.
*/
-
+
if(!(dev->flags&IFF_LOOPBACK) && atif_probe_device(atif)<0)
{
atif_drop_device(dev);
@@ -804,7 +729,7 @@ int atif_ioctl(int cmd, void *arg)
* Hey it worked - add the direct
* routes.
*/
-
+
sa=(struct sockaddr_at *)&rtdef.rt_gateway;
sa->sat_family=AF_APPLETALK;
sa->sat_addr.s_net=atif->address.s_net;
@@ -818,13 +743,13 @@ int atif_ioctl(int cmd, void *arg)
/*
* Routerless initial state.
*/
- if(nr->nr_firstnet==htons(0) && nr->nr_lastnet==htons(0xFFFE))
+ if(nr->nr_firstnet==htons(0) && nr->nr_lastnet==htons(0xFFFE))
{
sa->sat_addr.s_net=atif->address.s_net;
atrtr_create(&rtdef, dev);
atrtr_set_default(dev);
- }
- else
+ }
+ else
{
limit=ntohs(nr->nr_lastnet);
if(limit-ntohs(nr->nr_firstnet) > 256)
@@ -866,15 +791,15 @@ int atif_ioctl(int cmd, void *arg)
/*
* Routing ioctl() calls
*/
-
+
static int atrtr_ioctl(unsigned int cmd, void *arg)
{
int err;
struct rtentry rt;
-
+
err = copy_from_user(&rt,arg,sizeof(rt));
if (err)
- return -EFAULT;
+ return -EFAULT;
switch(cmd)
{
@@ -971,15 +896,15 @@ int atalk_rt_get_info(char *buffer, char **start, off_t offset, int length, int
* Checksum: This is 'optional'. It's quite likely also a good
* candidate for assembler hackery 8)
*/
-
+
unsigned short atalk_checksum(struct ddpehdr *ddp, int len)
{
unsigned long sum=0; /* Assume unsigned long is >16 bits */
unsigned char *data=(unsigned char *)ddp;
-
+
len-=4; /* skip header 4 bytes */
- data+=4;
-
+ data+=4;
+
/* This ought to be unwrapped neatly. I'll trust gcc for now */
while(len--)
{
@@ -996,133 +921,16 @@ unsigned short atalk_checksum(struct ddpehdr *ddp, int len)
return htons((unsigned short)sum);
return 0xFFFF; /* Use 0xFFFF for 0. 0 itself means none */
}
-
-/*
- * Generic fcntl calls are already dealt with. If we don't need funny ones
- * this is the all you need. Async I/O is also separate.
- */
-
-static int atalk_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
-/* atalk_socket *sk=(atalk_socket *)sock->data;*/
- switch(cmd)
- {
- default:
- return(-EINVAL);
- }
-}
-
-/*
- * Set 'magic' options for appletalk. If we don't have any this is fine
- * as it is.
- */
-
-static int atalk_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
- atalk_socket *sk;
- int err,opt;
-
- sk=(atalk_socket *)sock->data;
-
- if(optval==NULL)
- return(-EINVAL);
-
- err = get_user(opt, (int *)optval);
- if (err)
- return err;
-
- switch(level)
- {
- case SOL_ATALK:
- switch(optname)
- {
- default:
- return -EOPNOTSUPP;
- }
- break;
-
- case SOL_SOCKET:
- return sock_setsockopt(sk,level,optname,optval,optlen);
-
- default:
- return -EOPNOTSUPP;
- }
-}
-
-
-/*
- * Get any magic options. Comment above applies.
- */
-
-static int atalk_getsockopt(struct socket *sock, int level, int optname,
- char *optval, int *optlen)
-{
- atalk_socket *sk;
- int val=0;
- int err;
-
- sk=(atalk_socket *)sock->data;
-
- switch(level)
- {
-
- case SOL_ATALK:
- switch(optname)
- {
- default:
- return -ENOPROTOOPT;
- }
- break;
-
- case SOL_SOCKET:
- return sock_getsockopt(sk,level,optname,optval,optlen);
-
- default:
- return -EOPNOTSUPP;
- }
- err = put_user(sizeof(int),optlen);
- if (!err)
- err = put_user(val, (int *) optval);
- return err;
-}
-
-/*
- * Only for connection oriented sockets - ignore
- */
-
-static int atalk_listen(struct socket *sock, int backlog)
-{
- return -EOPNOTSUPP;
-}
-
-/*
- * These are standard.
- */
-
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk, int len)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket,0);
- }
-}
/*
* Create a socket. Initialise the socket, blank the addresses
* set the state.
*/
-
+
static int atalk_create(struct socket *sock, int protocol)
{
- atalk_socket *sk;
- sk=(atalk_socket *)sk_alloc(GFP_KERNEL);
+ struct sock *sk;
+ sk=sk_alloc(GFP_KERNEL);
if(sk==NULL)
return(-ENOMEM);
switch(sock->type)
@@ -1132,6 +940,7 @@ static int atalk_create(struct socket *sock, int protocol)
case SOCK_RAW:
/* We permit DDP datagram sockets */
case SOCK_DGRAM:
+ sock->ops = &atalk_dgram_ops;
break;
default:
sk_free((void *)sk);
@@ -1140,32 +949,10 @@ static int atalk_create(struct socket *sock, int protocol)
MOD_INC_USE_COUNT;
- sk->no_check=0; /* Checksums on by default */
- sk->allocation=GFP_KERNEL;
- sk->rcvbuf=SK_RMEM_MAX;
- sk->sndbuf=SK_WMEM_MAX;
- sk->pair=NULL;
- sk->priority=1;
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
- sk->state=TCP_CLOSE;
- sk->socket=sock;
- sk->type=sock->type;
+ sock_init_data(sock,sk);
+ /* Checksums on by default */
sk->mtu=DDP_MAXSZ;
-
- if(sock!=NULL)
- {
- sock->data=(void *)sk;
- sk->sleep=sock->wait;
- }
-
- sk->state_change=def_callback1;
- sk->data_ready=def_callback2;
- sk->write_space=def_callback1;
- sk->error_report=def_callback1;
-
sk->zapped=1;
return(0);
}
@@ -1173,7 +960,7 @@ static int atalk_create(struct socket *sock, int protocol)
/*
* Copy a socket. No work needed.
*/
-
+
static int atalk_dup(struct socket *newsock,struct socket *oldsock)
{
return(atalk_create(newsock,SOCK_DGRAM));
@@ -1182,25 +969,25 @@ static int atalk_dup(struct socket *newsock,struct socket *oldsock)
/*
* Free a socket. No work needed
*/
-
+
static int atalk_release(struct socket *sock, struct socket *peer)
{
- atalk_socket *sk=(atalk_socket *)sock->data;
+ struct sock *sk=sock->sk;
if(sk==NULL)
return(0);
if(!sk->dead)
sk->state_change(sk);
sk->dead=1;
- sock->data=NULL;
+ sock->sk=NULL;
atalk_destroy_socket(sk);
return(0);
}
-
+
/*
* Pick a source address if one is not given. Just return
* an error if not supportable.
*/
-
+
static int atalk_pick_port(struct sockaddr_at *sat)
{
for ( sat->sat_port = ATPORT_RESERVED; sat->sat_port < ATPORT_LAST; sat->sat_port++ )
@@ -1210,8 +997,8 @@ static int atalk_pick_port(struct sockaddr_at *sat)
}
return -EBUSY;
}
-
-static int atalk_autobind(atalk_socket *sk)
+
+static int atalk_autobind(struct sock *sk)
{
struct at_addr *ap = atalk_find_primary();
struct sockaddr_at sat;
@@ -1233,17 +1020,17 @@ static int atalk_autobind(atalk_socket *sk)
/*
* Set the address 'our end' of the connection.
*/
-
+
static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- atalk_socket *sk;
+ struct sock *sk;
struct sockaddr_at *addr=(struct sockaddr_at *)uaddr;
-
- sk=(atalk_socket *)sock->data;
-
+
+ sk=sock->sk;
+
if(sk->zapped==0)
return(-EINVAL);
-
+
if(addr_len!=sizeof(struct sockaddr_at))
return -EINVAL;
@@ -1259,7 +1046,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
sk->protinfo.af_at.src_node=addr->sat_addr.s_node=ap->s_node;
}
else
- {
+ {
if ( atalk_find_interface( addr->sat_addr.s_net, addr->sat_addr.s_node ) == NULL )
return -EADDRNOTAVAIL;
sk->protinfo.af_at.src_net=addr->sat_addr.s_net;
@@ -1277,7 +1064,7 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
sk->protinfo.af_at.src_port=addr->sat_port;
if(atalk_find_socket(addr)!=NULL)
- return -EADDRINUSE;
+ return -EADDRINUSE;
atalk_insert_socket(sk);
sk->zapped=0;
@@ -1287,35 +1074,40 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/*
* Set the address we talk to.
*/
-
+
static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
- atalk_socket *sk=(atalk_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_at *addr;
-
- sk->state = TCP_CLOSE;
+
+ sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
if(addr_len!=sizeof(*addr))
return(-EINVAL);
addr=(struct sockaddr_at *)uaddr;
-
+
if(addr->sat_family!=AF_APPLETALK)
return -EAFNOSUPPORT;
-#if 0 /* Netatalk doesn't check this - fix netatalk first!*/
if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
+ {
+#if 1
+ printk(KERN_WARNING "%s is broken and did not set SO_BROADCAST. It will break when 2.2 is released.\n",
+ current->comm);
+#else
return -EACCES;
-#endif
+#endif
+ }
if(sk->zapped)
{
if(atalk_autobind(sk)<0)
return -EBUSY;
- }
-
+ }
+
if(atrtr_get_dev(&addr->sat_addr)==NULL)
return -ENETUNREACH;
-
+
sk->protinfo.af_at.dest_port=addr->sat_port;
sk->protinfo.af_at.dest_net=addr->sat_addr.s_net;
sk->protinfo.af_at.dest_node=addr->sat_addr.s_node;
@@ -1327,7 +1119,7 @@ static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
/*
* Not relevant
*/
-
+
static int atalk_socketpair(struct socket *sock1, struct socket *sock2)
{
return(-EOPNOTSUPP);
@@ -1336,11 +1128,13 @@ static int atalk_socketpair(struct socket *sock1, struct socket *sock2)
/*
* Not relevant
*/
-
+
static int atalk_accept(struct socket *sock, struct socket *newsock, int flags)
{
- if(newsock->data)
- sk_free(newsock->data);
+ if(newsock->sk) {
+ sk_free(newsock->sk);
+ MOD_DEC_USE_COUNT;
+ }
return -EOPNOTSUPP;
}
@@ -1348,22 +1142,22 @@ static int atalk_accept(struct socket *sock, struct socket *newsock, int flags)
* Find the name of an appletalk socket. Just copy the right
* fields into the sockaddr.
*/
-
+
static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sockaddr_at sat;
- atalk_socket *sk;
-
- sk=(atalk_socket *)sock->data;
+ struct sock *sk;
+
+ sk=sock->sk;
if(sk->zapped)
{
if(atalk_autobind(sk)<0)
return -ENOBUFS;
- }
-
+ }
+
*uaddr_len = sizeof(struct sockaddr_at);
-
+
if(peer)
{
if(sk->state!=TCP_ESTABLISHED)
@@ -1383,29 +1177,233 @@ static int atalk_getname(struct socket *sock, struct sockaddr *uaddr,
return(0);
}
+/*
+ * IP-over-DDP support. Under construction.
+ */
+
+#ifdef CONFIG_IPDDP
+
+#define SIOCADDIPDDPRT SIOCDEVPRIVATE
+#define SIOCDELIPDDPRT SIOCDEVPRIVATE+1
+#define SIOCFINDIPDDPRT SIOCDEVPRIVATE+2
+
+struct ipddp_route
+{
+ struct device *dev; /* Carrier device */
+ __u32 ip; /* IP address */
+ struct at_addr at; /* Gateway appletalk address */
+ int flags;
+ struct ipddp_route *next;
+};
+
+static struct ipddp_route *ipddp_route_head;
+
+static struct ipddp_route ipddp_route_test;
+
+int ipddp_open(struct device *dev)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+int ipddp_close(struct device *dev)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int ipddp_xmit(struct sk_buff *skb, struct device *dev)
+{
+ /* Retrieve the saved address hint */
+ struct at_addr *a=(struct at_addr *)skb->data;
+ skb_pull(skb,4);
+
+ ((struct net_device_stats *) dev->priv)->tx_packets++;
+ ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
+
+ /* printk("ipddp_xmit called with headroom %d\n",skb_headroom(skb)); */
+
+ if( aarp_send_ddp(skb->dev,skb,a,NULL) < 0 )
+ dev_kfree_skb(skb,FREE_WRITE);
+
+ return 0;
+}
+
+struct net_device_stats *ipddp_get_stats(struct device *dev)
+{
+ return (struct net_device_stats *) dev->priv;
+}
+
+int ipddp_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ipddp_route *urt = (struct ipddp_route *)ifr->ifr_data;
+
+ if(!suser())
+ return -EPERM;
+
+ /* for now we only have one route at a time */
+
+ switch(cmd)
+ {
+ case SIOCADDIPDDPRT:
+ if(copy_from_user(&ipddp_route_test,urt,sizeof(struct ipddp_route)))
+ return -EFAULT;
+ ipddp_route_test.dev = atrtr_get_dev(&ipddp_route_test.at);
+ if (dev==NULL)
+ return -ENETUNREACH;
+ ipddp_route_test.next = NULL;
+ printk("added ipddp route through %s\n",ipddp_route_test.dev->name);
+ ipddp_route_head = &ipddp_route_test;
+ return 0;
+ case SIOCFINDIPDDPRT:
+ if(copy_to_user(urt,&ipddp_route_test,sizeof(struct ipddp_route)))
+ return -EFAULT;
+ return 0;
+ case SIOCDELIPDDPRT:
+ ipddp_route_test.dev = NULL;
+ ipddp_route_head = NULL;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+int ipddp_header (struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+ /* printk("ipddp_header\n"); */
+ /* Push down the header space and the type byte */
+ skb_push(skb, sizeof(struct ddpehdr)+1+4);
+ return 0;
+}
+
+/*
+ * Now the packet really wants to go out.
+ */
+
+int ipddp_rebuild_header (struct sk_buff *skb)
+{
+ struct ddpehdr *ddp;
+ struct at_addr at;
+ struct ipddp_route *rt;
+ struct at_addr *our_addr;
+ u32 paddr = ((struct rtable *)skb->dst)->rt_gateway;
+
+ /*
+ * On entry skb->data points to the ddpehdr we reserved earlier.
+ * skb->h.raw will be the higher level header.
+ */
+
+ /*
+ * We created this earlier
+ */
+
+ ddp = (struct ddpehdr *) (skb->data+4);
+
+ /* find appropriate route */
+
+ for(rt=ipddp_route_head;rt;rt=rt->next)
+ {
+ if(rt->ip == paddr)
+ break;
+ }
+
+ if(!rt) {
+ printk("ipddp unreachable dst %08lx\n",ntohl(paddr));
+ return -ENETUNREACH;
+ }
+
+ our_addr = atalk_find_dev_addr(rt->dev);
+
+ /* fill in ddpehdr */
+ ddp->deh_len = skb->len;
+ ddp->deh_hops = 1;
+ ddp->deh_pad = 0;
+ ddp->deh_sum = 0;
+ ddp->deh_dnet = rt->at.s_net; /* FIXME more hops?? */
+ ddp->deh_snet = our_addr->s_net;
+ ddp->deh_dnode = rt->at.s_node;
+ ddp->deh_snode = our_addr->s_node;
+ ddp->deh_dport = 72;
+ ddp->deh_sport = 72;
+
+ *((__u8 *)(ddp+1)) = 22; /* ddp type = IP */
+
+ /* fix up length field */
+ *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));
+
+ /* set skb->dev to appropriate device */
+ skb->dev = rt->dev;
+
+ /* skb->raddr = (unsigned long) at */
+ at = rt->at;
+ /* Hide it at the start of the buffer */
+ memcpy(skb->data,(void *)&at,sizeof(at));
+ skb->arp = 1; /* so the actual device doesn't try to arp it... */
+ skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
+
+ return 0;
+}
+
+int ipddp_init (struct device *dev)
+{
+ ether_setup(dev);
+ dev->hard_start_xmit = ipddp_xmit;
+ dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ if(!dev->priv)
+ return -ENOMEM;
+ memset(dev->priv,0,sizeof(struct enet_statistics));
+ dev->get_stats = ipddp_get_stats;
+ dev->do_ioctl = ipddp_ioctl;
+ dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */
+ dev->family = AF_INET;
+ dev->mtu = 585;
+ dev->flags |= IFF_NOARP;
+ dev->hard_header = ipddp_header; /* see ip_output.c */
+ dev->rebuild_header = ipddp_rebuild_header;
+ /*
+ * The worst case header we will need is currently a
+ * ethernet header (14 bytes) and a ddp header (sizeof ddpehdr+1)
+ * We send over SNAP so that takes another 8 bytes.
+ */
+ dev->hard_header_len = 14+8+sizeof(struct ddpehdr)+1;
+ dev->open = ipddp_open;
+ dev->stop = ipddp_close;
+
+ return 0;
+}
+
+static struct device dev_ipddp = {
+ "ipddp0\0 ",
+ 0, 0, 0, 0,
+ 0x0, 0,
+ 0, 0, 0, NULL, ipddp_init };
+
+#endif /* CONFIG_IPDDP */
+
/*
* Receive a packet (in skb) from device dev. This has come from the SNAP decoder, and on entry
- * skb->h.raw is the DDP header, skb->len is the DDP length. The physical headers have been
+ * skb->h.raw is the DDP header, skb->len is the DDP length. The physical headers have been
* extracted. PPP should probably pass frames marked as for this layer
* [ie ARPHRD_ETHERTALK]
*/
-
+
static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
- atalk_socket *sock;
+ struct sock *sock;
struct ddpehdr *ddp=(void *)skb->h.raw;
struct atalk_iface *atif;
struct sockaddr_at tosat;
int origlen;
-
+
/* Size check */
if(skb->len<sizeof(*ddp))
{
kfree_skb(skb,FREE_READ);
return(0);
}
-
-
+
+
/*
* Fix up the length field [Ok this is horrible but otherwise
* I end up with unions of bit fields and messy bit field order
@@ -1421,9 +1419,9 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
/*
* Trim buffer in case of stray trailing data
*/
-
+
origlen = skb->len;
-
+
skb_trim(skb,min(skb->len,ddp->deh_len));
/*
@@ -1431,7 +1429,7 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
* (Otherwise we'll detonate most spectacularly
* in the middle of recvmsg()).
*/
-
+
if(skb->len<sizeof(*ddp))
{
kfree_skb(skb,FREE_READ);
@@ -1440,7 +1438,7 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
/*
* Any checksums. Note we don't do htons() on this == is assumed to be
- * valid for net byte orders all over the networking code...
+ * valid for net byte orders all over the networking code...
*/
if(ddp->deh_sum && atalk_checksum(ddp, ddp->deh_len)!= ddp->deh_sum)
@@ -1450,16 +1448,12 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
return(0);
}
-#ifdef CONFIG_FIREWALL
-
- if(call_in_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+ if(call_in_firewall(AF_APPLETALK, skb->dev, ddp, NULL,&skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_READ);
return 0;
}
-
-#endif
-
+
/* Check the packet is aimed at us */
if(ddp->deh_dnet == 0) /* Net 0 is 'this network' */
@@ -1468,30 +1462,29 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
atif=atalk_find_interface(ddp->deh_dnet,ddp->deh_dnode);
/* Not ours */
- if(atif==NULL)
+ if(atif==NULL)
{
struct atalk_route *rt;
struct at_addr ta;
/* Don't route multicast, etc., packets, or packets
sent to "this network" */
- if (skb->pkt_type != PACKET_HOST || ddp->deh_dnet == 0)
+ if (skb->pkt_type != PACKET_HOST || ddp->deh_dnet == 0)
{
kfree_skb(skb, FREE_READ);
return(0);
}
-
-#ifdef CONFIG_FIREWALL
+
/*
* Check firewall allows this routing
*/
-
- if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+
+ if(call_fw_firewall(AF_APPLETALK, skb->dev, ddp, NULL, &skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_READ);
return(0);
}
-#endif
+
ta.s_net=ddp->deh_dnet;
ta.s_node=ddp->deh_dnode;
@@ -1508,7 +1501,7 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
* Route goes through another gateway, so
* set the target to the gateway instead.
*/
-
+
if(rt->flags&RTF_GATEWAY)
{
ta.s_net = rt->gateway.s_net;
@@ -1516,14 +1509,14 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
}
/* Fix up skb->len field */
- skb_trim(skb,min(origlen, rt->dev->hard_header_len +
+ skb_trim(skb,min(origlen, rt->dev->hard_header_len +
ddp_dl->header_length + ddp->deh_len));
*((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* Mend the byte order */
/*
* Send the buffer onwards
*/
-
+
skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
if(skb)
{
@@ -1548,7 +1541,29 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
return(0);
}
-
+#ifdef CONFIG_IPDDP
+ /*
+ * Check if IP-over-DDP
+ */
+
+ if(skb->data[12]==22)
+ {
+ struct net_device_stats *estats =
+ (struct net_device_stats *) dev_ipddp.priv;
+ skb->protocol=htons(ETH_P_IP);
+ skb_pull(skb,13);
+ skb->dev=&dev_ipddp;
+ skb->h.raw = skb->data;
+ /* printk("passing up ipddp, 0x%02x better be 45\n",skb->data[0]);
+ * printk("tot_len %d, skb->len %d\n",
+ * ntohs(skb->h.iph->tot_len),skb->len);
+ */
+ estats->rx_packets++;
+ estats->rx_bytes+=skb->len+13;
+ netif_rx(skb);
+ return 0;
+ }
+#endif /* CONFIG_IPDDP */
/*
* Queue packet (standard)
*/
@@ -1569,7 +1584,7 @@ static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
* header and append a long one.
*/
-
+
static int ltalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
struct ddpehdr *ddp;
@@ -1577,49 +1592,49 @@ static int ltalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
/*
* Expand any short form frames.
*/
-
+
if(skb->mac.raw[2]==1)
{
/*
* Find our address.
*/
-
+
ap=atalk_find_dev_addr(dev);
if(ap==NULL || skb->len<sizeof(struct ddpshdr))
{
kfree_skb(skb, FREE_READ);
return 0;
}
-
+
/*
* The push leaves us with a ddephdr not an shdr, and
* handily the port bytes in the right place preset.
*/
-
+
skb_push(skb, sizeof(*ddp)-4);
ddp=(struct ddpehdr *)skb->data;
-
+
/*
* Now fill in the long header.
*/
-
+
/*
* These two first. The mac overlays the new source/dest
* network information so we MUST copy these before
* we write the network numbers !
*/
-
+
ddp->deh_dnode=skb->mac.raw[0]; /* From physical header */
ddp->deh_snode=skb->mac.raw[1]; /* From physical header */
-
+
ddp->deh_dnet=ap->s_net; /* Network number */
- ddp->deh_snet=ap->s_net;
+ ddp->deh_snet=ap->s_net;
ddp->deh_sum=0; /* No checksum */
/*
* Not sure about this bit...
*/
ddp->deh_len=skb->len;
- ddp->deh_hops=15; /* Non routable, so force a drop
+ ddp->deh_hops=15; /* Non routable, so force a drop
if we slip up later */
*((__u16 *)ddp)=htons(*((__u16 *)ddp)); /* Mend the byte order */
}
@@ -1627,9 +1642,10 @@ static int ltalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type
return atalk_rcv(skb,dev,pt);
}
-static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
{
- atalk_socket *sk=(atalk_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name;
struct sockaddr_at local_satalk, gsat;
struct sk_buff *skb;
@@ -1639,13 +1655,14 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
struct atalk_route *rt;
int loopback=0;
int err;
-
- if(flags)
+ int flags = msg->msg_flags;
+
+ if(flags&~MSG_DONTWAIT)
return -EINVAL;
-
+
if(len>587)
return -EMSGSIZE;
-
+
if(usat)
{
if(sk->zapped)
@@ -1658,10 +1675,10 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
return(-EINVAL);
if(usat->sat_family != AF_APPLETALK)
return -EINVAL;
-#if 0 /* netatalk doesn't implement this check */
+#if 0 /* netatalk doesn't implement this check */
if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
return -EPERM;
-#endif
+#endif
}
else
{
@@ -1673,19 +1690,18 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
usat->sat_addr.s_node=sk->protinfo.af_at.dest_node;
usat->sat_addr.s_net=sk->protinfo.af_at.dest_net;
}
-
+
/* Build a packet */
-
- if(sk->debug)
- printk("SK %p: Got address.\n",sk);
-
+
+ SOCK_DEBUG(sk, "SK %p: Got address.\n",sk);
+
size=sizeof(struct ddpehdr)+len+ddp_dl->header_length; /* For headers */
if(usat->sat_addr.s_net!=0 || usat->sat_addr.s_node == ATADDR_ANYNODE)
{
rt=atrtr_find(&usat->sat_addr);
if(rt==NULL)
- return -ENETUNREACH;
+ return -ENETUNREACH;
dev=rt->dev;
}
else
@@ -1699,26 +1715,23 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
dev=rt->dev;
}
- if(sk->debug)
- printk("SK %p: Size needed %d, device %s\n", sk, size, dev->name);
-
+ SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name);
+
size += dev->hard_header_len;
- skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
+ skb = sock_alloc_send_skb(sk, size, 0, flags&MSG_DONTWAIT, &err);
if(skb==NULL)
return err;
skb->sk=sk;
- skb->free=1;
skb->arp=1;
skb_reserve(skb,ddp_dl->header_length);
skb_reserve(skb,dev->hard_header_len);
skb->dev=dev;
-
- if(sk->debug)
- printk("SK %p: Begin build.\n", sk);
-
+
+ SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
+
ddp=(struct ddpehdr *)skb_put(skb,sizeof(struct ddpehdr));
ddp->deh_pad=0;
ddp->deh_hops=0;
@@ -1737,36 +1750,31 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
ddp->deh_dport=usat->sat_port;
ddp->deh_sport=sk->protinfo.af_at.src_port;
- if(sk->debug)
- printk("SK %p: Copy user data (%d bytes).\n", sk, len);
-
+ SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len);
+
err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
if (err)
{
kfree_skb(skb, FREE_WRITE);
return -EFAULT;
}
-
+
if(sk->no_check==1)
ddp->deh_sum=0;
else
ddp->deh_sum=atalk_checksum(ddp, len+sizeof(*ddp));
-
-#ifdef CONFIG_FIREWALL
- if(call_out_firewall(AF_APPLETALK, skb->dev, ddp, NULL)!=FW_ACCEPT)
+ if(call_out_firewall(AF_APPLETALK, skb->dev, ddp, NULL, &skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_WRITE);
return -EPERM;
- }
-
-#endif
-
+ }
+
/*
* Loopback broadcast packets to non gateway targets (ie routes
* to group we are in)
*/
-
+
if(ddp->deh_dnode==ATADDR_BCAST)
{
if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
@@ -1775,8 +1783,7 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
if(skb2)
{
loopback=1;
- if(sk->debug)
- printk("SK %p: send out(copy).\n", sk);
+ SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk);
if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1)
kfree_skb(skb2, FREE_WRITE);
/* else queued/sent above in the aarp queue */
@@ -1784,53 +1791,46 @@ static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int n
}
}
- if((dev->flags&IFF_LOOPBACK) || loopback)
+ if((dev->flags&IFF_LOOPBACK) || loopback)
{
- if(sk->debug)
- printk("SK %p: Loop back.\n", sk);
+ SOCK_DEBUG(sk, "SK %p: Loop back.\n", sk);
/* loop back */
- atomic_sub(skb->truesize, &sk->wmem_alloc);
+ skb_orphan(skb);
ddp_dl->datalink_header(ddp_dl, skb, dev->dev_addr);
- skb->sk = NULL;
skb->mac.raw=skb->data;
skb->h.raw = skb->data + ddp_dl->header_length + dev->hard_header_len;
skb_pull(skb,dev->hard_header_len);
skb_pull(skb,ddp_dl->header_length);
atalk_rcv(skb,dev,NULL);
}
- else
+ else
{
- if(sk->debug)
- printk("SK %p: send out.\n", sk);
-
+ SOCK_DEBUG(sk, "SK %p: send out.\n", sk);
if ( rt->flags & RTF_GATEWAY ) {
gsat.sat_addr = rt->gateway;
usat = &gsat;
}
-
+
if(aarp_send_ddp(dev,skb,&usat->sat_addr, NULL)==-1)
kfree_skb(skb, FREE_WRITE);
/* else queued/sent above in the aarp queue */
}
- if(sk->debug)
- printk("SK %p: Done write (%d).\n", sk, len);
+ SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len);
return len;
}
-static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- atalk_socket *sk=(atalk_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name;
struct ddpehdr *ddp = NULL;
int copied = 0;
struct sk_buff *skb;
int er = 0;
-
- if(addr_len)
- *addr_len=sizeof(*sat);
- skb=skb_recv_datagram(sk,flags,noblock,&er);
+ skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&er);
if(skb==NULL)
return er;
@@ -1856,8 +1856,8 @@ static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size, int
msg->msg_flags|=MSG_TRUNC;
}
er = skb_copy_datagram_iovec(skb,sizeof(*ddp),msg->msg_iov,copied);
- if (er)
- goto out;
+ if (er)
+ goto out;
}
if(sat)
{
@@ -1866,10 +1866,11 @@ static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size, int
sat->sat_addr.s_node=ddp->deh_snode;
sat->sat_addr.s_net=ddp->deh_snet;
}
+ msg->msg_namelen=sizeof(*sat);
out:
skb_free_datagram(sk, skb);
return er ? er : (copied);
-}
+}
static int atalk_shutdown(struct socket *sk,int how)
@@ -1877,13 +1878,6 @@ static int atalk_shutdown(struct socket *sk,int how)
return -EOPNOTSUPP;
}
-static int atalk_select(struct socket *sock , int sel_type, select_table *wait)
-{
- atalk_socket *sk=(atalk_socket *)sock->data;
-
- return datagram_select(sk,sel_type,wait);
-}
-
/*
* Appletalk ioctl calls.
*/
@@ -1891,15 +1885,15 @@ static int atalk_select(struct socket *sock , int sel_type, select_table *wait)
static int atalk_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
{
long amount=0;
- atalk_socket *sk=(atalk_socket *)sock->data;
-
+ struct sock *sk=sock->sk;
+
switch(cmd)
{
/*
* Protocol layer
*/
case TIOCOUTQ:
- amount=sk->sndbuf-sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if(amount<0)
amount=0;
break;
@@ -1929,7 +1923,7 @@ static int atalk_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
return(atrtr_ioctl(cmd,(void *)arg));
/*
* Interface
- */
+ */
case SIOCGIFADDR:
case SIOCSIFADDR:
case SIOCGIFBRDADDR:
@@ -1965,10 +1959,14 @@ static int atalk_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
return put_user(amount, (int *)arg);
}
-static struct proto_ops atalk_proto_ops = {
+static struct net_proto_family atalk_family_ops = {
AF_APPLETALK,
-
- atalk_create,
+ atalk_create
+};
+
+static struct proto_ops atalk_dgram_ops = {
+ AF_APPLETALK,
+
atalk_dup,
atalk_release,
atalk_bind,
@@ -1976,13 +1974,13 @@ static struct proto_ops atalk_proto_ops = {
atalk_socketpair,
atalk_accept,
atalk_getname,
- atalk_select,
+ datagram_poll,
atalk_ioctl,
- atalk_listen,
+ sock_no_listen,
atalk_shutdown,
- atalk_setsockopt,
- atalk_getsockopt,
- atalk_fcntl,
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
atalk_sendmsg,
atalk_recvmsg
};
@@ -2038,16 +2036,16 @@ static struct proc_dir_entry proc_atalk_iface = {
void atalk_proto_init(struct net_proto *pro)
{
- (void) sock_register(atalk_proto_ops.family, &atalk_proto_ops);
+ (void) sock_register(&atalk_family_ops);
if ((ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv)) == NULL)
printk(KERN_CRIT "Unable to register DDP with SNAP.\n");
-
- ltalk_packet_type.type=htons(ETH_P_LOCALTALK);
+
+ ltalk_packet_type.type=htons(ETH_P_LOCALTALK);
dev_add_pack(&ltalk_packet_type);
-
+
ppptalk_packet_type.type=htons(ETH_P_PPPTALK);
dev_add_pack(&ppptalk_packet_type);
-
+
register_netdevice_notifier(&ddp_notifier);
aarp_proto_init();
@@ -2057,15 +2055,19 @@ void atalk_proto_init(struct net_proto *pro)
proc_net_register(&proc_atalk_iface);
#endif
+#ifdef CONFIG_IPDDP
+ register_netdev(&dev_ipddp);
+#endif /* CONFIG_IPDDP */
+
printk(KERN_INFO "Appletalk 0.18 for Linux NET3.037\n");
}
#ifdef MODULE
+EXPORT_NO_SYMBOLS;
int init_module(void)
{
atalk_proto_init(NULL);
- register_symtab(0);
return 0;
}
@@ -2073,7 +2075,7 @@ int init_module(void)
* FIX THIS: If there are any routes/devices configured
* for appletalk we must not be unloaded.
*/
-
+
/* Remove all route entries. Interrupts must be off. */
extern inline void free_route_list(void)
{
@@ -2113,12 +2115,12 @@ void cleanup_module(void)
proc_net_unregister(PROC_NET_ATALK);
proc_net_unregister(PROC_NET_AT_ROUTE);
proc_net_unregister(PROC_NET_ATIF);
-#endif
+#endif
unregister_netdevice_notifier(&ddp_notifier);
dev_remove_pack(&ltalk_packet_type);
dev_remove_pack(&ppptalk_packet_type);
unregister_snap_client(ddp_snap_id);
- sock_unregister(atalk_proto_ops.family);
+ sock_unregister(atalk_family_ops.family);
free_route_list();
free_interface_list();
diff --git a/net/ax25/Makefile b/net/ax25/Makefile
index 172d89eed..b89b517c7 100644
--- a/net/ax25/Makefile
+++ b/net/ax25/Makefile
@@ -9,11 +9,21 @@
O_TARGET := ax25.o
-O_OBJS := sysctl_net_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
+O_OBJS := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
+ ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
+ ax25_subr.o ax25_timer.o ax25_uid.o
M_OBJS := $(O_TARGET)
OX_OBJS += af_ax25.o
+ifeq ($(CONFIG_AX25_DAMA_SLAVE),y)
+O_OBJS += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
+endif
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_ax25.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 7d08fed10..489993da6 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 034
+ * AX.25 release 036
*
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.10 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -86,13 +86,16 @@
* Joerg(DL1BKE) Moved BPQ Ethernet to seperate driver.
* AX.25 034 Jonathan(G4KLX) 2.1 changes
* Alan(GW4PTS) Small POSIXisations
- *
- * To do:
- * Restructure the ax25_rcv code to be cleaner/faster and
- * copy only when needed.
- * Consider better arbitrary protocol support.
+ * AX.25 035 Alan(GW4PTS) Started fixing to the new
+ * format.
+ * Hans(PE1AYX) Fixed interface to IP layer.
+ * Alan(GW4PTS) Added asynchronous support.
+ * Frederic(F1OAT) Support for pseudo-digipeating.
+ * Jonathan(G4KLX) Support for packet forwarding.
+ * AX.25 036 Jonathan(G4KLX) Major restructuring.
+ * Joerg(DL1BKE) Fixed DAMA Slave.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#include <linux/module.h>
@@ -126,112 +129,21 @@
#include <net/ip.h>
#include <net/arp.h>
-/*
- * The null address is defined as a callsign of all spaces with an
- * SSID of zero.
- */
-ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
-
ax25_cb *volatile ax25_list = NULL;
-/*
- * ax25 -> ascii conversion
- */
-char *ax2asc(ax25_address *a)
-{
- static char buf[11];
- char c, *s;
- int n;
-
- for (n = 0, s = buf; n < 6; n++) {
- c = (a->ax25_call[n] >> 1) & 0x7F;
-
- if (c != ' ') *s++ = c;
- }
-
- *s++ = '-';
-
- if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
- *s++ = '1';
- n -= 10;
- }
-
- *s++ = n + '0';
- *s++ = '\0';
-
- if (*buf == '\0' || *buf == '-')
- return "*";
-
- return buf;
-
-}
-
-/*
- * ascii -> ax25 conversion
- */
-ax25_address *asc2ax(char *callsign)
-{
- static ax25_address addr;
- char *s;
- int n;
-
- for (s = callsign, n = 0; n < 6; n++) {
- if (*s != '\0' && *s != '-')
- addr.ax25_call[n] = *s++;
- else
- addr.ax25_call[n] = ' ';
- addr.ax25_call[n] <<= 1;
- addr.ax25_call[n] &= 0xFE;
- }
-
- if (*s++ == '\0') {
- addr.ax25_call[6] = 0x00;
- return &addr;
- }
-
- addr.ax25_call[6] = *s++ - '0';
-
- if (*s != '\0') {
- addr.ax25_call[6] *= 10;
- addr.ax25_call[6] += *s++ - '0';
- }
-
- addr.ax25_call[6] <<= 1;
- addr.ax25_call[6] &= 0x1E;
-
- return &addr;
-}
-
-/*
- * Compare two ax.25 addresses
- */
-int ax25cmp(ax25_address *a, ax25_address *b)
-{
- int ct = 0;
-
- while (ct < 6) {
- if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
- return 1;
- ct++;
- }
-
- if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
- return 0;
-
- return 2; /* Partial match */
-}
+static struct proto_ops ax25_proto_ops;
/*
* Free an allocated ax25 control block. This is done to centralise
* the MOD count code.
*/
-static void ax25_free_cb(ax25_cb *ax25)
+void ax25_free_cb(ax25_cb *ax25)
{
if (ax25->digipeat != NULL) {
kfree_s(ax25->digipeat, sizeof(ax25_digi));
ax25->digipeat = NULL;
}
-
+
kfree_s(ax25, sizeof(ax25_cb));
MOD_DEC_USE_COUNT;
@@ -244,7 +156,7 @@ static void ax25_remove_socket(ax25_cb *ax25)
{
ax25_cb *s;
unsigned long flags;
-
+
save_flags(flags);
cli();
@@ -272,12 +184,16 @@ static void ax25_remove_socket(ax25_cb *ax25)
*/
static void ax25_kill_by_device(struct device *dev)
{
+ ax25_dev *ax25_dev;
ax25_cb *s;
-
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ return;
+
for (s = ax25_list; s != NULL; s = s->next) {
- if (s->device == dev) {
- s->state = AX25_STATE_0;
- s->device = NULL;
+ if (s->ax25_dev == ax25_dev) {
+ s->state = AX25_STATE_0;
+ s->ax25_dev = NULL;
if (s->sk != NULL) {
s->sk->state = TCP_CLOSE;
s->sk->err = ENETUNREACH;
@@ -320,7 +236,7 @@ static int ax25_device_event(struct notifier_block *this,unsigned long event, vo
/*
* Add a socket to the bound sockets list.
*/
-static void ax25_insert_socket(ax25_cb *ax25)
+void ax25_insert_socket(ax25_cb *ax25)
{
unsigned long flags;
@@ -337,7 +253,7 @@ static void ax25_insert_socket(ax25_cb *ax25)
* Find a socket that wants to accept the SABM we have just
* received.
*/
-static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, int type)
+struct sock *ax25_find_listener(ax25_address *addr, int digi, struct device *dev, int type)
{
unsigned long flags;
ax25_cb *s;
@@ -346,9 +262,11 @@ static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, i
cli();
for (s = ax25_list; s != NULL; s = s->next) {
+ if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
+ continue;
if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) {
/* If device is null we match any device */
- if (s->device == NULL || s->device == dev) {
+ if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
restore_flags(flags);
return s->sk;
}
@@ -362,7 +280,7 @@ static struct sock *ax25_find_listener(ax25_address *addr, struct device *dev, i
/*
* Find an AX.25 socket given both ends.
*/
-static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
+struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
{
ax25_cb *s;
unsigned long flags;
@@ -386,7 +304,7 @@ static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_a
* Find an AX.25 control block given both ends. It will only pick up
* floating AX.25 control blocks or non Raw socket bound control blocks.
*/
-static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, struct device *dev)
+ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
{
ax25_cb *s;
unsigned long flags;
@@ -397,7 +315,13 @@ static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, str
for (s = ax25_list; s != NULL; s = s->next) {
if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
continue;
- if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->device == dev) {
+ if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
+ if (digi != NULL) {
+ if (s->digipeat == NULL && digi->ndigi != 0)
+ continue;
+ if (s->digipeat != NULL && ax25digicmp(s->digipeat, digi) != 0)
+ continue;
+ }
restore_flags(flags);
return s;
}
@@ -411,7 +335,7 @@ static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, str
/*
* Look for any matching address - RAW sockets can bind to arbitrary names
*/
-static struct sock *ax25_addr_match(ax25_address *addr)
+struct sock *ax25_addr_match(ax25_address *addr)
{
unsigned long flags;
ax25_cb *s;
@@ -431,25 +355,24 @@ static struct sock *ax25_addr_match(ax25_address *addr)
return NULL;
}
-static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
+void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
{
struct sk_buff *copy;
-
+
while (sk != NULL) {
- if (sk->type == SOCK_RAW && sk->protocol == proto && sk->rmem_alloc <= sk->rcvbuf) {
+ if (sk->type == SOCK_RAW &&
+ sk->protocol == proto &&
+ atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
return;
- copy->sk = sk;
- atomic_add(copy->truesize, &sk->rmem_alloc);
- skb_queue_tail(&sk->receive_queue, copy);
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
+ if (sock_queue_rcv_skb(sk, copy) != 0)
+ kfree_skb(copy, FREE_READ);
}
sk = sk->next;
}
-}
+}
/*
* Deferred destroy.
@@ -474,15 +397,15 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer
{
struct sk_buff *skb;
unsigned long flags;
-
+
save_flags(flags);
cli();
-
+
del_timer(&ax25->timer);
-
+
ax25_remove_socket(ax25);
ax25_clear_queues(ax25); /* Flush the queues */
-
+
if (ax25->sk != NULL) {
while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
if (skb->sk != ax25->sk) { /* A pending connection */
@@ -494,9 +417,11 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer
kfree_skb(skb, FREE_READ);
}
}
-
+
if (ax25->sk != NULL) {
- if (ax25->sk->wmem_alloc || ax25->sk->rmem_alloc) { /* Defer: outstanding buffers */
+ if (atomic_read(&ax25->sk->wmem_alloc) != 0 ||
+ atomic_read(&ax25->sk->rmem_alloc) != 0) {
+ /* Defer: outstanding buffers */
init_timer(&ax25->timer);
ax25->timer.expires = jiffies + 10 * HZ;
ax25->timer.function = ax25_destroy_timer;
@@ -514,80 +439,6 @@ void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer
}
/*
- * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
- */
-
-ax25_uid_assoc *ax25_uid_list;
-
-int ax25_uid_policy = 0;
-
-ax25_address *ax25_findbyuid(uid_t uid)
-{
- ax25_uid_assoc *a;
-
- for (a = ax25_uid_list; a != NULL; a = a->next) {
- if (a->uid == uid)
- return &a->call;
- }
-
- return NULL;
-}
-
-static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
-{
- ax25_uid_assoc *a;
-
- switch (cmd) {
- case SIOCAX25GETUID:
- for (a = ax25_uid_list; a != NULL; a = a->next) {
- if (ax25cmp(&sax->sax25_call, &a->call) == 0)
- return a->uid;
- }
- return -ENOENT;
-
- case SIOCAX25ADDUID:
- if (!suser())
- return -EPERM;
- if (ax25_findbyuid(sax->sax25_uid))
- return -EEXIST;
- if (sax->sax25_uid == 0)
- return -EINVAL;
- a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
- if (a == NULL)
- return -ENOMEM;
- a->uid = sax->sax25_uid;
- a->call = sax->sax25_call;
- a->next = ax25_uid_list;
- ax25_uid_list = a;
- return 0;
-
- case SIOCAX25DELUID: {
- ax25_uid_assoc **l;
-
- if (!suser())
- return -EPERM;
- l = &ax25_uid_list;
- while ((*l) != NULL) {
- if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) {
- a = *l;
- *l = (*l)->next;
- kfree_s(a, sizeof(*a));
- return 0;
- }
-
- l = &((*l)->next);
- }
- return -ENOENT;
- }
-
- default:
- return -EINVAL;
- }
-
- return -EINVAL; /*NOTREACHED */
-}
-
-/*
* dl1bke 960311: set parameters for existing AX.25 connections,
* includes a KILL command to abort any connection.
* VERY useful for debugging ;-)
@@ -595,28 +446,32 @@ static int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
{
struct ax25_ctl_struct ax25_ctl;
- struct device *dev;
+ ax25_dev *ax25_dev;
ax25_cb *ax25;
unsigned long flags;
int err;
-
+
if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0)
return err;
copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl));
-
- if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
+
+ if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
return -ENODEV;
- if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, dev)) == NULL)
+ if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, NULL, ax25_dev->dev)) == NULL)
return -ENOTCONN;
switch (ax25_ctl.cmd) {
case AX25_KILL:
ax25_clear_queues(ax25);
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
ax25->state = AX25_STATE_0;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+ ax25_dama_off(ax25);
+#endif
+
if (ax25->sk != NULL) {
ax25->sk->state = TCP_CLOSE;
ax25->sk->err = ENETRESET;
@@ -626,26 +481,25 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
ax25->sk->dead = 1;
}
- ax25_dama_off(ax25);
ax25_set_timer(ax25);
break;
case AX25_WINDOW:
- if (ax25->modulus == MODULUS) {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
+ if (ax25->modulus == AX25_MODULUS) {
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
return -EINVAL;
} else {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
return -EINVAL;
}
ax25->window = ax25_ctl.arg;
break;
case AX25_T1:
- if (ax25_ctl.arg < 1)
+ if (ax25_ctl.arg < 1)
return -EINVAL;
- ax25->rtt = (ax25_ctl.arg * PR_SLOWHZ) / 2;
- ax25->t1 = ax25_ctl.arg * PR_SLOWHZ;
+ ax25->rtt = (ax25_ctl.arg * AX25_SLOWHZ) / 2;
+ ax25->t1 = ax25_ctl.arg * AX25_SLOWHZ;
save_flags(flags); cli();
if (ax25->t1timer > ax25->t1)
ax25->t1timer = ax25->t1;
@@ -653,77 +507,69 @@ static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
break;
case AX25_T2:
- if (ax25_ctl.arg < 1)
+ if (ax25_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- ax25->t2 = ax25_ctl.arg * PR_SLOWHZ;
+ ax25->t2 = ax25_ctl.arg * AX25_SLOWHZ;
if (ax25->t2timer > ax25->t2)
ax25->t2timer = ax25->t2;
restore_flags(flags);
break;
case AX25_N2:
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
return -EINVAL;
ax25->n2count = 0;
ax25->n2 = ax25_ctl.arg;
break;
case AX25_T3:
- if (ax25_ctl.arg < 0)
+ if (ax25_ctl.arg < 0)
return -EINVAL;
save_flags(flags); cli();
- ax25->t3 = ax25_ctl.arg * PR_SLOWHZ;
+ ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ;
if (ax25->t3timer != 0)
ax25->t3timer = ax25->t3;
restore_flags(flags);
break;
case AX25_IDLE:
- if (ax25_ctl.arg < 0)
+ if (ax25_ctl.arg < 0)
return -EINVAL;
save_flags(flags); cli();
- ax25->idle = ax25_ctl.arg * PR_SLOWHZ * 60;
+ ax25->idle = ax25_ctl.arg * AX25_SLOWHZ * 60;
if (ax25->idletimer != 0)
ax25->idletimer = ax25->idle;
restore_flags(flags);
break;
case AX25_PACLEN:
- if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
+ if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
return -EINVAL;
-#if 0
- if (ax25_ctl.arg > 256) /* we probably want this */
- printk(KERN_WARNING "ax25_ctl_ioctl: Warning --- huge paclen %d\n", (int)ax25_ctl.arg);
-#endif
ax25->paclen = ax25_ctl.arg;
break;
- case AX25_MAXQUEUE:
- if (ax25_ctl.arg < 1)
- return -EINVAL;
- ax25->maxqueue = ax25_ctl.arg;
- break;
-
default:
return -EINVAL;
}
-
+
return 0;
}
/*
* Create an empty AX.25 control block.
*/
-static ax25_cb *ax25_create_cb(void)
+ax25_cb *ax25_create_cb(void)
{
ax25_cb *ax25;
- if ((ax25 = (ax25_cb *)kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
+ if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
return NULL;
MOD_INC_USE_COUNT;
+ memset(ax25, 0x00, sizeof(*ax25));
+
skb_queue_head_init(&ax25->write_queue);
skb_queue_head_init(&ax25->frag_queue);
skb_queue_head_init(&ax25->ack_queue);
@@ -731,235 +577,77 @@ static ax25_cb *ax25_create_cb(void)
init_timer(&ax25->timer);
- ax25->dama_slave = 0;
-
ax25->rtt = AX25_DEF_T1 / 2;
ax25->t1 = AX25_DEF_T1;
ax25->t2 = AX25_DEF_T2;
ax25->t3 = AX25_DEF_T3;
ax25->n2 = AX25_DEF_N2;
ax25->paclen = AX25_DEF_PACLEN;
- ax25->maxqueue= AX25_DEF_MAXQUEUE;
ax25->idle = AX25_DEF_IDLE;
if (AX25_DEF_AXDEFMODE) {
- ax25->modulus = EMODULUS;
+ ax25->modulus = AX25_EMODULUS;
ax25->window = AX25_DEF_EWINDOW;
} else {
- ax25->modulus = MODULUS;
+ ax25->modulus = AX25_MODULUS;
ax25->window = AX25_DEF_WINDOW;
}
- ax25->fragno = 0;
- ax25->fraglen = 0;
- ax25->hdrincl = 0;
- ax25->backoff = AX25_DEF_BACKOFF;
- ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t2timer = 0;
- ax25->t3timer = 0;
- ax25->n2count = 0;
- ax25->idletimer = 0;
-
- ax25->va = 0;
- ax25->vr = 0;
- ax25->vs = 0;
-
- ax25->device = NULL;
- ax25->digipeat = NULL;
- ax25->sk = NULL;
-
- ax25->state = AX25_STATE_0;
-
- memset(&ax25->dest_addr, '\0', AX25_ADDR_LEN);
- memset(&ax25->source_addr, '\0', AX25_ADDR_LEN);
+ ax25->backoff = AX25_DEF_BACKOFF;
+ ax25->state = AX25_STATE_0;
return ax25;
}
/*
- * Find out if we are a DAMA slave for this device and count the
- * number of connections.
- *
- * dl1bke 951121
- */
-int ax25_dev_is_dama_slave(struct device *dev)
-{
- ax25_cb *ax25;
- int count = 0;
-
- for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if (ax25->device == dev && ax25->dama_slave) {
- count++;
- break;
- }
- }
-
- return count;
-}
-
-/*
* Fill in a created AX.25 created control block with the default
* values for a particular device.
*/
-static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev)
+void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
{
- ax25->device = dev;
-
- ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1);
- ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1);
- ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2);
- ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3);
- ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2);
- ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
- ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_MAXQUEUE);
- ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
-
- ax25->dama_slave = 0;
-
- if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
- ax25->modulus = EMODULUS;
- ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ ax25->ax25_dev = ax25_dev;
+
+ ax25->rtt = ax25_dev->values[AX25_VALUES_T1];
+ ax25->t1 = ax25_dev->values[AX25_VALUES_T1];
+ ax25->t2 = ax25_dev->values[AX25_VALUES_T2];
+ ax25->t3 = ax25_dev->values[AX25_VALUES_T3];
+ ax25->n2 = ax25_dev->values[AX25_VALUES_N2];
+ ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN];
+ ax25->idle = ax25_dev->values[AX25_VALUES_IDLE];
+
+ if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
} else {
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
}
- ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
-}
-
-int ax25_send_frame(struct sk_buff *skb, ax25_address *src, ax25_address *dest,
- ax25_digi *digi, struct device *dev)
-{
- ax25_cb *ax25;
-
- if (skb == NULL)
- return 0;
-
- /*
- * Look for an existing connection.
- */
- for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
- continue;
-
- if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) {
- if (ax25_queue_length(ax25, skb) > ax25->maxqueue * ax25->window) {
- kfree_skb(skb, FREE_WRITE);
- } else {
- ax25_output(ax25, skb);
- }
- ax25->idletimer = ax25->idle;
- return 1; /* It already existed */
- }
- }
-
- if ((ax25 = ax25_create_cb()) == NULL)
- return 0;
-
- ax25_fillin_cb(ax25, dev);
-
- ax25->source_addr = *src;
- ax25->dest_addr = *dest;
-
- if (digi != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- ax25_free_cb(ax25);
- return 0;
- }
- *ax25->digipeat = *digi;
- } else {
- ax25_rt_build_path(ax25, dest, dev);
- }
-
- if (ax25_dev_is_dama_slave(ax25->device))
- dama_establish_data_link(ax25);
- else
- ax25_establish_data_link(ax25);
-
- /* idle timeouts only for mode vc connections */
-
- ax25->idletimer = ax25->idle;
-
- ax25_insert_socket(ax25);
-
- ax25->state = AX25_STATE_1;
-
- ax25_set_timer(ax25);
-
- ax25_output(ax25, skb);
-
- return 1; /* We had to create it */
-}
-
-/*
- * Return the state of an AX.25 link given source, destination, and
- * device.
- */
-int ax25_link_up(ax25_address *src, ax25_address *dest, struct device *dev)
-{
- ax25_cb *ax25;
-
- for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if (ax25->sk != NULL && ax25->sk->type != SOCK_SEQPACKET)
- continue;
-
- if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Find the AX.25 device that matches the hardware address supplied.
- */
-struct device *ax25rtr_get_dev(ax25_address *addr)
-{
- struct device *dev;
-
- for (dev = dev_base; dev != NULL; dev = dev->next)
- if ((dev->flags & IFF_UP) && dev->type == ARPHRD_AX25 &&
- ax25cmp(addr, (ax25_address*) dev->dev_addr) == 0)
- return dev;
-
- return NULL;
+ ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
}
/*
* Handling for system calls applied via the various interfaces to an
* AX25 socket object
*/
-static int ax25_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- return -EINVAL;
-}
-static int ax25_setsockopt(struct socket *sock, int level, int optname,
- char *optval, int optlen)
+static int ax25_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
- struct sock *sk;
- int err, opt;
-
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_setsockopt(sk, level, optname, optval, optlen);
+ struct sock *sk = sock->sk;
+ int opt;
if (level != SOL_AX25)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
- if (optval == NULL)
+ if (optlen < sizeof(int))
return -EINVAL;
- if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
- return err;
-
- get_user(opt, (int *)optval);
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
switch (optname) {
case AX25_WINDOW:
- if (sk->protinfo.ax25->modulus == MODULUS) {
+ if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
if (opt < 1 || opt > 7)
return -EINVAL;
} else {
@@ -972,13 +660,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname,
case AX25_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->rtt = (opt * PR_SLOWHZ) / 2;
+ sk->protinfo.ax25->rtt = (opt * AX25_SLOWHZ) / 2;
return 0;
case AX25_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t2 = opt * PR_SLOWHZ;
+ sk->protinfo.ax25->t2 = opt * AX25_SLOWHZ;
return 0;
case AX25_N2:
@@ -990,27 +678,33 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname,
case AX25_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t3 = opt * PR_SLOWHZ;
+ sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ;
return 0;
-
+
case AX25_IDLE:
if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->idle = opt * PR_SLOWHZ * 60;
+ sk->protinfo.ax25->idle = opt * AX25_SLOWHZ * 60;
return 0;
case AX25_BACKOFF:
- sk->protinfo.ax25->backoff = opt ? 1 : 0;
+ if (opt < 0 || opt > 2)
+ return -EINVAL;
+ sk->protinfo.ax25->backoff = opt;
return 0;
case AX25_EXTSEQ:
- sk->protinfo.ax25->modulus = opt ? EMODULUS : MODULUS;
+ sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
return 0;
case AX25_HDRINCL:
sk->protinfo.ax25->hdrincl = opt ? 1 : 0;
return 0;
-
+
+ case AX25_IAMDIGI:
+ sk->protinfo.ax25->iamdigi = opt ? 1 : 0;
+ return 0;
+
case AX25_PACLEN:
if (opt < 16 || opt > 65535)
return -EINVAL;
@@ -1022,20 +716,17 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname,
}
}
-static int ax25_getsockopt(struct socket *sock, int level, int optname,
- char *optval, int *optlen)
+static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
int val = 0;
- int err;
+ int len;
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_getsockopt(sk, level, optname, optval, optlen);
-
if (level != SOL_AX25)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
switch (optname) {
case AX25_WINDOW:
@@ -1043,11 +734,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname,
break;
case AX25_T1:
- val = (sk->protinfo.ax25->t1 * 2) / PR_SLOWHZ;
+ val = (sk->protinfo.ax25->t1 * 2) / AX25_SLOWHZ;
break;
case AX25_T2:
- val = sk->protinfo.ax25->t2 / PR_SLOWHZ;
+ val = sk->protinfo.ax25->t2 / AX25_SLOWHZ;
break;
case AX25_N2:
@@ -1055,11 +746,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname,
break;
case AX25_T3:
- val = sk->protinfo.ax25->t3 / PR_SLOWHZ;
+ val = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
break;
-
+
case AX25_IDLE:
- val = sk->protinfo.ax25->idle / (PR_SLOWHZ * 60);
+ val = sk->protinfo.ax25->idle / (AX25_SLOWHZ * 60);
break;
case AX25_BACKOFF:
@@ -1067,13 +758,17 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname,
break;
case AX25_EXTSEQ:
- val = (sk->protinfo.ax25->modulus == EMODULUS);
+ val = (sk->protinfo.ax25->modulus == AX25_EMODULUS);
break;
case AX25_HDRINCL:
val = sk->protinfo.ax25->hdrincl;
break;
-
+
+ case AX25_IAMDIGI:
+ val = sk->protinfo.ax25->iamdigi;
+ break;
+
case AX25_PACLEN:
val = sk->protinfo.ax25->paclen;
break;
@@ -1082,22 +777,20 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname,
return -ENOPROTOOPT;
}
- if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
- return err;
-
- put_user(sizeof(int), optlen);
+ len = min(len, sizeof(int));
- if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
- return err;
+ if (put_user(len, optlen))
+ return -EFAULT;
- put_user(val, (int *) optval);
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
return 0;
}
static int ax25_listen(struct socket *sock, int backlog)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
sk->max_ack_backlog = backlog;
@@ -1108,19 +801,7 @@ static int ax25_listen(struct socket *sock, int backlog)
return -EOPNOTSUPP;
}
-static void def_callback1(struct sock *sk)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk, int len)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static int ax25_create(struct socket *sock, int protocol)
+int ax25_create(struct socket *sock, int protocol)
{
struct sock *sk;
ax25_cb *ax25;
@@ -1141,13 +822,23 @@ static int ax25_create(struct socket *sock, int protocol)
case AX25_P_ARP:
case AX25_P_IP:
#endif
-#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#ifdef CONFIG_NETROM
case AX25_P_NETROM:
#endif
-#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#ifdef CONFIG_ROSE
case AX25_P_ROSE:
#endif
return -ESOCKTNOSUPPORT;
+#ifdef CONFIG_NETROM_MODULE
+ case AX25_P_NETROM:
+ if (ax25_protocol_is_registered(AX25_P_NETROM))
+ return -ESOCKTNOSUPPORT;
+#endif
+#ifdef CONFIG_ROSE_MODULE
+ case AX25_P_ROSE:
+ if (ax25_protocol_is_registered(AX25_P_ROSE))
+ return -ESOCKTNOSUPPORT;
+#endif
default:
break;
}
@@ -1166,39 +857,19 @@ static int ax25_create(struct socket *sock, int protocol)
return -ENOMEM;
}
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- sk->socket = sock;
- sk->type = sock->type;
- sk->protocol = protocol;
- sk->next = NULL;
- sk->allocation = GFP_KERNEL;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->sndbuf = SK_WMEM_MAX;
- sk->state = TCP_CLOSE;
- sk->priority = SOPRI_NORMAL;
- sk->mtu = AX25_MTU; /* 256 */
- sk->zapped = 1;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
-
- if (sock != NULL) {
- sock->data = (void *)sk;
- sk->sleep = sock->wait;
- }
+ sock_init_data(sock, sk);
+
+ sock->ops = &ax25_proto_ops;
+ sk->protocol = protocol;
+ sk->mtu = AX25_MTU; /* 256 */
ax25->sk = sk;
sk->protinfo.ax25 = ax25;
-
+
return 0;
}
-static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
+struct sock *ax25_make_new(struct sock *osk, struct device *dev)
{
struct sock *sk;
ax25_cb *ax25;
@@ -1211,11 +882,6 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
return NULL;
}
- ax25_fillin_cb(ax25, dev);
-
- sk->type = osk->type;
- sk->socket = osk->socket;
-
switch (osk->type) {
case SOCK_DGRAM:
break;
@@ -1227,30 +893,24 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
return NULL;
}
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- sk->next = NULL;
- sk->priority = osk->priority;
- sk->protocol = osk->protocol;
- sk->rcvbuf = osk->rcvbuf;
- sk->sndbuf = osk->sndbuf;
- sk->debug = osk->debug;
- sk->state = TCP_ESTABLISHED;
- sk->window = osk->window;
- sk->mtu = osk->mtu;
- sk->sleep = osk->sleep;
- sk->zapped = osk->zapped;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
+ sock_init_data(NULL, sk);
+
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->mtu = osk->mtu;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
ax25->modulus = osk->protinfo.ax25->modulus;
ax25->backoff = osk->protinfo.ax25->backoff;
ax25->hdrincl = osk->protinfo.ax25->hdrincl;
+ ax25->iamdigi = osk->protinfo.ax25->iamdigi;
ax25->rtt = osk->protinfo.ax25->rtt;
ax25->t1 = osk->protinfo.ax25->t1;
ax25->t2 = osk->protinfo.ax25->t2;
@@ -1258,19 +918,18 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
ax25->n2 = osk->protinfo.ax25->n2;
ax25->idle = osk->protinfo.ax25->idle;
ax25->paclen = osk->protinfo.ax25->paclen;
+ ax25->window = osk->protinfo.ax25->window;
- ax25->window = osk->protinfo.ax25->window;
- ax25->maxqueue = osk->protinfo.ax25->maxqueue;
-
+ ax25->ax25_dev = osk->protinfo.ax25->ax25_dev;
ax25->source_addr = osk->protinfo.ax25->source_addr;
-
+
if (osk->protinfo.ax25->digipeat != NULL) {
- if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+ if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
sk_free(sk);
ax25_free_cb(ax25);
return NULL;
}
-
+
*ax25->digipeat = *osk->protinfo.ax25->digipeat;
}
@@ -1282,14 +941,17 @@ static struct sock *ax25_make_new(struct sock *osk, struct device *dev)
static int ax25_dup(struct socket *newsock, struct socket *oldsock)
{
- struct sock *sk = (struct sock *)oldsock->data;
+ struct sock *sk = oldsock->sk;
+
+ if (sk == NULL || newsock == NULL)
+ return -EINVAL;
return ax25_create(newsock, sk->protocol);
}
static int ax25_release(struct socket *sock, struct socket *peer)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk == NULL) return 0;
@@ -1304,37 +966,32 @@ static int ax25_release(struct socket *sock, struct socket *peer)
break;
case AX25_STATE_1:
- ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
- sk->protinfo.ax25->state = AX25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- ax25_destroy_socket(sk->protinfo.ax25);
- break;
-
case AX25_STATE_2:
- if (sk->protinfo.ax25->dama_slave)
- ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
- else
- ax25_send_control(sk->protinfo.ax25, DM, POLLON, C_RESPONSE);
+ ax25_clear_queues(sk->protinfo.ax25);
+ ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
sk->protinfo.ax25->state = AX25_STATE_0;
sk->state = TCP_CLOSE;
sk->shutdown |= SEND_SHUTDOWN;
sk->state_change(sk);
sk->dead = 1;
ax25_destroy_socket(sk->protinfo.ax25);
- break;
+ break;
case AX25_STATE_3:
case AX25_STATE_4:
ax25_clear_queues(sk->protinfo.ax25);
sk->protinfo.ax25->n2count = 0;
- if (!sk->protinfo.ax25->dama_slave) {
- ax25_send_control(sk->protinfo.ax25, DISC, POLLON, C_COMMAND);
- sk->protinfo.ax25->t3timer = 0;
- } else {
- sk->protinfo.ax25->t3timer = sk->protinfo.ax25->t3; /* DAMA slave timeout */
+ switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ sk->protinfo.ax25->t3timer = 0;
+ break;
+#ifdef AX25_CONFIG_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ sk->protinfo.ax25->t3timer = 0;
+ break;
+#endif
}
sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25);
sk->protinfo.ax25->state = AX25_STATE_2;
@@ -1356,8 +1013,8 @@ static int ax25_release(struct socket *sock, struct socket *peer)
ax25_destroy_socket(sk->protinfo.ax25);
}
- sock->data = NULL;
- sk->socket = NULL; /* Not used, but we should do this. **/
+ sock->sk = NULL;
+ sk->socket = NULL; /* Not used, but we should do this */
return 0;
}
@@ -1370,92 +1027,91 @@ static int ax25_release(struct socket *sock, struct socket *peer)
*/
static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
- struct device *dev;
ax25_address *call;
-
- sk = (struct sock *)sock->data;
-
+ ax25_dev *ax25_dev = NULL;
+
if (sk->zapped == 0)
return -EINVAL;
-
+
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
+ if (addr->fsa_ax25.sax25_family != AF_AX25)
+ return -EINVAL;
+
call = ax25_findbyuid(current->euid);
if (call == NULL && ax25_uid_policy && !suser())
return -EACCES;
-
+
if (call == NULL)
sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call;
else
sk->protinfo.ax25->source_addr = *call;
- if (sk->debug)
- printk("AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
+ SOCK_DEBUG(sk, "AX25: source address set to %s\n", ax2asc(&sk->protinfo.ax25->source_addr));
if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) == 0) {
- dev = NULL;
- if (sk->debug)
- printk("AX25: bound to any device\n");
+ ax25_dev = NULL;
+ SOCK_DEBUG(sk, "AX25: bound to any device\n");
} else {
- if ((dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) {
- if (sk->debug)
- printk("AX25: bind failed - no device\n");
+ if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) {
+ SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
return -EADDRNOTAVAIL;
}
- if (sk->debug)
- printk("AX25: bound to device %s\n", dev->name);
+ SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
}
} else {
- if ((dev = ax25rtr_get_dev(&addr->fsa_ax25.sax25_call)) == NULL) {
- if (sk->debug)
- printk("AX25: bind failed - no device\n");
+ if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) {
+ SOCK_DEBUG(sk, "AX25: bind failed - no device\n");
return -EADDRNOTAVAIL;
}
- if (sk->debug)
- printk("AX25: bound to device %s\n", dev->name);
+ SOCK_DEBUG(sk, "AX25: bound to device %s\n", ax25_dev->dev->name);
}
- ax25_fillin_cb(sk->protinfo.ax25, dev);
+ if (ax25_dev != NULL)
+ ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
+
ax25_insert_socket(sk->protinfo.ax25);
sk->zapped = 0;
-
- if (sk->debug)
- printk("AX25: socket is bound\n");
-
+ SOCK_DEBUG(sk, "AX25: socket is bound\n");
return 0;
}
-static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
- int addr_len, int flags)
+/*
+ * FIXME: nonblock behaviour looks like it may have a bug.
+ */
+static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
int err;
-
+
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
sock->state = SS_CONNECTED;
return 0; /* Connect completed during a ERESTARTSYS event */
}
-
+
if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
sock->state = SS_UNCONNECTED;
return -ECONNREFUSED;
}
-
+
if (sk->state == TCP_ESTABLISHED && sk->type == SOCK_SEQPACKET)
return -EISCONN; /* No reconnect on a seqpacket socket */
-
- sk->state = TCP_CLOSE;
+
+ sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
+ if (addr->sax25_family != AF_AX25)
+ return -EINVAL;
+
/*
* Handle digi-peaters to be used.
*/
@@ -1468,19 +1124,23 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
return -EINVAL;
if (sk->protinfo.ax25->digipeat == NULL) {
- if ((sk->protinfo.ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
+ if ((sk->protinfo.ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
return -ENOBUFS;
}
- sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis;
+ sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis;
+ sk->protinfo.ax25->digipeat->lastrepeat = -1;
while (ct < addr->sax25_ndigis) {
- sk->protinfo.ax25->digipeat->repeated[ct] = 0;
- sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct];
+ if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) {
+ sk->protinfo.ax25->digipeat->repeated[ct] = 1;
+ sk->protinfo.ax25->digipeat->lastrepeat = ct;
+ } else {
+ sk->protinfo.ax25->digipeat->repeated[ct] = 0;
+ }
+ sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct];
ct++;
}
-
- sk->protinfo.ax25->digipeat->lastrepeat = 0;
}
/*
@@ -1491,41 +1151,52 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
if (sk->zapped) {
if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0)
return err;
- ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->device);
+ ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
ax25_insert_socket(sk->protinfo.ax25);
} else {
- if (sk->protinfo.ax25->device == NULL)
+ if (sk->protinfo.ax25->ax25_dev == NULL)
return -EHOSTUNREACH;
}
-
- if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, sk->protinfo.ax25->device) != NULL)
+
+ if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->ax25_dev->dev) != NULL)
return -EADDRINUSE; /* Already such a connection */
sk->protinfo.ax25->dest_addr = addr->sax25_call;
-
+
/* First the easy one */
if (sk->type != SOCK_SEQPACKET) {
sock->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED;
return 0;
}
-
- /* Move to connecting socket, ax.25 lapb WAIT_UA.. */
+
+ /* Move to connecting socket, ax.25 lapb WAIT_UA.. */
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
-
- if (ax25_dev_is_dama_slave(sk->protinfo.ax25->device))
- dama_establish_data_link(sk->protinfo.ax25);
- else
- ax25_establish_data_link(sk->protinfo.ax25);
-
+
+ switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_establish_data_link(sk->protinfo.ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (sk->protinfo.ax25->ax25_dev->dama.slave)
+ ax25_ds_establish_data_link(sk->protinfo.ax25);
+ else
+ ax25_std_establish_data_link(sk->protinfo.ax25);
+ break;
+#endif
+ }
+
sk->protinfo.ax25->state = AX25_STATE_1;
ax25_set_timer(sk->protinfo.ax25); /* Start going SABM SABM until a UA or a give up and DM */
-
+
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS;
-
+
cli(); /* To avoid races on the sleep */
/* A DM or timeout will go to closed, a UA will go to ABM */
@@ -1543,14 +1214,14 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
sock->state = SS_UNCONNECTED;
return sock_error(sk); /* Always set at this point */
}
-
+
sock->state = SS_CONNECTED;
sti();
-
+
return 0;
}
-
+
static int ax25_socketpair(struct socket *sock1, struct socket *sock2)
{
return -EOPNOTSUPP;
@@ -1562,19 +1233,20 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
struct sock *newsk;
struct sk_buff *skb;
- if (newsock->data)
- sk_free(newsock->data);
+ if (newsock->sk != NULL)
+ ax25_destroy_socket(newsock->sk->protinfo.ax25);
- newsock->data = NULL;
-
- sk = (struct sock *)sock->data;
+ newsock->sk = NULL;
+
+ if ((sk = sock->sk) == NULL)
+ return -EINVAL;
if (sk->type != SOCK_SEQPACKET)
return -EOPNOTSUPP;
-
+
if (sk->state != TCP_LISTEN)
return -EINVAL;
-
+
/*
* The write queue this time is holding sockets ready to use
* hooked into the SABM we saved
@@ -1584,7 +1256,7 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
if (flags & O_NONBLOCK) {
sti();
- return 0;
+ return -EWOULDBLOCK;
}
interruptible_sleep_on(sk->sleep);
if (current->signal & ~current->blocked) {
@@ -1602,20 +1274,17 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
sk->ack_backlog--;
- newsock->data = newsk;
+ newsock->sk = newsk;
return 0;
}
-static int ax25_getname(struct socket *sock, struct sockaddr *uaddr,
- int *uaddr_len, int peer)
+static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
- struct sock *sk;
+ struct sock *sk = sock->sk;
unsigned char ndigi, i;
-
- sk = (struct sock *)sock->data;
-
+
if (peer != 0) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
@@ -1637,321 +1306,18 @@ static int ax25_getname(struct socket *sock, struct sockaddr *uaddr,
sax->fsa_ax25.sax25_ndigis = 1;
*uaddr_len = sizeof(struct full_sockaddr_ax25);
- if (sk->protinfo.ax25->device != NULL)
- memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
+ if (sk->protinfo.ax25->ax25_dev != NULL)
+ memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN);
else
sax->fsa_digipeater[0] = null_ax25_address;
}
-
- return 0;
-}
-
-static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
-{
- struct sock *make;
- struct sock *sk;
- int type = 0;
- ax25_digi dp;
- ax25_cb *ax25;
- ax25_address src, dest;
- struct sock *raw;
- int mine = 0;
- int dama;
-
- /*
- * Process the AX.25/LAPB frame.
- */
-
- skb->h.raw = skb->data;
-
-#ifdef CONFIG_FIREWALL
- if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL) != FW_ACCEPT) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-#endif
-
- /*
- * Parse the address header.
- */
-
- if (ax25_parse_addr(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /*
- * Ours perhaps ?
- */
- if (dp.lastrepeat + 1 < dp.ndigi) { /* Not yet digipeated completely */
- if (ax25cmp(&dp.calls[dp.lastrepeat + 1], dev_addr) == 0) {
- struct device *dev_out = dev;
-
- skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
- if(skb==NULL)
- return 0;
-
- /* We are the digipeater. Mark ourselves as repeated
- and throw the packet back out of the same device */
- dp.lastrepeat++;
- dp.repeated[(int)dp.lastrepeat] = 1;
-
- if (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) {
- while (dp.lastrepeat + 1 < dp.ndigi) {
- struct device *dev_scan;
- if ((dev_scan = ax25rtr_get_dev(&dp.calls[dp.lastrepeat + 1])) == NULL)
- break;
- dp.lastrepeat++;
- dp.repeated[(int)dp.lastrepeat] = 1;
- dev_out = dev_scan;
- }
- if (dev != dev_out && (ax25_dev_get_value(dev_out, AX25_VALUES_DIGI) & AX25_DIGI_XBAND) == 0) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- }
-
- if (dev == dev_out && (ax25_dev_get_value(dev, AX25_VALUES_DIGI) & AX25_DIGI_INBAND) == 0) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- build_ax25_addr(skb->data, &src, &dest, &dp, type, MODULUS);
-#ifdef CONFIG_FIREWALL
- if (call_fw_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-#endif
-
- skb->arp = 1;
- ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL);
- } else {
- kfree_skb(skb, FREE_READ);
- }
-
- return 0;
- }
-
- /*
- * Pull of the AX.25 headers leaving the CTRL/PID bytes
- */
- skb_pull(skb, size_ax25_addr(&dp));
-
- /* For our port addresses ? */
- if (ax25cmp(&dest, dev_addr) == 0)
- mine = 1;
-
- /* Also match on any registered callsign from L3/4 */
- if (!mine && ax25_listen_mine(&dest, dev))
- mine = 1;
-
- if ((*skb->data & ~0x10) == LAPB_UI) { /* UI frame - bypass LAPB processing */
- skb->h.raw = skb->data + 2; /* skip control and pid */
-
- if ((raw = ax25_addr_match(&dest)) != NULL)
- ax25_send_to_raw(raw, skb, skb->data[1]);
-
- if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* Now we are pointing at the pid byte */
- switch (skb->data[1]) {
-#ifdef CONFIG_INET
- case AX25_P_IP:
- skb_pull(skb,2); /* drop PID/CTRL */
- ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
- break;
-
- case AX25_P_ARP:
- skb_pull(skb,2);
- arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
- break;
-#endif
- case AX25_P_TEXT:
- /* Now find a suitable dgram socket */
- if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
- if (sk->rmem_alloc >= sk->rcvbuf) {
- kfree_skb(skb, FREE_READ);
- } else {
- /*
- * Remove the control and PID.
- */
- skb_pull(skb, 2);
- skb_queue_tail(&sk->receive_queue, skb);
- skb->sk = sk;
- atomic_add(skb->truesize, &sk->rmem_alloc);
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
- }
- } else {
- kfree_skb(skb, FREE_READ);
- }
- break;
-
- default:
- kfree_skb(skb, FREE_READ); /* Will scan SOCK_AX25 RAW sockets */
- break;
- }
-
- return 0;
- }
-
- /*
- * Is connected mode supported on this device ?
- * If not, should we DM the incoming frame (except DMs) or
- * silently ignore them. For now we stay quiet.
- */
- if (!ax25_dev_get_value(dev, AX25_VALUES_CONMODE)) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* LAPB */
-
- /* AX.25 state 1-4 */
-
- if ((ax25 = ax25_find_cb(&dest, &src, dev)) != NULL) {
- /*
- * Process the frame. If it is queued up internally it returns one otherwise we
- * free it immediately. This routine itself wakes the user context layers so we
- * do no further work
- */
- if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
- kfree_skb(skb, FREE_READ);
-
- return 0;
- }
-
- /* AX.25 state 0 (disconnected) */
-
- /* a) received not a SABM(E) */
-
- if ((*skb->data & ~PF) != SABM && (*skb->data & ~PF) != SABME) {
- /*
- * Never reply to a DM. Also ignore any connects for
- * addresses that are not our interfaces and not a socket.
- */
- if ((*skb->data & ~PF) != DM && mine)
- ax25_return_dm(dev, &src, &dest, &dp);
-
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- /* b) received SABM(E) */
-
- if ((sk = ax25_find_listener(&dest, dev, SOCK_SEQPACKET)) != NULL) {
- if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
- if (mine)
- ax25_return_dm(dev, &src, &dest, &dp);
-
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- ax25 = make->protinfo.ax25;
-
- skb_queue_head(&sk->receive_queue, skb);
-
- skb->sk = make;
- make->state = TCP_ESTABLISHED;
- make->pair = sk;
-
- sk->ack_backlog++;
- } else {
- if (!mine) {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- if ((ax25 = ax25_create_cb()) == NULL) {
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- ax25_fillin_cb(ax25, dev);
- ax25->idletimer = ax25->idle;
- }
-
- ax25->source_addr = dest;
- ax25->dest_addr = src;
-
- /*
- * Sort out any digipeated paths.
- */
- if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree_skb(skb, FREE_READ);
- ax25_destroy_socket(ax25);
- return 0;
- }
-
- if (dp.ndigi == 0) {
- if (ax25->digipeat != NULL) {
- kfree_s(ax25->digipeat, sizeof(ax25_digi));
- ax25->digipeat = NULL;
- }
- } else {
- /* Reverse the source SABM's path */
- ax25_digi_invert(&dp, ax25->digipeat);
- }
-
- if ((*skb->data & ~PF) == SABME) {
- ax25->modulus = EMODULUS;
- ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
- } else {
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
- }
-
- ax25->device = dev;
-
- ax25_send_control(ax25, UA, POLLON, C_RESPONSE);
-
- if (dama) ax25_dama_on(ax25); /* bke 951121 */
-
- ax25->dama_slave = dama;
- ax25->t3timer = ax25->t3;
- ax25->state = AX25_STATE_3;
-
- ax25_insert_socket(ax25);
-
- ax25_set_timer(ax25);
-
- if (sk != NULL) {
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
- } else {
- kfree_skb(skb, FREE_READ);
- }
return 0;
}
-/*
- * Receive an AX.25 frame via a SLIP interface.
- */
-static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
- skb->sk = NULL; /* Initially we don't know who it's for */
-
- if ((*skb->data & 0x0F) != 0) {
- kfree_skb(skb, FREE_READ); /* Not a KISS data frame */
- return 0;
- }
-
- skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */
-
- return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
-}
-
-
-static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
-{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err;
struct sockaddr_ax25 sax;
@@ -1962,25 +1328,22 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
ax25_digi dtmp;
int lv;
int addr_len = msg->msg_namelen;
-
- if (sk->err)
- return sock_error(sk);
- if (flags || msg->msg_control)
+ if (msg->msg_flags & ~MSG_DONTWAIT)
return -EINVAL;
if (sk->zapped)
return -EADDRNOTAVAIL;
-
+
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 0);
return -EPIPE;
}
-
- if (sk->protinfo.ax25->device == NULL)
+
+ if (sk->protinfo.ax25->ax25_dev == NULL)
return -ENETUNREACH;
-
- if (usax) {
+
+ if (usax != NULL) {
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
if (usax->sax25_family != AF_AX25)
@@ -2023,28 +1386,21 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
sax.sax25_call = sk->protinfo.ax25->dest_addr;
dp = sk->protinfo.ax25->digipeat;
}
-
- if (sk->debug)
- printk("AX.25: sendto: Addresses built.\n");
+
+ SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n");
/* Build a packet */
- if (sk->debug)
- printk("AX.25: sendto: building packet.\n");
+ SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n");
/* Assume the worst case */
- size = len + 3 + size_ax25_addr(dp) + AX25_BPQ_HEADER_LEN;
+ size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN;
- if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+ if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
- skb->sk = sk;
- skb->free = 1;
- skb->arp = 1;
-
skb_reserve(skb, size - len);
- if (sk->debug)
- printk("AX.25: Appending user data\n");
+ SOCK_DEBUG(sk, "AX.25: Appending user data\n");
/* User data follows immediately after the AX.25 data */
memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
@@ -2053,8 +1409,7 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
asmptr = skb_push(skb, 1);
*asmptr = sk->protocol;
- if (sk->debug)
- printk("AX.25: Transmitting buffer\n");
+ SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
if (sk->type == SOCK_SEQPACKET) {
/* Connected mode sockets go via the LAPB machine */
@@ -2063,51 +1418,47 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
return -ENOTCONN;
}
- ax25_output(sk->protinfo.ax25, skb); /* Shove it onto the queue and kick */
+ ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */
return len;
} else {
- asmptr = skb_push(skb, 1 + size_ax25_addr(dp));
+ asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
- if (sk->debug) {
- printk("Building AX.25 Header (dp=%p).\n", dp);
- if (dp != 0)
- printk("Num digipeaters=%d\n", dp->ndigi);
- }
+ SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
+
+ if (dp != NULL)
+ SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
/* Build an AX.25 header */
- asmptr += (lv = build_ax25_addr(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, C_COMMAND, MODULUS));
+ asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
- if (sk->debug)
- printk("Built header (%d bytes)\n",lv);
+ SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
skb->h.raw = asmptr;
-
- if (sk->debug)
- printk("base=%p pos=%p\n", skb->data, asmptr);
- *asmptr = LAPB_UI;
+ SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
+
+ *asmptr = AX25_UI;
/* Datagram frames go straight out of the door as UI */
- ax25_queue_xmit(skb, sk->protinfo.ax25->device, SOPRI_NORMAL);
+ skb->dev = sk->protinfo.ax25->ax25_dev->dev;
+ skb->priority = SOPRI_NORMAL;
+
+ ax25_queue_xmit(skb);
return len;
}
-
}
-static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
int copied, length;
struct sk_buff *skb;
int er;
int dama;
- if (addr_len != NULL)
- *addr_len = sizeof(*sax);
-
/*
* This works for seqpacket too. The receiver has ordered the
* queue for us! We do one quick check first though
@@ -2116,7 +1467,7 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
return -ENOTCONN;
/* Now we can treat all alike */
- if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
return er;
if (sk->protinfo.ax25->hdrincl) {
@@ -2137,16 +1488,11 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
- if (sax) {
+ if (sax != NULL) {
ax25_digi digi;
ax25_address dest;
- if (addr_len == (int *)0)
- return -EINVAL;
- if (*addr_len != sizeof(struct sockaddr_ax25) && *addr_len != sizeof(struct full_sockaddr_ax25))
- return -EINVAL;
-
- ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
+ ax25_addr_parse(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
sax->sax25_family = AF_AX25;
/* We set this correctly, even though we may not let the
@@ -2155,9 +1501,7 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
sax->sax25_ndigis = digi.ndigi;
sax->sax25_call = dest;
- *addr_len = sizeof(struct sockaddr_ax25);
-
- if (*addr_len == sizeof(struct full_sockaddr_ax25) && sax->sax25_ndigis != 0) {
+ if (sax->sax25_ndigis != 0) {
int ct = 0;
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
@@ -2165,15 +1509,15 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
fsa->fsa_digipeater[ct] = digi.calls[ct];
ct++;
}
-
- *addr_len = sizeof(struct full_sockaddr_ax25);
}
}
+ msg->msg_namelen = sizeof(struct full_sockaddr_ax25);
+
skb_free_datagram(sk, skb);
return copied;
-}
+}
static int ax25_shutdown(struct socket *sk, int how)
{
@@ -2181,16 +1525,10 @@ static int ax25_shutdown(struct socket *sk, int how)
return -EOPNOTSUPP;
}
-static int ax25_select(struct socket *sock , int sel_type, select_table *wait)
-{
- struct sock *sk = (struct sock *)sock->data;
-
- return datagram_select(sk, sel_type, wait);
-}
-
static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
+ struct ax25_info_struct ax25_info;
int err;
long amount = 0;
@@ -2198,7 +1536,7 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCOUTQ:
if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
return err;
- amount = sk->sndbuf - sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
put_user(amount, (int *)arg);
@@ -2253,12 +1591,42 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if (!suser())
return -EPERM;
return ax25_rt_ioctl(cmd, (void *)arg);
-
+
case SIOCAX25CTLCON:
if (!suser())
return -EPERM;
return ax25_ctl_ioctl(cmd, (void *)arg);
+ case SIOCAX25GETINFO:
+ if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_info))) != 0)
+ return err;
+ ax25_info.t1 = sk->protinfo.ax25->t1;
+ ax25_info.t2 = sk->protinfo.ax25->t2;
+ ax25_info.t3 = sk->protinfo.ax25->t3;
+ ax25_info.idle = sk->protinfo.ax25->idle;
+ ax25_info.n2 = sk->protinfo.ax25->n2;
+ ax25_info.t1timer = sk->protinfo.ax25->t1timer;
+ ax25_info.t2timer = sk->protinfo.ax25->t2timer;
+ ax25_info.t3timer = sk->protinfo.ax25->t3timer;
+ ax25_info.idletimer = sk->protinfo.ax25->idletimer;
+ ax25_info.n2count = sk->protinfo.ax25->n2count;
+ ax25_info.state = sk->protinfo.ax25->state;
+ ax25_info.rcv_q = atomic_read(&sk->rmem_alloc);
+ ax25_info.snd_q = atomic_read(&sk->wmem_alloc);
+ copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info));
+ return 0;
+
+ case SIOCAX25ADDFWD:
+ case SIOCAX25DELFWD: {
+ struct ax25_fwd_struct ax25_fwd;
+ if (!suser())
+ return -EPERM;
+ if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_fwd))) != 0)
+ return err;
+ copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd));
+ return ax25_fwd_ioctl(cmd, &ax25_fwd);
+ }
+
case SIOCGIFADDR:
case SIOCSIFADDR:
case SIOCGIFDSTADDR:
@@ -2279,11 +1647,9 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return 0;
}
-
static int ax25_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
ax25_cb *ax25;
- struct device *dev;
const char *devname;
int len = 0;
off_t pos = 0;
@@ -2291,13 +1657,13 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i
cli();
- len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen dama Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n");
for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if ((dev = ax25->device) == NULL)
+ if (ax25->ax25_dev == NULL)
devname = "???";
else
- devname = dev->name;
+ devname = ax25->ax25_dev->dev->name;
len += sprintf(buffer + len, "%-9s ",
ax2asc(&ax25->dest_addr));
@@ -2305,36 +1671,34 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i
ax2asc(&ax25->source_addr), devname,
ax25->state,
ax25->vs, ax25->vr, ax25->va,
- ax25->t1timer / PR_SLOWHZ,
- ax25->t1 / PR_SLOWHZ,
- ax25->t2timer / PR_SLOWHZ,
- ax25->t2 / PR_SLOWHZ,
- ax25->t3timer / PR_SLOWHZ,
- ax25->t3 / PR_SLOWHZ,
- ax25->idletimer / (PR_SLOWHZ * 60),
- ax25->idle / (PR_SLOWHZ * 60),
+ ax25->t1timer / AX25_SLOWHZ,
+ ax25->t1 / AX25_SLOWHZ,
+ ax25->t2timer / AX25_SLOWHZ,
+ ax25->t2 / AX25_SLOWHZ,
+ ax25->t3timer / AX25_SLOWHZ,
+ ax25->t3 / AX25_SLOWHZ,
+ ax25->idletimer / (AX25_SLOWHZ * 60),
+ ax25->idle / (AX25_SLOWHZ * 60),
ax25->n2count, ax25->n2,
- ax25->rtt / PR_SLOWHZ,
+ ax25->rtt / AX25_SLOWHZ,
ax25->window,
ax25->paclen);
-
- len += sprintf(buffer + len, " %s", ax25->dama_slave ? " slave" : " no");
if (ax25->sk != NULL) {
len += sprintf(buffer + len, " %5d %5d\n",
- ax25->sk->wmem_alloc,
- ax25->sk->rmem_alloc);
+ atomic_read(&ax25->sk->wmem_alloc),
+ atomic_read(&ax25->sk->rmem_alloc));
} else {
len += sprintf(buffer + len, "\n");
}
-
+
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -2347,12 +1711,17 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length, i
if (len > length) len = length;
return(len);
-}
+}
+
+static struct net_proto_family ax25_family_ops =
+{
+ AF_AX25,
+ ax25_create
+};
static struct proto_ops ax25_proto_ops = {
AF_AX25,
-
- ax25_create,
+
ax25_dup,
ax25_release,
ax25_bind,
@@ -2360,13 +1729,13 @@ static struct proto_ops ax25_proto_ops = {
ax25_socketpair,
ax25_accept,
ax25_getname,
- ax25_select,
+ datagram_poll,
ax25_ioctl,
ax25_listen,
ax25_shutdown,
ax25_setsockopt,
ax25_getsockopt,
- ax25_fcntl,
+ sock_no_fcntl,
ax25_sendmsg,
ax25_recvmsg
};
@@ -2374,11 +1743,11 @@ static struct proto_ops ax25_proto_ops = {
/*
* Called by socket.c on kernel start up
*/
-static struct packet_type ax25_packet_type =
+static struct packet_type ax25_packet_type =
{
0, /* MUTTER ntohs(ETH_P_AX25),*/
0, /* copy */
- kiss_rcv,
+ ax25_kiss_rcv,
NULL,
NULL,
};
@@ -2388,30 +1757,24 @@ static struct notifier_block ax25_dev_notifier = {
0
};
-static struct symbol_table ax25_syms = {
-#include <linux/symtab_begin.h>
- X(ax25_encapsulate),
- X(ax25_rebuild_header),
-#if defined(CONFIG_NETROM_MODULE) || defined(CONFIG_ROSE_MODULE)
- X(ax25_findbyuid),
- X(ax25_link_up),
- X(ax25_linkfail_register),
- X(ax25_linkfail_release),
- X(ax25_listen_register),
- X(ax25_listen_release),
- X(ax25_protocol_register),
- X(ax25_protocol_release),
- X(ax25_send_frame),
- X(ax25_uid_policy),
- X(ax25cmp),
- X(ax2asc),
- X(asc2ax),
- X(null_ax25_address),
-#endif
-#include <linux/symtab_end.h>
-};
+EXPORT_SYMBOL(ax25_encapsulate);
+EXPORT_SYMBOL(ax25_rebuild_header);
+EXPORT_SYMBOL(ax25_findbyuid);
+EXPORT_SYMBOL(ax25_link_up);
+EXPORT_SYMBOL(ax25_linkfail_register);
+EXPORT_SYMBOL(ax25_linkfail_release);
+EXPORT_SYMBOL(ax25_listen_register);
+EXPORT_SYMBOL(ax25_listen_release);
+EXPORT_SYMBOL(ax25_protocol_register);
+EXPORT_SYMBOL(ax25_protocol_release);
+EXPORT_SYMBOL(ax25_send_frame);
+EXPORT_SYMBOL(ax25_uid_policy);
+EXPORT_SYMBOL(ax25cmp);
+EXPORT_SYMBOL(ax2asc);
+EXPORT_SYMBOL(asc2ax);
+EXPORT_SYMBOL(null_ax25_address);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_ax25_route = {
PROC_NET_AX25_ROUTE, 10, "ax25_route",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -2428,172 +1791,29 @@ static struct proc_dir_entry proc_ax25_calls = {
PROC_NET_AX25_CALLS, 10, "ax25_calls",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
- ax25_cs_get_info
+ ax25_uid_get_info
};
#endif
void ax25_proto_init(struct net_proto *pro)
{
- sock_register(ax25_proto_ops.family, &ax25_proto_ops);
+ sock_register(&ax25_family_ops);
ax25_packet_type.type = htons(ETH_P_AX25);
- dev_add_pack(&ax25_packet_type);
+ dev_add_pack(&ax25_packet_type);
register_netdevice_notifier(&ax25_dev_notifier);
- register_symtab(&ax25_syms);
+#ifdef CONFIG_SYSCTL
ax25_register_sysctl();
+#endif
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
proc_net_register(&proc_ax25_route);
proc_net_register(&proc_ax25);
proc_net_register(&proc_ax25_calls);
-#endif
-
- printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.34 for Linux NET3.037 (Linux 2.1)\n");
-}
-
-/*
- * A small shim to dev_queue_xmit to add the KISS control byte.
- */
-void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
-{
- unsigned char *ptr;
-
-#ifdef CONFIG_FIREWALL
- if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL) != FW_ACCEPT) {
- dev_kfree_skb(skb, FREE_WRITE);
- return;
- }
-#endif
-
- skb->protocol = htons(ETH_P_AX25);
-
- ptr = skb_push(skb, 1);
- *ptr++ = 0; /* KISS */
- dev_queue_xmit(skb, dev, pri);
-}
-
-/*
- * IP over AX.25 encapsulation.
- */
-
-/*
- * Shove an AX.25 UI header on an IP packet and handle ARP
- */
-
-#ifdef CONFIG_INET
-
-int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr,
- void *saddr, unsigned len)
-{
- /* header is an AX.25 UI frame from us to them */
- unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
-
- *buff++ = 0; /* KISS DATA */
-
- if (daddr != NULL)
- memcpy(buff, daddr, dev->addr_len); /* Address specified */
-
- buff[6] &= ~LAPB_C;
- buff[6] &= ~LAPB_E;
- buff[6] |= SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- if (saddr != NULL)
- memcpy(buff, saddr, dev->addr_len);
- else
- memcpy(buff, dev->dev_addr, dev->addr_len);
-
- buff[6] &= ~LAPB_C;
- buff[6] |= LAPB_E;
- buff[6] |= SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- *buff++ = LAPB_UI; /* UI */
-
- /* Append a suitable AX.25 PID */
- switch (type) {
- case ETH_P_IP:
- *buff++ = AX25_P_IP;
- break;
-
- case ETH_P_ARP:
- *buff++ = AX25_P_ARP;
- break;
- default:
- printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
- *buff++ = 0;
- break;
- }
-
- if (daddr != NULL)
- return AX25_HEADER_LEN;
+#endif
- return -AX25_HEADER_LEN; /* Unfinished header */
+ printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.36 for Linux NET3.038 (Linux 2.1)\n");
}
-int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long dest, struct sk_buff *skb)
-{
- struct sk_buff *ourskb;
- int mode;
-
- if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb))
- return 1;
-
- if (bp[16] == AX25_P_IP) {
- mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
- if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) {
- /*
- * This is a workaround to try to keep the device locking
- * straight until skb->free=0 is abolished post 1.4.
- *
- * We clone the buffer and release the original thereby
- * keeping it straight
- *
- * Note: we report 1 back so the caller will
- * not feed the frame direct to the physical device
- * We don't want that to happen. (It won't be upset
- * as we have pulled the frame from the queue by
- * freeing it).
- */
- if ((ourskb = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- dev_kfree_skb(skb, FREE_WRITE);
- return 1;
- }
-
- ourskb->sk = skb->sk;
-
- if (ourskb->sk != NULL)
- atomic_add(ourskb->truesize, &ourskb->sk->wmem_alloc);
-
- dev_kfree_skb(skb, FREE_WRITE);
-
- skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
-
- ax25_send_frame(ourskb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
-
- return 1;
- }
- }
-
- bp[7] &= ~LAPB_C;
- bp[7] &= ~LAPB_E;
- bp[7] |= SSSID_SPARE;
-
- bp[14] &= ~LAPB_C;
- bp[14] |= LAPB_E;
- bp[14] |= SSSID_SPARE;
-
- /*
- * dl1bke 960317: we use ax25_queue_xmit here to allow mode datagram
- * over ethernet. I don't know if this is valid, though.
- */
- ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
- ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
-
- return 1;
-}
-
-#endif
-
#ifdef MODULE
int init_module(void)
{
@@ -2611,15 +1831,18 @@ void cleanup_module(void)
proc_net_unregister(PROC_NET_AX25_ROUTE);
#endif
ax25_rt_free();
+ ax25_uid_free();
+ ax25_dev_free();
+#ifdef CONFIG_SYSCTL
ax25_unregister_sysctl();
-
+#endif
unregister_netdevice_notifier(&ax25_dev_notifier);
ax25_packet_type.type = htons(ETH_P_AX25);
- dev_remove_pack(&ax25_packet_type);
+ dev_remove_pack(&ax25_packet_type);
- sock_unregister(ax25_proto_ops.family);
+ sock_unregister(AF_AX25);
}
#endif
diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c
new file mode 100644
index 000000000..4c2bd8996
--- /dev/null
+++ b/net/ax25/ax25_addr.c
@@ -0,0 +1,311 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Most of this code is based on the SDL diagrams published in the 7th
+ * ARRL Computer Networking Conference papers. The diagrams have mistakes
+ * in them, but are mostly correct. Before you modify the code could you
+ * read the SDL diagrams as the code is not obvious and probably very
+ * easy to break;
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ * The null address is defined as a callsign of all spaces with an
+ * SSID of zero.
+ */
+ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
+
+/*
+ * ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+ static char buf[11];
+ char c, *s;
+ int n;
+
+ for (n = 0, s = buf; n < 6; n++) {
+ c = (a->ax25_call[n] >> 1) & 0x7F;
+
+ if (c != ' ') *s++ = c;
+ }
+
+ *s++ = '-';
+
+ if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+ *s++ = '1';
+ n -= 10;
+ }
+
+ *s++ = n + '0';
+ *s++ = '\0';
+
+ if (*buf == '\0' || *buf == '-')
+ return "*";
+
+ return buf;
+
+}
+
+/*
+ * ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+ static ax25_address addr;
+ char *s;
+ int n;
+
+ for (s = callsign, n = 0; n < 6; n++) {
+ if (*s != '\0' && *s != '-')
+ addr.ax25_call[n] = *s++;
+ else
+ addr.ax25_call[n] = ' ';
+ addr.ax25_call[n] <<= 1;
+ addr.ax25_call[n] &= 0xFE;
+ }
+
+ if (*s++ == '\0') {
+ addr.ax25_call[6] = 0x00;
+ return &addr;
+ }
+
+ addr.ax25_call[6] = *s++ - '0';
+
+ if (*s != '\0') {
+ addr.ax25_call[6] *= 10;
+ addr.ax25_call[6] += *s++ - '0';
+ }
+
+ addr.ax25_call[6] <<= 1;
+ addr.ax25_call[6] &= 0x1E;
+
+ return &addr;
+}
+
+/*
+ * Compare two ax.25 addresses
+ */
+int ax25cmp(ax25_address *a, ax25_address *b)
+{
+ int ct = 0;
+
+ while (ct < 6) {
+ if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
+ return 1;
+ ct++;
+ }
+
+ if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
+ return 0;
+
+ return 2; /* Partial match */
+}
+
+/*
+ * Compare two AX.25 digipeater paths.
+ */
+int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
+{
+ int i;
+
+ if (digi1->ndigi != digi2->ndigi)
+ return 1;
+
+ if (digi1->lastrepeat != digi2->lastrepeat)
+ return 1;
+
+ for (i = 0; i < digi1->ndigi; i++)
+ if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
+ *
+ */
+unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
+{
+ int d = 0;
+
+ if (len < 14) return NULL;
+
+ if (flags != NULL) {
+ *flags = 0;
+
+ if (buf[6] & AX25_CBIT)
+ *flags = AX25_COMMAND;
+ if (buf[13] & AX25_CBIT)
+ *flags = AX25_RESPONSE;
+ }
+
+ if (dama != NULL)
+ *dama = ~buf[13] & AX25_DAMA_FLAG;
+
+ /* Copy to, from */
+ if (dest != NULL)
+ memcpy(dest, buf + 0, AX25_ADDR_LEN);
+
+ if (src != NULL)
+ memcpy(src, buf + 7, AX25_ADDR_LEN);
+
+ buf += 2 * AX25_ADDR_LEN;
+ len -= 2 * AX25_ADDR_LEN;
+ digi->lastrepeat = -1;
+ digi->ndigi = 0;
+
+ while (!(buf[-1] & AX25_EBIT)) {
+ if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
+ if (len < 7) return NULL; /* Short packet */
+
+ if (digi != NULL) {
+ memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
+ digi->ndigi = d + 1;
+ if (buf[6] & AX25_HBIT) {
+ digi->repeated[d] = 1;
+ digi->lastrepeat = d;
+ } else {
+ digi->repeated[d] = 0;
+ }
+ }
+
+ buf += AX25_ADDR_LEN;
+ len -= AX25_ADDR_LEN;
+ d++;
+ }
+
+ return buf;
+}
+
+/*
+ * Assemble an AX.25 header from the bits
+ */
+int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
+{
+ int len = 0;
+ int ct = 0;
+
+ memcpy(buf, dest, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+
+ if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ memcpy(buf, src, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] &= ~AX25_SSSID_SPARE;
+
+ if (modulus == AX25_MODULUS)
+ buf[6] |= AX25_SSSID_SPARE;
+ else
+ buf[6] |= AX25_ESSID_SPARE;
+
+ if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
+
+ /*
+ * Fast path the normal digiless path
+ */
+ if (d == NULL || d->ndigi == 0) {
+ buf[6] |= AX25_EBIT;
+ return 2 * AX25_ADDR_LEN;
+ }
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ while (ct < d->ndigi) {
+ memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
+
+ if (d->repeated[ct])
+ buf[6] |= AX25_HBIT;
+ else
+ buf[6] &= ~AX25_HBIT;
+
+ buf[6] &= ~AX25_EBIT;
+ buf[6] |= AX25_SSSID_SPARE;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+ ct++;
+ }
+
+ buf[-1] |= AX25_EBIT;
+
+ return len;
+}
+
+int ax25_addr_size(ax25_digi *dp)
+{
+ if (dp == NULL)
+ return 2 * AX25_ADDR_LEN;
+
+ return AX25_ADDR_LEN * (2 + dp->ndigi);
+}
+
+/*
+ * Reverse Digipeat List. May not pass both parameters as same struct
+ */
+void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
+{
+ int ct = 0;
+
+ out->ndigi = in->ndigi;
+ out->lastrepeat = in->ndigi - in->lastrepeat - 2;
+
+ /* Invert the digipeaters */
+
+ while (ct < in->ndigi) {
+ out->calls[ct] = in->calls[in->ndigi - ct - 1];
+ if (ct <= out->lastrepeat) {
+ out->calls[ct].ax25_call[6] |= AX25_HBIT;
+ out->repeated[ct] = 1;
+ } else {
+ out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
+ out->repeated[ct] = 0;
+ }
+ ct++;
+ }
+}
+
+#endif
diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c
new file mode 100644
index 000000000..1dc039233
--- /dev/null
+++ b/net/ax25/ax25_dev.c
@@ -0,0 +1,238 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Other kernels modules in this kit are generally BSD derived. See the copyright headers.
+ *
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from ax25_route.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+ax25_dev *ax25_dev_list = NULL;
+
+ax25_dev *ax25_dev_ax25dev(struct device *dev)
+{
+ ax25_dev *ax25_dev;
+
+ for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+ if (ax25_dev->dev == dev)
+ return ax25_dev;
+
+ return NULL;
+}
+
+ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
+{
+ ax25_dev *ax25_dev;
+
+ for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+ if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0)
+ return ax25_dev;
+
+ return NULL;
+}
+
+/*
+ * This is called when an interface is brought up. These are
+ * reasonable defaults.
+ */
+void ax25_dev_device_up(struct device *dev)
+{
+ ax25_dev *ax25_dev;
+ unsigned long flags;
+
+ if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
+ printk(KERN_ERR "ax25_dev_device_up out of memory\n");
+ return;
+ }
+
+#ifdef CONFIG_SYSCTL
+ ax25_unregister_sysctl();
+#endif
+
+ memset(ax25_dev, 0x00, sizeof(*ax25_dev));
+
+ ax25_dev->dev = dev;
+ ax25_dev->forward = NULL;
+
+ ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
+ ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
+ ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
+ ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
+ ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
+ ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
+ ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1;
+ ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2;
+ ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3;
+ ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE;
+ ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
+ ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_PROTO_DAMA_SLAVE;
+#else
+ ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL;
+#endif
+ ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
+
+ save_flags(flags); cli();
+ ax25_dev->next = ax25_dev_list;
+ ax25_dev_list = ax25_dev;
+ restore_flags(flags);
+
+#ifdef CONFIG_SYSCTL
+ ax25_register_sysctl();
+#endif
+}
+
+void ax25_dev_device_down(struct device *dev)
+{
+ ax25_dev *s, *ax25_dev;
+ unsigned long flags;
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ return;
+
+#ifdef CONFIG_SYSCTL
+ ax25_unregister_sysctl();
+#endif
+
+ save_flags(flags); cli();
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ ax25_ds_del_timer(ax25_dev);
+#endif
+
+ /*
+ * Remove any packet forwarding that points to this device.
+ */
+ for (s = ax25_dev_list; s != NULL; s = s->next)
+ if (s->forward == dev)
+ s->forward = NULL;
+
+ if ((s = ax25_dev_list) == ax25_dev) {
+ ax25_dev_list = s->next;
+ restore_flags(flags);
+ kfree_s(ax25_dev, sizeof(ax25_dev));
+#ifdef CONFIG_SYSCTL
+ ax25_register_sysctl();
+#endif
+ return;
+ }
+
+ while (s != NULL && s->next != NULL) {
+ if (s->next == ax25_dev) {
+ s->next = ax25_dev->next;
+ restore_flags(flags);
+ kfree_s(ax25_dev, sizeof(ax25_dev));
+#ifdef CONFIG_SYSCTL
+ ax25_register_sysctl();
+#endif
+ return;
+ }
+
+ s = s->next;
+ }
+
+ restore_flags(flags);
+#ifdef CONFIG_SYSCTL
+ ax25_register_sysctl();
+#endif
+}
+
+int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
+{
+ ax25_dev *ax25_dev, *fwd_dev;
+
+ if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCAX25ADDFWD:
+ if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL)
+ return -EINVAL;
+ if (ax25_dev->forward != NULL)
+ return -EINVAL;
+ ax25_dev->forward = fwd_dev->dev;
+ break;
+
+ case SIOCAX25DELFWD:
+ if (ax25_dev->forward == NULL)
+ return -EINVAL;
+ ax25_dev->forward = NULL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct device *ax25_fwd_dev(struct device *dev)
+{
+ ax25_dev *ax25_dev;
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ return dev;
+
+ if (ax25_dev->forward == NULL)
+ return dev;
+
+ return ax25_dev->forward;
+}
+
+#ifdef MODULE
+
+/*
+ * Free all memory associated with device structures.
+ */
+void ax25_dev_free(void)
+{
+ ax25_dev *s, *ax25_dev = ax25_dev_list;
+
+ while (ax25_dev != NULL) {
+ s = ax25_dev;
+ ax25_dev = ax25_dev->next;
+
+ kfree_s(s, sizeof(ax25_dev));
+ }
+}
+
+#endif
+
+#endif
diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c
new file mode 100644
index 000000000..6af2ba91b
--- /dev/null
+++ b/net/ax25/ax25_ds_in.c
@@ -0,0 +1,562 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Most of this code is based on the SDL diagrams published in the 7th
+ * ARRL Computer Networking Conference papers. The diagrams have mistakes
+ * in them, but are mostly correct. Before you modify the code could you
+ * read the SDL diagrams as the code is not obvious and probably very
+ * easy to break;
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c
+ * Joerg(DL1BKE) Fixed it.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h> /* For ip_rcv */
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ * State machine for state 1, Awaiting Connection State.
+ * The handling of the timer(s) is in file ax25_timer.c.
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+ switch (frametype) {
+ case AX25_SABM:
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_SABME:
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_DISC:
+ ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_UA:
+ ax25_calculate_rtt(ax25);
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->n2count = 0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_ESTABLISHED;
+ /* For WAIT_SABM connections we will produce an accept ready socket here */
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ }
+ ax25_dama_on(ax25);
+
+ /* according to DK4EG´s spec we are required to
+ * send a RR RESPONSE FINAL NR=0. Please mail
+ * <jr@lykos.oche.de> if this causes problems
+ * with the TheNetNode DAMA Master implementation.
+ */
+
+ ax25_std_enquiry_response(ax25);
+ break;
+
+ case AX25_DM:
+ if (pf) {
+ if (ax25->modulus == AX25_MODULUS) {
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNREFUSED;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ }
+ }
+ break;
+
+ default:
+ if (pf)
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Release State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25->dama_slave = 0;
+ ax25_dama_off(ax25);
+ break;
+
+ case AX25_DISC:
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ case AX25_UA:
+ if (pf) {
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ }
+ break;
+
+ case AX25_I:
+ case AX25_REJ:
+ case AX25_RNR:
+ case AX25_RR:
+ if (pf) {
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25->dama_slave = 0;
+ ax25_dama_off(ax25);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+ int queued = 0;
+
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (frametype == AX25_SABM) {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ } else {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ }
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->condition = 0x00;
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25_requeue_frames(ax25);
+ ax25_dama_on(ax25);
+ break;
+
+ case AX25_DISC:
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ ax25_clear_queues(ax25);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNRESET;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (frametype == AX25_RR)
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ else
+ ax25->condition |= AX25_COND_PEER_RX_BUSY;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_check_iframes_acked(ax25, nr);
+ if (type == AX25_COMMAND && pf)
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_REJ:
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25_calculate_rtt(ax25);
+ ax25->n2count = 0;
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25_requeue_frames(ax25);
+ if (type == AX25_COMMAND && pf)
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, nr)) {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+ }
+ if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ } else {
+ ax25_check_iframes_acked(ax25, nr);
+ }
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (pf) ax25_ds_enquiry_response(ax25);
+ break;
+ }
+ if (ns == ax25->vr) {
+ ax25->vr = (ax25->vr + 1) % ax25->modulus;
+ queued = ax25_rx_iframe(ax25, skb);
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ ax25->vr = ns; /* ax25->vr - 1 */
+ if (pf) ax25_ds_enquiry_response(ax25);
+ break;
+ }
+ ax25->condition &= ~AX25_COND_REJECT;
+ if (pf) {
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+ ax25->t2timer = ax25->t2;
+ ax25->condition |= AX25_COND_ACK_PENDING;
+ }
+ }
+ } else {
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pf) ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25->condition |= AX25_COND_REJECT;
+ ax25_ds_enquiry_response(ax25);
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ }
+ }
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_ds_establish_data_link(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Timer Recovery State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_ds_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+ int queued = 0;
+
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (frametype == AX25_SABM) {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ } else {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ }
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->condition = 0x00;
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->n2count = 0;
+ ax25_requeue_frames(ax25);
+ ax25_dama_on(ax25);
+ break;
+
+ case AX25_DISC:
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ ax25_clear_queues(ax25);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNRESET;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (frametype == AX25_RR)
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ else
+ ax25->condition |= AX25_COND_PEER_RX_BUSY;
+ if (type == AX25_RESPONSE && pf) {
+ ax25->t1timer = 0;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ if (ax25->vs == ax25->va) {
+ ax25->t3timer = ax25->t3;
+ ax25->state = AX25_STATE_3;
+ } else {
+ ax25_requeue_frames(ax25);
+ }
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+ }
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ if (type == AX25_COMMAND && pf)
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_REJ:
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ if (pf) {
+ ax25->t1timer = 0;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ if (ax25->vs == ax25->va) {
+ ax25->t3timer = ax25->t3;
+ ax25->state = AX25_STATE_3;
+ } else {
+ ax25_requeue_frames(ax25);
+ }
+ if (type == AX25_COMMAND && pf)
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+ }
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ ax25_requeue_frames(ax25);
+ if (type == AX25_COMMAND && pf)
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, nr)) {
+ ax25_ds_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+ }
+ ax25_frames_acked(ax25, nr);
+ ax25->n2count = 0;
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (pf) ax25_ds_enquiry_response(ax25);
+ break;
+ }
+ if (ns == ax25->vr) {
+ ax25->vr = (ax25->vr + 1) % ax25->modulus;
+ queued = ax25_rx_iframe(ax25, skb);
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ ax25->vr = ns; /* ax25->vr - 1 */
+ if (pf) ax25_ds_enquiry_response(ax25);
+ break;
+ }
+ ax25->condition &= ~AX25_COND_REJECT;
+ if (pf) {
+ ax25_ds_enquiry_response(ax25);
+ } else {
+ if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+ ax25->t2timer = ax25->t2;
+ ax25->condition |= AX25_COND_ACK_PENDING;
+ }
+ }
+ } else {
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pf) ax25_ds_enquiry_response(ax25);
+ } else {
+ ax25->condition |= AX25_COND_REJECT;
+ ax25_ds_enquiry_response(ax25);
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ }
+ }
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_ds_establish_data_link(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * Higher level upcall for a LAPB frame
+ */
+int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+ int queued = 0, frametype, ns, nr, pf;
+
+ frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
+
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type);
+ break;
+ case AX25_STATE_2:
+ queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type);
+ break;
+ case AX25_STATE_3:
+ queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
+ break;
+ case AX25_STATE_4:
+ queued = ax25_ds_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
+ break;
+ }
+
+ return queued;
+}
+
+#endif
diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c
new file mode 100644
index 000000000..cc15542ab
--- /dev/null
+++ b/net/ax25/ax25_ds_subr.c
@@ -0,0 +1,225 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Most of this code is based on the SDL diagrams published in the 7th
+ * ARRL Computer Networking Conference papers. The diagrams have mistakes
+ * in them, but are mostly correct. Before you modify the code could you
+ * read the SDL diagrams as the code is not obvious and probably very
+ * easy to break;
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c.
+ * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(),
+ * fixed ax25_dama_on() and ax25_dama_off().
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+void ax25_ds_nr_error_recovery(ax25_cb *ax25)
+{
+ ax25_ds_establish_data_link(ax25);
+}
+
+/*
+ * dl1bke 960114: transmit I frames on DAMA poll
+ */
+void ax25_ds_enquiry_response(ax25_cb *ax25)
+{
+ ax25_cb *ax25o;
+
+ /* Please note that neither DK4EG´s nor DG2FEF´s
+ * DAMA spec mention the following behaviour as seen
+ * with TheFirmware:
+ *
+ * DB0ACH->DL1BKE <RR C P R0> [DAMA]
+ * DL1BKE->DB0ACH <I NR=0 NS=0>
+ * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
+ * DL1BKE->DB0ACH <RR R F R0>
+ *
+ * The Flexnet DAMA Master implementation apparently
+ * insists on the "proper" AX.25 behaviour:
+ *
+ * DB0ACH->DL1BKE <RR C P R0> [DAMA]
+ * DL1BKE->DB0ACH <RR R F R0>
+ * DL1BKE->DB0ACH <I NR=0 NS=0>
+ * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
+ *
+ * Flexnet refuses to send us *any* I frame if we send
+ * a REJ in case AX25_COND_REJECT is set. It is superfluous in
+ * this mode anyway (a RR or RNR invokes the retransmission).
+ * Is this a Flexnet bug?
+ */
+
+ ax25_std_enquiry_response(ax25);
+
+ if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
+ ax25_requeue_frames(ax25);
+ ax25_kick(ax25);
+ }
+
+ if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
+ ax25_ds_t1_timeout(ax25);
+ else
+ ax25->n2count = 0;
+
+ ax25->t3timer = ax25->t3;
+ ax25_ds_set_timer(ax25->ax25_dev);
+
+ for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
+ if (ax25o == ax25)
+ continue;
+
+ if (ax25o->ax25_dev != ax25->ax25_dev)
+ continue;
+
+ if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
+ ax25_ds_t1_timeout(ax25o);
+ continue;
+ }
+
+ if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) &&
+ (ax25o->state == AX25_STATE_3 ||
+ (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
+ ax25_requeue_frames(ax25o);
+ ax25_kick(ax25o);
+ }
+
+ if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
+ ax25_ds_t1_timeout(ax25o);
+
+ ax25o->t3timer = ax25o->t3;
+ }
+}
+
+void ax25_ds_establish_data_link(ax25_cb *ax25)
+{
+ ax25->condition = 0x00;
+ ax25->n2count = 0;
+
+ ax25->t3timer = ax25->t3;
+ ax25->t2timer = 0;
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+
+/*
+ * :::FIXME:::
+ * This is a kludge. Not all drivers recognize kiss commands.
+ * We need a driver level request to switch duplex mode, that does
+ * either SCC changing, PI config or KISS as required. Currently
+ * this request isn't reliable.
+ *
+ */
+static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+
+ if (ax25_dev->dev == NULL)
+ return;
+
+ if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
+ return;
+
+ p = skb_put(skb, 2);
+
+ *p++ = cmd;
+ *p++ = param;
+
+ skb->arp = 1;
+ skb->dev = ax25_dev->dev;
+ skb->priority = SOPRI_NORMAL;
+ skb->protocol = htons(ETH_P_AX25);
+
+ dev_queue_xmit(skb);
+}
+
+/*
+ * A nasty problem arises if we count the number of DAMA connections
+ * wrong, especially when connections on the device already existed
+ * and our network node (or the sysop) decides to turn on DAMA Master
+ * mode. We thus flag the 'real' slave connections with
+ * ax25->dama_slave=1 and look on every disconnect if still slave
+ * connections exist.
+ */
+
+static int ax25_check_dama_slave(ax25_dev *ax25_dev)
+{
+ ax25_cb *ax25;
+
+ for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
+ if (ax25->ax25_dev == ax25_dev && ax25->dama_slave && ax25->state > AX25_STATE_1)
+ return 1;
+
+ return 0;
+}
+
+void ax25_dev_dama_on(ax25_dev *ax25_dev)
+{
+ if (ax25_dev == NULL)
+ return;
+
+ if (ax25_dev->dama.slave == 0)
+ ax25_kiss_cmd(ax25_dev, 5, 1);
+
+ ax25_dev->dama.slave = 1;
+ ax25_ds_set_timer(ax25_dev);
+}
+
+void ax25_dev_dama_off(ax25_dev *ax25_dev)
+{
+ if (ax25_dev == NULL)
+ return;
+
+ if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev))
+ {
+ ax25_kiss_cmd(ax25_dev, 5, 0);
+ ax25_dev->dama.slave = 0;
+ ax25_ds_del_timer(ax25_dev);
+ }
+}
+
+void ax25_dama_on(ax25_cb *ax25)
+{
+ ax25_dev_dama_on(ax25->ax25_dev);
+ ax25->dama_slave = 1;
+}
+
+void ax25_dama_off(ax25_cb *ax25)
+{
+ ax25_dev_dama_off(ax25->ax25_dev);
+ ax25->dama_slave = 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c
new file mode 100644
index 000000000..1b9d3c95f
--- /dev/null
+++ b/net/ax25/ax25_ds_timer.c
@@ -0,0 +1,313 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c.
+ * Joerg(DL1BKE) Added DAMA Slave Timeout timer
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25_DAMA_SLAVE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+static void ax25_ds_timeout(unsigned long);
+
+/*
+ * Add DAMA slave timeout timer to timer list.
+ * Unlike the connection based timers the timeout function gets
+ * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT
+ * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in
+ * 1/10th of a second.
+ */
+
+static void ax25_ds_add_timer(ax25_dev *ax25_dev)
+{
+ struct timer_list *t = &ax25_dev->dama.slave_timer;
+ t->data = (unsigned long) ax25_dev;
+ t->function = &ax25_ds_timeout;
+ t->expires = jiffies + HZ;
+ add_timer(t);
+}
+
+void ax25_ds_del_timer(ax25_dev *ax25_dev)
+{
+ if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer);
+}
+
+void ax25_ds_set_timer(ax25_dev *ax25_dev)
+{
+ if (ax25_dev == NULL) /* paranoia */
+ return;
+
+ del_timer(&ax25_dev->dama.slave_timer);
+ ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10;
+ ax25_ds_add_timer(ax25_dev);
+}
+
+/*
+ * DAMA Slave Timeout
+ * Silently discard all (slave) connections in case our master forgot us...
+ */
+
+static void ax25_ds_timeout(unsigned long arg)
+{
+ ax25_dev *ax25_dev = (struct ax25_dev *) arg;
+ ax25_cb *ax25;
+
+ if (ax25_dev == NULL || !ax25_dev->dama.slave)
+ return; /* Yikes! */
+
+ if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout)
+ {
+ ax25_ds_set_timer(ax25_dev);
+ return;
+ }
+
+ for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next)
+ {
+ if (ax25->ax25_dev != ax25_dev || !ax25->dama_slave)
+ continue;
+
+ ax25_link_failed(&ax25->dest_addr, ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25->state = AX25_STATE_0;
+
+ if (ax25->sk != NULL)
+ {
+ SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n");
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+
+ ax25_set_timer(ax25); /* notify socket... */
+ }
+
+ ax25_dev_dama_off(ax25_dev);
+}
+
+
+/*
+ * AX.25 TIMER
+ *
+ * This routine is called every 100ms. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+void ax25_ds_timer(ax25_cb *ax25)
+{
+ switch (ax25->state) {
+ case AX25_STATE_0:
+ /* Magic here: If we listen() and a new link dies before it
+ is accepted() it isn't 'dead' so doesn't get removed. */
+ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
+ del_timer(&ax25->timer);
+ ax25_destroy_socket(ax25);
+ return;
+ }
+ break;
+
+ case AX25_STATE_3:
+ case AX25_STATE_4:
+ /*
+ * Check the state of the receive buffer.
+ */
+ if (ax25->sk != NULL) {
+ if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+ ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+ break;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* dl1bke 960114: T3 works much like the IDLE timeout, but
+ * gets reloaded with every frame for this
+ * connection.
+ */
+
+ if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+ ax25->state = AX25_STATE_0;
+ ax25_dama_off(ax25);
+
+ if (ax25->sk != NULL) {
+ SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n");
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+
+ ax25_set_timer(ax25);
+
+ return;
+ }
+
+ /* dl1bke 960228: close the connection when IDLE expires.
+ * unlike T3 this timer gets reloaded only on
+ * I frames.
+ */
+
+ if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
+ ax25_clear_queues(ax25);
+
+ ax25->n2count = 0;
+ ax25->t3timer = ax25->t3;
+
+ /* state 1 or 2 should not happen, but... */
+
+ if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
+ ax25->state = AX25_STATE_0;
+ else
+ ax25->state = AX25_STATE_2;
+
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ ax25->sk->destroy = 1;
+ }
+ }
+
+ ax25_set_timer(ax25);
+}
+
+/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
+ * within the poll of any connected channel. Remember
+ * that we are not allowed to send anything unless we
+ * get polled by the Master.
+ *
+ * Thus we'll have to do parts of our T1 handling in
+ * ax25_enquiry_response().
+ */
+void ax25_ds_t1_timeout(ax25_cb *ax25)
+{
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->modulus == AX25_MODULUS) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ ax25->n2count = 0;
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
+ }
+ } else {
+ ax25->n2count++;
+ if (ax25->modulus == AX25_MODULUS)
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
+ else
+ ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND);
+ }
+ break;
+
+ case AX25_STATE_2:
+ if (ax25->n2count == ax25->n2) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->n2count++;
+ }
+ break;
+
+ case AX25_STATE_3:
+ ax25->n2count = 1;
+ ax25->state = AX25_STATE_4;
+ break;
+
+ case AX25_STATE_4:
+ if (ax25->n2count == ax25->n2) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->n2count++;
+ }
+ break;
+ }
+
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+ ax25_set_timer(ax25);
+}
+
+#endif
diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c
new file mode 100644
index 000000000..94e62000a
--- /dev/null
+++ b/net/ax25/ax25_iface.c
@@ -0,0 +1,282 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+static struct protocol_struct {
+ struct protocol_struct *next;
+ unsigned int pid;
+ int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+ struct linkfail_struct *next;
+ void (*func)(ax25_address *, struct device *);
+} *linkfail_list = NULL;
+
+static struct listen_struct {
+ struct listen_struct *next;
+ ax25_address callsign;
+ struct device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
+{
+ struct protocol_struct *protocol;
+ unsigned long flags;
+
+ if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+ return 0;
+#ifdef CONFIG_INET
+ if (pid == AX25_P_IP || pid == AX25_P_ARP)
+ return 0;
+#endif
+ if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ protocol->pid = pid;
+ protocol->func = func;
+
+ save_flags(flags);
+ cli();
+
+ protocol->next = protocol_list;
+ protocol_list = protocol;
+
+ restore_flags(flags);
+
+ return 1;
+}
+
+void ax25_protocol_release(unsigned int pid)
+{
+ struct protocol_struct *s, *protocol = protocol_list;
+ unsigned long flags;
+
+ if (protocol == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (protocol->pid == pid) {
+ protocol_list = protocol->next;
+ restore_flags(flags);
+ kfree_s(protocol, sizeof(struct protocol_struct));
+ return;
+ }
+
+ while (protocol != NULL && protocol->next != NULL) {
+ if (protocol->next->pid == pid) {
+ s = protocol->next;
+ protocol->next = protocol->next->next;
+ restore_flags(flags);
+ kfree_s(s, sizeof(struct protocol_struct));
+ return;
+ }
+
+ protocol = protocol->next;
+ }
+
+ restore_flags(flags);
+}
+
+int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
+{
+ struct linkfail_struct *linkfail;
+ unsigned long flags;
+
+ if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ linkfail->func = func;
+
+ save_flags(flags);
+ cli();
+
+ linkfail->next = linkfail_list;
+ linkfail_list = linkfail;
+
+ restore_flags(flags);
+
+ return 1;
+}
+
+void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
+{
+ struct linkfail_struct *s, *linkfail = linkfail_list;
+ unsigned long flags;
+
+ if (linkfail == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (linkfail->func == func) {
+ linkfail_list = linkfail->next;
+ restore_flags(flags);
+ kfree_s(linkfail, sizeof(struct linkfail_struct));
+ return;
+ }
+
+ while (linkfail != NULL && linkfail->next != NULL) {
+ if (linkfail->next->func == func) {
+ s = linkfail->next;
+ linkfail->next = linkfail->next->next;
+ restore_flags(flags);
+ kfree_s(s, sizeof(struct linkfail_struct));
+ return;
+ }
+
+ linkfail = linkfail->next;
+ }
+
+ restore_flags(flags);
+}
+
+int ax25_listen_register(ax25_address *callsign, struct device *dev)
+{
+ struct listen_struct *listen;
+ unsigned long flags;
+
+ if (ax25_listen_mine(callsign, dev))
+ return 0;
+
+ if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ listen->callsign = *callsign;
+ listen->dev = dev;
+
+ save_flags(flags);
+ cli();
+
+ listen->next = listen_list;
+ listen_list = listen;
+
+ restore_flags(flags);
+
+ return 1;
+}
+
+void ax25_listen_release(ax25_address *callsign, struct device *dev)
+{
+ struct listen_struct *s, *listen = listen_list;
+ unsigned long flags;
+
+ if (listen == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
+ listen_list = listen->next;
+ restore_flags(flags);
+ kfree_s(listen, sizeof(struct listen_struct));
+ return;
+ }
+
+ while (listen != NULL && listen->next != NULL) {
+ if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
+ s = listen->next;
+ listen->next = listen->next->next;
+ restore_flags(flags);
+ kfree_s(s, sizeof(struct listen_struct));
+ return;
+ }
+
+ listen = listen->next;
+ }
+
+ restore_flags(flags);
+}
+
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
+{
+ struct protocol_struct *protocol;
+
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return protocol->func;
+
+ return NULL;
+}
+
+int ax25_listen_mine(ax25_address *callsign, struct device *dev)
+{
+ struct listen_struct *listen;
+
+ for (listen = listen_list; listen != NULL; listen = listen->next)
+ if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+ return 1;
+
+ return 0;
+}
+
+void ax25_link_failed(ax25_address *callsign, struct device *dev)
+{
+ struct linkfail_struct *linkfail;
+
+ for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+ (linkfail->func)(callsign, dev);
+}
+
+/*
+ * Return the state of an AX.25 link given source, destination, and
+ * device.
+ */
+int ax25_link_up(ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
+{
+ return ax25_find_cb(src, dest, digi, dev) != NULL;
+}
+
+int ax25_protocol_is_registered(unsigned int pid)
+{
+ struct protocol_struct *protocol;
+
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return 1;
+
+ return 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 3ef1c3fdf..ec9ce4da0 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 033
+ * AX.25 release 036
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -35,6 +35,9 @@
* AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
* AX.25 033 Jonathan(G4KLX) Remove auto-router.
* Modularisation changes.
+ * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
+ * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file.
+ * Joerg(DL1BKE) Fixed DAMA Slave.
*/
#include <linux/config.h>
@@ -53,16 +56,16 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/firewall.h>
#include <net/sock.h>
#include <net/ip.h> /* For ip_rcv */
+#include <net/arp.h> /* For arp_rcv */
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
-static int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
-
/*
* Given a fragment, queue it on the fragment queue and if the fragment
* is complete, send it back to ax25_rx_iframe.
@@ -71,12 +74,12 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
{
struct sk_buff *skbn, *skbo;
int hdrlen, nhdrlen;
-
+
if (ax25->fragno != 0) {
- if (!(*skb->data & SEG_FIRST)) {
- if ((ax25->fragno - 1) == (*skb->data & SEG_REM)) {
+ if (!(*skb->data & AX25_SEG_FIRST)) {
+ if ((ax25->fragno - 1) == (*skb->data & AX25_SEG_REM)) {
/* Enqueue fragment */
- ax25->fragno = *skb->data & SEG_REM;
+ ax25->fragno = *skb->data & AX25_SEG_REM;
skb_pull(skb, 1); /* skip fragno */
ax25->fraglen += skb->len;
skb_queue_tail(&ax25->frag_queue, skb);
@@ -89,14 +92,7 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
return 1;
}
- skbn->free = 1;
- skbn->arp = 1;
- skbn->dev = ax25->device;
-
- if (ax25->sk != NULL) {
- skbn->sk = ax25->sk;
- atomic_add(skbn->truesize, &ax25->sk->rmem_alloc);
- }
+ skbn->dev = ax25->ax25_dev->dev;
skb_reserve(skbn, AX25_MAX_HEADER_LEN);
@@ -125,16 +121,16 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
if (ax25_rx_iframe(ax25, skbn) == 0)
kfree_skb(skbn, FREE_READ);
}
-
+
return 1;
}
}
} else {
/* First fragment received */
- if (*skb->data & SEG_FIRST) {
+ if (*skb->data & AX25_SEG_FIRST) {
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL)
kfree_skb(skbo, FREE_READ);
- ax25->fragno = *skb->data & SEG_REM;
+ ax25->fragno = *skb->data & AX25_SEG_REM;
skb_pull(skb, 1); /* skip fragno */
ax25->fraglen = skb->len;
skb_queue_tail(&ax25->frag_queue, skb);
@@ -149,23 +145,36 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
* This is where all valid I frames are sent to, to be dispatched to
* whichever protocol requires them.
*/
-static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
{
int (*func)(struct sk_buff *, ax25_cb *);
volatile int queued = 0;
unsigned char pid;
-
+
if (skb == NULL) return 0;
ax25->idletimer = ax25->idle;
-
+
pid = *skb->data;
#ifdef CONFIG_INET
if (pid == AX25_P_IP) {
+ /* working around a TCP bug to keep additional listeners
+ * happy. TCP re-uses the buffer and destroys the original
+ * content.
+ */
+ struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
+ if (skbn != NULL) {
+ kfree_skb(skb, FREE_READ);
+ skb = skbn;
+ }
+
skb_pull(skb, 1); /* Remove PID */
- skb->h.raw = skb->data;
- ip_rcv(skb, ax25->device, NULL); /* Wrong ptype */
+ skb->h.raw = skb->data;
+ skb->nh.raw = skb->data;
+ skb->dev = ax25->ax25_dev->dev;
+ skb->pkt_type = PACKET_HOST;
+ ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */
return 1;
}
#endif
@@ -178,643 +187,319 @@ static int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
skb_pull(skb, 1); /* Remove PID */
return (*func)(skb, ax25);
}
-
- if (ax25->sk != NULL && ax25_dev_get_value(ax25->device, AX25_VALUES_TEXT) && ax25->sk->protocol == pid) {
+
+ if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2 && ax25->sk->protocol == pid) {
if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
queued = 1;
else
- ax25->condition |= OWN_RX_BUSY_CONDITION;
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
}
return queued;
}
/*
- * State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_timer.c.
- * Handling of state 0 and connection release is in ax25.c.
+ * Higher level upcall for a LAPB frame
*/
-static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type, int dama)
+static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
{
- switch (frametype) {
- case SABM:
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- break;
-
- case SABME:
- ax25->modulus = EMODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- break;
+ int queued = 0;
- case DISC:
- ax25_send_control(ax25, DM, pf, C_RESPONSE);
- break;
+ if (ax25->state == AX25_STATE_0)
+ return 0;
- case UA:
- if (pf || dama) {
- if (dama) ax25_dama_on(ax25); /* bke */
-
- ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- ax25->dama_slave = dama; /* bke */
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- }
- }
- break;
+ del_timer(&ax25->timer);
- case DM:
- if (pf) {
- if (ax25->modulus == MODULUS) {
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNREFUSED;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- } else {
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
- }
- }
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ queued = ax25_std_frame_in(ax25, skb, type);
break;
- default:
- if (dama && pf)
- ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (dama || ax25->ax25_dev->dama.slave)
+ queued = ax25_ds_frame_in(ax25, skb, type);
+ else
+ queued = ax25_std_frame_in(ax25, skb, type);
break;
+#endif
}
- return 0;
+ ax25_set_timer(ax25);
+
+ return queued;
}
-/*
- * State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+static int ax25_rcv(struct sk_buff *skb, struct device *dev, ax25_address *dev_addr, struct packet_type *ptype)
{
- switch (frametype) {
- case SABM:
- case SABME:
- ax25_send_control(ax25, DM, pf, C_RESPONSE);
- if (ax25->dama_slave)
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
- break;
-
- case DISC:
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- if (ax25->dama_slave) {
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- }
- break;
+ struct sock *make;
+ struct sock *sk;
+ int type = 0;
+ ax25_digi dp, reverse_dp;
+ ax25_cb *ax25;
+ ax25_address src, dest;
+ ax25_address *next_digi = NULL;
+ ax25_dev *ax25_dev;
+ struct sock *raw;
+ int mine = 0;
+ int dama;
+
+ /*
+ * Process the AX.25/LAPB frame.
+ */
+
+ skb->h.raw = skb->data;
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
- case UA:
- if (pf) {
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- }
- break;
+ if (call_in_firewall(PF_AX25, skb->dev, skb->h.raw, NULL,&skb) != FW_ACCEPT) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
- case DM:
- if (pf) {
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- }
- break;
+ /*
+ * Parse the address header.
+ */
- case I:
- case REJ:
- case RNR:
- case RR:
- if (pf) {
- if (ax25->dama_slave)
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
- else
- ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
- }
- break;
-
- default:
- break;
+ if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
}
- return 0;
-}
+ /*
+ * Ours perhaps ?
+ */
+ if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */
+ next_digi = &dp.calls[dp.lastrepeat + 1];
-/*
- * State machine for state 3, Connected State.
- * The handling of the timer(s) is in file ax25_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
-{
- int queued = 0;
+ /*
+ * Pull of the AX.25 headers leaving the CTRL/PID bytes
+ */
+ skb_pull(skb, ax25_addr_size(&dp));
- switch (frametype) {
- case SABM:
- if (dama) ax25_dama_on(ax25);
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->dama_slave = dama;
- break;
+ /* For our port addresses ? */
+ if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
+ mine = 1;
- case SABME:
- if (dama) ax25_dama_on(ax25);
- ax25->modulus = EMODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->dama_slave = dama;
- break;
+ /* Also match on any registered callsign from L3/4 */
+ if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
+ mine = 1;
- case DISC:
- ax25_clear_queues(ax25);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- break;
+ /* UI frame - bypass LAPB processing */
+ if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
+ skb->h.raw = skb->data + 2; /* skip control and pid */
- case DM:
- ax25_clear_queues(ax25);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- break;
+ if ((raw = ax25_addr_match(&dest)) != NULL)
+ ax25_send_to_raw(raw, skb, skb->data[1]);
- case RNR:
- ax25->condition |= PEER_RX_BUSY_CONDITION;
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_check_iframes_acked(ax25, nr);
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case RR:
- ax25->condition &= ~PEER_RX_BUSY_CONDITION;
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_check_iframes_acked(ax25, nr);
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case REJ:
- ax25->condition &= ~PEER_RX_BUSY_CONDITION;
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25_requeue_frames(ax25);
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case I:
-#ifndef AX25_BROKEN_NETMAC
- if (type != C_COMMAND)
- break;
-#endif
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
+ if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* Now we are pointing at the pid byte */
+ switch (skb->data[1]) {
+#ifdef CONFIG_INET
+ case AX25_P_IP:
+ skb_pull(skb,2); /* drop PID/CTRL */
+ skb->h.raw = skb->data;
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
break;
- }
- if (ax25->condition & PEER_RX_BUSY_CONDITION) {
- ax25_frames_acked(ax25, nr);
- } else {
- ax25_check_iframes_acked(ax25, nr);
- }
- if (ax25->condition & OWN_RX_BUSY_CONDITION) {
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- }
+
+ case AX25_P_ARP:
+ skb_pull(skb,2);
+ skb->h.raw = skb->data;
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & OWN_RX_BUSY_CONDITION) {
- ax25->vr = ns; /* ax25->vr - 1 */
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- }
- break;
- }
- ax25->condition &= ~REJECT_CONDITION;
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & ACK_PENDING_CONDITION)) {
- ax25->t2timer = ax25->t2;
- ax25->condition |= ACK_PENDING_CONDITION;
- }
- }
- } else {
- if (ax25->condition & REJECT_CONDITION) {
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
+#endif
+ case AX25_P_TEXT:
+ /* Now find a suitable dgram socket */
+ if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
+ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
+ kfree_skb(skb, FREE_READ);
+ } else {
+ /*
+ * Remove the control and PID.
+ */
+ skb_pull(skb, 2);
+ if (sock_queue_rcv_skb(sk, skb) != 0)
+ kfree_skb(skb, FREE_READ);
}
} else {
- ax25->condition |= REJECT_CONDITION;
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_send_control(ax25, REJ, pf, C_RESPONSE);
- ax25->condition &= ~ACK_PENDING_CONDITION;
+ kfree_skb(skb, FREE_READ);
}
- }
- break;
+ break;
- case FRMR:
- case ILLEGAL:
- ax25_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
+ default:
+ kfree_skb(skb, FREE_READ); /* Will scan SOCK_AX25 RAW sockets */
+ break;
+ }
- default:
- break;
+ return 0;
}
- return queued;
-}
+ /*
+ * Is connected mode supported on this device ?
+ * If not, should we DM the incoming frame (except DMs) or
+ * silently ignore them. For now we stay quiet.
+ */
+ if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
-/*
- * State machine for state 4, Timer Recovery State.
- * The handling of the timer(s) is in file ax25_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type, int dama)
-{
- int queued = 0;
+ /* LAPB */
- switch (frametype) {
- case SABM:
- if (dama) ax25_dama_on(ax25);
- ax25->dama_slave = dama;
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- break;
+ /* AX.25 state 1-4 */
- case SABME:
- if (dama) ax25_dama_on(ax25);
- ax25->dama_slave = dama;
- ax25->modulus = EMODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- break;
+ ax25_digi_invert(&dp, &reverse_dp);
- case DISC:
- ax25_clear_queues(ax25);
- ax25_send_control(ax25, UA, pf, C_RESPONSE);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- break;
+ if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
+ /*
+ * Process the frame. If it is queued up internally it returns one otherwise we
+ * free it immediately. This routine itself wakes the user context layers so we
+ * do no further work
+ */
+ if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
+ kfree_skb(skb, FREE_READ);
- case DM:
- ax25_clear_queues(ax25);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- break;
+ return 0;
+ }
- case RNR:
- ax25->condition |= PEER_RX_BUSY_CONDITION;
- if (type == C_RESPONSE && pf) {
- ax25->t1timer = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25->t3timer = ax25->t3;
- ax25->n2count = 0;
- ax25->state = AX25_STATE_3;
- }
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
-
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case RR:
- ax25->condition &= ~PEER_RX_BUSY_CONDITION;
- if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) {
- ax25->t1timer = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25->t3timer = ax25->t3;
- ax25->n2count = 0;
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
+ /* AX.25 state 0 (disconnected) */
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
+ /* a) received not a SABM(E) */
- case REJ:
- ax25->condition &= ~PEER_RX_BUSY_CONDITION;
- if (pf && (type == C_RESPONSE || (ax25->dama_slave && type == C_COMMAND))) {
- ax25->t1timer = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25->t3timer = ax25->t3;
- ax25->n2count = 0;
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
-
- ax25_check_need_response(ax25, type, pf);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if(ax25->vs != ax25->va) {
- ax25_requeue_frames(ax25);
- }
- dama_check_need_response(ax25, type, pf);
- } else {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
+ if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
+ /*
+ * Never reply to a DM. Also ignore any connects for
+ * addresses that are not our interfaces and not a socket.
+ */
+ if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
+ ax25_return_dm(dev, &src, &dest, &dp);
- case I:
-#ifndef AX25_BROKEN_NETMAC
- if (type != C_COMMAND)
- break;
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ /* b) received SABM(E) */
+
+ if (dp.lastrepeat + 1 == dp.ndigi)
+ sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
+ else
+ sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+
+ if (sk != NULL) {
+ if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
+ if (mine) ax25_return_dm(dev, &src, &dest, &dp);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ ax25 = make->protinfo.ax25;
+
+ skb_queue_head(&sk->receive_queue, skb);
+
+ skb->sk = make;
+ make->state = TCP_ESTABLISHED;
+ make->pair = sk;
+
+ sk->ack_backlog++;
+ } else {
+ if (!mine) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_return_dm(dev, &src, &dest, &dp);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ ax25_fillin_cb(ax25, ax25_dev);
+ ax25->idletimer = ax25->idle;
+ }
+
+ ax25->source_addr = dest;
+ ax25->dest_addr = src;
+
+ /*
+ * Sort out any digipeated paths.
+ */
+ if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+ kfree_skb(skb, FREE_READ);
+ ax25_destroy_socket(ax25);
+ return 0;
+ }
+
+ if (dp.ndigi == 0) {
+ if (ax25->digipeat != NULL) {
+ kfree_s(ax25->digipeat, sizeof(ax25_digi));
+ ax25->digipeat = NULL;
+ }
+ } else {
+ /* Reverse the source SABM's path */
+ *ax25->digipeat = reverse_dp;
+ }
+
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
+ } else {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
+ }
+
+ ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
+ ax25_dama_on(ax25);
#endif
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- ax25_frames_acked(ax25, nr);
- if (ax25->condition & OWN_RX_BUSY_CONDITION) {
- if (pf) {
- if (ax25->dama_slave)
- ax25_enquiry_response(ax25);
- else
- dama_enquiry_response(ax25);
- }
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & OWN_RX_BUSY_CONDITION) {
- ax25->vr = ns; /* ax25->vr - 1 */
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- }
- break;
- }
- ax25->condition &= ~REJECT_CONDITION;
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & ACK_PENDING_CONDITION)) {
- ax25->t2timer = ax25->t2;
- ax25->condition |= ACK_PENDING_CONDITION;
- }
- }
- } else {
- if (ax25->condition & REJECT_CONDITION) {
- if (pf) {
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_enquiry_response(ax25);
- }
- } else {
- ax25->condition |= REJECT_CONDITION;
- if (ax25->dama_slave)
- dama_enquiry_response(ax25);
- else
- ax25_send_control(ax25, REJ, pf, C_RESPONSE);
- ax25->condition &= ~ACK_PENDING_CONDITION;
- }
- }
- break;
-
- case FRMR:
- case ILLEGAL:
- ax25_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
- default:
- break;
+ ax25->t3timer = ax25->t3;
+ ax25->state = AX25_STATE_3;
+
+ ax25_insert_socket(ax25);
+
+ ax25_set_timer(ax25);
+
+ if (sk != NULL) {
+ if (!sk->dead)
+ sk->data_ready(sk, skb->len);
+ } else {
+ kfree_skb(skb, FREE_READ);
}
- return queued;
+ return 0;
}
/*
- * Higher level upcall for a LAPB frame
+ * Receive an AX.25 frame via a SLIP interface.
*/
-int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
+int ax25_kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
{
- int queued = 0, frametype, ns, nr, pf;
-
- if (ax25->state == AX25_STATE_0)
- return 0;
-
- del_timer(&ax25->timer);
-
- frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
+ skb->sk = NULL; /* Initially we don't know who it's for */
- switch (ax25->state) {
- case AX25_STATE_1:
- queued = ax25_state1_machine(ax25, skb, frametype, pf, type, dama);
- break;
- case AX25_STATE_2:
- queued = ax25_state2_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_3:
- queued = ax25_state3_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
- break;
- case AX25_STATE_4:
- queued = ax25_state4_machine(ax25, skb, frametype, ns, nr, pf, type, dama);
- break;
+ if ((*skb->data & 0x0F) != 0) {
+ kfree_skb(skb, FREE_READ); /* Not a KISS data frame */
+ return 0;
}
- ax25_set_timer(ax25);
+ skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */
- return queued;
+ return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
}
#endif
diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
new file mode 100644
index 000000000..ec882c50d
--- /dev/null
+++ b/net/ax25/ax25_ip.c
@@ -0,0 +1,178 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/sysctl.h>
+#include <net/ip.h>
+#include <net/arp.h>
+
+/*
+ * IP over AX.25 encapsulation.
+ */
+
+/*
+ * Shove an AX.25 UI header on an IP packet and handle ARP
+ */
+
+#ifdef CONFIG_INET
+
+int ax25_encapsulate(struct sk_buff *skb, struct device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+ /* header is an AX.25 UI frame from us to them */
+ unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
+
+ *buff++ = 0x00; /* KISS DATA */
+
+ if (daddr != NULL)
+ memcpy(buff, daddr, dev->addr_len); /* Address specified */
+
+ buff[6] &= ~AX25_CBIT;
+ buff[6] &= ~AX25_EBIT;
+ buff[6] |= AX25_SSSID_SPARE;
+ buff += AX25_ADDR_LEN;
+
+ if (saddr != NULL)
+ memcpy(buff, saddr, dev->addr_len);
+ else
+ memcpy(buff, dev->dev_addr, dev->addr_len);
+
+ buff[6] &= ~AX25_CBIT;
+ buff[6] |= AX25_EBIT;
+ buff[6] |= AX25_SSSID_SPARE;
+ buff += AX25_ADDR_LEN;
+
+ *buff++ = AX25_UI; /* UI */
+
+ /* Append a suitable AX.25 PID */
+ switch (type) {
+ case ETH_P_IP:
+ *buff++ = AX25_P_IP;
+ break;
+ case ETH_P_ARP:
+ *buff++ = AX25_P_ARP;
+ break;
+ default:
+ printk(KERN_ERR "AX.25 wrong protocol type 0x%x2.2\n", type);
+ *buff++ = 0;
+ break;
+ }
+
+ if (daddr != NULL)
+ return AX25_HEADER_LEN;
+
+ return -AX25_HEADER_LEN; /* Unfinished header */
+}
+
+int ax25_rebuild_header(struct sk_buff *skb)
+{
+ struct sk_buff *ourskb;
+ int mode;
+ unsigned char *bp = skb->data;
+ struct device *dev = skb->dev;
+ ax25_dev *ax25_dev;
+
+ if (arp_find(bp + 1, skb))
+ return 1;
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ return 1;
+
+ if (bp[16] == AX25_P_IP) {
+ mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
+ if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
+ /*
+ * We copy the buffer and release the original thereby
+ * keeping it straight
+ *
+ * Note: we report 1 back so the caller will
+ * not feed the frame direct to the physical device
+ * We don't want that to happen. (It won't be upset
+ * as we have pulled the frame from the queue by
+ * freeing it).
+ *
+ * NB: TCP modifies buffers that are still
+ * on a device queue, thus we use skb_copy()
+ * instead of using skb_clone() unless this
+ * gets fixed.
+ */
+ if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 1;
+ }
+
+ if (skb->sk != NULL)
+ skb_set_owner_w(ourskb, skb->sk);
+
+ kfree_skb(skb, FREE_WRITE);
+
+ skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
+
+ ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+
+ return 1;
+ }
+ }
+
+ bp[7] &= ~AX25_CBIT;
+ bp[7] &= ~AX25_EBIT;
+ bp[7] |= AX25_SSSID_SPARE;
+
+ bp[14] &= ~AX25_CBIT;
+ bp[14] |= AX25_EBIT;
+ bp[14] |= AX25_SSSID_SPARE;
+
+ ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
+
+ skb->dev = dev;
+ skb->priority = SOPRI_NORMAL;
+
+ ax25_queue_xmit(skb);
+
+ return 1;
+}
+
+#endif
+
+#endif
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index be265b344..7072c632e 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 033
+ * AX.25 release 036
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -47,6 +47,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
+#include <linux/firewall.h>
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -54,68 +55,119 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
+int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
+{
+ ax25_dev *ax25_dev;
+ ax25_cb *ax25;
+
+ if (skb == NULL)
+ return 0;
+
+ /*
+ * Look for an existing connection.
+ */
+ if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
+ ax25_output(ax25, paclen, skb);
+ ax25->idletimer = ax25->idle;
+ return 1; /* It already existed */
+ }
+
+ if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
+ return 0;
+
+ if ((ax25 = ax25_create_cb()) == NULL)
+ return 0;
+
+ ax25_fillin_cb(ax25, ax25_dev);
+
+ ax25->source_addr = *src;
+ ax25->dest_addr = *dest;
+
+ if (digi != NULL) {
+ if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
+ ax25_free_cb(ax25);
+ return 0;
+ }
+ *ax25->digipeat = *digi;
+ } else {
+ ax25_rt_build_path(ax25, dest, dev);
+ }
+
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_establish_data_link(ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (ax25_dev->dama.slave)
+ ax25_ds_establish_data_link(ax25);
+ else
+ ax25_std_establish_data_link(ax25);
+ break;
+#endif
+ }
+
+ /* idle timeouts only for mode vc connections */
+
+ ax25->idletimer = ax25->idle;
+
+ ax25_insert_socket(ax25);
+
+ ax25->state = AX25_STATE_1;
+
+ ax25_set_timer(ax25);
+
+ ax25_output(ax25, paclen, skb);
+
+ return 1; /* We had to create it */
+}
+
/*
- * All outgoing AX.25 I frames pass via this routine. Therefore this is
- * where the fragmentation of frames takes place.
+ * All outgoing AX.25 I frames pass via this routine. Therefore this is
+ * where the fragmentation of frames takes place. If fragment is set to
+ * zero then we are not allowed to do fragmentation, even if the frame
+ * is too large.
*/
-void ax25_output(ax25_cb *ax25, struct sk_buff *skb)
+void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
{
struct sk_buff *skbn;
unsigned char *p;
- int frontlen, mtu, len, fragno, ka9qfrag, first = 1;
+ int frontlen, len, fragno, ka9qfrag, first = 1;
long flags;
-
- /*
- * dl1bke 960301: We use the new PACLEN parameter as MTU of the AX.25 layer.
- * This will (hopefully) allow user programs to write() data
- * w/o having to think of the maximal amount of data we can
- * send with one call. It's called PACLEN to (1) avoid confusion
- * with (IP) MTU and (2) TAPR calls this PACLEN, too ;-)
- */
- mtu = ax25->paclen;
-
- if ((skb->len - 1) > mtu) {
+ if ((skb->len - 1) > paclen) {
if (*skb->data == AX25_P_TEXT) {
skb_pull(skb, 1); /* skip PID */
ka9qfrag = 0;
} else {
- mtu -= 2; /* Allow for fragment control info */
+ paclen -= 2; /* Allow for fragment control info */
ka9qfrag = 1;
}
-
- fragno = skb->len / mtu;
- if (skb->len % mtu == 0) fragno--;
+
+ fragno = skb->len / paclen;
+ if (skb->len % paclen == 0) fragno--;
frontlen = skb_headroom(skb); /* Address space + CTRL */
while (skb->len > 0) {
save_flags(flags);
cli();
- /*
- * do _not_ use sock_alloc_send_skb, our socket may have
- * sk->shutdown set...
- */
- if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) {
+
+ if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
restore_flags(flags);
printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n");
- if (skb_device_locked(skb))
- skb_device_unlock(skb);
return;
}
- skbn->sk = skb->sk;
-
- if (skbn->sk)
- atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
restore_flags(flags);
- skbn->free = 1;
- skbn->arp = 1;
+ len = (paclen > skb->len) ? skb->len : paclen;
- len = (mtu > skb->len) ? skb->len : mtu;
-
if (ka9qfrag == 1) {
skb_reserve(skbn, frontlen + 2);
@@ -126,7 +178,7 @@ void ax25_output(ax25_cb *ax25, struct sk_buff *skb)
*p = fragno--;
if (first) {
- *p |= SEG_FIRST;
+ *p |= AX25_SEG_FIRST;
first = 0;
}
} else {
@@ -139,16 +191,16 @@ void ax25_output(ax25_cb *ax25, struct sk_buff *skb)
skb_pull(skb, len);
skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */
}
-
- skb->free = 1;
+
kfree_skb(skb, FREE_WRITE);
} else {
skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */
}
- if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
- if (!ax25->dama_slave) /* bke 960114: we aren't allowed to transmit */
- ax25_kick(ax25); /* in DAMA mode unless we received a Poll */
+ if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX ||
+ ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) {
+ if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4)
+ ax25_kick(ax25);
}
}
@@ -163,23 +215,23 @@ static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
if (skb == NULL)
return;
- if (ax25->modulus == MODULUS) {
+ if (ax25->modulus == AX25_MODULUS) {
frame = skb_push(skb, 1);
- *frame = I;
- *frame |= (poll_bit) ? PF : 0;
+ *frame = AX25_I;
+ *frame |= (poll_bit) ? AX25_PF : 0;
*frame |= (ax25->vr << 5);
*frame |= (ax25->vs << 1);
} else {
frame = skb_push(skb, 2);
- frame[0] = I;
+ frame[0] = AX25_I;
frame[0] |= (ax25->vs << 1);
- frame[1] = (poll_bit) ? EPF : 0;
+ frame[1] = (poll_bit) ? AX25_EPF : 0;
frame[1] |= (ax25->vr << 1);
}
- ax25_transmit_buffer(ax25, skb, C_COMMAND);
+ ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
}
void ax25_kick(ax25_cb *ax25)
@@ -193,8 +245,8 @@ void ax25_kick(ax25_cb *ax25)
start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
end = (ax25->va + ax25->window) % ax25->modulus;
- if (!(ax25->condition & PEER_RX_BUSY_CONDITION) &&
- start != end &&
+ if (!(ax25->condition & AX25_COND_PEER_RX_BUSY) &&
+ start != end &&
skb_peek(&ax25->write_queue) != NULL) {
ax25->vs = start;
@@ -216,18 +268,29 @@ void ax25_kick(ax25_cb *ax25)
break;
}
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
+
next = (ax25->vs + 1) % ax25->modulus;
-#ifdef notdef
- last = (next == end) || skb_peek(&ax25->write_queue) == NULL;
-#else
last = (next == end);
-#endif
+
/*
* Transmit the frame copy.
* bke 960114: do not set the Poll bit on the last frame
* in DAMA mode.
*/
- ax25_send_iframe(ax25, skbn, (last && !ax25->dama_slave) ? POLLON : POLLOFF);
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
+ break;
+#endif
+ }
ax25->vs = next;
@@ -235,12 +298,10 @@ void ax25_kick(ax25_cb *ax25)
* Requeue the original data frame.
*/
skb_queue_tail(&ax25->ack_queue, skb);
-#ifdef notdef
- } while (!last);
-#else
+
} while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
-#endif
- ax25->condition &= ~ACK_PENDING_CONDITION;
+
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
if (ax25->t1timer == 0) {
ax25->t3timer = 0;
@@ -255,7 +316,7 @@ void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
{
unsigned char *ptr;
- if (ax25->device == NULL) {
+ if (ax25->ax25_dev == NULL) {
if (ax25->sk != NULL) {
ax25->sk->state = TCP_CLOSE;
ax25->sk->err = ENETUNREACH;
@@ -267,77 +328,42 @@ void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
return;
}
- if (skb_headroom(skb) < size_ax25_addr(ax25->digipeat)) {
+ if (skb_headroom(skb) < ax25_addr_size(ax25->digipeat)) {
printk(KERN_CRIT "ax25_transmit_buffer: not enough room for digi-peaters\n");
- skb->free = 1;
kfree_skb(skb, FREE_WRITE);
return;
}
- ptr = skb_push(skb, size_ax25_addr(ax25->digipeat));
- build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
+ ptr = skb_push(skb, ax25_addr_size(ax25->digipeat));
+ ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
- skb->arp = 1;
+ skb->dev = ax25->ax25_dev->dev;
+ skb->priority = SOPRI_NORMAL;
- ax25_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
+ ax25_queue_xmit(skb);
}
/*
- * The following routines are taken from page 170 of the 7th ARRL Computer
- * Networking Conference paper, as is the whole state machine.
+ * A small shim to dev_queue_xmit to add the KISS control byte, and do
+ * any packet forwarding in operation.
*/
-
-void ax25_nr_error_recovery(ax25_cb *ax25)
-{
- ax25_establish_data_link(ax25);
-}
-
-void ax25_establish_data_link(ax25_cb *ax25)
+void ax25_queue_xmit(struct sk_buff *skb)
{
- ax25->condition = 0x00;
- ax25->n2count = 0;
+ unsigned char *ptr;
- if (ax25->modulus == MODULUS) {
- ax25_send_control(ax25, SABM, POLLON, C_COMMAND);
- } else {
- ax25_send_control(ax25, SABME, POLLON, C_COMMAND);
+ if (call_out_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return;
}
-
- ax25->t3timer = 0;
- ax25->t2timer = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-}
-
-void ax25_transmit_enquiry(ax25_cb *ax25)
-{
- if (ax25->condition & OWN_RX_BUSY_CONDITION)
- ax25_send_control(ax25, RNR, POLLON, C_COMMAND);
- else
- ax25_send_control(ax25, RR, POLLON, C_COMMAND);
-
- ax25->condition &= ~ACK_PENDING_CONDITION;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-}
-
-void ax25_enquiry_response(ax25_cb *ax25)
-{
- if (ax25->condition & OWN_RX_BUSY_CONDITION)
- ax25_send_control(ax25, RNR, POLLON, C_RESPONSE);
- else
- ax25_send_control(ax25, RR, POLLON, C_RESPONSE);
-
- ax25->condition &= ~ACK_PENDING_CONDITION;
-}
+ skb->protocol = htons(ETH_P_AX25);
+ skb->dev = ax25_fwd_dev(skb->dev);
+ skb->arp = 1;
-void ax25_timeout_response(ax25_cb *ax25)
-{
- if (ax25->condition & OWN_RX_BUSY_CONDITION)
- ax25_send_control(ax25, RNR, POLLOFF, C_RESPONSE);
- else
- ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE);
+ ptr = skb_push(skb, 1);
+ *ptr = 0x00; /* KISS */
- ax25->condition &= ~ACK_PENDING_CONDITION;
+ dev_queue_xmit(skb);
}
void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
@@ -355,100 +381,4 @@ void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
}
}
-/*
- * dl1bke 960114: shouldn't ax25/dama_check_need_response reside as
- * static inline void ...() in ax25.h, should it? ;-)
- */
-void ax25_check_need_response(ax25_cb *ax25, int type, int pf)
-{
- if (!ax25->dama_slave && type == C_COMMAND && pf)
- ax25_enquiry_response(ax25);
-}
-
-/*
- * dl1bke 960114: transmit I frames on DAMA poll
- */
-void dama_enquiry_response(ax25_cb *ax25)
-{
- ax25_cb *ax25o;
-
- if (!(ax25->condition & PEER_RX_BUSY_CONDITION)) {
- ax25_requeue_frames(ax25);
- ax25_kick(ax25);
- }
-
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 ||
- skb_peek(&ax25->ack_queue) != NULL) {
- ax25_t1_timeout(ax25);
- } else {
- ax25->n2count = 0;
- }
-
- ax25->t3timer = ax25->t3;
-
-
- /* The FLEXNET DAMA master implementation refuses to send us ANY */
- /* I frame for this connection if we send a REJ here, probably */
- /* due to its frame collector scheme? A simple RR or RNR will */
- /* invoke the retransmission, and in fact REJs are superfluous */
- /* in DAMA mode anyway... */
-
-#if 0
- if (ax25->condition & REJECT_CONDITION)
- ax25_send_control(ax25, REJ, POLLOFF, C_RESPONSE);
- else
-#endif
- ax25_enquiry_response(ax25);
-
- /* Note that above response to the poll could be sent behind the */
- /* transmissions of the other channels as well... This version */
- /* gives better performance on FLEXNET nodes. (Why, Gunter?) */
-
- for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
- if (ax25o == ax25)
- continue;
-
- if (ax25o->device != ax25->device)
- continue;
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
- ax25_t1_timeout(ax25o);
- continue;
- }
-
- if (!ax25o->dama_slave)
- continue;
-
- if ( !(ax25o->condition & PEER_RX_BUSY_CONDITION) &&
- (ax25o->state == AX25_STATE_3 ||
- (ax25o->state == AX25_STATE_4 && ax25o->t1timer == 0))) {
- ax25_requeue_frames(ax25o);
- ax25_kick(ax25o);
- }
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 ||
- skb_peek(&ax25o->ack_queue) != NULL) {
- ax25_t1_timeout(ax25o);
- }
-
- ax25o->t3timer = ax25o->t3;
- }
-}
-
-void dama_check_need_response(ax25_cb *ax25, int type, int pf)
-{
- if (ax25->dama_slave && type == C_COMMAND && pf)
- dama_enquiry_response(ax25);
-}
-
-void dama_establish_data_link(ax25_cb *ax25)
-{
- ax25->condition = 0x00;
- ax25->n2count = 0;
-
- ax25->t3timer = ax25->t3;
- ax25->t2timer = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-}
-
#endif
diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c
index b4606111e..04a7c8542 100644
--- a/net/ax25/ax25_route.c
+++ b/net/ax25/ax25_route.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 033
+ * AX.25 release 036
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -39,8 +39,10 @@
* on routes.
* AX.25 033 Jonathan(G4KLX) Remove auto-router.
* Joerg(DL1BKE) Moved BPQ Ethernet driver to seperate device.
+ * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
+ * Jonathan(G4KLX) Support for packet forwarding.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#include <linux/errno.h>
@@ -65,22 +67,9 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
-static struct ax25_route {
- struct ax25_route *next;
- ax25_address callsign;
- struct device *dev;
- ax25_digi *digipeat;
- char ip_mode;
-} *ax25_route = NULL;
-
-struct ax25_dev ax25_device[AX25_MAX_DEVICES] = {
- {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
- {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
- {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL},
- {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}, {"", NULL}
-};
+static ax25_route *ax25_route_list = NULL;
-static struct ax25_route *ax25_find_route(ax25_address *, struct device *);
+static ax25_route *ax25_find_route(ax25_address *, struct device *);
/*
* small macro to drop non-digipeated digipeaters and reverse path
@@ -100,28 +89,28 @@ static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
void ax25_rt_device_down(struct device *dev)
{
- struct ax25_route *s, *t, *ax25_rt = ax25_route;
+ ax25_route *s, *t, *ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
if (s->dev == dev) {
- if (ax25_route == s) {
- ax25_route = s->next;
+ if (ax25_route_list == s) {
+ ax25_route_list = s->next;
if (s->digipeat != NULL)
- kfree_s((void *)s->digipeat, sizeof(ax25_digi));
- kfree_s((void *)s, (sizeof *s));
+ kfree_s(s->digipeat, sizeof(ax25_digi));
+ kfree_s(s, sizeof(ax25_route));
} else {
- for (t = ax25_route; t != NULL; t = t->next) {
+ for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) {
t->next = s->next;
if (s->digipeat != NULL)
- kfree_s((void *)s->digipeat, sizeof(ax25_digi));
- kfree_s((void *)s, sizeof(*s));
+ kfree_s(s->digipeat, sizeof(ax25_digi));
+ kfree_s(s, sizeof(ax25_route));
break;
}
- }
+ }
}
}
}
@@ -130,10 +119,10 @@ void ax25_rt_device_down(struct device *dev)
int ax25_rt_ioctl(unsigned int cmd, void *arg)
{
unsigned long flags;
- struct ax25_route *s, *t, *ax25_rt;
+ ax25_route *s, *t, *ax25_rt;
struct ax25_routes_struct route;
struct ax25_route_opt_struct rt_option;
- struct device *dev;
+ ax25_dev *ax25_dev;
int i, err;
switch (cmd) {
@@ -141,12 +130,12 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
return err;
copy_from_user(&route, arg, sizeof(route));
- if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+ if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
return -EINVAL;
if (route.digi_count > AX25_MAX_DIGIS)
return -EINVAL;
- for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == dev) {
+ for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
if (ax25_rt->digipeat != NULL) {
kfree_s(ax25_rt->digipeat, sizeof(ax25_digi));
ax25_rt->digipeat = NULL;
@@ -154,7 +143,7 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
if (route.digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- ax25_rt->digipeat->lastrepeat = 0;
+ ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route.digi_count;
for (i = 0; i < route.digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0;
@@ -164,28 +153,27 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
return 0;
}
}
- if ((ax25_rt = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_ATOMIC)) == NULL)
+ if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
return -ENOMEM;
ax25_rt->callsign = route.dest_addr;
- ax25_rt->dev = dev;
+ ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL;
ax25_rt->ip_mode = ' ';
if (route.digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree_s(ax25_rt, sizeof(struct ax25_route));
+ kfree_s(ax25_rt, sizeof(ax25_route));
return -ENOMEM;
}
- ax25_rt->digipeat->lastrepeat = 0;
+ ax25_rt->digipeat->lastrepeat = -1;
ax25_rt->digipeat->ndigi = route.digi_count;
for (i = 0; i < route.digi_count; i++) {
ax25_rt->digipeat->repeated[i] = 0;
ax25_rt->digipeat->calls[i] = route.digi_addr[i];
}
}
- save_flags(flags);
- cli();
- ax25_rt->next = ax25_route;
- ax25_route = ax25_rt;
+ save_flags(flags); cli();
+ ax25_rt->next = ax25_route_list;
+ ax25_route_list = ax25_rt;
restore_flags(flags);
break;
@@ -193,25 +181,25 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
return err;
copy_from_user(&route, arg, sizeof(route));
- if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+ if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
return -EINVAL;
- ax25_rt = ax25_route;
+ ax25_rt = ax25_route_list;
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
- if (s->dev == dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
- if (ax25_route == s) {
- ax25_route = s->next;
+ if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
+ if (ax25_route_list == s) {
+ ax25_route_list = s->next;
if (s->digipeat != NULL)
- kfree_s((void *)s->digipeat, sizeof(ax25_digi));
- kfree_s((void *)s, (sizeof *s));
+ kfree_s(s->digipeat, sizeof(ax25_digi));
+ kfree_s(s, sizeof(ax25_route));
} else {
- for (t = ax25_route; t != NULL; t = t->next) {
+ for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) {
t->next = s->next;
if (s->digipeat != NULL)
- kfree_s((void *)s->digipeat, sizeof(ax25_digi));
- kfree_s((void *)s, sizeof(*s));
+ kfree_s(s->digipeat, sizeof(ax25_digi));
+ kfree_s(s, sizeof(ax25_route));
break;
}
}
@@ -224,10 +212,10 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0)
return err;
copy_from_user(&rt_option, arg, sizeof(rt_option));
- if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
+ if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
return -EINVAL;
- for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25_rt->dev == dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
+ for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
switch (rt_option.cmd) {
case AX25_SET_RT_IPMODE:
switch (rt_option.arg) {
@@ -256,7 +244,7 @@ int ax25_rt_ioctl(unsigned int cmd, void *arg)
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
- struct ax25_route *ax25_rt;
+ ax25_route *ax25_rt;
int len = 0;
off_t pos = 0;
off_t begin = 0;
@@ -267,7 +255,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int d
len += sprintf(buffer, "callsign dev mode digipeaters\n");
- for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
callsign = "default";
else
@@ -287,20 +275,20 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int d
len += sprintf(buffer + len, " *");
break;
}
-
+
if (ax25_rt->digipeat != NULL)
for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
-
+
len += sprintf(buffer + len, "\n");
-
+
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -315,55 +303,20 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length, int d
return len;
}
-int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
- ax25_uid_assoc *pt;
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
-
- cli();
-
- len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
-
- for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
- len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
-
- pos = begin + len;
-
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
-
- if (pos > offset + length)
- break;
- }
-
- sti();
-
- *start = buffer + (offset - begin);
- len -= offset - begin;
-
- if (len > length) len = length;
-
- return len;
-}
-
/*
* Find AX.25 route
*/
-static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev)
+static ax25_route *ax25_find_route(ax25_address *addr, struct device *dev)
{
- struct ax25_route *ax25_spe_rt = NULL;
- struct ax25_route *ax25_def_rt = NULL;
- struct ax25_route *ax25_rt;
-
+ ax25_route *ax25_spe_rt = NULL;
+ ax25_route *ax25_def_rt = NULL;
+ ax25_route *ax25_rt;
+
/*
* Bind to the physical interface we heard them on, or the default
* route if none is found;
*/
- for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
if (dev == NULL) {
if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
ax25_spe_rt = ax25_rt;
@@ -379,7 +332,7 @@ static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev
if (ax25_spe_rt != NULL)
return ax25_spe_rt;
-
+
return ax25_def_rt;
}
@@ -391,12 +344,12 @@ static struct ax25_route *ax25_find_route(ax25_address *addr, struct device *dev
static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
{
int k;
-
+
for (k = 0; k < digipeat->ndigi; k++) {
if (ax25cmp(addr, &digipeat->calls[k]) == 0)
break;
}
-
+
digipeat->ndigi = k;
}
@@ -406,18 +359,19 @@ static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
*/
int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
{
- struct ax25_route *ax25_rt;
+ ax25_route *ax25_rt;
ax25_address *call;
if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
return -EHOSTUNREACH;
-
- ax25->device = ax25_rt->dev;
+
+ if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
+ return -EHOSTUNREACH;
if ((call = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !suser())
return -EPERM;
- call = (ax25_address *)ax25->device->dev_addr;
+ call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
}
ax25->source_addr = *call;
@@ -441,25 +395,27 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
*/
void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev)
{
- struct ax25_route *ax25_rt;
-
+ ax25_route *ax25_rt;
+
if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
return;
-
+
if (ax25_rt->digipeat == NULL)
return;
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
return;
- ax25->device = ax25_rt->dev;
+ if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
+ return;
+
*ax25->digipeat = *ax25_rt->digipeat;
ax25_adjust_path(addr, ax25->digipeat);
}
void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
{
- struct ax25_route *ax25_rt;
+ ax25_route *ax25_rt;
ax25_digi digipeat;
ax25_address src, dest;
unsigned char *bp;
@@ -472,24 +428,24 @@ void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *
if (ax25_rt->digipeat == NULL)
return;
-
+
digipeat = *ax25_rt->digipeat;
-
+
ax25_adjust_path(addr, &digipeat);
len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
-
+
if (skb_headroom(skb) < len) {
printk(KERN_CRIT "ax25_dg_build_path: not enough headroom for digis in skb\n");
return;
}
-
- memcpy(&dest, skb->data , AX25_ADDR_LEN);
+
+ memcpy(&dest, skb->data + 0, AX25_ADDR_LEN);
memcpy(&src, skb->data + 7, AX25_ADDR_LEN);
bp = skb_push(skb, len);
- build_ax25_addr(bp, &src, &dest, ax25_rt->digipeat, C_COMMAND, MODULUS);
+ ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS);
}
/*
@@ -497,99 +453,24 @@ void ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *
*/
char ax25_ip_mode_get(ax25_address *callsign, struct device *dev)
{
- struct ax25_route *ax25_rt;
+ ax25_route *ax25_rt;
- for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
+ for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next)
if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev)
return ax25_rt->ip_mode;
return ' ';
}
-/*
- * Wow, a bit of data hiding. Is this C++ or what ?
- */
-int ax25_dev_get_value(struct device *dev, int valueno)
-{
- int i;
-
- for (i = 0; i < AX25_MAX_DEVICES; i++)
- if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
- return ax25_device[i].values[valueno];
-
- printk(KERN_WARNING "ax25_dev_get_value called with invalid device\n");
-
- return 0;
-}
-
-/*
- * This is called when an interface is brought up. These are
- * reasonable defaults.
- */
-void ax25_dev_device_up(struct device *dev)
-{
- struct ax25_dev *ax25_dev = NULL;
- int i;
-
- for (i = 0; i < AX25_MAX_DEVICES; i++) {
- if (ax25_device[i].dev == NULL) {
- ax25_dev = ax25_device + i;
- break;
- }
- }
-
- if (ax25_dev == NULL) {
- printk(KERN_ERR "ax25_dev_device_up cannot find free AX.25 device\n");
- return;
- }
-
- ax25_unregister_sysctl();
-
- sprintf(ax25_dev->name, "%s.parms", dev->name);
-
- ax25_dev->dev = dev;
-
- ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
- ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
- ax25_dev->values[AX25_VALUES_TEXT] = AX25_DEF_TEXT;
- ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
- ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
- ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
- ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
- ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1;
- ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2;
- ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3;
- ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE;
- ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
- ax25_dev->values[AX25_VALUES_DIGI] = AX25_DEF_DIGI;
- ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN;
- ax25_dev->values[AX25_VALUES_MAXQUEUE] = AX25_DEF_MAXQUEUE;
-
- ax25_register_sysctl();
-}
-
-void ax25_dev_device_down(struct device *dev)
-{
- int i;
-
- ax25_unregister_sysctl();
-
- for (i = 0; i < AX25_MAX_DEVICES; i++)
- if (ax25_device[i].dev != NULL && ax25_device[i].dev == dev)
- ax25_device[i].dev = NULL;
-
- ax25_register_sysctl();
-}
-
#ifdef MODULE
/*
- * Free all memory associated with routing and device structures.
+ * Free all memory associated with routing structures.
*/
void ax25_rt_free(void)
{
- struct ax25_route *s, *ax25_rt = ax25_route;
-
+ ax25_route *s, *ax25_rt = ax25_route_list;
+
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
@@ -597,11 +478,10 @@ void ax25_rt_free(void)
if (s->digipeat != NULL)
kfree_s(s->digipeat, sizeof(ax25_digi));
- kfree_s(s, sizeof(struct ax25_route));
+ kfree_s(s, sizeof(ax25_route));
}
}
#endif
#endif
-
diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c
new file mode 100644
index 000000000..c4b8914d4
--- /dev/null
+++ b/net/ax25/ax25_std_in.c
@@ -0,0 +1,542 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Most of this code is based on the SDL diagrams published in the 7th
+ * ARRL Computer Networking Conference papers. The diagrams have mistakes
+ * in them, but are mostly correct. Before you modify the code could you
+ * read the SDL diagrams as the code is not obvious and probably very
+ * easy to break;
+ *
+ * History
+ * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
+ * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
+ * the sock structure.
+ * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
+ * Jonathan(G4KLX) Added IP mode registration.
+ * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
+ * Upgraded state machine for SABME.
+ * Added arbitrary protocol id support.
+ * AX.25 031 Joerg(DL1BKE) Added DAMA support
+ * HaJo(DD8NE) Added Idle Disc Timer T5
+ * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
+ * different behaviour. Fixed defrag
+ * routine (I hope)
+ * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
+ * AX.25 033 Jonathan(G4KLX) Remove auto-router.
+ * Modularisation changes.
+ * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
+ * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h> /* For ip_rcv */
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ * State machine for state 1, Awaiting Connection State.
+ * The handling of the timer(s) is in file ax25_std_timer.c.
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+ switch (frametype) {
+ case AX25_SABM:
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_SABME:
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_DISC:
+ ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_UA:
+ if (pf) {
+ ax25_calculate_rtt(ax25);
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->n2count = 0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_ESTABLISHED;
+ /* For WAIT_SABM connections we will produce an accept ready socket here */
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ }
+ }
+ break;
+
+ case AX25_DM:
+ if (pf) {
+ if (ax25->modulus == AX25_MODULUS) {
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNREFUSED;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Release State.
+ * The handling of the timer(s) is in file ax25_std_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
+{
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
+ break;
+
+ case AX25_DISC:
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ case AX25_UA:
+ if (pf) {
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ }
+ break;
+
+ case AX25_I:
+ case AX25_REJ:
+ case AX25_RNR:
+ case AX25_RR:
+ if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file ax25_std_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+ int queued = 0;
+
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (frametype == AX25_SABM) {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ } else {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ }
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->condition = 0x00;
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25_requeue_frames(ax25);
+ break;
+
+ case AX25_DISC:
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ ax25_clear_queues(ax25);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNRESET;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (frametype == AX25_RR)
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ else
+ ax25->condition |= AX25_COND_PEER_RX_BUSY;
+ if (type == AX25_COMMAND && pf)
+ ax25_std_enquiry_response(ax25);
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_check_iframes_acked(ax25, nr);
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_REJ:
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ if (type == AX25_COMMAND && pf)
+ ax25_std_enquiry_response(ax25);
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25_calculate_rtt(ax25);
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25_requeue_frames(ax25);
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, nr)) {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+ }
+ if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
+ ax25_frames_acked(ax25, nr);
+ } else {
+ ax25_check_iframes_acked(ax25, nr);
+ }
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (pf) ax25_std_enquiry_response(ax25);
+ break;
+ }
+ if (ns == ax25->vr) {
+ ax25->vr = (ax25->vr + 1) % ax25->modulus;
+ queued = ax25_rx_iframe(ax25, skb);
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ ax25->vr = ns; /* ax25->vr - 1 */
+ if (pf) ax25_std_enquiry_response(ax25);
+ break;
+ }
+ ax25->condition &= ~AX25_COND_REJECT;
+ if (pf) {
+ ax25_std_enquiry_response(ax25);
+ } else {
+ if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+ ax25->t2timer = ax25->t2;
+ ax25->condition |= AX25_COND_ACK_PENDING;
+ }
+ }
+ } else {
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pf) ax25_std_enquiry_response(ax25);
+ } else {
+ ax25->condition |= AX25_COND_REJECT;
+ ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ }
+ }
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_std_establish_data_link(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Timer Recovery State.
+ * The handling of the timer(s) is in file ax25_std_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
+{
+ int queued = 0;
+
+ switch (frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (frametype == AX25_SABM) {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ } else {
+ ax25->modulus = AX25_EMODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
+ }
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->condition = 0x00;
+ ax25->t1timer = 0;
+ ax25->t3timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->n2count = 0;
+ ax25_requeue_frames(ax25);
+ break;
+
+ case AX25_DISC:
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_DM:
+ ax25_clear_queues(ax25);
+ ax25->t3timer = 0;
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ECONNRESET;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (frametype == AX25_RR)
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ else
+ ax25->condition |= AX25_COND_PEER_RX_BUSY;
+ if (type == AX25_RESPONSE && pf) {
+ ax25->t1timer = 0;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ if (ax25->vs == ax25->va) {
+ ax25->t3timer = ax25->t3;
+ ax25->n2count = 0;
+ ax25->state = AX25_STATE_3;
+ } else {
+ ax25_requeue_frames(ax25);
+ }
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+ }
+ if (type == AX25_COMMAND && pf)
+ ax25_std_enquiry_response(ax25);
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_REJ:
+ ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
+ if (pf && type == AX25_RESPONSE) {
+ ax25->t1timer = 0;
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ if (ax25->vs == ax25->va) {
+ ax25->t3timer = ax25->t3;
+ ax25->n2count = 0;
+ ax25->state = AX25_STATE_3;
+ } else {
+ ax25_requeue_frames(ax25);
+ }
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+ }
+ if (type == AX25_COMMAND && pf)
+ ax25_std_enquiry_response(ax25);
+ if (ax25_validate_nr(ax25, nr)) {
+ ax25_frames_acked(ax25, nr);
+ ax25_requeue_frames(ax25);
+ } else {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ }
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, nr)) {
+ ax25_std_nr_error_recovery(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+ }
+ ax25_frames_acked(ax25, nr);
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (pf) ax25_std_enquiry_response(ax25);
+ break;
+ }
+ if (ns == ax25->vr) {
+ ax25->vr = (ax25->vr + 1) % ax25->modulus;
+ queued = ax25_rx_iframe(ax25, skb);
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ ax25->vr = ns; /* ax25->vr - 1 */
+ if (pf) ax25_std_enquiry_response(ax25);
+ break;
+ }
+ ax25->condition &= ~AX25_COND_REJECT;
+ if (pf) {
+ ax25_std_enquiry_response(ax25);
+ } else {
+ if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
+ ax25->t2timer = ax25->t2;
+ ax25->condition |= AX25_COND_ACK_PENDING;
+ }
+ }
+ } else {
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pf) ax25_std_enquiry_response(ax25);
+ } else {
+ ax25->condition |= AX25_COND_REJECT;
+ ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ }
+ }
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_std_establish_data_link(ax25);
+ ax25->state = AX25_STATE_1;
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * Higher level upcall for a LAPB frame
+ */
+int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+ int queued = 0, frametype, ns, nr, pf;
+
+ frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
+
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type);
+ break;
+ case AX25_STATE_2:
+ queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type);
+ break;
+ case AX25_STATE_3:
+ queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
+ break;
+ case AX25_STATE_4:
+ queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
+ break;
+ }
+
+ return queued;
+}
+
+#endif
diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c
new file mode 100644
index 000000000..46688e3c0
--- /dev/null
+++ b/net/ax25/ax25_std_subr.c
@@ -0,0 +1,105 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * Most of this code is based on the SDL diagrams published in the 7th
+ * ARRL Computer Networking Conference papers. The diagrams have mistakes
+ * in them, but are mostly correct. Before you modify the code could you
+ * read the SDL diagrams as the code is not obvious and probably very
+ * easy to break;
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void ax25_std_nr_error_recovery(ax25_cb *ax25)
+{
+ ax25_std_establish_data_link(ax25);
+}
+
+void ax25_std_establish_data_link(ax25_cb *ax25)
+{
+ ax25->condition = 0x00;
+ ax25->n2count = 0;
+
+ if (ax25->modulus == AX25_MODULUS)
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+ else
+ ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
+
+ ax25->t3timer = 0;
+ ax25->t2timer = 0;
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+
+void ax25_std_transmit_enquiry(ax25_cb *ax25)
+{
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+ ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
+ else
+ ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
+
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+}
+
+void ax25_std_enquiry_response(ax25_cb *ax25)
+{
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+ ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
+ else
+ ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
+
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+}
+
+void ax25_std_timeout_response(ax25_cb *ax25)
+{
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
+ ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
+ else
+ ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
+
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+}
+
+#endif
diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c
new file mode 100644
index 000000000..5f73caf7b
--- /dev/null
+++ b/net/ax25/ax25_std_timer.c
@@ -0,0 +1,218 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
+ * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
+ * sock structure.
+ * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
+ * AX.25 031 Joerg(DL1BKE) Added DAMA support
+ * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
+ * AX.25 033 Jonathan(G4KLX) Modularisation functions.
+ * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
+ * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+
+void ax25_std_timer(ax25_cb *ax25)
+{
+ switch (ax25->state) {
+ case AX25_STATE_0:
+ /* Magic here: If we listen() and a new link dies before it
+ is accepted() it isn't 'dead' so doesn't get removed. */
+ if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
+ del_timer(&ax25->timer);
+ ax25_destroy_socket(ax25);
+ return;
+ }
+ break;
+
+ case AX25_STATE_3:
+ case AX25_STATE_4:
+ /*
+ * Check the state of the receive buffer.
+ */
+ if (ax25->sk != NULL) {
+ if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
+ (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
+ ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
+ break;
+ }
+ }
+ /*
+ * Check for frames to transmit.
+ */
+ ax25_kick(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
+ if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
+ if (ax25->condition & AX25_COND_ACK_PENDING) {
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ ax25_std_timeout_response(ax25);
+ }
+ }
+ }
+
+ if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
+ if (ax25->state == AX25_STATE_3) {
+ ax25->n2count = 0;
+ ax25_std_transmit_enquiry(ax25);
+ ax25->state = AX25_STATE_4;
+ }
+ ax25->t3timer = ax25->t3;
+ }
+
+ if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
+ /* dl1bke 960228: close the connection when IDLE expires */
+ /* similar to DAMA T3 timeout but with */
+ /* a "clean" disconnect of the connection */
+
+ ax25_clear_queues(ax25);
+
+ ax25->n2count = 0;
+ ax25->t3timer = 0;
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25->state = AX25_STATE_2;
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ ax25->sk->destroy = 1;
+ }
+ }
+
+ if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
+ ax25_set_timer(ax25);
+ return;
+ }
+
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->modulus == AX25_MODULUS) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->modulus = AX25_MODULUS;
+ ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
+ ax25->n2count = 0;
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+ }
+ } else {
+ ax25->n2count++;
+ if (ax25->modulus == AX25_MODULUS)
+ ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+ else
+ ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
+ }
+ break;
+
+ case AX25_STATE_2:
+ if (ax25->n2count == ax25->n2) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25->state = AX25_STATE_0;
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->n2count++;
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ }
+ break;
+
+ case AX25_STATE_3:
+ ax25->n2count = 1;
+ ax25_std_transmit_enquiry(ax25);
+ ax25->state = AX25_STATE_4;
+ break;
+
+ case AX25_STATE_4:
+ if (ax25->n2count == ax25->n2) {
+ ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
+ ax25_clear_queues(ax25);
+ ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
+ ax25->state = AX25_STATE_0;
+ if (ax25->sk != NULL) {
+ SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = ETIMEDOUT;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+ } else {
+ ax25->n2count++;
+ ax25_std_transmit_enquiry(ax25);
+ }
+ break;
+ }
+
+ ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+
+ ax25_set_timer(ax25);
+}
+
+#endif
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 071043d1e..59e464c1c 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 033
+ * AX.25 release 036
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.3.61 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -32,6 +32,7 @@
* Joerg(DL1BKE) Found the real bug in ax25.h, sri.
* AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of
* enqueued buffers of a socket..
+ * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
*/
#include <linux/config.h>
@@ -64,23 +65,17 @@ void ax25_clear_queues(ax25_cb *ax25)
{
struct sk_buff *skb;
- while ((skb = skb_dequeue(&ax25->write_queue)) != NULL) {
- skb->free = 1;
+ while ((skb = skb_dequeue(&ax25->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
- while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
- skb->free = 1;
+ while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
- while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL) {
+ while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
- while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL) {
+ while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
}
/*
@@ -98,19 +93,12 @@ void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
if (ax25->va != nr) {
while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
skb = skb_dequeue(&ax25->ack_queue);
- skb->free = 1;
kfree_skb(skb, FREE_WRITE);
ax25->va = (ax25->va + 1) % ax25->modulus;
- if (ax25->dama_slave)
- ax25->n2count = 0;
}
}
}
-/* Maybe this should be your ax25_invoke_retransmission(), which appears
- * to be used but not do anything. ax25_invoke_retransmission() used to
- * be in AX 0.29, but has now gone in 0.30.
- */
void ax25_requeue_frames(ax25_cb *ax25)
{
struct sk_buff *skb, *skb_prev = NULL;
@@ -141,7 +129,7 @@ int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
if (nr == vc) return 1;
vc = (vc + 1) % ax25->modulus;
}
-
+
if (nr == ax25->vs) return 1;
return 0;
@@ -154,41 +142,41 @@ int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
{
unsigned char *frame;
- int frametype = ILLEGAL;
+ int frametype = AX25_ILLEGAL;
frame = skb->data;
*ns = *nr = *pf = 0;
- if (ax25->modulus == MODULUS) {
- if ((frame[0] & S) == 0) {
- frametype = I; /* I frame - carries NR/NS/PF */
+ if (ax25->modulus == AX25_MODULUS) {
+ if ((frame[0] & AX25_S) == 0) {
+ frametype = AX25_I; /* I frame - carries NR/NS/PF */
*ns = (frame[0] >> 1) & 0x07;
*nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & PF;
- } else if ((frame[0] & U) == 1) { /* S frame - take out PF/NR */
+ *pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
frametype = frame[0] & 0x0F;
*nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & PF;
- } else if ((frame[0] & U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~PF;
- *pf = frame[0] & PF;
+ *pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ frametype = frame[0] & ~AX25_PF;
+ *pf = frame[0] & AX25_PF;
}
skb_pull(skb, 1);
} else {
- if ((frame[0] & S) == 0) {
- frametype = I; /* I frame - carries NR/NS/PF */
+ if ((frame[0] & AX25_S) == 0) {
+ frametype = AX25_I; /* I frame - carries NR/NS/PF */
*ns = (frame[0] >> 1) & 0x7F;
*nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & EPF;
+ *pf = frame[1] & AX25_EPF;
skb_pull(skb, 2);
- } else if ((frame[0] & U) == 1) { /* S frame - take out PF/NR */
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
frametype = frame[0] & 0x0F;
*nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & EPF;
+ *pf = frame[1] & AX25_EPF;
skb_pull(skb, 2);
- } else if ((frame[0] & U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~PF;
- *pf = frame[0] & PF;
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ frametype = frame[0] & ~AX25_PF;
+ *pf = frame[0] & AX25_PF;
skb_pull(skb, 1);
}
}
@@ -205,43 +193,32 @@ void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
{
struct sk_buff *skb;
unsigned char *dptr;
- struct device *dev;
-
- if ((dev = ax25->device) == NULL)
- return; /* Route died */
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
return;
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
-
- if (ax25->sk != NULL) {
- skb->sk = ax25->sk;
- atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
- }
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat));
/* Assume a response - address structure for DTE */
- if (ax25->modulus == MODULUS) {
+ if (ax25->modulus == AX25_MODULUS) {
dptr = skb_put(skb, 1);
*dptr = frametype;
- *dptr |= (poll_bit) ? PF : 0;
- if ((frametype & U) == S) /* S frames carry NR */
+ *dptr |= (poll_bit) ? AX25_PF : 0;
+ if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
*dptr |= (ax25->vr << 5);
} else {
- if ((frametype & U) == U) {
+ if ((frametype & AX25_U) == AX25_U) {
dptr = skb_put(skb, 1);
*dptr = frametype;
- *dptr |= (poll_bit) ? PF : 0;
+ *dptr |= (poll_bit) ? AX25_PF : 0;
} else {
dptr = skb_put(skb, 2);
dptr[0] = frametype;
dptr[1] = (ax25->vr << 1);
- dptr[1] |= (poll_bit) ? EPF : 0;
+ dptr[1] |= (poll_bit) ? AX25_EPF : 0;
}
}
- skb->free = 1;
-
ax25_transmit_buffer(ax25, skb, type);
}
@@ -259,29 +236,27 @@ void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, a
if (dev == NULL)
return;
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + size_ax25_addr(digi) + 1, GFP_ATOMIC)) == NULL)
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL)
return; /* Next SABM will get DM'd */
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(digi));
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi));
ax25_digi_invert(digi, &retdigi);
dptr = skb_put(skb, 1);
- skb->sk = NULL;
- *dptr = DM | PF;
+ *dptr = AX25_DM | AX25_PF;
/*
* Do the address ourselves
*/
+ dptr = skb_push(skb, ax25_addr_size(digi));
+ dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
- dptr = skb_push(skb, size_ax25_addr(digi));
- dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE, MODULUS);
+ skb->dev = dev;
+ skb->priority = SOPRI_NORMAL;
- skb->arp = 1;
- skb->free = 1;
-
- ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
+ ax25_queue_xmit(skb);
}
/*
@@ -291,11 +266,19 @@ unsigned short ax25_calculate_t1(ax25_cb *ax25)
{
int n, t = 2;
- if (ax25->backoff) {
- for (n = 0; n < ax25->n2count; n++)
- t *= 2;
+ switch (ax25->backoff) {
+ case 0:
+ break;
+
+ case 1:
+ t += 2 * ax25->n2count;
+ break;
- if (t > 8) t = 8;
+ case 2:
+ for (n = 0; n < ax25->n2count; n++)
+ t *= 2;
+ if (t > 8) t = 8;
+ break;
}
return t * ax25->rtt;
@@ -309,266 +292,11 @@ void ax25_calculate_rtt(ax25_cb *ax25)
if (ax25->t1timer > 0 && ax25->n2count == 0)
ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10;
-#ifdef AX25_T1CLAMPLO
- /* Don't go below one tenth of a second */
- if (ax25->rtt < (AX25_T1CLAMPLO))
- ax25->rtt = (AX25_T1CLAMPLO);
-#else /* Failsafe - some people might have sub 1/10th RTTs :-) **/
- if (ax25->rtt == 0)
- ax25->rtt = PR_SLOWHZ;
-#endif
-#ifdef AX25_T1CLAMPHI
- /* OR above clamped seconds **/
- if (ax25->rtt > (AX25_T1CLAMPHI))
- ax25->rtt = (AX25_T1CLAMPHI);
-#endif
-}
-
-/*
- * Digipeated address processing
- */
-
-
-/*
- * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
- *
- */
-unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
-{
- int d = 0;
-
- if (len < 14) return NULL;
-
- if (flags != NULL) {
- *flags = 0;
-
- if (buf[6] & LAPB_C) {
- *flags = C_COMMAND;
- }
- if (buf[13] & LAPB_C) {
- *flags = C_RESPONSE;
- }
- }
-
- if (dama != NULL)
- *dama = ~buf[13] & DAMA_FLAG;
-
- /* Copy to, from */
- if (dest != NULL)
- memcpy(dest, buf + 0, AX25_ADDR_LEN);
- if (src != NULL)
- memcpy(src, buf + 7, AX25_ADDR_LEN);
- buf += 2 * AX25_ADDR_LEN;
- len -= 2 * AX25_ADDR_LEN;
- digi->lastrepeat = -1;
- digi->ndigi = 0;
-
- while (!(buf[-1] & LAPB_E)) {
- if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
- if (len < 7) return NULL; /* Short packet */
-
- if (digi != NULL) {
- memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
- digi->ndigi = d + 1;
- if (buf[6] & AX25_REPEATED) {
- digi->repeated[d] = 1;
- digi->lastrepeat = d;
- } else {
- digi->repeated[d] = 0;
- }
- }
-
- buf += AX25_ADDR_LEN;
- len -= AX25_ADDR_LEN;
- d++;
- }
-
- return buf;
-}
-
-/*
- * Assemble an AX.25 header from the bits
- */
-int build_ax25_addr(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
-{
- int len = 0;
- int ct = 0;
-
- memcpy(buf, dest, AX25_ADDR_LEN);
- buf[6] &= ~(LAPB_E | LAPB_C);
- buf[6] |= SSSID_SPARE;
-
- if (flag == C_COMMAND) buf[6] |= LAPB_C;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- memcpy(buf, src, AX25_ADDR_LEN);
- buf[6] &= ~(LAPB_E | LAPB_C);
- buf[6] &= ~SSSID_SPARE;
-
- if (modulus == MODULUS) {
- buf[6] |= SSSID_SPARE;
- } else {
- buf[6] |= ESSID_SPARE;
- }
-
- if (flag == C_RESPONSE) buf[6] |= LAPB_C;
-
- /*
- * Fast path the normal digiless path
- */
- if (d == NULL || d->ndigi == 0) {
- buf[6] |= LAPB_E;
- return 2 * AX25_ADDR_LEN;
- }
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- while (ct < d->ndigi) {
- memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
- if (d->repeated[ct])
- buf[6] |= AX25_REPEATED;
- else
- buf[6] &= ~AX25_REPEATED;
- buf[6] &= ~LAPB_E;
- buf[6] |= SSSID_SPARE;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
- ct++;
- }
-
- buf[-1] |= LAPB_E;
-
- return len;
-}
-
-int size_ax25_addr(ax25_digi *dp)
-{
- if (dp == NULL)
- return 2 * AX25_ADDR_LEN;
-
- return AX25_ADDR_LEN * (2 + dp->ndigi);
-}
-
-/*
- * Reverse Digipeat List. May not pass both parameters as same struct
- */
-void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
-{
- int ct = 0;
-
- /* Invert the digipeaters */
-
- while (ct < in->ndigi) {
- out->calls[ct] = in->calls[in->ndigi - ct - 1];
- out->repeated[ct] = 0;
- ct++;
- }
-
- /* Copy ndigis */
- out->ndigi = in->ndigi;
-
- /* Finish off */
- out->lastrepeat = 0;
-}
-
-/*
- * count the number of buffers on a list belonging to the same
- * socket as skb
- */
-
-static int ax25_list_length(struct sk_buff_head *list, struct sk_buff *skb)
-{
- int count = 0;
- long flags;
- struct sk_buff *skbq;
-
- save_flags(flags);
- cli();
-
- if (list == NULL) {
- restore_flags(flags);
- return 0;
- }
-
- for (skbq = list->next; skbq != (struct sk_buff *)list; skbq = skbq->next)
- if (skb->sk == skbq->sk)
- count++;
+ if (ax25->rtt < AX25_T1CLAMPLO)
+ ax25->rtt = AX25_T1CLAMPLO;
- restore_flags(flags);
- return count;
-}
-
-/*
- * count the number of buffers of one socket on the write/ack-queue
- */
-
-int ax25_queue_length(ax25_cb *ax25, struct sk_buff *skb)
-{
- return ax25_list_length(&ax25->write_queue, skb) + ax25_list_length(&ax25->ack_queue, skb);
-}
-
-/*
- * :::FIXME:::
- * This is ****NOT**** the right approach. Not all drivers do kiss. We
- * need a driver level request to switch duplex mode, that does either
- * SCC changing, PI config or KISS as required.
- *
- * Not to mention this request isn't currently reliable.
- */
-
-void ax25_kiss_cmd(ax25_cb *ax25, unsigned char cmd, unsigned char param)
-{
- struct sk_buff *skb;
- unsigned char *p;
-
- if (ax25->device == NULL)
- return;
-
- if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
- return;
-
- skb->free = 1;
- skb->arp = 1;
-
- if (ax25->sk != NULL) {
- skb->sk = ax25->sk;
- atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
- }
-
- skb->protocol = htons(ETH_P_AX25);
-
- p = skb_put(skb, 2);
-
- *p++=cmd;
- *p =param;
-
- dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
-}
-
-void ax25_dama_on(ax25_cb *ax25)
-{
- if (ax25_dev_is_dama_slave(ax25->device) == 0) {
- if (ax25->sk != NULL && ax25->sk->debug)
- printk("ax25_dama_on: DAMA on\n");
- ax25_kiss_cmd(ax25, 5, 1);
- }
-}
-
-void ax25_dama_off(ax25_cb *ax25)
-{
- if (ax25->dama_slave == 0)
- return;
-
- ax25->dama_slave = 0;
- if (ax25_dev_is_dama_slave(ax25->device) == 0) {
- if (ax25->sk != NULL && ax25->sk->debug)
- printk("ax25_dama_off: DAMA off\n");
- ax25_kiss_cmd(ax25, 5, 0);
- }
+ if (ax25->rtt > AX25_T1CLAMPHI)
+ ax25->rtt = AX25_T1CLAMPHI;
}
#endif
diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c
index f6ce6e00b..a99a02b4a 100644
--- a/net/ax25/ax25_timer.c
+++ b/net/ax25/ax25_timer.c
@@ -1,10 +1,10 @@
/*
- * AX.25 release 033
+ * AX.25 release 036
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -20,6 +20,10 @@
* AX.25 031 Joerg(DL1BKE) Added DAMA support
* AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
* AX.25 033 Jonathan(G4KLX) Modularisation functions.
+ * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
+ * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into seperate files.
+ * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with
+ * standard AX.25 mode.
*/
#include <linux/config.h>
@@ -48,37 +52,20 @@
static void ax25_timer(unsigned long);
/*
- * Linux set/reset timer routines
+ * Linux set timer
*/
void ax25_set_timer(ax25_cb *ax25)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
del_timer(&ax25->timer);
restore_flags(flags);
- ax25->timer.next = ax25->timer.prev = NULL;
ax25->timer.data = (unsigned long)ax25;
ax25->timer.function = &ax25_timer;
+ ax25->timer.expires = jiffies + (HZ / 10);
- ax25->timer.expires = jiffies + 10;
- add_timer(&ax25->timer);
-}
-
-static void ax25_reset_timer(ax25_cb *ax25)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- del_timer(&ax25->timer);
- restore_flags(flags);
-
- ax25->timer.data = (unsigned long)ax25;
- ax25->timer.function = &ax25_timer;
- ax25->timer.expires = jiffies + 10;
add_timer(&ax25->timer);
}
@@ -92,456 +79,21 @@ static void ax25_timer(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
- switch (ax25->state) {
- case AX25_STATE_0:
- /* Magic here: If we listen() and a new link dies before it
- is accepted() it isn't 'dead' so doesn't get removed. */
- if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- del_timer(&ax25->timer);
- ax25_destroy_socket(ax25);
- return;
- }
- break;
-
- case AX25_STATE_3:
- case AX25_STATE_4:
- /*
- * Check the state of the receive buffer.
- */
- if (ax25->sk != NULL) {
- if (ax25->sk->rmem_alloc < (ax25->sk->rcvbuf / 2) && (ax25->condition & OWN_RX_BUSY_CONDITION)) {
- ax25->condition &= ~OWN_RX_BUSY_CONDITION;
- if (!ax25->dama_slave)
- ax25_send_control(ax25, RR, POLLOFF, C_RESPONSE);
- ax25->condition &= ~ACK_PENDING_CONDITION;
- break;
- }
- }
- /*
- * Check for frames to transmit.
- */
- if (!ax25->dama_slave)
- ax25_kick(ax25);
- break;
-
- default:
- break;
- }
-
- if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
- if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
- if (ax25->condition & ACK_PENDING_CONDITION) {
- ax25->condition &= ~ACK_PENDING_CONDITION;
- if (!ax25->dama_slave)
- ax25_timeout_response(ax25);
- }
- }
- }
-
- if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
- /* dl1bke 960114: T3 expires and we are in DAMA mode: */
- /* send a DISC and abort the connection */
- if (ax25->dama_slave) {
- ax25_link_failed(&ax25->dest_addr, ax25->device);
- ax25_clear_queues(ax25);
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- if (ax25->sk->debug)
- printk(KERN_DEBUG "AX.25 T3 Timeout\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-
- ax25_reset_timer(ax25);
- return;
- }
-
- if (ax25->state == AX25_STATE_3) {
- ax25->n2count = 0;
- ax25_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
- }
- ax25->t3timer = ax25->t3;
- }
-
- if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
- /* dl1bke 960228: close the connection when IDLE expires */
- /* similar to DAMA T3 timeout but with */
- /* a "clean" disconnect of the connection */
-
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- if (!ax25->dama_slave) {
- ax25->t3timer = 0;
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
- } else {
- ax25->t3timer = ax25->t3;
- }
-
- /* state 1 or 2 should not happen, but... */
-
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
- ax25->state = AX25_STATE_0;
- else
- ax25->state = AX25_STATE_2;
-
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- ax25->sk->destroy = 1;
- }
- }
-
- /* dl1bke 960114: DAMA T1 timeouts are handled in ax25_dama_slave_transmit */
- /* nevertheless we have to re-enqueue the timer struct... */
-
- if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
- ax25_reset_timer(ax25);
- return;
- }
-
- if (!ax25_dev_is_dama_slave(ax25->device)) {
- if (ax25->dama_slave)
- ax25->dama_slave = 0;
- ax25_t1_timeout(ax25);
- }
-}
-
-
-/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
- * within the poll of any connected channel. Remember
- * that we are not allowed to send anything unless we
- * get polled by the Master.
- *
- * Thus we'll have to do parts of our T1 handling in
- * ax25_enquiry_response().
- */
-void ax25_t1_timeout(ax25_cb * ax25)
-{
- switch (ax25->state) {
- case AX25_STATE_1:
- if (ax25->n2count == ax25->n2) {
- if (ax25->modulus == MODULUS) {
- ax25_link_failed(&ax25->dest_addr, ax25->device);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- } else {
- ax25->modulus = MODULUS;
- ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
- ax25->n2count = 0;
- ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
- }
- } else {
- ax25->n2count++;
- if (ax25->modulus == MODULUS) {
- ax25_send_control(ax25, SABM, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
- } else {
- ax25_send_control(ax25, SABME, ax25_dev_is_dama_slave(ax25->device)? POLLOFF : POLLON, C_COMMAND);
- }
- }
- break;
-
- case AX25_STATE_2:
- if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->device);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- } else {
- ax25->n2count++;
- if (!ax25_dev_is_dama_slave(ax25->device))
- ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
- }
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_timer(ax25);
break;
- case AX25_STATE_3:
- ax25->n2count = 1;
- if (!ax25->dama_slave)
- ax25_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (ax25->ax25_dev->dama.slave)
+ ax25_ds_timer(ax25);
+ else
+ ax25_std_timer(ax25);
break;
-
- case AX25_STATE_4:
- if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->device);
- ax25_clear_queues(ax25);
- ax25_send_control(ax25, DM, POLLON, C_RESPONSE);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- if (ax25->sk->debug)
- printk(KERN_DEBUG "AX.25 link Failure\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- } else {
- ax25->n2count++;
- if (!ax25->dama_slave)
- ax25_transmit_enquiry(ax25);
- }
- break;
- }
-
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
- ax25_set_timer(ax25);
-}
-
-/************************************************************************/
-/* Module support functions follow. */
-/************************************************************************/
-
-static struct protocol_struct {
- struct protocol_struct *next;
- unsigned int pid;
- int (*func)(struct sk_buff *, ax25_cb *);
-} *protocol_list = NULL;
-
-static struct linkfail_struct {
- struct linkfail_struct *next;
- void (*func)(ax25_address *, struct device *);
-} *linkfail_list = NULL;
-
-static struct listen_struct {
- struct listen_struct *next;
- ax25_address callsign;
- struct device *dev;
-} *listen_list = NULL;
-
-int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
-{
- struct protocol_struct *protocol;
- unsigned long flags;
-
- if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
- return 0;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP || pid == AX25_P_ARP)
- return 0;
#endif
- if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
- return 0;
-
- protocol->pid = pid;
- protocol->func = func;
-
- save_flags(flags);
- cli();
-
- protocol->next = protocol_list;
- protocol_list = protocol;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_protocol_release(unsigned int pid)
-{
- struct protocol_struct *s, *protocol = protocol_list;
- unsigned long flags;
-
- if (protocol == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (protocol->pid == pid) {
- protocol_list = protocol->next;
- restore_flags(flags);
- kfree_s(protocol, sizeof(struct protocol_struct));
- return;
- }
-
- while (protocol != NULL && protocol->next != NULL) {
- if (protocol->next->pid == pid) {
- s = protocol->next;
- protocol->next = protocol->next->next;
- restore_flags(flags);
- kfree_s(s, sizeof(struct protocol_struct));
- return;
- }
-
- protocol = protocol->next;
}
-
- restore_flags(flags);
-}
-
-int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
-{
- struct linkfail_struct *linkfail;
- unsigned long flags;
-
- if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
- return 0;
-
- linkfail->func = func;
-
- save_flags(flags);
- cli();
-
- linkfail->next = linkfail_list;
- linkfail_list = linkfail;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
-{
- struct linkfail_struct *s, *linkfail = linkfail_list;
- unsigned long flags;
-
- if (linkfail == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (linkfail->func == func) {
- linkfail_list = linkfail->next;
- restore_flags(flags);
- kfree_s(linkfail, sizeof(struct linkfail_struct));
- return;
- }
-
- while (linkfail != NULL && linkfail->next != NULL) {
- if (linkfail->next->func == func) {
- s = linkfail->next;
- linkfail->next = linkfail->next->next;
- restore_flags(flags);
- kfree_s(s, sizeof(struct linkfail_struct));
- return;
- }
-
- linkfail = linkfail->next;
- }
-
- restore_flags(flags);
-}
-
-int ax25_listen_register(ax25_address *callsign, struct device *dev)
-{
- struct listen_struct *listen;
- unsigned long flags;
-
- if (ax25_listen_mine(callsign, dev))
- return 0;
-
- if ((listen = (struct listen_struct *)kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
- return 0;
-
- listen->callsign = *callsign;
- listen->dev = dev;
-
- save_flags(flags);
- cli();
-
- listen->next = listen_list;
- listen_list = listen;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_listen_release(ax25_address *callsign, struct device *dev)
-{
- struct listen_struct *s, *listen = listen_list;
- unsigned long flags;
-
- if (listen == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
- listen_list = listen->next;
- restore_flags(flags);
- kfree_s(listen, sizeof(struct listen_struct));
- return;
- }
-
- while (listen != NULL && listen->next != NULL) {
- if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
- s = listen->next;
- listen->next = listen->next->next;
- restore_flags(flags);
- kfree_s(s, sizeof(struct listen_struct));
- return;
- }
-
- listen = listen->next;
- }
-
- restore_flags(flags);
-}
-
-int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
-{
- struct protocol_struct *protocol;
-
- for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
- if (protocol->pid == pid)
- return protocol->func;
-
- return NULL;
-}
-
-int ax25_listen_mine(ax25_address *callsign, struct device *dev)
-{
- struct listen_struct *listen;
-
- for (listen = listen_list; listen != NULL; listen = listen->next)
- if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
- return 1;
-
- return 0;
-}
-
-void ax25_link_failed(ax25_address *callsign, struct device *dev)
-{
- struct linkfail_struct *linkfail;
-
- for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
- (linkfail->func)(callsign, dev);
}
#endif
diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
new file mode 100644
index 000000000..3325f5056
--- /dev/null
+++ b/net/ax25/ax25_uid.c
@@ -0,0 +1,189 @@
+/*
+ * AX.25 release 036
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <net/ax25.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+#include <linux/sysctl.h>
+#include <net/ip.h>
+#include <net/arp.h>
+
+/*
+ * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
+ */
+
+static ax25_uid_assoc *ax25_uid_list = NULL;
+
+int ax25_uid_policy = 0;
+
+ax25_address *ax25_findbyuid(uid_t uid)
+{
+ ax25_uid_assoc *ax25_uid;
+
+ for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+ if (ax25_uid->uid == uid)
+ return &ax25_uid->call;
+ }
+
+ return NULL;
+}
+
+int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
+{
+ ax25_uid_assoc *s, *ax25_uid;
+ unsigned long flags;
+
+ switch (cmd) {
+ case SIOCAX25GETUID:
+ for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+ if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
+ return ax25_uid->uid;
+ }
+ return -ENOENT;
+
+ case SIOCAX25ADDUID:
+ if (!suser())
+ return -EPERM;
+ if (ax25_findbyuid(sax->sax25_uid))
+ return -EEXIST;
+ if (sax->sax25_uid == 0)
+ return -EINVAL;
+ if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ ax25_uid->uid = sax->sax25_uid;
+ ax25_uid->call = sax->sax25_call;
+ save_flags(flags); cli();
+ ax25_uid->next = ax25_uid_list;
+ ax25_uid_list = ax25_uid;
+ restore_flags(flags);
+ return 0;
+
+ case SIOCAX25DELUID:
+ if (!suser())
+ return -EPERM;
+ for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
+ if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
+ break;
+ }
+ if (ax25_uid == NULL)
+ return -ENOENT;
+ save_flags(flags); cli();
+ if ((s = ax25_uid_list) == ax25_uid) {
+ ax25_uid_list = s->next;
+ restore_flags(flags);
+ kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+ return 0;
+ }
+ while (s != NULL && s->next != NULL) {
+ if (s->next == ax25_uid) {
+ s->next = ax25_uid->next;
+ restore_flags(flags);
+ kfree_s(ax25_uid, sizeof(ax25_uid_assoc));
+ return 0;
+ }
+ s = s->next;
+ }
+ restore_flags(flags);
+ return -ENOENT;
+
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL; /*NOTREACHED */
+}
+
+int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ ax25_uid_assoc *pt;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ cli();
+
+ len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
+
+ for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
+ len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= offset - begin;
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+#ifdef MODULE
+
+/*
+ * Free all memory associated with UID/Callsign structures.
+ */
+void ax25_uid_free(void)
+{
+ ax25_uid_assoc *s, *ax25_uid = ax25_uid_list;
+
+ while (ax25_uid != NULL) {
+ s = ax25_uid;
+ ax25_uid = ax25_uid->next;
+
+ kfree_s(s, sizeof(ax25_uid_assoc));
+ }
+}
+
+#endif
+
+#endif
diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c
index 302d210f8..02b7fa484 100644
--- a/net/ax25/sysctl_net_ax25.c
+++ b/net/ax25/sysctl_net_ax25.c
@@ -7,19 +7,31 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
#include <net/ax25.h>
-static int min_ax25[] = {0, 0, 0, 0, 0, 1, 1, 1, 1,
- 0, 0, 1, 1, 1, 0x00};
-static int max_ax25[] = {1, 1, 1, 1, 1, 7, 63, 30 * PR_SLOWHZ, 20 * PR_SLOWHZ,
- 3600 * PR_SLOWHZ, 65535 * PR_SLOWHZ, 31, 512, 20, 0x03};
+static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1};
+static int min_axdefmode[] = {0}, max_axdefmode[] = {1};
+static int min_backoff[] = {0}, max_backoff[] = {2};
+static int min_conmode[] = {0}, max_conmode[] = {2};
+static int min_window[] = {1}, max_window[] = {7};
+static int min_ewindow[] = {1}, max_ewindow[] = {63};
+static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ};
+static int min_t2[] = {1}, max_t2[] = {20 * AX25_SLOWHZ};
+static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ};
+static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ};
+static int min_n2[] = {1}, max_n2[] = {31};
+static int min_paclen[] = {1}, max_paclen[] = {512};
+static int min_proto[] = {0}, max_proto[] = {3};
+static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * AX25_SLOWHZ};
static struct ctl_table_header *ax25_table_header;
-static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
+static ctl_table *ax25_table = NULL;
+static int ax25_table_size = 0;
static ctl_table ax25_dir_table[] = {
- {NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
+ {NET_AX25, "ax25", NULL, 0, 0555, NULL},
{0}
};
@@ -28,33 +40,118 @@ static ctl_table ax25_root_table[] = {
{0}
};
+static const ctl_table ax25_param_table[] = {
+ {NET_AX25_IP_DEFAULT_MODE, "ip_default_mode",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_ipdefmode, &max_ipdefmode},
+ {NET_AX25_DEFAULT_MODE, "ax25_default_mode",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_axdefmode, &max_axdefmode},
+ {NET_AX25_BACKOFF_TYPE, "backoff_type",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_backoff, &max_backoff},
+ {NET_AX25_CONNECT_MODE, "connect_mode",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_conmode, &max_conmode},
+ {NET_AX25_STANDARD_WINDOW, "standard_window_size",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_window, &max_window},
+ {NET_AX25_EXTENDED_WINDOW, "extended_window_size",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_ewindow, &max_ewindow},
+ {NET_AX25_T1_TIMEOUT, "t1_timeout",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_t1, &max_t1},
+ {NET_AX25_T2_TIMEOUT, "t2_timeout",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_t2, &max_t2},
+ {NET_AX25_T3_TIMEOUT, "t3_timeout",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_t3, &max_t3},
+ {NET_AX25_IDLE_TIMEOUT, "idle_timeout",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_idle, &max_idle},
+ {NET_AX25_N2, "maximum_retry_count",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_n2, &max_n2},
+ {NET_AX25_PACLEN, "maximum_packet_length",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_paclen, &max_paclen},
+ {NET_AX25_PROTOCOL, "protocol",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_proto, &max_proto},
+ {NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout",
+ NULL, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL,
+ &min_ds_timeout, &max_ds_timeout},
+ {0} /* that's all, folks! */
+};
+
void ax25_register_sysctl(void)
{
- int i, n;
-
- memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
-
- for (n = 0, i = 0; i < AX25_MAX_DEVICES; i++) {
- if (ax25_device[i].dev != NULL) {
- ax25_table[n].ctl_name = n + 1;
- ax25_table[n].procname = ax25_device[i].name;
- ax25_table[n].data = &ax25_device[i].values;
- ax25_table[n].maxlen = AX25_MAX_VALUES * sizeof(int);
- ax25_table[n].mode = 0644;
- ax25_table[n].child = NULL;
- ax25_table[n].proc_handler = &proc_dointvec_minmax;
- ax25_table[n].strategy = &sysctl_intvec;
- ax25_table[n].de = NULL;
- ax25_table[n].extra1 = &min_ax25;
- ax25_table[n].extra2 = &max_ax25;
- n++;
- }
+ ax25_dev *ax25_dev;
+ int n, k;
+
+ for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
+ ax25_table_size += sizeof(ctl_table);
+
+ if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL)
+ return;
+
+ memset(ax25_table, 0x00, ax25_table_size);
+
+ for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
+ ax25_table[n].ctl_name = n + 1;
+ ax25_table[n].procname = ax25_dev->dev->name;
+ ax25_table[n].data = NULL;
+ ax25_table[n].maxlen = 0;
+ ax25_table[n].mode = 0555;
+ ax25_table[n].child = ax25_dev->systable;
+ ax25_table[n].proc_handler = NULL;
+
+ memcpy(ax25_dev->systable, ax25_param_table, sizeof(ax25_dev->systable));
+
+#ifndef CONFIG_AX25_DAMA_SLAVE
+ /*
+ * We do not wish to have a representation of this parameter
+ * in /proc/sys/ when configured *not* to include the
+ * AX.25 DAMA slave code, do we?
+ */
+
+ ax25_dev->systable[AX25_VALUES_DS_TIMEOUT].procname = NULL;
+#endif
+
+ ax25_dev->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
+
+ for (k = 0; k < AX25_MAX_VALUES; k++)
+ ax25_dev->systable[k].data = &ax25_dev->values[k];
+
+ n++;
}
+ ax25_dir_table[0].child = ax25_table;
+
ax25_table_header = register_sysctl_table(ax25_root_table, 1);
}
void ax25_unregister_sysctl(void)
{
unregister_sysctl_table(ax25_table_header);
+
+ kfree_s(ax25_table, ax25_table_size);
+
+ ax25_dir_table[0].child = NULL;
}
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index 981c47dcd..bc432f316 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -8,9 +8,13 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := bridge.o
-O_OBJS := br.o br_tree.o sysctl_net_bridge.o
+O_OBJS := br.o br_tree.o
M_OBJS := $(O_TARGET)
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_bridge.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 70e54dbc3..7e8cd2a23 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -14,6 +14,7 @@
* 2 of the License, or (at your option) any later version.
*
* Fixes:
+ * Yury Shevchuk : Bridge with non bridging ports
*
* Todo:
* Don't bring up devices automatically. Start ports disabled
@@ -30,6 +31,7 @@
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
+
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/timer.h>
@@ -40,30 +42,88 @@
#include <linux/string.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
+#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <net/br.h>
+static void transmit_config(int port_no);
+static int root_bridge(void);
+static int supersedes_port_info(int port_no, Config_bpdu *config);
+static void record_config_information(int port_no, Config_bpdu *config);
+static void record_config_timeout_values(Config_bpdu *config);
+static void config_bpdu_generation(void);
+static int designated_port(int port_no);
+static void reply(int port_no);
+static void transmit_tcn(void);
+static void configuration_update(void);
+static void root_selection(void);
+static void designated_port_selection(void);
+static void become_designated_port(int port_no);
+static void port_state_selection(void);
+static void make_forwarding(int port_no);
+static void topology_change_detection(void);
+static void topology_change_acknowledged(void);
+static void acknowledge_topology_change(int port_no);
+static void make_blocking(int port_no);
+static void set_port_state(int port_no, int state);
+static void received_config_bpdu(int port_no, Config_bpdu *config);
+static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn);
+static void hello_timer_expiry(void);
+static void message_age_timer_expiry(int port_no);
+static void forward_delay_timer_expiry(int port_no);
+static int designated_for_some_port(void);
+static void tcn_timer_expiry(void);
+static void topology_change_timer_expiry(void);
+static void hold_timer_expiry(int port_no);
+static void br_init_port(int port_no);
+static void enable_port(int port_no);
+static void disable_port(int port_no);
+static void set_bridge_priority(bridge_id_t *new_bridge_id);
+static void set_port_priority(int port_no, unsigned short new_port_id);
+static void set_path_cost(int port_no, unsigned short path_cost);
+static void start_hello_timer(void);
+static void stop_hello_timer(void);
+static int hello_timer_expired(void);
+static void start_tcn_timer(void);
+static void stop_tcn_timer(void);
+static int tcn_timer_expired(void);
+static void start_topology_change_timer(void);
+static void stop_topology_change_timer(void);
+static int topology_change_timer_expired(void);
+static void start_message_age_timer(int port_no, unsigned short message_age);
+static void stop_message_age_timer(int port_no);
+static int message_age_timer_expired(int port_no);
+static void start_forward_delay_timer(int port_no);
+static void stop_forward_delay_timer(int port_no);
+static int forward_delay_timer_expired(int port_no);
+static void start_hold_timer(int port_no);
+static void stop_hold_timer(int port_no);
+static int hold_timer_expired(int port_no);
static int br_device_event(struct notifier_block *dnot, unsigned long event, void *ptr);
static void br_tick(unsigned long arg);
-int br_forward(struct sk_buff *skb, int port); /* 3.7 */
-int br_port_cost(struct device *dev); /* 4.10.2 */
-void br_bpdu(struct sk_buff *skb); /* consumes skb */
-int br_tx_frame(struct sk_buff *skb);
-int br_cmp(unsigned int *a, unsigned int *b);
-
-unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
-
-Bridge_data bridge_info; /* (4.5.3) */
+static int br_forward(struct sk_buff *skb, int port); /* 3.7 */
+static int br_port_cost(struct device *dev); /* 4.10.2 */
+static void br_bpdu(struct sk_buff *skb); /* consumes skb */
+static int br_cmp(unsigned int *a, unsigned int *b);
+static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu);
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu);
+static int find_port(struct device *dev);
+static int br_flood(struct sk_buff *skb, int port);
+static int br_drop(struct sk_buff *skb);
+static int br_learn(struct sk_buff *skb, int port); /* 3.8 */
+
+static unsigned char bridge_ula[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
+static Bridge_data bridge_info; /* (4.5.3) */
Port_data port_info[All_ports]; /* (4.5.5) */
Config_bpdu config_bpdu[All_ports];
Tcn_bpdu tcn_bpdu[All_ports];
-Timer hello_timer; /* (4.5.4.1) */
-Timer tcn_timer; /* (4.5.4.2) */
-Timer topology_change_timer; /* (4.5.4.3) */
-Timer message_age_timer[All_ports]; /* (4.5.6.1) */
-Timer forward_delay_timer[All_ports]; /* (4.5.6.2) */
-Timer hold_timer[All_ports]; /* (4.5.6.3) */
+static Timer hello_timer; /* (4.5.4.1) */
+static Timer tcn_timer; /* (4.5.4.2) */
+static Timer topology_change_timer; /* (4.5.4.3) */
+static Timer message_age_timer[All_ports]; /* (4.5.6.1) */
+static Timer forward_delay_timer[All_ports]; /* (4.5.6.2) */
+static Timer hold_timer[All_ports]; /* (4.5.6.3) */
/* entries timeout after this many seconds */
unsigned int fdb_aging_time = FDB_TIMEOUT;
@@ -117,7 +177,7 @@ int br_protocol_ok(unsigned short protocol)
/* Add a protocol to be handled opposite to the standard policy of the bridge */
-int br_add_exempt_protocol(unsigned short p)
+static int br_add_exempt_protocol(unsigned short p)
{
unsigned x;
if (p == 0) return -EINVAL;
@@ -133,7 +193,7 @@ int br_add_exempt_protocol(unsigned short p)
}
/* Valid Policies are 0=No Protocols bridged 1=Bridge all protocols */
-int br_set_policy(int policy)
+static int br_set_policy(int policy)
{
if (policy>1) return -EINVAL;
br_stats.policy=policy;
@@ -154,7 +214,7 @@ int br_set_policy(int policy)
* 802.1d bridging protocol.
*/
-void transmit_config(int port_no) /* (4.6.1) */
+static void transmit_config(int port_no) /* (4.6.1) */
{
if (hold_timer[port_no].active) { /* (4.6.1.3.1) */
port_info[port_no].config_pending = TRUE; /* (4.6.1.3.1) */
@@ -197,13 +257,13 @@ void transmit_config(int port_no) /* (4.6.1) */
}
}
-int root_bridge(void)
+static int root_bridge(void)
{
return (br_cmp(bridge_info.designated_root.BRIDGE_ID,
bridge_info.bridge_id.BRIDGE_ID)?FALSE:TRUE);
}
-int supersedes_port_info(int port_no, Config_bpdu *config) /* (4.6.2.2) */
+static int supersedes_port_info(int port_no, Config_bpdu *config) /* (4.6.2.2) */
{
return (
(br_cmp(config->root_id.BRIDGE_ID,
@@ -240,7 +300,7 @@ int supersedes_port_info(int port_no, Config_bpdu *config) /* (4.6.2.2) */
);
}
-void record_config_information(int port_no, Config_bpdu *config) /* (4.6.2) */
+static void record_config_information(int port_no, Config_bpdu *config) /* (4.6.2) */
{
port_info[port_no].designated_root = config->root_id; /* (4.6.2.3.1) */
port_info[port_no].designated_cost = config->root_path_cost;
@@ -249,7 +309,7 @@ void record_config_information(int port_no, Config_bpdu *config) /* (4.6.2) *
start_message_age_timer(port_no, config->message_age); /* (4.6.2.3.2) */
}
-void record_config_timeout_values(Config_bpdu *config) /* (4.6.3) */
+static void record_config_timeout_values(Config_bpdu *config) /* (4.6.3) */
{
bridge_info.max_age = config->max_age; /* (4.6.3.3) */
bridge_info.hello_time = config->hello_time;
@@ -258,7 +318,7 @@ void record_config_timeout_values(Config_bpdu *config) /* (4.6.3) */
bridge_info.top_change = 1;
}
-void config_bpdu_generation(void)
+static void config_bpdu_generation(void)
{ /* (4.6.4) */
int port_no;
for (port_no = One; port_no <= No_of_ports; port_no++) { /* (4.6.4.3) */
@@ -271,7 +331,7 @@ void config_bpdu_generation(void)
}
}
-int designated_port(int port_no)
+static int designated_port(int port_no)
{
return ((br_cmp(port_info[port_no].designated_bridge.BRIDGE_ID,
bridge_info.bridge_id.BRIDGE_ID) == 0
@@ -283,12 +343,12 @@ int designated_port(int port_no)
);
}
-void reply(int port_no) /* (4.6.5) */
+static void reply(int port_no) /* (4.6.5) */
{
transmit_config(port_no); /* (4.6.5.3) */
}
-void transmit_tcn(void)
+static void transmit_tcn(void)
{ /* (4.6.6) */
int port_no;
@@ -297,7 +357,7 @@ void transmit_tcn(void)
send_tcn_bpdu(port_no, &tcn_bpdu[bridge_info.root_port]); /* (4.6.6.3) */
}
-void configuration_update(void) /* (4.6.7) */
+static void configuration_update(void) /* (4.6.7) */
{
root_selection(); /* (4.6.7.3.1) */
/* (4.6.8.2) */
@@ -305,7 +365,7 @@ void configuration_update(void) /* (4.6.7) */
/* (4.6.9.2) */
}
-void root_selection(void)
+static void root_selection(void)
{ /* (4.6.8) */
int root_port;
int port_no;
@@ -385,7 +445,7 @@ void root_selection(void)
}
}
-void designated_port_selection(void)
+static void designated_port_selection(void)
{ /* (4.6.9) */
int port_no;
@@ -422,7 +482,7 @@ void designated_port_selection(void)
}
}
-void become_designated_port(int port_no)
+static void become_designated_port(int port_no)
{ /* (4.6.10) */
/* (4.6.10.3.1) */
@@ -435,7 +495,7 @@ void become_designated_port(int port_no)
port_info[port_no].designated_port = port_info[port_no].port_id;
}
-void port_state_selection(void)
+static void port_state_selection(void)
{ /* (4.6.11) */
int port_no;
for (port_no = One; port_no <= No_of_ports; port_no++) {
@@ -455,7 +515,7 @@ void port_state_selection(void)
}
-void make_forwarding(int port_no)
+static void make_forwarding(int port_no)
{ /* (4.6.12) */
if (port_info[port_no].state == Blocking) { /* (4.6.12.3) */
set_port_state(port_no, Listening); /* (4.6.12.3.1) */
@@ -463,7 +523,7 @@ void make_forwarding(int port_no)
}
}
-void topology_change_detection(void)
+static void topology_change_detection(void)
{ /* (4.6.14) */
if (root_bridge()) { /* (4.6.14.3.1) */
bridge_info.top_change = 1;
@@ -475,19 +535,19 @@ void topology_change_detection(void)
bridge_info.top_change = 1;
}
-void topology_change_acknowledged(void)
+static void topology_change_acknowledged(void)
{ /* (4.6.15) */
bridge_info.top_change_detected = 0;
stop_tcn_timer(); /* (4.6.15.3.2) */
}
-void acknowledge_topology_change(int port_no)
+static void acknowledge_topology_change(int port_no)
{ /* (4.6.16) */
port_info[port_no].top_change_ack = 1;
transmit_config(port_no); /* (4.6.16.3.2) */
}
-void make_blocking(int port_no) /* (4.6.13) */
+static void make_blocking(int port_no) /* (4.6.13) */
{
if ((port_info[port_no].state != Disabled)
@@ -507,12 +567,12 @@ void make_blocking(int port_no) /* (4.6.13) */
}
}
-void set_port_state(int port_no, int state)
+static void set_port_state(int port_no, int state)
{
port_info[port_no].state = state;
}
-void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */
+static void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */
{
int root;
@@ -549,7 +609,7 @@ void received_config_bpdu(int port_no, Config_bpdu *config) /* (4.7.1) */
}
}
-void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn) /* (4.7.2) */
+static void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn) /* (4.7.2) */
{
if (port_info[port_no].state != Disabled) {
if (designated_port(port_no)) {
@@ -560,13 +620,13 @@ void received_tcn_bpdu(int port_no, Tcn_bpdu *tcn) /* (4.7.2) */
}
}
-void hello_timer_expiry(void)
+static void hello_timer_expiry(void)
{ /* (4.7.3) */
config_bpdu_generation(); /* (4.6.4.2.2) */
start_hello_timer();
}
-void message_age_timer_expiry(int port_no) /* (4.7.4) */
+static void message_age_timer_expiry(int port_no) /* (4.7.4) */
{
int root;
root = root_bridge();
@@ -591,7 +651,7 @@ void message_age_timer_expiry(int port_no) /* (4.7.4) */
}
}
-void forward_delay_timer_expiry(int port_no) /* (4.7.5) */
+static void forward_delay_timer_expiry(int port_no) /* (4.7.5) */
{
if (port_info[port_no].state == Listening) { /* (4.7.5.1) */
set_port_state(port_no, Learning); /* (4.7.5.1.1) */
@@ -605,7 +665,7 @@ void forward_delay_timer_expiry(int port_no) /* (4.7.5) */
}
}
-int designated_for_some_port(void)
+static int designated_for_some_port(void)
{
int port_no;
@@ -620,27 +680,27 @@ int designated_for_some_port(void)
return (FALSE);
}
-void tcn_timer_expiry(void)
+static void tcn_timer_expiry(void)
{ /* (4.7.6) */
transmit_tcn(); /* (4.7.6.1) */
start_tcn_timer(); /* (4.7.6.2) */
}
-void topology_change_timer_expiry(void)
+static void topology_change_timer_expiry(void)
{ /* (4.7.7) */
bridge_info.top_change_detected = 0;
bridge_info.top_change = 0;
/* (4.7.7.2) */
}
-void hold_timer_expiry(int port_no) /* (4.7.8) */
+static void hold_timer_expiry(int port_no) /* (4.7.8) */
{
if (port_info[port_no].config_pending) {
transmit_config(port_no); /* (4.7.8.1) */
} /* (4.6.1.2.3) */
}
-void br_init(void)
+__initfunc(void br_init(void))
{ /* (4.8.1) */
int port_no;
@@ -681,7 +741,7 @@ void br_init(void)
/*start_hello_timer();*/
}
-void br_init_port(int port_no)
+static void br_init_port(int port_no)
{
become_designated_port(port_no); /* (4.8.1.4.1) */
set_port_state(port_no, Blocking); /* (4.8.1.4.2) */
@@ -692,13 +752,13 @@ void br_init_port(int port_no)
stop_hold_timer(port_no); /* (4.8.1.4.7) */
}
-void enable_port(int port_no) /* (4.8.2) */
+static void enable_port(int port_no) /* (4.8.2) */
{
br_init_port(port_no);
port_state_selection(); /* (4.8.2.7) */
} /* */
-void disable_port(int port_no) /* (4.8.3) */
+static void disable_port(int port_no) /* (4.8.3) */
{
int root;
@@ -723,7 +783,8 @@ void disable_port(int port_no) /* (4.8.3) */
}
-void set_bridge_priority(bridge_id_t *new_bridge_id) /* (4.8.4) */
+static void set_bridge_priority(bridge_id_t *new_bridge_id)
+ /* (4.8.4) */
{
int root;
@@ -749,7 +810,8 @@ void set_bridge_priority(bridge_id_t *new_bridge_id) /* (4.8.4) */
}
}
-void set_port_priority(int port_no, unsigned short new_port_id) /* (4.8.5) */
+static void set_port_priority(int port_no, unsigned short new_port_id)
+ /* (4.8.5) */
{
if (designated_port(port_no)) { /* (4.8.5.2) */
port_info[port_no].designated_port = new_port_id;
@@ -769,7 +831,8 @@ void set_port_priority(int port_no, unsigned short new_port_id) /* (4.8.5) *
}
}
-void set_path_cost(int port_no, unsigned short path_cost) /* (4.8.6) */
+static void set_path_cost(int port_no, unsigned short path_cost)
+ /* (4.8.6) */
{
port_info[port_no].path_cost = path_cost; /* (4.8.6.1) */
configuration_update(); /* (4.8.6.2) */
@@ -806,18 +869,18 @@ static void br_tick(unsigned long arg)
add_timer(&tl);
}
-void start_hello_timer(void)
+static void start_hello_timer(void)
{
hello_timer.value = 0;
hello_timer.active = TRUE;
}
-void stop_hello_timer(void)
+static void stop_hello_timer(void)
{
hello_timer.active = FALSE;
}
-int hello_timer_expired(void)
+static int hello_timer_expired(void)
{
if (hello_timer.active && (++hello_timer.value >= bridge_info.hello_time)) {
hello_timer.active = FALSE;
@@ -826,18 +889,18 @@ int hello_timer_expired(void)
return (FALSE);
}
-void start_tcn_timer(void)
+static void start_tcn_timer(void)
{
tcn_timer.value = 0;
tcn_timer.active = TRUE;
}
-void stop_tcn_timer(void)
+static void stop_tcn_timer(void)
{
tcn_timer.active = FALSE;
}
-int tcn_timer_expired(void)
+static int tcn_timer_expired(void)
{
if (tcn_timer.active && (++tcn_timer.value >=
bridge_info.bridge_hello_time)) {
@@ -848,18 +911,18 @@ int tcn_timer_expired(void)
}
-void start_topology_change_timer(void)
+static void start_topology_change_timer(void)
{
topology_change_timer.value = 0;
topology_change_timer.active = TRUE;
}
-void stop_topology_change_timer(void)
+static void stop_topology_change_timer(void)
{
topology_change_timer.active = FALSE;
}
-int topology_change_timer_expired(void)
+static int topology_change_timer_expired(void)
{
if (topology_change_timer.active
&& (++topology_change_timer.value
@@ -871,18 +934,18 @@ int topology_change_timer_expired(void)
return (FALSE);
}
-void start_message_age_timer(int port_no, unsigned short message_age)
+static void start_message_age_timer(int port_no, unsigned short message_age)
{
message_age_timer[port_no].value = message_age;
message_age_timer[port_no].active = TRUE;
}
-void stop_message_age_timer(int port_no)
+static void stop_message_age_timer(int port_no)
{
message_age_timer[port_no].active = FALSE;
}
-int message_age_timer_expired(int port_no)
+static int message_age_timer_expired(int port_no)
{
if (message_age_timer[port_no].active &&
(++message_age_timer[port_no].value >= bridge_info.max_age)) {
@@ -892,18 +955,18 @@ int message_age_timer_expired(int port_no)
return (FALSE);
}
-void start_forward_delay_timer(int port_no)
+static void start_forward_delay_timer(int port_no)
{
forward_delay_timer[port_no].value = 0;
forward_delay_timer[port_no].active = TRUE;
}
-void stop_forward_delay_timer(int port_no)
+static void stop_forward_delay_timer(int port_no)
{
forward_delay_timer[port_no].active = FALSE;
}
-int forward_delay_timer_expired(int port_no)
+static int forward_delay_timer_expired(int port_no)
{
if (forward_delay_timer[port_no].active &&
(++forward_delay_timer[port_no].value >= bridge_info.forward_delay)) {
@@ -913,19 +976,18 @@ int forward_delay_timer_expired(int port_no)
return (FALSE);
}
-void start_hold_timer(int port_no)
+static void start_hold_timer(int port_no)
{
hold_timer[port_no].value = 0;
hold_timer[port_no].active = TRUE;
}
-void stop_hold_timer(int port_no)
+static void stop_hold_timer(int port_no)
{
hold_timer[port_no].active = FALSE;
}
-
-int hold_timer_expired(int port_no)
+static int hold_timer_expired(int port_no)
{
if (hold_timer[port_no].active &&
(++hold_timer[port_no].value >= bridge_info.hold_time)) {
@@ -936,12 +998,13 @@ int hold_timer_expired(int port_no)
}
-int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
+static int send_config_bpdu(int port_no, Config_bpdu *config_bpdu)
{
struct sk_buff *skb;
struct device *dev = port_info[port_no].dev;
int size;
unsigned long flags;
+struct ethhdr *eth;
if (port_info[port_no].state == Disabled) {
printk(KERN_DEBUG "send_config_bpdu: port %i not valid\n",port_no);
@@ -952,34 +1015,34 @@ unsigned long flags;
/*
* create and send the message
*/
- size = sizeof(Config_bpdu) + dev->hard_header_len;
+ size = dev->hard_header_len + sizeof(Config_bpdu);
skb = alloc_skb(size, GFP_ATOMIC);
if (skb == NULL) {
printk(KERN_DEBUG "send_config_bpdu: no skb available\n");
return(-1);
}
skb->dev = dev;
- skb->free = 1;
- skb->h.eth = (struct ethhdr *)skb_put(skb, size);
- memcpy(skb->h.eth->h_dest, bridge_ula, ETH_ALEN);
- memcpy(skb->h.eth->h_source, dev->dev_addr, ETH_ALEN);
+ skb->mac.raw = skb->h.raw = skb_put(skb, size);
+ eth = skb->mac.ethernet;
+ memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
if (br_stats.flags & BR_DEBUG)
printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
dest %02x:%02x:%02x:%02x:%02x:%02x\n",
port_no,
- skb->h.eth->h_source[0],
- skb->h.eth->h_source[1],
- skb->h.eth->h_source[2],
- skb->h.eth->h_source[3],
- skb->h.eth->h_source[4],
- skb->h.eth->h_source[5],
- skb->h.eth->h_dest[0],
- skb->h.eth->h_dest[1],
- skb->h.eth->h_dest[2],
- skb->h.eth->h_dest[3],
- skb->h.eth->h_dest[4],
- skb->h.eth->h_dest[5]);
- skb->h.eth->h_proto = htonl(0x8038); /* XXX verify */
+ eth->h_source[0],
+ eth->h_source[1],
+ eth->h_source[2],
+ eth->h_source[3],
+ eth->h_source[4],
+ eth->h_source[5],
+ eth->h_dest[0],
+ eth->h_dest[1],
+ eth->h_dest[2],
+ eth->h_dest[3],
+ eth->h_dest[4],
+ eth->h_dest[5]);
+ eth->h_proto = htons(0x8038);
skb->h.raw += skb->dev->hard_header_len;
memcpy(skb->h.raw, config_bpdu, sizeof(Config_bpdu));
@@ -987,7 +1050,6 @@ unsigned long flags;
/* won't get bridged again... */
skb->pkt_bridged = IS_BRIDGED;
skb->arp = 1; /* do not resolve... */
- skb->h.raw = skb->data + ETH_HLEN;
save_flags(flags);
cli();
skb_queue_tail(dev->buffs, skb);
@@ -995,12 +1057,13 @@ unsigned long flags;
return(0);
}
-int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
+static int send_tcn_bpdu(int port_no, Tcn_bpdu *bpdu)
{
struct sk_buff *skb;
struct device *dev = port_info[port_no].dev;
int size;
unsigned long flags;
+struct ethhdr *eth;
if (port_info[port_no].state == Disabled) {
printk(KERN_DEBUG "send_tcn_bpdu: port %i not valid\n",port_no);
@@ -1015,35 +1078,34 @@ unsigned long flags;
return(-1);
}
skb->dev = dev;
- skb->free = 1;
- skb->h.eth = (struct ethhdr *)skb_put(skb,size);
- memcpy(skb->h.eth->h_dest, bridge_ula, ETH_ALEN);
- memcpy(skb->h.eth->h_source, dev->dev_addr, ETH_ALEN);
+ skb->mac.raw = skb->h.raw = skb_put(skb,size);
+ eth = skb->mac.ethernet;
+ memcpy(eth->h_dest, bridge_ula, ETH_ALEN);
+ memcpy(eth->h_source, dev->dev_addr, ETH_ALEN);
if (br_stats.flags & BR_DEBUG)
printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
dest %02x:%02x:%02x:%02x:%02x:%02x\n",
port_no,
- skb->h.eth->h_source[0],
- skb->h.eth->h_source[1],
- skb->h.eth->h_source[2],
- skb->h.eth->h_source[3],
- skb->h.eth->h_source[4],
- skb->h.eth->h_source[5],
- skb->h.eth->h_dest[0],
- skb->h.eth->h_dest[1],
- skb->h.eth->h_dest[2],
- skb->h.eth->h_dest[3],
- skb->h.eth->h_dest[4],
- skb->h.eth->h_dest[5]);
- skb->h.eth->h_proto = 0x8038; /* XXX verify */
+ eth->h_source[0],
+ eth->h_source[1],
+ eth->h_source[2],
+ eth->h_source[3],
+ eth->h_source[4],
+ eth->h_source[5],
+ eth->h_dest[0],
+ eth->h_dest[1],
+ eth->h_dest[2],
+ eth->h_dest[3],
+ eth->h_dest[4],
+ eth->h_dest[5]);
+ eth->h_proto = htons(0x8038);
skb->h.raw += skb->dev->hard_header_len;
memcpy(skb->h.raw, bpdu, sizeof(Tcn_bpdu));
- /* mark that's we've been here... */
+ /* mark that we've been here... */
skb->pkt_bridged = IS_BRIDGED;
skb->arp = 1; /* do not resolve... */
- skb->h.raw = skb->data + ETH_HLEN;
save_flags(flags);
cli();
skb_queue_tail(dev->buffs, skb);
@@ -1119,6 +1181,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
int br_receive_frame(struct sk_buff *skb) /* 3.5 */
{
int port;
+ struct ethhdr *eth;
if (br_stats.flags & BR_DEBUG)
printk("br_receive_frame: ");
@@ -1138,22 +1201,23 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
skb->arp = 1; /* Received frame so it is resolved */
skb->h.raw = skb->mac.raw;
+ eth = skb->mac.ethernet;
if (br_stats.flags & BR_DEBUG)
printk("port %i src %02x:%02x:%02x:%02x:%02x:%02x\
dest %02x:%02x:%02x:%02x:%02x:%02x\n",
port,
- skb->h.eth->h_source[0],
- skb->h.eth->h_source[1],
- skb->h.eth->h_source[2],
- skb->h.eth->h_source[3],
- skb->h.eth->h_source[4],
- skb->h.eth->h_source[5],
- skb->h.eth->h_dest[0],
- skb->h.eth->h_dest[1],
- skb->h.eth->h_dest[2],
- skb->h.eth->h_dest[3],
- skb->h.eth->h_dest[4],
- skb->h.eth->h_dest[5]);
+ eth->h_source[0],
+ eth->h_source[1],
+ eth->h_source[2],
+ eth->h_source[3],
+ eth->h_source[4],
+ eth->h_source[5],
+ eth->h_dest[0],
+ eth->h_dest[1],
+ eth->h_dest[2],
+ eth->h_dest[3],
+ eth->h_dest[4],
+ eth->h_dest[5]);
if (!port) {
if(br_stats.flags&BR_DEBUG)
@@ -1168,7 +1232,7 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
/* fall through */
case Listening:
/* process BPDUs */
- if (memcmp(skb->h.eth->h_dest, bridge_ula, 6) == 0) {
+ if (memcmp(eth->h_dest, bridge_ula, 6) == 0) {
br_bpdu(skb);
return(1); /* br_bpdu consumes skb */
}
@@ -1185,7 +1249,7 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
case Forwarding:
(void) br_learn(skb, port); /* 3.8 */
/* process BPDUs */
- if (memcmp(skb->h.eth->h_dest, bridge_ula,
+ if (memcmp(eth->h_dest, bridge_ula,
ETH_ALEN) == 0)
{
/*printk("frame bpdu processor for me!!!\n");*/
@@ -1193,7 +1257,7 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
return(1); /* br_bpdu consumes skb */
}
/* is frame for me? */
- if (memcmp(skb->h.eth->h_dest,
+ if (memcmp(eth->h_dest,
port_info[port].dev->dev_addr,
ETH_ALEN) == 0)
{
@@ -1202,7 +1266,13 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
return(0); /* pass frame up our stack (this will */
/* happen in net_bh() in dev.c) */
}
- /* ok, forward this frame... */
+ /* Now this frame came from one of bridged
+ ports, and it appears to be not for me;
+ this means we should attempt to forward it.
+ But actually this frame can still be for me
+ [as well] if it is destined to one of our
+ multicast groups. br_forward() will not
+ consume the frame if this is the case */
return(br_forward(skb, port));
default:
printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n",
@@ -1220,6 +1290,7 @@ int br_receive_frame(struct sk_buff *skb) /* 3.5 */
int br_tx_frame(struct sk_buff *skb) /* 3.5 */
{
int port;
+ struct ethhdr *eth;
/* sanity */
if (!skb)
@@ -1238,24 +1309,30 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */
if (skb->dev->flags & IFF_LOOPBACK)
return(0);
- skb->h.raw = skb->data;
+ /* if bridging is not enabled on the port we are going to send
+ to, we have nothing to do with this frame, hands off */
+ if (! find_port(skb->dev))
+ return(0);
+
+ skb->mac.raw = skb->h.raw = skb->data;
+ eth = skb->mac.ethernet;
port = 0; /* an impossible port */
if (br_stats.flags & BR_DEBUG)
printk("br_tx_fr : port %i src %02x:%02x:%02x:%02x:%02x:%02x\
dest %02x:%02x:%02x:%02x:%02x:%02x\n",
port,
- skb->h.eth->h_source[0],
- skb->h.eth->h_source[1],
- skb->h.eth->h_source[2],
- skb->h.eth->h_source[3],
- skb->h.eth->h_source[4],
- skb->h.eth->h_source[5],
- skb->h.eth->h_dest[0],
- skb->h.eth->h_dest[1],
- skb->h.eth->h_dest[2],
- skb->h.eth->h_dest[3],
- skb->h.eth->h_dest[4],
- skb->h.eth->h_dest[5]);
+ eth->h_source[0],
+ eth->h_source[1],
+ eth->h_source[2],
+ eth->h_source[3],
+ eth->h_source[4],
+ eth->h_source[5],
+ eth->h_dest[0],
+ eth->h_dest[1],
+ eth->h_dest[2],
+ eth->h_dest[3],
+ eth->h_dest[4],
+ eth->h_dest[5]);
return(br_forward(skb, port));
}
@@ -1265,7 +1342,7 @@ int br_tx_frame(struct sk_buff *skb) /* 3.5 */
* state or lack of resources...
*/
-int br_learn(struct sk_buff *skb, int port) /* 3.8 */
+static int br_learn(struct sk_buff *skb, int port) /* 3.8 */
{
struct fdb *f;
@@ -1279,7 +1356,7 @@ int br_learn(struct sk_buff *skb, int port) /* 3.8 */
case Learning:
case Forwarding:
/* don't keep group addresses in the tree */
- if (skb->h.eth->h_source[0] & 0x01)
+ if (skb->mac.ethernet->h_source[0] & 0x01)
return(-1);
f = (struct fdb *)kmalloc(sizeof(struct fdb),
@@ -1290,7 +1367,7 @@ int br_learn(struct sk_buff *skb, int port) /* 3.8 */
return(-1);
}
f->port = port; /* source port */
- memcpy(f->ula, skb->h.eth->h_source, 6);
+ memcpy(f->ula, skb->mac.ethernet->h_source, 6);
f->timer = CURRENT_TIME;
f->flags = FDB_ENT_VALID;
/*
@@ -1314,7 +1391,7 @@ int br_learn(struct sk_buff *skb, int port) /* 3.8 */
* this routine always consumes the frame
*/
-int br_drop(struct sk_buff *skb)
+static int br_drop(struct sk_buff *skb)
{
kfree_skb(skb, 0);
return(1);
@@ -1324,18 +1401,22 @@ int br_drop(struct sk_buff *skb)
* this routine always consumes the frame
*/
-int br_dev_drop(struct sk_buff *skb)
+static int br_dev_drop(struct sk_buff *skb)
{
dev_kfree_skb(skb, 0);
return(1);
}
/*
+ * Forward the frame SKB to proper port[s]. PORT is the port that the
+ * frame has come from; we will not send the frame back there. PORT == 0
+ * means we have been called from br_tx_fr(), not from br_receive_frame().
+ *
* this routine returns 1 if it consumes the frame, 0
* if not...
*/
-int br_forward(struct sk_buff *skb, int port) /* 3.7 */
+static int br_forward(struct sk_buff *skb, int port) /* 3.7 */
{
struct fdb *f;
@@ -1346,7 +1427,7 @@ int br_forward(struct sk_buff *skb, int port) /* 3.7 */
* Multicast frames will also need to be seen
* by our upper layers.
*/
- if (skb->h.eth->h_dest[0] & 0x01)
+ if (skb->mac.ethernet->h_dest[0] & 0x01)
{
/* group address */
br_flood(skb, port);
@@ -1360,7 +1441,7 @@ int br_forward(struct sk_buff *skb, int port) /* 3.7 */
return(0);
} else {
/* locate port to forward to */
- f = br_avl_find_addr(skb->h.eth->h_dest);
+ f = br_avl_find_addr(skb->mac.ethernet->h_dest);
/*
* Send flood and drop.
*/
@@ -1400,7 +1481,8 @@ int br_forward(struct sk_buff *skb, int port) /* 3.7 */
/*
* We send this still locked
*/
- dev_queue_xmit(skb, skb->dev,1);
+ skb->priority = 1;
+ dev_queue_xmit(skb);
return(1); /* skb has been consumed */
} else {
/*
@@ -1417,7 +1499,7 @@ int br_forward(struct sk_buff *skb, int port) /* 3.7 */
* consumes the original frame.
*/
-int br_flood(struct sk_buff *skb, int port)
+static int br_flood(struct sk_buff *skb, int port)
{
int i;
struct sk_buff *nskb;
@@ -1441,13 +1523,14 @@ int br_flood(struct sk_buff *skb, int port)
/* printk("Flood to port %d\n",i);*/
nskb->h.raw = nskb->data + ETH_HLEN;
- dev_queue_xmit(nskb,nskb->dev,1);
+ nskb->priority = 1;
+ dev_queue_xmit(nskb);
}
}
return(0);
}
-int find_port(struct device *dev)
+static int find_port(struct device *dev)
{
int i;
@@ -1458,7 +1541,7 @@ int find_port(struct device *dev)
return(0);
}
-int br_port_cost(struct device *dev) /* 4.10.2 */
+static int br_port_cost(struct device *dev) /* 4.10.2 */
{
if (strncmp(dev->name, "eth", 3) == 0) /* ethernet */
return(100);
@@ -1473,7 +1556,7 @@ int br_port_cost(struct device *dev) /* 4.10.2 */
* this routine always consumes the skb
*/
-void br_bpdu(struct sk_buff *skb) /* consumes skb */
+static void br_bpdu(struct sk_buff *skb) /* consumes skb */
{
Tcn_bpdu *bpdu;
int port;
@@ -1602,7 +1685,7 @@ int br_ioctl(unsigned int cmd, void *arg)
return 0;
}
-int br_cmp(unsigned int *a, unsigned int *b)
+static int br_cmp(unsigned int *a, unsigned int *b)
{
int i;
for (i=0; i<2; i++)
@@ -1616,4 +1699,3 @@ int br_cmp(unsigned int *a, unsigned int *b)
}
return(0);
}
-
diff --git a/net/bridge/br_tree.c b/net/bridge/br_tree.c
index a1965d498..8234249c5 100644
--- a/net/bridge/br_tree.c
+++ b/net/bridge/br_tree.c
@@ -18,12 +18,12 @@
* <hayes@netplumbing.com>
*/
-struct fdb fdb_head;
-struct fdb *fhp = &fdb_head;
-struct fdb **fhpp = &fhp;
+static struct fdb fdb_head;
+static struct fdb *fhp = &fdb_head;
+static struct fdb **fhpp = &fhp;
static int fdb_inited = 0;
-int addr_cmp(unsigned char *a1, unsigned char *a2);
+static int addr_cmp(unsigned char *a1, unsigned char *a2);
/*
* fdb_head is the AVL tree corresponding to fdb
@@ -50,7 +50,7 @@ int addr_cmp(unsigned char *a1, unsigned char *a2);
* foreach node in tree->fdb_avl_right: node->fdb_avl_key >= tree->fdb_avl_key.
*/
-int
+static int
fdb_init(void)
{
fdb_head.fdb_avl_height = 0;
@@ -60,8 +60,7 @@ fdb_init(void)
return(0);
}
-struct fdb *
-br_avl_find_addr(unsigned char addr[6])
+struct fdb *br_avl_find_addr(unsigned char addr[6])
{
struct fdb * result = NULL;
struct fdb * tree;
@@ -109,14 +108,15 @@ br_avl_find_addr(unsigned char addr[6])
}
}
+
+#if (0)
/*
* Rebalance a tree.
* After inserting or deleting a node of a tree we have a sequence of subtrees
* nodes[0]..nodes[k-1] such that
* nodes[0] is the root and nodes[i+1] = nodes[i]->{fdb_avl_left|fdb_avl_right}.
*/
-static void
-br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
+static void br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
{
if (!fdb_inited)
fdb_init();
@@ -196,10 +196,10 @@ br_avl_rebalance (struct fdb *** nodeplaces_ptr, int count)
printk_avl(&fdb_head);
#endif /* DEBUG_AVL */
}
+#endif /* (0) */
/* Insert a node into a tree. */
-int
-br_avl_insert (struct fdb * new_node)
+int br_avl_insert (struct fdb * new_node)
{
struct fdb ** nodeplace = fhpp;
struct fdb ** stack[avl_maxheight];
@@ -248,9 +248,10 @@ br_avl_insert (struct fdb * new_node)
return(1);
}
+
+#if (0)
/* Removes a node out of a tree. */
-int
-br_avl_remove (struct fdb * node_to_delete)
+static int br_avl_remove (struct fdb * node_to_delete)
{
struct fdb ** nodeplace = fhpp;
struct fdb ** stack[avl_maxheight];
@@ -301,6 +302,7 @@ br_avl_remove (struct fdb * node_to_delete)
br_avl_rebalance(stack_ptr,stack_count);
return(0);
}
+#endif /* (0) */
#ifdef DEBUG_AVL
@@ -388,8 +390,7 @@ static void avl_checkorder (struct fdb * tree)
#endif /* (0) */
#endif /* DEBUG_AVL */
-int
-addr_cmp(unsigned char a1[], unsigned char a2[])
+static int addr_cmp(unsigned char a1[], unsigned char a2[])
{
int i;
diff --git a/net/core/Makefile b/net/core/Makefile
index c4216d0e9..b7efbe6b4 100644
--- a/net/core/Makefile
+++ b/net/core/Makefile
@@ -9,7 +9,12 @@
O_TARGET := core.o
-O_OBJS := sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o
+O_OBJS := sock.o skbuff.o iovec.o datagram.o dst.o scm.o \
+ neighbour.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_core.o
+endif
ifdef CONFIG_NET
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 74f10f8a5..70c939dbd 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -3,17 +3,17 @@
*
* Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
* of these would make sense. Not tonight however 8-).
- * This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical select code and mostly
- * identical recvmsg() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
+ * This is used because UDP, RAW, PACKET, DDP, IPX, AX.25 and NetROM layer all have identical poll code and mostly
+ * identical recvmsg() code. So we share it here. The poll was shared before but buried in udp.c so I moved it.
*
- * Authors: Alan Cox <alan@cymru.net>. (datagram_select() from old udp.c code)
+ * Authors: Alan Cox <alan@cymru.net>. (datagram_poll() from old udp.c code)
*
* Fixes:
* Alan Cox : NULL return from skb_peek_copy() understood
* Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
* Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
* AX.25 now works right, and SPX is feasible.
- * Alan Cox : Fixed write select of non IP protocol crash.
+ * Alan Cox : Fixed write poll of non IP protocol crash.
* Florian La Roche: Changed for my new skbuff handling.
* Darryl Miles : Fixed non-blocking SOCK_SEQPACKET.
* Linus Torvalds : BSD semantic fixes.
@@ -34,6 +34,8 @@
#include <linux/sched.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
+#include <linux/poll.h>
+
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
@@ -121,7 +123,7 @@ restart:
goto no_packet;
wait_for_packet(sk);
- }
+ }
/* Again only user level code calls this function, so nothing interrupt level
will suddenly eat the receive_queue */
@@ -132,7 +134,7 @@ restart:
cli();
skb=skb_peek(&sk->receive_queue);
if(skb!=NULL)
- skb->users++;
+ atomic_inc(&skb->users);
restore_flags(flags);
if(skb==NULL) /* shouldn't happen but .. */
goto restart;
@@ -141,7 +143,6 @@ restart:
skb = skb_dequeue(&sk->receive_queue);
if (!skb) /* Avoid race if someone beats us to the data */
goto restart;
- skb->users++;
return skb;
no_packet:
@@ -152,18 +153,7 @@ no_packet:
void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- skb->users--;
- if(skb->users <= 0) {
- /* See if it needs destroying */
- /* Been dequeued by someone - ie it's read */
- if(!skb->next && !skb->prev)
- kfree_skb(skb,FREE_READ);
- }
- restore_flags(flags);
+ kfree_skb(skb, FREE_READ);
release_sock(sk);
}
@@ -200,54 +190,48 @@ int skb_copy_datagram_iovec(struct sk_buff *skb, int offset, struct iovec *to,
}
/*
- * Datagram select: Again totally generic. Moved from udp.c
- * Now does seqpacket.
+ * Datagram poll: Again totally generic. This also handles
+ * sequenced packet sockets providing the socket receive queue
+ * is only ever holding data ready to receive.
*/
-int datagram_select(struct sock *sk, int sel_type, select_table *wait)
+unsigned int datagram_poll(struct socket *sock, poll_table *wait)
{
+ struct sock *sk = sock->sk;
+ unsigned int mask;
+
+ poll_wait(sk->sleep, wait);
+ mask = 0;
+
+ /* exceptional events? */
if (sk->err)
- return 1;
- switch(sel_type)
- {
- case SEL_IN:
- if (sk->shutdown & RCV_SHUTDOWN)
- return 1;
- if (connection_based(sk) && sk->state==TCP_CLOSE)
- {
- /* Connection closed: Wake up */
- return 1;
- }
- if (!skb_queue_empty(&sk->receive_queue))
- { /* This appears to be consistent
- with other stacks */
- return 1;
- }
- break;
-
- case SEL_OUT:
- if (sk->shutdown & SEND_SHUTDOWN)
- return 1;
- if (connection_based(sk) && sk->state==TCP_SYN_SENT)
- {
- /* Connection still in progress */
- break;
- }
- if (sk->prot && sock_wspace(sk) >= MIN_WRITE_SPACE)
- {
- return 1;
- }
- if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
- {
- return 1;
- }
- break;
-
- case SEL_EX:
- break;
+ mask |= POLLERR;
+ if (sk->shutdown & RCV_SHUTDOWN)
+ mask |= POLLHUP;
+
+ /* readable? */
+ if (!skb_queue_empty(&sk->receive_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ /* Connection-based need to check for termination and startup */
+ if (connection_based(sk)) {
+ if (sk->state==TCP_CLOSE)
+ mask |= POLLHUP;
+ /* connection hasn't started yet? */
+ if (sk->state == TCP_SYN_SENT)
+ return mask;
+ }
+
+ /* writable? */
+ if (!(sk->shutdown & SEND_SHUTDOWN)) {
+ if (sk->prot) {
+ if (sock_wspace(sk) >= MIN_WRITE_SPACE)
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ } else {
+ if (sk->sndbuf - atomic_read(&sk->wmem_alloc) >= MIN_WRITE_SPACE)
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+ }
}
- /* select failed.. */
- select_wait(sk->sleep, wait);
- return 0;
+ return mask;
}
diff --git a/net/core/dev.c b/net/core/dev.c
index f6bc01f9d..c02d4052e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -78,26 +78,52 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <net/br.h>
-#ifdef CONFIG_NET_ALIAS
#include <linux/net_alias.h>
-#endif
+#include <linux/init.h>
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
+#ifdef CONFIG_PLIP
+extern int plip_init(void);
+#endif
+
+/*
+ * The list of devices, that are able to output.
+ */
+
+static struct device *dev_up_base;
/*
* The list of packet types we will receive (as opposed to discard)
* and the routines to invoke.
+ *
+ * Why 16. Because with 16 the only overlap we get on a hash of the
+ * low nibble of the protocol value is RARP/SNAP/X.25.
+ *
+ * 0800 IP
+ * 0001 802.3
+ * 0002 AX.25
+ * 0004 802.2
+ * 8035 RARP
+ * 0005 SNAP
+ * 0805 X.25
+ * 0806 ARP
+ * 8137 IPX
+ * 0009 Localtalk
+ * 86DD IPv6
*/
-struct packet_type *ptype_base[16];
+struct packet_type *ptype_base[16]; /* 16 way hashed list */
struct packet_type *ptype_all = NULL; /* Taps */
/*
* Device list lock
*/
-int dev_lockct=0;
+atomic_t dev_lockct = ATOMIC_INIT(0);
/*
* Our notifier list
@@ -202,14 +228,79 @@ struct device *dev_get(const char *name)
}
return NULL;
}
-
+
+struct device * dev_get_by_index(int ifindex)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->ifindex == ifindex)
+ return(dev);
+ }
+ return NULL;
+}
+
+struct device *dev_getbyhwaddr(unsigned short type, char *ha)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->type == type &&
+ !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)) &&
+ memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
+ return(dev);
+ }
+ return(NULL);
+}
+
+/*
+ * Passed a format string - eg "lt%d" it will try and find a suitable
+ * id. Not efficient for many devices, not called a lot..
+ */
+
+int dev_alloc_name(struct device *dev, const char *name)
+{
+ int i;
+ /*
+ * If you need over 100 please also fix the algorithm...
+ */
+ for(i=0;i<100;i++)
+ {
+ sprintf(dev->name,name,i);
+ if(dev_get(dev->name)==NULL)
+ return i;
+ }
+ return -ENFILE; /* Over 100 of the things .. bail out! */
+}
+
+struct device *dev_alloc(const char *name, int *err)
+{
+ struct device *dev=kmalloc(sizeof(struct device)+16, GFP_KERNEL);
+ if(dev==NULL)
+ {
+ *err=-ENOBUFS;
+ return NULL;
+ }
+ dev->name=(char *)(dev+1); /* Name string space */
+ *err=dev_alloc_name(dev,name);
+ if(*err<0)
+ {
+ kfree(dev);
+ return NULL;
+ }
+ return dev;
+}
+
+
/*
* Find and possibly load an interface.
*/
#ifdef CONFIG_KERNELD
-extern __inline__ void dev_load(const char *name)
+void dev_load(const char *name)
{
if(!dev_get(name)) {
#ifdef CONFIG_NET_ALIAS
@@ -230,11 +321,12 @@ extern __inline__ void dev_load(const char *name)
int dev_open(struct device *dev)
{
- int ret = -ENODEV;
+ int ret = 0;
/*
* Call device private open method
*/
+
if (dev->open)
ret = dev->open(dev);
@@ -250,6 +342,18 @@ int dev_open(struct device *dev)
*/
dev_mc_upload(dev);
notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+
+ /*
+ * Passive non transmitting devices (including
+ * aliases) need not be on this chain.
+ */
+ if (!net_alias_is(dev) && dev->tx_queue_len)
+ {
+ cli();
+ dev->next_up = dev_up_base;
+ dev_up_base = dev;
+ sti();
+ }
}
return(ret);
}
@@ -262,6 +366,7 @@ int dev_open(struct device *dev)
int dev_close(struct device *dev)
{
int ct=0;
+ struct device **devp;
/*
* Call the device specific close. This cannot fail.
@@ -280,6 +385,7 @@ int dev_close(struct device *dev)
/*
* Tell people we are going down
*/
+
notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
/*
* Flush the multicast chain
@@ -293,10 +399,24 @@ int dev_close(struct device *dev)
{
struct sk_buff *skb;
while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
- if(skb->free)
- kfree_skb(skb,FREE_WRITE);
+ kfree_skb(skb,FREE_WRITE);
ct++;
}
+
+ /*
+ * The device is no longer up. Drop it from the list.
+ */
+
+ devp = &dev_up_base;
+ while (*devp)
+ {
+ if (*devp == dev)
+ {
+ *devp = dev->next_up;
+ break;
+ }
+ devp = &(*devp)->next_up;
+ }
return(0);
}
@@ -317,6 +437,34 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
}
/*
+ * Support routine. Sends outgoing frames to any network
+ * taps currently in use.
+ */
+
+static void queue_xmit_nit(struct sk_buff *skb, struct device *dev)
+{
+ struct packet_type *ptype;
+ get_fast_time(&skb->stamp);
+
+ for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
+ {
+ /* Never send packets back to the socket
+ * they originated from - MvS (miquels@drinkel.ow.org)
+ */
+ if ((ptype->dev == dev || !ptype->dev) &&
+ ((struct sock *)ptype->data != skb->sk))
+ {
+ struct sk_buff *skb2;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+ skb2->mac.raw = skb2->data;
+ skb2->nh.raw = skb2->h.raw = skb2->data + dev->hard_header_len;
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ }
+}
+
+/*
* Send (or queue for sending) a packet.
*
* IMPORTANT: When this is called to resend frames. The caller MUST
@@ -332,13 +480,6 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
/* at the front or the back of the */
/* queue - front is a retransmit try */
- if(pri>=0 && !skb_device_locked(skb))
- skb_device_lock(skb); /* Shove a lock on the frame */
-#if CONFIG_SKB_CHECK
- IS_SKB(skb);
-#endif
- skb->dev = dev;
-
/*
* Negative priority is used to flag a frame that is being pulled from the
* queue front as a retransmit attempt. It therefore goes back on the queue
@@ -354,34 +495,12 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
#ifdef CONFIG_NET_DEBUG
if (pri >= DEV_NUMBUFFS)
{
- printk(KERN_WARNING "bad priority in dev_queue_xmit.\n");
+ printk(KERN_WARNING "bad priority in do_dev_queue_xmit.\n");
pri = 1;
}
#endif
/*
- * If the address has not been resolved. Call the device header rebuilder.
- * This can cover all protocols and technically not just ARP either.
- */
-
- if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
- return;
- }
-
- /*
- *
- * If dev is an alias, switch to its main device.
- * "arp" resolution has been made with alias device, so
- * arp entries refer to alias, not main.
- *
- */
-
-#ifdef CONFIG_NET_ALIAS
- if (net_alias_is(dev))
- skb->dev = dev = net_alias_main_dev(dev);
-#endif
-
- /*
* If we are bridging and this is directly generated output
* pass the frame via the bridge.
*/
@@ -397,46 +516,34 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
list = dev->buffs + pri;
save_flags(flags);
- /* if this isn't a retransmission, use the first packet instead... */
- if (!retransmission) {
- if (skb_queue_len(list)) {
- /* avoid overrunning the device queue.. */
- if (skb_queue_len(list) > dev->tx_queue_len) {
- dev_kfree_skb(skb, FREE_WRITE);
- return;
- }
- }
- /* copy outgoing packets to any sniffer packet handlers */
- if (dev_nit) {
- struct packet_type *ptype;
-
- get_fast_time(&skb->stamp);
+ /*
+ * If this isn't a retransmission, use the first packet instead.
+ * Note: We don't do strict priority ordering here. We will in
+ * fact kick the queue that is our priority. The dev_tint reload
+ * does strict priority queueing. In effect what we are doing here
+ * is to add some random jitter to the queues and to do so by
+ * saving clocks. Doing a perfect priority queue isn't a good idea
+ * as you get some fascinating timing interactions.
+ */
- for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
- {
- /* Never send packets back to the socket
- * they originated from - MvS (miquels@drinkel.ow.org)
- */
- if ((ptype->dev == dev || !ptype->dev) &&
- ((struct sock *)ptype->data != skb->sk))
- {
- struct sk_buff *skb2;
- if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
- break;
- skb2->h.raw = skb2->data + dev->hard_header_len;
- skb2->mac.raw = skb2->data;
- ptype->func(skb2, skb->dev, ptype);
- }
- }
+ if (!retransmission)
+ {
+ /* avoid overrunning the device queue.. */
+ if (skb_queue_len(list) > dev->tx_queue_len)
+ {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return;
}
+ /* copy outgoing packets to any sniffer packet handlers */
+ if (dev_nit)
+ queue_xmit_nit(skb,dev);
+
if (skb_queue_len(list)) {
cli();
- skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */
__skb_queue_tail(list, skb);
skb = __skb_dequeue(list);
- skb_device_lock(skb); /* New buffer needs locking down */
restore_flags(flags);
}
}
@@ -452,22 +559,82 @@ static void do_dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
* no longer device locked (it can be freed safely from the device queue)
*/
cli();
- skb_device_unlock(skb);
__skb_queue_head(list,skb);
restore_flags(flags);
}
-void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+/*
+ * Entry point for transmitting frames.
+ */
+
+int dev_queue_xmit(struct sk_buff *skb)
{
+ struct device *dev = skb->dev;
+
start_bh_atomic();
- do_dev_queue_xmit(skb, dev, pri);
+
+ /*
+ * If the address has not been resolved. Call the device header rebuilder.
+ * This can cover all protocols and technically not just ARP either.
+ */
+
+ if (!skb->arp)
+ {
+ /*
+ * FIXME: we should make the printk for no rebuild
+ * header a default rebuild_header routine and drop
+ * this call. Similarly we should make hard_header
+ * have a default NULL operation not check conditions.
+ */
+ if (dev->rebuild_header)
+ {
+ if (dev->rebuild_header(skb))
+ {
+ end_bh_atomic();
+ return 0;
+ }
+ }
+ else
+ printk(KERN_DEBUG "%s: !skb->arp & !rebuild_header!\n", dev->name);
+ }
+
+ /*
+ *
+ * If dev is an alias, switch to its main device.
+ * "arp" resolution has been made with alias device, so
+ * arp entries refer to alias, not main.
+ *
+ */
+
+ if (net_alias_is(dev))
+ skb->dev = dev = net_alias_main_dev(dev);
+
+ do_dev_queue_xmit(skb, dev, skb->priority);
end_bh_atomic();
+ return 0;
}
/*
+ * Fast path for loopback frames.
+ */
+
+void dev_loopback_xmit(struct sk_buff *skb)
+{
+ struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+ if (newskb==NULL)
+ return;
+
+ skb_pull(newskb, newskb->nh.raw - newskb->data);
+ newskb->ip_summed = CHECKSUM_UNNECESSARY;
+ if (newskb->dst==NULL)
+ printk(KERN_DEBUG "BUG: packet without dst looped back 1\n");
+ netif_rx(newskb);
+}
+
+
+/*
* Receive a packet from a device driver and queue it for the upper
- * (protocol) levels. It always succeeds. This is the recommended
- * interface to use.
+ * (protocol) levels. It always succeeds.
*/
void netif_rx(struct sk_buff *skb)
@@ -481,7 +648,6 @@ void netif_rx(struct sk_buff *skb)
*/
skb->sk = NULL;
- skb->free = 1;
if(skb->stamp.tv_sec==0)
get_fast_time(&skb->stamp);
@@ -503,9 +669,7 @@ void netif_rx(struct sk_buff *skb)
/*
* Add it to the "backlog" queue.
*/
-#if CONFIG_SKB_CHECK
- IS_SKB(skb);
-#endif
+
skb_queue_tail(&backlog,skb);
backlog_size++;
@@ -526,9 +690,10 @@ static void dev_transmit(void)
{
struct device *dev;
- for (dev = dev_base; dev != NULL; dev = dev->next)
+ for (dev = dev_up_base; dev != NULL; dev = dev->next_up)
{
- if (dev->flags != 0 && !dev->tbusy) {
+ if (dev->flags != 0 && !dev->tbusy)
+ {
/*
* Kick the device
*/
@@ -557,6 +722,7 @@ void net_bh(void)
struct packet_type *ptype;
struct packet_type *pt_prev;
unsigned short type;
+ int nit = 301;
/*
* Can we send anything now? We want to clear the
@@ -581,7 +747,8 @@ void net_bh(void)
* disabling interrupts.
*/
- while (!skb_queue_empty(&backlog)) {
+ while (!skb_queue_empty(&backlog))
+ {
struct sk_buff * skb = backlog.next;
/*
@@ -591,8 +758,18 @@ void net_bh(void)
__skb_unlink(skb, &backlog);
backlog_size--;
sti();
-
+ /*
+ * We do not want to spin in net_bh infinitely. --ANK
+ */
+ if (--nit <= 0)
+ {
+ if (nit == 0)
+ printk(KERN_WARNING "net_bh: too many loops, dropping...\n");
+ kfree_skb(skb, FREE_WRITE);
+ continue;
+ }
+
#ifdef CONFIG_BRIDGE
/*
@@ -629,10 +806,11 @@ void net_bh(void)
* Bump the pointer to the next structure.
*
* On entry to the protocol layer. skb->data and
- * skb->h.raw point to the MAC and encapsulated data
+ * skb->nh.raw point to the MAC and encapsulated data
*/
- skb->h.raw = skb->data;
+ /* XXX until we figure out every place to modify.. */
+ skb->h.raw = skb->nh.raw = skb->data;
/*
* Fetch the packet protocol ID.
@@ -715,9 +893,7 @@ void net_bh(void)
* One last output flush.
*/
-#ifdef XMIT_AFTER
dev_transmit();
-#endif
}
@@ -736,9 +912,11 @@ void dev_tint(struct device *dev)
* aliases do not transmit (for now :) )
*/
-#ifdef CONFIG_NET_ALIAS
- if (net_alias_is(dev)) return;
-#endif
+ if (net_alias_is(dev)) {
+ printk(KERN_DEBUG "net alias %s transmits\n", dev->name);
+ return;
+ }
+
head = dev->buffs;
save_flags(flags);
cli();
@@ -757,7 +935,6 @@ void dev_tint(struct device *dev)
/*
* Stop anyone freeing the buffer while we retransmit it
*/
- skb_device_lock(skb);
restore_flags(flags);
/*
* Feed them to the output stage and if it fails
@@ -778,7 +955,7 @@ void dev_tint(struct device *dev)
/*
* Perform a SIOCGIFCONF call. This structure will change
- * size shortly, and there is nothing I can do about it.
+ * size eventually, and there is nothing I can do about it.
* Thus we will need a 'compatibility mode'.
*/
@@ -812,8 +989,6 @@ static int dev_ifconf(char *arg)
for (dev = dev_base; dev != NULL; dev = dev->next)
{
- if(!(dev->flags & IFF_UP)) /* Downed devices don't count */
- continue;
/*
* Have we run out of space here ?
*/
@@ -864,17 +1039,19 @@ static int dev_ifconf(char *arg)
#ifdef CONFIG_PROC_FS
static int sprintf_stats(char *buffer, struct device *dev)
{
- struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+ struct net_device_stats *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
int size;
if (stats)
- size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
+ size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu\n",
dev->name,
+ stats->rx_bytes,
stats->rx_packets, stats->rx_errors,
stats->rx_dropped + stats->rx_missed_errors,
stats->rx_fifo_errors,
stats->rx_length_errors + stats->rx_over_errors
+ stats->rx_crc_errors + stats->rx_frame_errors,
+ stats->tx_bytes,
stats->tx_packets, stats->tx_errors, stats->tx_dropped,
stats->tx_fifo_errors, stats->collisions,
stats->tx_carrier_errors + stats->tx_aborted_errors
@@ -901,7 +1078,7 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy
size = sprintf(buffer, "Inter-| Receive | Transmit\n"
- " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
+ " face |bytes packets errs drop fifo frame|bytes packets errs drop fifo colls carrier\n");
pos+=size;
len+=size;
@@ -931,19 +1108,87 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy
#endif /* CONFIG_PROC_FS */
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+
/*
- * This checks bitmasks for the ioctl calls for devices.
+ * Print one entry of /proc/net/wireless
+ * This is a clone of /proc/net/dev (just above)
*/
-
-static inline int bad_mask(unsigned long mask, unsigned long addr)
+static int sprintf_wireless_stats(char *buffer, struct device *dev)
{
- if (addr & (mask = ~mask))
- return 1;
- mask = ntohl(mask);
- if (mask & (mask+1))
- return 1;
- return 0;
+ /* Get stats from the driver */
+ struct iw_statistics *stats = (dev->get_wireless_stats ?
+ dev->get_wireless_stats(dev) :
+ (struct iw_statistics *) NULL);
+ int size;
+
+ if(stats != (struct iw_statistics *) NULL)
+ size = sprintf(buffer,
+ "%6s: %02x %3d%c %3d%c %3d%c %5d %5d %5d\n",
+ dev->name,
+ stats->status,
+ stats->qual.qual,
+ stats->qual.updated & 1 ? '.' : ' ',
+ stats->qual.level,
+ stats->qual.updated & 2 ? '.' : ' ',
+ stats->qual.noise,
+ stats->qual.updated & 3 ? '.' : ' ',
+ stats->discard.nwid,
+ stats->discard.code,
+ stats->discard.misc);
+ else
+ size = 0;
+
+ return size;
+}
+
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ * This is a clone of /proc/net/dev (just above)
+ */
+int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+ int length, int dummy)
+{
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ int size;
+
+ struct device * dev;
+
+ size = sprintf(buffer,
+ "Inter-|sta| Quality | Discarded packets\n"
+ " face |tus|link level noise| nwid crypt misc\n");
+
+ pos+=size;
+ len+=size;
+
+ for(dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_wireless_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos < offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos > offset + length)
+ break;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if(len > length)
+ len = length; /* Ending slop */
+
+ return len;
}
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
/*
* Perform the SIOCxIFxxx calls.
@@ -1004,6 +1249,7 @@ static int dev_ifsioc(void *arg, unsigned int getset)
*/
dev_lock_wait();
+ dev_lock_list();
/*
* Set the flags on our device.
@@ -1044,134 +1290,21 @@ static int dev_ifsioc(void *arg, unsigned int getset)
*/
dev_mc_upload(dev);
- }
- break;
-
- case SIOCGIFADDR: /* Get interface address (and family) */
- if(ifr.ifr_addr.sa_family==AF_UNSPEC)
- {
- memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
- ifr.ifr_hwaddr.sa_family=dev->type;
- goto rarok;
- }
- else
- {
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_port = 0;
- }
- goto rarok;
-
- case SIOCSIFADDR: /* Set interface address (and family) */
-
- /*
- * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
- * physical address. We can cope with this now.
- */
-
- if(ifr.ifr_addr.sa_family==AF_UNSPEC)
- {
- if(dev->set_mac_address==NULL)
- return -EOPNOTSUPP;
- ret=dev->set_mac_address(dev,&ifr.ifr_addr);
- }
- else
- {
- u32 new_pa_addr = (*(struct sockaddr_in *)
- &ifr.ifr_addr).sin_addr.s_addr;
- u16 new_family = ifr.ifr_addr.sa_family;
-
- if (new_family == dev->family &&
- new_pa_addr == dev->pa_addr) {
- ret =0;
- break;
+ if ((dev->flags&IFF_UP) && ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC)))
+ {
+ printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
}
- if (dev->flags & IFF_UP)
- notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
-
- /*
- * if dev is an alias, must rehash to update
- * address change
- */
-
-#ifdef CONFIG_NET_ALIAS
- if (net_alias_is(dev))
- net_alias_dev_rehash(dev ,&ifr.ifr_addr);
-#endif
- dev->pa_addr = new_pa_addr;
- dev->family = new_family;
-
-#ifdef CONFIG_INET
- /* This is naughty. When net-032e comes out It wants moving into the net032
- code not the kernel. Till then it can sit here (SIGH) */
- if (!dev->pa_mask)
- dev->pa_mask = ip_get_mask(dev->pa_addr);
-#endif
- if (!dev->pa_brdaddr)
- dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
- if (dev->flags & IFF_UP)
- notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
- ret = 0;
- }
- break;
-
- case SIOCGIFBRDADDR: /* Get the broadcast address */
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_port = 0;
- goto rarok;
-
- case SIOCSIFBRDADDR: /* Set the broadcast address */
- dev->pa_brdaddr = (*(struct sockaddr_in *)
- &ifr.ifr_broadaddr).sin_addr.s_addr;
- ret = 0;
- break;
-
- case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
- (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
- (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_port = 0;
- goto rarok;
-
- case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
- dev->pa_dstaddr = (*(struct sockaddr_in *)
- &ifr.ifr_dstaddr).sin_addr.s_addr;
- ret = 0;
- break;
-
- case SIOCGIFNETMASK: /* Get the netmask for the interface */
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_family = dev->family;
- (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_port = 0;
- goto rarok;
-
- case SIOCSIFNETMASK: /* Set the netmask for the interface */
- {
- unsigned long mask = (*(struct sockaddr_in *)
- &ifr.ifr_netmask).sin_addr.s_addr;
- ret = -EINVAL;
- /*
- * The mask we set must be legal.
- */
- if (bad_mask(mask,0))
- break;
- dev->pa_mask = mask;
- ret = 0;
+ if ((dev->flags^old_flags)&IFF_PROMISC) {
+ if (dev->flags&IFF_PROMISC)
+ printk(KERN_INFO "%s enters promiscuous mode.\n", dev->name);
+ else
+ printk(KERN_INFO "%s leave promiscuous mode.\n", dev->name);
+ }
+ dev_unlock_list();
}
break;
-
+
case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
ifr.ifr_metric = dev->metric;
@@ -1188,6 +1321,11 @@ static int dev_ifsioc(void *arg, unsigned int getset)
case SIOCSIFMTU: /* Set the MTU of a device */
+ if (ifr.ifr_mtu == dev->mtu) {
+ ret = 0;
+ break;
+ }
+
/*
* MTU must be positive.
*/
@@ -1202,6 +1340,10 @@ static int dev_ifsioc(void *arg, unsigned int getset)
dev->mtu = ifr.ifr_mtu;
ret = 0;
}
+ if (!ret && dev->flags&IFF_UP) {
+ printk(KERN_DEBUG "SIFMTU %s(%s)\n", dev->name, current->comm);
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEMTU, dev);
+ }
break;
case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
@@ -1224,6 +1366,8 @@ static int dev_ifsioc(void *arg, unsigned int getset)
if(ifr.ifr_hwaddr.sa_family!=dev->type)
return -EINVAL;
ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr);
+ if (!ret)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
break;
case SIOCGIFMAP:
@@ -1255,6 +1399,12 @@ static int dev_ifsioc(void *arg, unsigned int getset)
return -EINVAL;
dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
return 0;
+
+ case SIOGIFINDEX:
+ ifr.ifr_ifindex = dev->ifindex;
+ goto rarok;
+
+
/*
* Unknown or private ioctl
*/
@@ -1273,7 +1423,27 @@ static int dev_ifsioc(void *arg, unsigned int getset)
}
break;
}
-
+
+#ifdef CONFIG_NET_RADIO
+ if((getset >= SIOCIWFIRST) && (getset <= SIOCIWLAST))
+ {
+ if(dev->do_ioctl==NULL)
+ return -EOPNOTSUPP;
+ /* Perform the ioctl */
+ ret=dev->do_ioctl(dev, &ifr, getset);
+ /* If return args... */
+ if(IW_IS_GET(getset))
+ {
+ if (copy_to_user(arg, &ifr,
+ sizeof(struct ifreq)))
+ {
+ ret = -EFAULT;
+ }
+ }
+ break;
+ }
+#endif /* CONFIG_NET_RADIO */
+
ret = -EINVAL;
}
return(ret);
@@ -1306,10 +1476,6 @@ int dev_ioctl(unsigned int cmd, void *arg)
*/
case SIOCGIFFLAGS:
- case SIOCGIFADDR:
- case SIOCGIFDSTADDR:
- case SIOCGIFBRDADDR:
- case SIOCGIFNETMASK:
case SIOCGIFMETRIC:
case SIOCGIFMTU:
case SIOCGIFMEM:
@@ -1317,6 +1483,7 @@ int dev_ioctl(unsigned int cmd, void *arg)
case SIOCSIFHWADDR:
case SIOCGIFSLAVE:
case SIOCGIFMAP:
+ case SIOGIFINDEX:
return dev_ifsioc(arg, cmd);
/*
@@ -1324,10 +1491,6 @@ int dev_ioctl(unsigned int cmd, void *arg)
*/
case SIOCSIFFLAGS:
- case SIOCSIFADDR:
- case SIOCSIFDSTADDR:
- case SIOCSIFBRDADDR:
- case SIOCSIFNETMASK:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
case SIOCSIFMEM:
@@ -1351,10 +1514,23 @@ int dev_ioctl(unsigned int cmd, void *arg)
(cmd <= (SIOCDEVPRIVATE + 15))) {
return dev_ifsioc(arg, cmd);
}
+#ifdef CONFIG_NET_RADIO
+ if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
+ {
+ if((IW_IS_SET(cmd)) && (!suser()))
+ return -EPERM;
+ return dev_ifsioc(arg, cmd);
+ }
+#endif /* CONFIG_NET_RADIO */
return -EINVAL;
}
}
+int dev_new_index()
+{
+ static int ifindex;
+ return ++ifindex;
+}
/*
* Initialize the DEV module. At boot time this walks the device list and
@@ -1363,7 +1539,6 @@ int dev_ioctl(unsigned int cmd, void *arg)
*
*/
extern int lance_init(void);
-extern int ni65_init(void);
extern int pi_init(void);
extern int bpq_init(void);
extern int scc_init(void);
@@ -1372,6 +1547,7 @@ extern void dlci_setup(void);
extern int pt_init(void);
extern int sm_init(void);
extern int baycom_init(void);
+extern int lapbeth_init(void);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_net_dev = {
@@ -1382,7 +1558,18 @@ static struct proc_dir_entry proc_net_dev = {
};
#endif
-int net_dev_init(void)
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_wireless = {
+ PROC_NET_WIRELESS, 8, "wireless",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dev_get_wireless_info
+};
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
+__initfunc(int net_dev_init(void))
{
struct device *dev, **dp;
@@ -1432,6 +1619,12 @@ int net_dev_init(void)
#if defined(CONFIG_SOUNDMODEM)
sm_init();
#endif
+#if defined(CONFIG_LAPBETHER)
+ lapbeth_init();
+#endif
+#if defined(CONFIG_PLIP)
+ plip_init();
+#endif
/*
* SLHC if present needs attaching so other people see it
* even if not opened.
@@ -1467,6 +1660,7 @@ int net_dev_init(void)
else
{
dp = &dev->next;
+ dev->ifindex = dev_new_index();
}
}
@@ -1474,6 +1668,12 @@ int net_dev_init(void)
proc_net_register(&proc_net_dev);
#endif
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_wireless);
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
/*
* Initialise net_alias engine
*
diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c
index 183d3fc3b..4aa6cbb0c 100644
--- a/net/core/dev_mcast.c
+++ b/net/core/dev_mcast.c
@@ -42,16 +42,17 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
+#include <linux/net_alias.h>
/*
- * Device multicast list maintenance. This knows about such little matters as promiscuous mode and
- * converting from the list to the array the drivers use. At least until I fix the drivers up.
+ * Device multicast list maintenance.
*
- * This is used both by IP and by the user level maintenance functions. Unlike BSD we maintain a usage count
- * on a given multicast address so that a casual user application can add/delete multicasts used by protocols
- * without doing damage to the protocols when it deletes the entries. It also helps IP as it tracks overlapping
- * maps.
+ * This is used both by IP and by the user level maintenance functions.
+ * Unlike BSD we maintain a usage count on a given multicast address so
+ * that a casual user application can add/delete multicasts used by
+ * protocols without doing damage to the protocols when it deletes the
+ * entries. It also helps IP as it tracks overlapping maps.
*/
@@ -67,6 +68,19 @@ void dev_mc_upload(struct device *dev)
if(!(dev->flags&IFF_UP))
return;
+
+ /*
+ * An aliased device should end up with the combined
+ * multicast list of all its aliases.
+ * Really, multicasting with logical interfaces is very
+ * subtle question. Now we DO forward multicast packets
+ * to logical interfcases, that doubles multicast
+ * traffic but allows mrouted to work.
+ * Alas, mrouted does not understand aliases even
+ * in 4.4BSD --ANK
+ */
+
+ dev = net_alias_main_dev(dev);
/*
* Devices with no set multicast don't get set
@@ -85,16 +99,29 @@ void dev_mc_upload(struct device *dev)
void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
{
struct dev_mc_list **dmi;
+ dev = net_alias_main_dev(dev);
+
for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
{
+ /*
+ * Find the entry we want to delete. The device could
+ * have variable length entries so check these too.
+ */
if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
{
struct dev_mc_list *tmp= *dmi;
if(--(*dmi)->dmi_users && !all)
return;
+ /*
+ * Last user. So delete the entry.
+ */
*dmi=(*dmi)->next;
dev->mc_count--;
kfree_s(tmp,sizeof(*tmp));
+ /*
+ * We have altered the list, so the card
+ * loaded filter is now wrong. Fix it
+ */
dev_mc_upload(dev);
return;
}
@@ -108,6 +135,9 @@ void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
{
struct dev_mc_list *dmi;
+
+ dev = net_alias_main_dev(dev);
+
for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
{
if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
@@ -135,6 +165,8 @@ void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
void dev_mc_discard(struct device *dev)
{
+ if (net_alias_is(dev))
+ return;
while(dev->mc_list!=NULL)
{
struct dev_mc_list *tmp=dev->mc_list;
diff --git a/net/core/dst.c b/net/core/dst.c
new file mode 100644
index 000000000..8ebdb0bb5
--- /dev/null
+++ b/net/core/dst.c
@@ -0,0 +1,110 @@
+/*
+ * net/dst.c Protocol independent destination cache.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/dst.h>
+
+struct dst_entry * dst_garbage_list;
+atomic_t dst_total = ATOMIC_INIT(0);
+
+static unsigned long dst_gc_timer_expires;
+static unsigned long dst_gc_timer_inc = DST_GC_MAX;
+static void dst_run_gc(unsigned long);
+
+static struct timer_list dst_gc_timer =
+ { NULL, NULL, DST_GC_MIN, 0L, dst_run_gc };
+
+#if RT_CACHE_DEBUG >= 2
+atomic_t hh_count;
+#endif
+
+static void dst_run_gc(unsigned long dummy)
+{
+ int delayed = 0;
+ struct dst_entry * dst, **dstp;
+
+ del_timer(&dst_gc_timer);
+ dstp = &dst_garbage_list;
+ while ((dst = *dstp) != NULL) {
+ if (atomic_read(&dst->use)) {
+ dstp = &dst->next;
+ delayed++;
+ continue;
+ }
+ *dstp = dst->next;
+ dst_destroy(dst);
+ }
+ if (!dst_garbage_list) {
+ dst_gc_timer_inc = DST_GC_MAX;
+ return;
+ }
+ if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX)
+ dst_gc_timer_expires = DST_GC_MAX;
+ dst_gc_timer_inc += DST_GC_INC;
+ dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+#if RT_CACHE_DEBUG >= 2
+ printk("dst_total: %d/%d/%d %ld\n",
+ atomic_read(&dst_total), delayed,
+ atomic_read(&hh_count), dst_gc_timer_expires);
+#endif
+ add_timer(&dst_gc_timer);
+}
+
+static int dst_discard(struct sk_buff *skb)
+{
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+static int dst_blackhole(struct sk_buff *skb)
+{
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+void * dst_alloc(int size, struct dst_ops * ops)
+{
+ struct dst_entry * dst;
+ dst = kmalloc(size, GFP_ATOMIC);
+ if (!dst)
+ return NULL;
+ memset(dst, 0, size);
+ dst->ops = ops;
+ atomic_set(&dst->refcnt, 1);
+ dst->lastuse = jiffies;
+ dst->input = dst_discard;
+ dst->output = dst_blackhole;
+ atomic_inc(&dst_total);
+ return dst;
+}
+
+void __dst_free(struct dst_entry * dst)
+{
+ start_bh_atomic();
+ dst->obsolete = 1;
+ dst->next = dst_garbage_list;
+ dst_garbage_list = dst;
+ if (dst_gc_timer_inc > DST_GC_INC) {
+ del_timer(&dst_gc_timer);
+ dst_gc_timer_inc = DST_GC_INC;
+ dst_gc_timer_expires = DST_GC_MIN;
+ dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+ add_timer(&dst_gc_timer);
+ }
+ end_bh_atomic();
+}
diff --git a/net/core/firewall.c b/net/core/firewall.c
index a57f67eaf..32cf52655 100644
--- a/net/core/firewall.c
+++ b/net/core/firewall.c
@@ -6,35 +6,35 @@
* much hacked by: Alan Cox
*/
-#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/firewall.h>
+#include <asm/semaphore.h>
-static int firewall_lock=0;
+struct semaphore firewall_sem = MUTEX;
static int firewall_policy[NPROTO];
static struct firewall_ops *firewall_chain[NPROTO];
/*
* Register a firewall
*/
-
+
int register_firewall(int pf, struct firewall_ops *fw)
{
struct firewall_ops **p;
-
+
if(pf<0||pf>=NPROTO)
return -EINVAL;
-
+
/*
* Don't allow two people to adjust at once.
*/
-
- while(firewall_lock)
- schedule();
- firewall_lock=1;
-
+
+ down(&firewall_sem);
+
p=&firewall_chain[pf];
-
+
while(*p)
{
if(fw->fw_priority > (*p)->fw_priority)
@@ -42,7 +42,6 @@ int register_firewall(int pf, struct firewall_ops *fw)
p=&((*p)->next);
}
-
/*
* We need to use a memory barrier to make sure that this
* works correctly even in SMP with weakly ordered writes.
@@ -51,6 +50,7 @@ int register_firewall(int pf, struct firewall_ops *fw)
* chain), but not wrt itself (so you can't call this from
* an interrupt. Not that you'd want to).
*/
+
fw->next=*p;
mb();
*p = fw;
@@ -59,7 +59,7 @@ int register_firewall(int pf, struct firewall_ops *fw)
* And release the sleep lock
*/
- firewall_lock=0;
+ up(&firewall_sem);
return 0;
}
@@ -70,42 +70,40 @@ int register_firewall(int pf, struct firewall_ops *fw)
int unregister_firewall(int pf, struct firewall_ops *fw)
{
struct firewall_ops **nl;
-
+
if(pf<0||pf>=NPROTO)
return -EINVAL;
-
+
/*
* Don't allow two people to adjust at once.
*/
-
- while(firewall_lock)
- schedule();
- firewall_lock=1;
+
+ down(&firewall_sem);
nl=&firewall_chain[pf];
-
+
while(*nl!=NULL)
{
if(*nl==fw)
{
struct firewall_ops *f=fw->next;
*nl = f;
- firewall_lock=0;
+ up(&firewall_sem);
return 0;
- }
+ }
nl=&((*nl)->next);
}
- firewall_lock=0;
+ up(&firewall_sem);
return -ENOENT;
}
-int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg)
+int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
{
struct firewall_ops *fw=firewall_chain[pf];
-
+
while(fw!=NULL)
{
- int rc=fw->fw_forward(fw,pf,dev,phdr,arg);
+ int rc=fw->fw_forward(fw,pf,dev,phdr,arg,skb);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
@@ -116,14 +114,14 @@ int call_fw_firewall(int pf, struct device *dev, void *phdr, void *arg)
/*
* Actual invocation of the chains
*/
-
-int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg)
+
+int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
{
struct firewall_ops *fw=firewall_chain[pf];
-
+
while(fw!=NULL)
{
- int rc=fw->fw_input(fw,pf,dev,phdr,arg);
+ int rc=fw->fw_input(fw,pf,dev,phdr,arg,skb);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
@@ -131,13 +129,13 @@ int call_in_firewall(int pf, struct device *dev, void *phdr, void *arg)
return firewall_policy[pf];
}
-int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg)
+int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **skb)
{
struct firewall_ops *fw=firewall_chain[pf];
-
+
while(fw!=NULL)
{
- int rc=fw->fw_output(fw,pf,dev,phdr,arg);
+ int rc=fw->fw_output(fw,pf,dev,phdr,arg,skb);
if(rc!=FW_SKIP)
return rc;
fw=fw->next;
@@ -146,20 +144,15 @@ int call_out_firewall(int pf, struct device *dev, void *phdr, void *arg)
return firewall_policy[pf];
}
-static struct symbol_table firewall_syms = {
-#include <linux/symtab_begin.h>
- X(register_firewall),
- X(unregister_firewall),
- X(call_in_firewall),
- X(call_out_firewall),
- X(call_fw_firewall),
-#include <linux/symtab_end.h>
-};
+EXPORT_SYMBOL(register_firewall);
+EXPORT_SYMBOL(unregister_firewall);
+EXPORT_SYMBOL(call_in_firewall);
+EXPORT_SYMBOL(call_out_firewall);
+EXPORT_SYMBOL(call_fw_firewall);
void fwchain_init(void)
{
int i;
for(i=0;i<NPROTO;i++)
firewall_policy[i]=FW_ACCEPT;
- register_symtab(&firewall_syms);
}
diff --git a/net/core/iovec.c b/net/core/iovec.c
index 6db6ac3e9..9bc21ffc5 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -11,7 +11,8 @@
* Andrew Lunn : Errors in iovec copying.
* Pedro Roque : Added memcpy_fromiovecend and
* csum_..._fromiovecend.
- * Andi Kleen : fixed error handling for 2.1
+ * Andi Kleen : fixed error handling for 2.1
+ * Alexey Kuznetsov: 2.1 optimisations
*/
@@ -19,6 +20,7 @@
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
+#include <linux/malloc.h>
#include <linux/net.h>
#include <linux/in6.h>
#include <asm/uaccess.h>
@@ -34,6 +36,9 @@ extern inline int min(int x, int y)
/*
* Verify iovec
* verify area does a simple check for completly bogus addresses
+ *
+ * Save time not doing verify_area. copy_*_user will make this work
+ * in any case.
*/
int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
@@ -42,42 +47,38 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode)
int len=0;
int ct;
- if(m->msg_name!=NULL)
+ if(m->msg_namelen)
{
if(mode==VERIFY_READ)
{
err=move_addr_to_kernel(m->msg_name, m->msg_namelen, address);
- }
- else
- {
- err=verify_area(mode, m->msg_name, m->msg_namelen);
+ if(err<0)
+ return err;
}
- if(err<0)
- return err;
m->msg_name = address;
+ } else
+ m->msg_name = NULL;
+
+ if (m->msg_iovlen > UIO_FASTIOV)
+ {
+ iov = kmalloc(m->msg_iovlen*sizeof(struct iovec), GFP_KERNEL);
+ if (!iov)
+ return -ENOMEM;
}
- if(m->msg_control!=NULL)
+ err = copy_from_user(iov, m->msg_iov, sizeof(struct iovec)*m->msg_iovlen);
+ if (err)
{
- err=verify_area(mode, m->msg_control, m->msg_controllen);
- if(err)
- return err;
+ if (m->msg_iovlen > UIO_FASTIOV)
+ kfree(iov);
+ return -EFAULT;
}
-
+
for(ct=0;ct<m->msg_iovlen;ct++)
- {
- err = copy_from_user(&iov[ct], &m->msg_iov[ct],
- sizeof(struct iovec));
- if (err)
- return err;
-
- err = verify_area(mode, iov[ct].iov_base, iov[ct].iov_len);
- if(err)
- return err;
len+=iov[ct].iov_len;
- }
- m->msg_iov=&iov[0];
+
+ m->msg_iov=iov;
return len;
}
@@ -121,7 +122,7 @@ int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
err = copy_from_user(kdata, iov->iov_base, copy);
if (err)
{
- return err;
+ return -EFAULT;
}
len-=copy;
kdata+=copy;
@@ -161,7 +162,7 @@ int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
err = copy_from_user(kdata, base, copy);
if (err)
{
- return err;
+ return -EFAULT;
}
len-=copy;
kdata+=copy;
@@ -175,7 +176,7 @@ int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset,
err = copy_from_user(kdata, iov->iov_base, copy);
if (err)
{
- return err;
+ return -EFAULT;
}
len-=copy;
kdata+=copy;
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
new file mode 100644
index 000000000..427189234
--- /dev/null
+++ b/net/core/neighbour.c
@@ -0,0 +1,287 @@
+/*
+ * Generic address resultion entity
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <net/neighbour.h>
+
+
+static void neigh_purge_send_q(struct neighbour *neigh);
+
+void neigh_table_init(struct neigh_table *tbl, struct neigh_ops *ops, int size)
+{
+ int bmemlen;
+
+ memset(tbl, 0, sizeof(struct neigh_table));
+
+ tbl->tbl_size = size;
+ tbl->neigh_ops = ops;
+
+ /*
+ * This should only be called on initialization
+ * And interrupts should be on
+ */
+
+ bmemlen = size * sizeof(struct neighbour *);
+ tbl->hash_buckets = kmalloc(bmemlen, GFP_KERNEL);
+
+ if (tbl->hash_buckets == NULL)
+ {
+ panic("unable to initialize neigh_table");
+ }
+
+ memset(tbl->hash_buckets, 0, bmemlen);
+}
+
+struct neighbour *neigh_alloc(int size, struct neigh_ops *ops)
+{
+ struct neighbour *neigh;
+
+ neigh = kmalloc(size, GFP_ATOMIC);
+ if (neigh == NULL)
+ {
+ return NULL;
+ }
+
+ memset(neigh, 0, size);
+
+ skb_queue_head_init(&neigh->arp_queue);
+ neigh->ops = ops;
+ return neigh;
+}
+
+void neigh_queue_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+ struct neighbour *entry, **head;
+ entry = tbl->request_queue;
+
+ head = &tbl->request_queue;
+
+ for (; entry; entry = entry->next)
+ {
+ head = &entry->next;
+ }
+
+ *head = neigh;
+ neigh->next = neigh->prev = NULL;
+}
+
+static struct neighbour *neigh_dequeue(struct neigh_table *tbl)
+{
+ struct neighbour *neigh;
+
+ if ((neigh = tbl->request_queue))
+ {
+ tbl->request_queue = neigh->next;
+ }
+ return neigh;
+}
+
+void neigh_table_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+ unsigned int hash_val;
+ struct neighbour **head;
+
+ hash_val = tbl->neigh_ops->hash(neigh->primary_key) % tbl->tbl_size;
+
+ neigh->tbl = tbl;
+
+ head = &tbl->hash_buckets[hash_val];
+
+ if (!(*head))
+ {
+ neigh->next = neigh;
+ neigh->prev = neigh;
+ }
+ else
+ {
+ struct neighbour *prev;
+ struct neighbour *next;
+
+ next = *head;
+ prev = next->prev;
+
+
+ neigh->next = next;
+ neigh->prev = prev;
+ next->prev = neigh;
+ prev->next = neigh;
+ }
+
+ *head = neigh;
+}
+
+struct neighbour * neigh_lookup(struct neigh_table *tbl, void *pkey,
+ int key_len, struct device *dev)
+{
+ struct neighbour *neigh, *head;
+ unsigned int hash_val;
+
+ hash_val = tbl->neigh_ops->hash(pkey) % tbl->tbl_size;
+ head = tbl->hash_buckets[hash_val];
+
+ neigh = head;
+
+ if (neigh)
+ {
+ do {
+ if (memcmp(neigh->primary_key, pkey, key_len) == 0)
+ {
+ if (!dev || dev == neigh->dev)
+ return neigh;
+ }
+ neigh = neigh->next;
+
+ } while (neigh != head);
+ }
+
+ return NULL;
+}
+
+/*
+ * neighbour must already be out of the table;
+ *
+ */
+void neigh_destroy(struct neighbour *neigh)
+{
+ if (neigh->tbl)
+ {
+ printk(KERN_DEBUG "neigh_destroy: neighbour still in table. "
+ "called from %p\n", __builtin_return_address(0));
+ }
+
+ if (neigh->ops->destructor)
+ {
+ (neigh->ops->destructor)(neigh);
+ }
+
+ neigh_purge_send_q(neigh);
+
+ kfree(neigh);
+}
+
+void neigh_unlink(struct neighbour *neigh)
+{
+ struct neigh_table *tbl;
+ struct neighbour **head;
+ unsigned int hash_val;
+ struct neighbour *next, *prev;
+
+ tbl = neigh->tbl;
+ neigh->tbl = NULL;
+
+ hash_val = neigh->ops->hash(neigh->primary_key) % tbl->tbl_size;
+
+ head = &tbl->hash_buckets[hash_val];
+ tbl->tbl_entries--;
+
+ next = neigh->next;
+ if (neigh == (*head))
+ {
+ if (next == neigh)
+ {
+ *head = NULL;
+ goto out;
+ }
+ *head = next;
+ }
+
+ prev = neigh->prev;
+ next->prev = prev;
+ prev->next = next;
+ out:
+ neigh->next = neigh->prev = NULL;
+}
+
+/*
+ * Must only be called with an exclusive lock and bh disabled
+ *
+ */
+
+void ntbl_walk_table(struct neigh_table *tbl, ntbl_examine_t func,
+ unsigned long filter, int max, void *args)
+{
+ int i;
+
+ if (max == 0)
+ max = tbl->tbl_size;
+
+ for (i=0; i < max; i++)
+ {
+ struct neighbour **head;
+ struct neighbour *entry;
+
+ head = &tbl->hash_buckets[i];
+ entry = *head;
+
+ if (!entry)
+ continue;
+
+ do {
+ if (entry->flags & (~filter))
+ {
+ int ret;
+ ret = (*func)(entry, args);
+
+ if (ret)
+ {
+ struct neighbour *curp;
+
+ curp = entry;
+ entry = curp->next;
+
+ neigh_unlink(curp);
+ neigh_destroy(curp);
+
+ if ((*head) == NULL)
+ break;
+ continue;
+ }
+ }
+ entry = entry->next;
+
+ } while (entry != *head);
+ }
+}
+
+void neigh_tbl_run_bh(struct neigh_table *tbl)
+{
+ if ((tbl->tbl_bh_mask & NT_MASK_QUEUE))
+ {
+ struct neighbour *neigh;
+
+ while((neigh = neigh_dequeue(tbl)))
+ {
+ neigh_table_ins(tbl, neigh);
+ }
+ tbl->tbl_bh_mask &= ~NT_MASK_QUEUE;
+ }
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void neigh_purge_send_q(struct neighbour *neigh)
+{
+ struct sk_buff *skb;
+
+ /* Release the list of `skb' pointers. */
+ while ((skb = skb_dequeue(&neigh->arp_queue)))
+ {
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ return;
+}
diff --git a/net/core/net_alias.c b/net/core/net_alias.c
index 358303705..6a4a13167 100644
--- a/net/core/net_alias.c
+++ b/net/core/net_alias.c
@@ -43,11 +43,7 @@
#include <linux/in.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-
-#ifdef ALIAS_USER_LAND_DEBUG
-#include "net_alias.h"
-#include "user_stubs.h"
-#endif
+#include <linux/init.h>
#include <linux/net_alias.h>
@@ -56,17 +52,16 @@
#endif
/*
- * Only allow the following flags to pass from main device to aliases
+ * Only allow the following flags to pass from main device to aliases
*/
-#define NET_ALIAS_IFF_MASK (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+#define NET_ALIAS_IFF_MASK (IFF_UP|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MULTICAST)
static struct net_alias_type * nat_getbytype(int type);
static int nat_attach_chg(struct net_alias_type *nat, int delta);
static int nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa);
static int nat_unbind(struct net_alias_type *nat, struct net_alias *alias);
-
static int net_alias_devinit(struct device *dev);
static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
@@ -76,54 +71,53 @@ static struct device *net_alias_dev_delete(struct device *main_dev, int slot, in
static void net_alias_free(struct device *dev);
/*
- * net_alias_type base array, will hold net_alias_type obj hashed list heads.
+ * net_alias_type base array, will hold net_alias_type obj hashed list
+ * heads.
*/
struct net_alias_type *nat_base[16];
/*
- * get net_alias_type ptr by type
+ * Get net_alias_type ptr by type
*/
-static __inline__ struct net_alias_type *
-nat_getbytype(int type)
+extern __inline__ struct net_alias_type *nat_getbytype(int type)
{
- struct net_alias_type *nat;
- for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
- {
- if (nat->type == type) return nat;
- }
- return NULL;
+ struct net_alias_type *nat;
+ for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+ {
+ if (nat->type == type)
+ return nat;
+ }
+ return NULL;
}
/*
- * get addr32 representation (pre-hashing) of address.
- * if NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
+ * Get addr32 representation (pre-hashing) of address.
+ * If NULL nat->get_addr32, assume sockaddr_in struct (IP-ish).
*/
-static __inline__ __u32
-nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
+extern __inline__ __u32 nat_addr32(struct net_alias_type *nat, struct sockaddr *sa)
{
- if (nat->get_addr32)
- return nat->get_addr32(nat, sa);
- else
- return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
+ if (nat->get_addr32)
+ return nat->get_addr32(nat, sa);
+ else
+ return (*(struct sockaddr_in *)sa).sin_addr.s_addr;
}
/*
- * hashing code for alias_info->hash_tab entries
- * 4 bytes -> 1/2 byte using xor complemented by af
+ * Hashing code for alias_info->hash_tab entries
+ * 4 bytes -> 1/2 byte using xor complemented by af
*/
-static __inline__ unsigned
-HASH(__u32 addr, int af)
+extern __inline__ unsigned HASH(__u32 addr, int af)
{
- unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
- tmp ^= (tmp>>8); /* 2 -> 1 */
- return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */
+ unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
+ tmp ^= (tmp>>8); /* 2 -> 1 */
+ return (tmp^(tmp>>4)^af) & 0x0f; /* 1 -> 1/2 */
}
@@ -134,967 +128,1015 @@ HASH(__u32 addr, int af)
* address to a hash code.
*/
-static __inline__ int
-nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+extern __inline__ int nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
{
- return HASH(nat_addr32(nat,sa), sa->sa_family);
+ return HASH(nat_addr32(nat,sa), sa->sa_family);
}
/*
- * change net_alias_type number of attachments (bindings)
+ * Change net_alias_type number of attachments (bindings)
*/
-static int
-nat_attach_chg(struct net_alias_type *nat, int delta)
+static int nat_attach_chg(struct net_alias_type *nat, int delta)
{
- unsigned long flags;
- int n_at;
- if (!nat) return -1;
- save_flags(flags);
- cli();
- n_at = nat->n_attach + delta;
- if (n_at < 0)
- {
- restore_flags(flags);
- printk(KERN_WARNING "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
- nat->type);
- return -1;
- }
- nat->n_attach = n_at;
- restore_flags(flags);
- return 0;
+ unsigned long flags;
+ int n_at;
+ if (!nat)
+ return -1;
+ save_flags(flags);
+ cli();
+ n_at = nat->n_attach + delta;
+ if (n_at < 0)
+ {
+ restore_flags(flags);
+ printk(KERN_WARNING
+ "net_alias: tried to set n_attach < 0 for (family==%d) nat object.\n",
+ nat->type);
+ return -1;
+ }
+ nat->n_attach = n_at;
+ restore_flags(flags);
+ return 0;
}
/*
- * bind alias to its type (family) object and call initialization hook
+ * Bind alias to its type (family) object and call initialization hook
*/
-static __inline__ int
-nat_bind(struct net_alias_type *nat,struct net_alias *alias, struct sockaddr *sa)
+extern __inline__ int nat_bind(struct net_alias_type *nat,
+ struct net_alias *alias, struct sockaddr *sa)
{
- if (nat->alias_init_1) nat->alias_init_1(nat, alias, sa);
- return nat_attach_chg(nat, +1);
+ if (nat->alias_init_1)
+ nat->alias_init_1(nat, alias, sa);
+ return nat_attach_chg(nat, +1);
}
/*
- * unbind alias from type object and call alias destructor
+ * Unbind alias from type object and call alias destructor
*/
-static __inline__ int
-nat_unbind(struct net_alias_type *nat, struct net_alias *alias)
+extern __inline__ int nat_unbind(struct net_alias_type *nat,
+ struct net_alias *alias)
{
- if (nat->alias_done_1) nat->alias_done_1(nat, alias);
- return nat_attach_chg(nat, -1);
+ if (nat->alias_done_1)
+ nat->alias_done_1(nat, alias);
+ return nat_attach_chg(nat, -1);
}
/*
- * compare device address with given. if NULL nat->dev_addr_chk,
- * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
+ * Compare device address with given. if NULL nat->dev_addr_chk,
+ * compare dev->pa_addr with (sockaddr_in) 32 bits address (IP-ish)
*/
static __inline__ int nat_dev_addr_chk_1(struct net_alias_type *nat,
struct device *dev, struct sockaddr *sa)
{
- if (nat->dev_addr_chk)
- return nat->dev_addr_chk(nat, dev, sa);
- else
- return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
+ if (nat->dev_addr_chk)
+ return nat->dev_addr_chk(nat, dev, sa);
+ else
+ return (dev->pa_addr == (*(struct sockaddr_in *)sa).sin_addr.s_addr);
}
/*
- * alias device init()
- * do nothing.
+ * Alias device init()
+ * do nothing.
*/
-static int
-net_alias_devinit(struct device *dev)
+static int net_alias_devinit(struct device *dev)
{
#ifdef ALIAS_USER_LAND_DEBUG
- printk("net_alias_devinit(%s) called.\n", dev->name);
+ printk("net_alias_devinit(%s) called.\n", dev->name);
#endif
- return 0;
+ return 0;
}
/*
- * hard_start_xmit() should not be called.
- * ignore ... but shout!.
+ * Hard_start_xmit() should not be called.
+ * ignore ... but shout!.
*/
-static int
-net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
+static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev)
{
- printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
- dev_kfree_skb(skb, FREE_WRITE);
- return 0;
+ printk(KERN_WARNING "net_alias: net_alias_hard_start_xmit() for %s called (ignored)!!\n", dev->name);
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
}
-static int
-net_alias_open(struct device * dev)
+static int net_alias_open(struct device * dev)
{
- return 0;
+ return 0;
}
-static int
-net_alias_close(struct device * dev)
+static int net_alias_close(struct device * dev)
{
- return 0;
+ return 0;
}
/*
* setups a new (alias) device
*/
-static int
-net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat,
- struct sockaddr *sa)
+static int net_alias_devsetup(struct net_alias *alias,
+ struct net_alias_type *nat, struct sockaddr *sa)
{
- struct device *main_dev;
- struct device *dev;
- int family;
- int i;
-
- /*
- *
- * generic device setup based on main_dev info
- *
- * FIXME: is NULL bitwise 0 for all Linux platforms?
- */
-
- main_dev = alias->main_dev;
- dev = &alias->dev;
- memset(dev, '\0', sizeof(struct device));
- family = (sa)? sa->sa_family : main_dev->family;
-
- dev->alias_info = NULL; /* no aliasing recursion */
- dev->my_alias = alias; /* point to alias */
- dev->name = alias->name;
- dev->type = main_dev->type;
- dev->open = net_alias_open;
- dev->stop = net_alias_close;
- dev->hard_header_len = main_dev->hard_header_len;
- memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
- memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
- dev->addr_len = main_dev->addr_len;
- dev->init = net_alias_devinit;
- dev->hard_start_xmit = net_alias_hard_start_xmit;
- dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
-
- /*
- * only makes sense if same family
- */
-
- if (family == main_dev->family)
- {
- dev->metric = main_dev->metric;
- dev->mtu = main_dev->mtu;
- dev->pa_alen = main_dev->pa_alen;
- dev->hard_header = main_dev->hard_header;
- dev->rebuild_header = main_dev->rebuild_header;
- }
-
- /*
- * Fill in the generic fields of the device structure.
- * not actually used, avoids some dev.c #ifdef's
- */
-
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&dev->buffs[i]);
-
- dev->family = family;
- return 0;
+ struct device *main_dev;
+ struct device *dev;
+ int family;
+ int i;
+
+ /*
+ *
+ * generic device setup based on main_dev info
+ *
+ * FIXME: is NULL bitwise 0 for all Linux platforms?
+ */
+
+ main_dev = alias->main_dev;
+ dev = &alias->dev;
+ memset(dev, '\0', sizeof(struct device));
+ family = (sa)? sa->sa_family : main_dev->family;
+
+ dev->alias_info = NULL; /* no aliasing recursion */
+ dev->my_alias = alias; /* point to alias */
+ dev->name = alias->name;
+ dev->type = main_dev->type;
+ dev->open = net_alias_open;
+ dev->stop = net_alias_close;
+ dev->hard_header_len = main_dev->hard_header_len;
+ memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
+ memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
+ dev->addr_len = main_dev->addr_len;
+ dev->init = net_alias_devinit;
+ dev->hard_start_xmit = net_alias_hard_start_xmit;
+ dev->flags = main_dev->flags & NET_ALIAS_IFF_MASK & ~IFF_UP;
+ dev->ifindex = dev_new_index();
+
+ /*
+ * Only makes sense if same family (arguable)
+ */
+
+ if (family == main_dev->family)
+ {
+ dev->metric = main_dev->metric;
+ dev->mtu = main_dev->mtu;
+ dev->pa_alen = main_dev->pa_alen;
+ dev->hard_header = main_dev->hard_header;
+ dev->hard_header_cache = main_dev->hard_header_cache;
+ dev->header_cache_update = main_dev->header_cache_update;
+ dev->rebuild_header = main_dev->rebuild_header;
+ }
+
+ /*
+ * Fill in the generic fields of the device structure.
+ * not actually used, avoids some dev.c #ifdef's
+ */
+
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->family = family;
+ return 0;
}
/*
- * slow alias find (parse the whole hash_tab)
- * returns: alias' pointer address
+ * Slow alias find (parse the whole hash_tab)
+ * returns: alias' pointer address
*/
-static struct net_alias **
-net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias)
+static struct net_alias **net_alias_slow_findp(struct net_alias_info
+ *alias_info, struct net_alias *alias)
{
- unsigned idx, n_aliases;
- struct net_alias **aliasp;
-
- /*
- * for each alias_info's hash_tab entry, for every alias ...
- */
-
- n_aliases = alias_info->n_aliases;
- for (idx=0; idx < 16 ; idx++)
- for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
- if (*aliasp == alias)
- return aliasp;
- else
- if (--n_aliases == 0) break; /* faster give up */
- return NULL;
+ unsigned idx, n_aliases;
+ struct net_alias **aliasp;
+
+ /*
+ * For each alias_info's hash_tab entry, for every alias ...
+ */
+
+ n_aliases = alias_info->n_aliases;
+ for (idx=0; idx < 16 ; idx++)
+ {
+ for (aliasp = &alias_info->hash_tab[idx];*aliasp;
+ aliasp = &(*aliasp)->next)
+ {
+ if (*aliasp == alias)
+ return aliasp;
+ else
+ if (--n_aliases == 0)
+ break; /* faster give up */
+ }
+ }
+ return NULL;
}
/*
- * create alias device for main_dev with given slot num.
- * if sa==NULL will create a same_family alias device
+ * Create alias device for main_dev with given slot num.
+ * if sa==NULL will create a same_family alias device.
*/
-static struct device *
-net_alias_dev_create(struct device *main_dev, int slot, int *err, struct sockaddr *sa, void *data)
+static struct device *net_alias_dev_create(struct device *main_dev, int slot,
+ int *err, struct sockaddr *sa, void *data)
{
- struct net_alias_info *alias_info;
- struct net_alias *alias, **aliasp;
- struct net_alias_type *nat;
- struct device *dev;
- unsigned long flags;
- int family;
- __u32 addr32;
-
- /* FIXME: lock */
- alias_info = main_dev->alias_info;
+ struct net_alias_info *alias_info;
+ struct net_alias *alias, **aliasp;
+ struct net_alias_type *nat;
+ struct device *dev;
+ unsigned long flags;
+ int family;
+ __u32 addr32;
+
+ /* FIXME: lock */
+
+ alias_info = main_dev->alias_info;
- /*
- * if NULL address given, take family from main_dev
- */
+ /*
+ * If NULL address given, take family from main_dev
+ */
- family = (sa)? sa->sa_family : main_dev->family;
+ family = (sa)? sa->sa_family : main_dev->family;
- /*
- * check if wanted family has a net_alias_type object registered
- */
+ /*
+ * Check if wanted family has a net_alias_type object registered
+ */
- nat = nat_getbytype(family);
- if (!nat) {
+ nat = nat_getbytype(family);
+ if (!nat)
+ {
#ifdef CONFIG_KERNELD
- char modname[20];
- sprintf (modname,"netalias-%d", family);
- request_module(modname);
+ char modname[20];
+ sprintf (modname,"netalias-%d", family);
+ request_module(modname);
- nat = nat_getbytype(family);
- if (!nat) {
+ nat = nat_getbytype(family);
+ if (!nat)
+ {
#endif
- printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n",
- main_dev->name, slot, family);
- /* *err = -EAFNOSUPPORT; */
- *err = -EINVAL;
- return NULL;
+ printk(KERN_WARNING "net_alias_dev_create(%s:%d): unregistered family==%d\n",
+ main_dev->name, slot, family);
+ /* *err = -EAFNOSUPPORT; */
+ *err = -EINVAL;
+ return NULL;
#ifdef CONFIG_KERNELD
- }
+ }
#endif
- }
+ }
- /*
- * do not allow creation over downed devices
- */
+ /*
+ * Do not allow creation over downed devices
+ */
- *err = -EIO;
+ *err = -EIO;
- if (! (main_dev->flags & IFF_UP) )
- return NULL;
+ if (! (main_dev->flags & IFF_UP) )
+ return NULL;
- /*
- * if first alias, must also create alias_info
- */
+ /*
+ * If first alias, must also create alias_info
+ */
- *err = -ENOMEM;
-
- if (!alias_info)
- {
- alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
- if (!alias_info) return NULL; /* ENOMEM */
- memset(alias_info, 0, sizeof(struct net_alias_info));
- }
+ *err = -ENOMEM;
+
+ if (!alias_info)
+ {
+ alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
+ if (!alias_info)
+ return NULL; /* ENOMEM */
+ memset(alias_info, 0, sizeof(struct net_alias_info));
+ }
+
+ if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
+ return NULL; /* ENOMEM */
+
+ memset(alias, 0, sizeof(struct net_alias));
+ alias->slot = slot;
+ alias->main_dev = main_dev;
+ alias->nat = nat;
+ alias->next = NULL;
+ alias->data = data;
+ sprintf(alias->name, "%s:%d", main_dev->name, slot);
- if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
- return NULL; /* ENOMEM */
-
- /*
- * FIXME: is NULL bitwise 0 for all Linux platforms?
- */
-
- memset(alias, 0, sizeof(struct net_alias));
- alias->slot = slot;
- alias->main_dev = main_dev;
- alias->nat = nat;
- alias->next = NULL;
- alias->data = data;
- sprintf(alias->name, "%s:%d", main_dev->name, slot);
-
- /*
- * initialise alias' device structure
- */
+ /*
+ * Initialise alias' device structure
+ */
- net_alias_devsetup(alias, nat, sa);
+ net_alias_devsetup(alias, nat, sa);
- dev = &alias->dev;
+ dev = &alias->dev;
- save_flags(flags);
- cli();
+ save_flags(flags);
+ cli();
- /*
- * bind alias to its object type
- * nat_bind calls nat->alias_init_1
- */
+ /*
+ * bind alias to its object type
+ * nat_bind calls nat->alias_init_1
+ */
- nat_bind(nat, alias, sa);
+ nat_bind(nat, alias, sa);
- /*
- * if no address passed, take from device (could have been
- * set by nat->alias_init_1)
- */
+ /*
+ * If no address passed, take from device (could have been
+ * set by nat->alias_init_1)
+ */
- addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
+ addr32 = (sa)? nat_addr32(nat, sa) : alias->dev.pa_addr;
- /*
- * store hash key in alias: will speed-up rehashing and deletion
- */
+ /*
+ * Store hash key in alias: will speed-up rehashing and deletion
+ */
- alias->hash = HASH(addr32, family);
+ alias->hash = HASH(addr32, family);
- /*
- * insert alias in hashed linked list
- */
+ /*
+ * Insert alias in hashed linked list
+ */
- aliasp = &alias_info->hash_tab[alias->hash];
- alias->next = *aliasp;
- *aliasp = alias;
+ aliasp = &alias_info->hash_tab[alias->hash];
+ alias->next = *aliasp;
+ *aliasp = alias;
- /*
- * if first alias ...
- */
+ /*
+ * If first alias ...
+ */
- if (!alias_info->n_aliases++)
- {
- alias_info->taildev = main_dev;
- main_dev->alias_info = alias_info;
- }
+ if (!alias_info->n_aliases++)
+ {
+ alias_info->taildev = main_dev;
+ main_dev->alias_info = alias_info;
+ }
- /*
- * add device at tail (just after last main_dev alias)
- */
+ /*
+ * add device at tail (just after last main_dev alias)
+ */
- dev->next = alias_info->taildev->next;
- alias_info->taildev->next = dev;
- alias_info->taildev = dev;
- restore_flags(flags);
- return dev;
+ dev->next = alias_info->taildev->next;
+ alias_info->taildev->next = dev;
+ alias_info->taildev = dev;
+ restore_flags(flags);
+ return dev;
}
/*
- * delete one main_dev alias (referred by its slot num)
+ * Delete one main_dev alias (referred by its slot num)
*/
-static struct device *
-net_alias_dev_delete(struct device *main_dev, int slot, int *err)
+static struct device *net_alias_dev_delete(struct device *main_dev, int slot,
+ int *err)
{
- struct net_alias_info *alias_info;
- struct net_alias *alias, **aliasp;
- struct device *dev;
- unsigned n_aliases;
- unsigned long flags;
- struct net_alias_type *nat;
- struct device *prevdev;
-
- /* FIXME: lock */
- *err = -ENODEV;
-
- if (main_dev == NULL) return NULL;
+ struct net_alias_info *alias_info;
+ struct net_alias *alias, **aliasp;
+ struct device *dev;
+ unsigned n_aliases;
+ unsigned long flags;
+ struct net_alias_type *nat;
+ struct device *prevdev;
- /*
- * does main_dev have aliases?
- */
+ /* FIXME: lock */
+ *err = -ENODEV;
- alias_info = main_dev->alias_info;
- if (!alias_info) return NULL; /* ENODEV */
-
- n_aliases = alias_info->n_aliases;
+ if (main_dev == NULL)
+ return NULL;
+
+ /*
+ * Does main_dev have aliases?
+ */
- /*
- * find device that holds the same slot number (could also
- * be strcmp() ala dev_get).
- */
+ alias_info = main_dev->alias_info;
+ if (!alias_info)
+ return NULL; /* ENODEV */
- for (prevdev=main_dev, alias = NULL;prevdev->next && n_aliases; prevdev = prevdev->next)
- {
- if (!(alias = prevdev->next->my_alias))
- {
- printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n");
- continue; /* or should give up? */
- }
- if (alias->slot == slot) break;
- alias = NULL;
- n_aliases--;
- }
+ n_aliases = alias_info->n_aliases;
- if (!alias) return NULL; /* ENODEV */
+ /*
+ * Find device that holds the same slot number (could also
+ * be strcmp() ala dev_get).
+ */
- dev = &alias->dev;
+ for (prevdev=main_dev, alias = NULL;
+ prevdev->next && n_aliases; prevdev = prevdev->next)
+ {
+ if (!(alias = prevdev->next->my_alias))
+ {
+ printk(KERN_ERR "net_alias_dev_delete(): incorrect non-alias device after maindev\n");
+ continue; /* or should give up? */
+ }
+ if (alias->slot == slot)
+ break;
+ alias = NULL;
+ n_aliases--;
+ }
+
+ if (!alias)
+ return NULL; /* ENODEV */
+
+ dev = &alias->dev;
- /*
- * find alias hashed entry
- */
+ /*
+ * Find alias hashed entry
+ */
- for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
- if(*aliasp == alias) break;
+ for(aliasp = &alias_info->hash_tab[alias->hash]; *aliasp;
+ aliasp = &(*aliasp)->next)
+ {
+ if(*aliasp == alias)
+ break;
+ }
- /*
- * if not found (???), try a full search
- */
-
- if (*aliasp != alias)
- if ((aliasp = net_alias_slow_findp(alias_info, alias)))
- printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
- else
- {
- printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
- return NULL; /* ENODEV */
- }
+ /*
+ * If not found (???), try a full search
+ */
- nat = alias->nat;
+ if (*aliasp != alias)
+ {
+ if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+ printk(KERN_WARNING "net_alias_dev_delete(%s): bad hashing recovered\n", alias->name);
+ else
+ {
+ printk(KERN_ERR "net_alias_dev_delete(%s): unhashed alias!\n",alias->name);
+ return NULL; /* ENODEV */
+ }
+ }
+ nat = alias->nat;
- save_flags(flags);
- cli();
+ save_flags(flags);
+ cli();
- /*
- * unbind alias from alias_type obj.
- */
+ /*
+ * Unbind alias from alias_type obj.
+ */
- nat_unbind(nat, alias);
+ nat_unbind(nat, alias);
- /*
- * is alias at tail?
- */
+ /*
+ * Is alias at tail?
+ */
- if ( dev == alias_info->taildev )
- alias_info->taildev = prevdev;
+ if ( dev == alias_info->taildev )
+ alias_info->taildev = prevdev;
- /*
- * unlink and close device
- */
- prevdev->next = dev->next;
- dev_close(dev);
+ /*
+ * Unlink and close device
+ */
+ prevdev->next = dev->next;
+ dev_close(dev);
- /*
- * unlink alias
- */
+ /*
+ * Unlink alias
+ */
- *aliasp = (*aliasp)->next;
+ *aliasp = (*aliasp)->next;
+ if (--alias_info->n_aliases == 0) /* last alias */
+ main_dev->alias_info = NULL;
- if (--alias_info->n_aliases == 0) /* last alias */
- main_dev->alias_info = NULL;
- restore_flags(flags);
+ restore_flags(flags);
- /*
- * now free structures
- */
+ /*
+ * Now free structures
+ */
- kfree_s(alias, sizeof(struct net_alias));
- if (main_dev->alias_info == NULL)
- kfree_s(alias_info, sizeof(struct net_alias_info));
+ kfree_s(alias, sizeof(struct net_alias));
+ if (main_dev->alias_info == NULL)
+ kfree_s(alias_info, sizeof(struct net_alias_info));
- /*
- * deletion ok (*err=0), NULL device returned.
- */
+ /*
+ * Deletion ok (*err=0), NULL device returned.
+ */
- *err = 0;
- return NULL;
+ *err = 0;
+ return NULL;
}
/*
- * free all main device aliasing stuff
- * will be called on dev_close(main_dev)
+ * Free all main device aliasing stuff
+ * will be called on dev_close(main_dev)
*/
-static void
-net_alias_free(struct device *main_dev)
+static void net_alias_free(struct device *main_dev)
{
- struct net_alias_info *alias_info;
- struct net_alias *alias;
- struct net_alias_type *nat;
- struct device *dev;
- unsigned long flags;
+ struct net_alias_info *alias_info;
+ struct net_alias *alias;
+ struct net_alias_type *nat;
+ struct device *dev;
+ unsigned long flags;
- /*
- * do I really have aliases?
- */
+ /*
+ * Do I really have aliases?
+ */
- if (!(alias_info = main_dev->alias_info)) return;
+ if (!(alias_info = main_dev->alias_info))
+ return;
- /*
- * fast device link "short-circuit": set main_dev->next to
- * device after last alias
- */
+ /*
+ * Fast device link "short-circuit": set main_dev->next to
+ * device after last alias
+ */
- save_flags(flags);
- cli();
+ save_flags(flags);
+ cli();
- dev = main_dev->next;
- main_dev->next = alias_info->taildev->next;
- main_dev->alias_info = NULL;
- alias_info->taildev->next = NULL;
+ dev = main_dev->next;
+ main_dev->next = alias_info->taildev->next;
+ main_dev->alias_info = NULL;
+ alias_info->taildev->next = NULL;
- restore_flags(flags);
+ restore_flags(flags);
- /*
- * loop over alias devices, free and dev_close()
- */
-
- while (dev)
- {
- if (net_alias_is(dev))
- {
- alias = dev->my_alias;
- if (alias->main_dev == main_dev)
- {
/*
- * unbind alias from alias_type object
+ * Loop over alias devices, free and dev_close()
*/
-
- nat = alias->nat;
- if (nat)
+
+ while (dev)
{
- nat_unbind(nat, alias);
- } /* else error/printk ??? */
-
- dev_close(dev);
- dev = dev->next;
+ if (net_alias_is(dev))
+ {
+ alias = dev->my_alias;
+ if (alias->main_dev == main_dev)
+ {
+ /*
+ * unbind alias from alias_type object
+ */
+ nat = alias->nat;
+ if (nat)
+ {
+ nat_unbind(nat, alias);
+ } /* else error/printk ??? */
+
+ dev_close(dev);
+ dev = dev->next;
- kfree_s(alias, sizeof(struct net_alias));
- continue;
- }
- else
- printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n",
- main_dev->name, alias->name);
- }
- else
- printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n",
- main_dev->name);
- dev = dev->next;
- }
-
- kfree_s(alias_info, sizeof(alias_info));
- return;
+ kfree_s(alias, sizeof(struct net_alias));
+ continue;
+ }
+ else
+ printk(KERN_ERR "net_alias_free(%s): '%s' is not my alias\n",
+ main_dev->name, alias->name);
+ }
+ else
+ {
+ printk(KERN_ERR "net_alias_free(%s): found a non-alias after device!\n",
+ main_dev->name);
+ }
+ dev = dev->next;
+ }
+
+ kfree_s(alias_info, sizeof(alias_info));
+ return;
}
/*
- * dev_get() with added alias naming magic.
+ * dev_get() with added alias naming magic.
*/
-struct device *
-net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
+struct device *net_alias_dev_get(char *dev_name, int aliasing_ok, int *err,
struct sockaddr *sa, void *data)
{
- struct device *dev;
- char *sptr,*eptr;
- int slot = 0;
- int delete = 0;
+ struct device *dev;
+ char *sptr,*eptr;
+ int slot = 0;
+ int delete = 0;
- *err = -ENODEV;
- if ((dev=dev_get(dev_name)))
- return dev;
+ *err = -ENODEV;
+ if ((dev=dev_get(dev_name)))
+ return dev;
- /*
- * want alias naming magic?
- */
+ /*
+ * Want alias naming magic?
+ */
- if (!aliasing_ok) return NULL;
+ if (!aliasing_ok)
+ return NULL;
- if (!dev_name || !*dev_name)
- return NULL;
-
- /*
- * find the first ':' , must be followed by, at least, 1 char
- */
-
- for (sptr=dev_name ; *sptr ; sptr++) if(*sptr==':') break;
- if (!*sptr || !*(sptr+1))
- return NULL;
+ if (!dev_name || !*dev_name)
+ return NULL;
- /*
- * seems to be an alias name, fetch main device
- */
+ /*
+ * Find the first ':' , must be followed by, at least, 1 char
+ */
+
+ sptr=strchr(dev_name,':');
+ if (sptr==NULL || !sptr[1])
+ return NULL;
+
+#if 0
+ for (sptr=dev_name ; *sptr ; sptr++)
+ if(*sptr==':')
+ break;
+ if (!*sptr || !*(sptr+1))
+ return NULL;
+#endif
+ /*
+ * Seems to be an alias name, fetch main device
+ */
- *sptr='\0';
- if (!(dev=dev_get(dev_name)))
- return NULL;
- *sptr++=':';
+ *sptr='\0';
+ if (!(dev=dev_get(dev_name)))
+ return NULL;
+ *sptr++=':';
- /*
- * fetch slot number
- */
+ /*
+ * Fetch slot number
+ */
- slot = simple_strtoul(sptr,&eptr,10);
- if (slot >= NET_ALIAS_MAX_SLOT)
- return NULL;
+ slot = simple_strtoul(sptr,&eptr,10);
+ if (slot >= NET_ALIAS_MAX_SLOT)
+ return NULL;
- /*
- * if last char is '-', it is a deletion request
- */
+ /*
+ * If last char is '-', it is a deletion request
+ */
- if (eptr[0] == '-' && !eptr[1] ) delete++;
- else if (eptr[0])
- return NULL;
+ if (eptr[0] == '-' && !eptr[1] )
+ delete++;
+ else if (eptr[0])
+ return NULL;
- /*
- * well... let's work.
- */
+ /*
+ * Well... let's work.
+ */
- if (delete)
- return net_alias_dev_delete(dev, slot, err);
- else
- return net_alias_dev_create(dev, slot, err, sa, data);
+ if (delete)
+ return net_alias_dev_delete(dev, slot, err);
+ else
+ return net_alias_dev_create(dev, slot, err, sa, data);
}
/*
- * rehash alias device with address supplied.
+ * Rehash alias device with address supplied.
*/
-int
-net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
+int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa)
{
- struct net_alias_info *alias_info;
- struct net_alias *alias, **aliasp;
- struct device *main_dev;
- unsigned long flags;
- struct net_alias_type *o_nat, *n_nat;
- unsigned n_hash;
-
- /*
- * defensive ...
- */
-
- if (dev == NULL) return -1;
- if ( (alias = dev->my_alias) == NULL ) return -1;
-
- if (!sa)
- {
- printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n");
- return -1;
- }
-
- /*
- * defensive. should not happen.
- */
-
- if ( (main_dev = alias->main_dev) == NULL )
- {
- printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name);
- return -1;
- }
-
- /*
- * defensive. should not happen.
- */
-
- if (!(alias_info=main_dev->alias_info))
- {
- printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name);
- return -1;
- }
-
- /*
- * will the request also change device family?
- */
-
- o_nat = alias->nat;
- if (!o_nat)
- {
- printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name);
- return -1;
- }
-
- /*
- * point to new alias_type obj.
- */
-
- if (o_nat->type == sa->sa_family)
- n_nat = o_nat;
- else
- {
- n_nat = nat_getbytype(sa->sa_family);
- if (!n_nat)
- {
- printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
- return -1;
- }
- }
-
- /*
- * new hash key. if same as old AND same type (family) return;
- */
-
- n_hash = nat_hash_key(n_nat, sa);
- if (n_hash == alias->hash && o_nat == n_nat )
- return 0;
-
- /*
- * find alias in hashed list
- */
-
- for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp; aliasp = &(*aliasp)->next)
- if (*aliasp == alias) break;
-
- /*
- * not found (???). try a full search
- */
-
- if(!*aliasp)
- if ((aliasp = net_alias_slow_findp(alias_info, alias)))
- printk(KERN_WARNING "net_alias_rehash(%s): bad hashing recovered\n", alias->name);
- else
- {
- printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name);
- return -1;
- }
-
- save_flags(flags);
- cli();
-
- /*
- * if type (family) changed, unlink from old type object (o_nat)
- * will call o_nat->alias_done_1()
- */
-
- if (o_nat != n_nat)
- nat_unbind(o_nat, alias);
-
- /*
- * if diff hash key, change alias position in hashed list
- */
-
- if (n_hash != alias->hash)
- {
- *aliasp = (*aliasp)->next;
- alias->hash = n_hash;
- aliasp = &alias_info->hash_tab[n_hash];
- alias->next = *aliasp;
- *aliasp = alias;
- }
-
- /*
- * if type (family) changed link to new type object (n_nat)
- * will call n_nat->alias_init_1()
- */
-
- if (o_nat != n_nat)
- nat_bind(n_nat, alias, sa);
-
- restore_flags(flags);
- return 0;
+ struct net_alias_info *alias_info;
+ struct net_alias *alias, **aliasp;
+ struct device *main_dev;
+ unsigned long flags;
+ struct net_alias_type *o_nat, *n_nat;
+ unsigned n_hash;
+
+ /*
+ * Defensive ...
+ */
+
+ if (dev == NULL)
+ return -1;
+ if ( (alias = dev->my_alias) == NULL )
+ return -1;
+
+ if (!sa)
+ {
+ printk(KERN_ERR "net_alias_rehash(): NULL sockaddr passed\n");
+ return -1;
+ }
+
+ /*
+ * Defensive. should not happen.
+ */
+
+ if ( (main_dev = alias->main_dev) == NULL )
+ {
+ printk(KERN_ERR "net_alias_rehash for %s: NULL maindev\n", alias->name);
+ return -1;
+ }
+
+ /*
+ * Defensive. should not happen.
+ */
+
+ if (!(alias_info=main_dev->alias_info))
+ {
+ printk(KERN_ERR "net_alias_rehash for %s: NULL alias_info\n", alias->name);
+ return -1;
+ }
+
+ /*
+ * Will the request also change device family?
+ */
+
+ o_nat = alias->nat;
+ if (!o_nat)
+ {
+ printk(KERN_ERR "net_alias_rehash(%s): unbound alias.\n", alias->name);
+ return -1;
+ }
+
+ /*
+ * Point to new alias_type obj.
+ */
+
+ if (o_nat->type == sa->sa_family)
+ n_nat = o_nat;
+ else
+ {
+ n_nat = nat_getbytype(sa->sa_family);
+ if (!n_nat)
+ {
+ printk(KERN_ERR "net_alias_rehash(%s): unreg family==%d.\n", alias->name, sa->sa_family);
+ return -1;
+ }
+ }
+
+ /*
+ * New hash key. if same as old AND same type (family) return;
+ */
+
+ n_hash = nat_hash_key(n_nat, sa);
+ if (n_hash == alias->hash && o_nat == n_nat )
+ return 0;
+
+ /*
+ * Find alias in hashed list
+ */
+
+ for (aliasp = &alias_info->hash_tab[alias->hash]; *aliasp;
+ aliasp = &(*aliasp)->next)
+ {
+ if (*aliasp == alias)
+ break;
+ }
+
+ /*
+ * Not found (???). try a full search
+ */
+
+ if(!*aliasp)
+ {
+ if ((aliasp = net_alias_slow_findp(alias_info, alias)))
+ {
+ printk(KERN_WARNING
+ "net_alias_rehash(%s): bad hashing recovered\n", alias->name);
+ }
+ else
+ {
+ printk(KERN_ERR "net_alias_rehash(%s): unhashed alias!\n", alias->name);
+ return -1;
+ }
+ }
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * If type (family) changed, unlink from old type object (o_nat)
+ * Will call o_nat->alias_done_1()
+ */
+
+ if (o_nat != n_nat)
+ nat_unbind(o_nat, alias);
+
+ /*
+ * If diff hash key, change alias position in hashed list
+ */
+
+ if (n_hash != alias->hash)
+ {
+ *aliasp = (*aliasp)->next;
+ alias->hash = n_hash;
+ aliasp = &alias_info->hash_tab[n_hash];
+ alias->next = *aliasp;
+ *aliasp = alias;
+ }
+
+ /*
+ * If type (family) changed link to new type object (n_nat)
+ * will call n_nat->alias_init_1()
+ */
+
+ if (o_nat != n_nat)
+ nat_bind(n_nat, alias, sa);
+
+ restore_flags(flags);
+ return 0;
}
/*
- * implements /proc/net/alias_types entry
- * shows net_alias_type objects registered.
+ * Implements /proc/net/alias_types entry
+ * Shows net_alias_type objects registered.
*/
int net_alias_types_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
{
- off_t pos=0, begin=0;
- int len=0;
- struct net_alias_type *nat;
- unsigned idx;
- len=sprintf(buffer,"type name n_attach\n");
- for (idx=0 ; idx < 16 ; idx++)
- for (nat = nat_base[idx]; nat ; nat = nat->next)
- {
- len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
- nat->type, nat->name,nat->n_attach);
- pos=begin+len;
- if(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- }
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
+ off_t pos=0, begin=0;
+ int len=0;
+ struct net_alias_type *nat;
+ unsigned idx;
+ len=sprintf(buffer,"type name n_attach\n");
+ for (idx=0 ; idx < 16 ; idx++)
+ {
+ for (nat = nat_base[idx]; nat ; nat = nat->next)
+ {
+ len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
+ nat->type, nat->name,nat->n_attach);
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
/*
- * implements /proc/net/aliases entry, shows alias devices.
- * calls alias nat->alias_print_1 if not NULL and formats everything
- * to a fixed rec. size without using local (stack) buffers
+ * Implements /proc/net/aliases entry, shows alias devices.
+ * calls alias nat->alias_print_1 if not NULL and formats everything
+ * to a fixed rec. size without using local (stack) buffers
*
*/
#define NET_ALIASES_RECSIZ 64
-int net_alias_getinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+
+int net_alias_getinfo(char *buffer, char **start, off_t offset,
+ int length, int dummy)
{
- off_t pos=0, begin=0;
- int len=0;
- int dlen;
- struct net_alias_type *nat;
- struct net_alias *alias;
- struct device *dev;
-
- len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address");
- for (dev = dev_base; dev ; dev = dev->next)
- if (net_alias_is(dev))
- {
- alias = dev->my_alias;
- nat = alias->nat;
- dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
+ off_t pos=0, begin=0;
+ int len=0;
+ int dlen;
+ struct net_alias_type *nat;
+ struct net_alias *alias;
+ struct device *dev;
+
+ len=sprintf(buffer,"%-*s\n",NET_ALIASES_RECSIZ-1,"device family address");
+ for (dev = dev_base; dev ; dev = dev->next)
+ {
+ if (net_alias_is(dev))
+ {
+ alias = dev->my_alias;
+ nat = alias->nat;
+ dlen=sprintf(buffer+len, "%-16s %-6d ", alias->name, alias->dev.family);
- /*
- * call alias_type specific print function.
- */
+ /*
+ * Call alias_type specific print function.
+ */
- if (nat->alias_print_1)
- dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
- else
- dlen += sprintf(buffer+len+dlen, "-");
-
- /*
- * fill with spaces if needed
- */
+ if (nat->alias_print_1)
+ dlen += nat->alias_print_1(nat, alias, buffer+len+dlen, NET_ALIASES_RECSIZ - dlen);
+ else
+ dlen += sprintf(buffer+len+dlen, "-");
+
+ /*
+ * Fill with spaces if needed
+ */
- if (dlen < NET_ALIASES_RECSIZ) memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
- /*
- * truncate to NET_ALIASES_RECSIZ
- */
+ if (dlen < NET_ALIASES_RECSIZ)
+ memset(buffer+len+dlen, ' ', NET_ALIASES_RECSIZ - dlen);
+
+ /*
+ * Truncate to NET_ALIASES_RECSIZ
+ */
- len += NET_ALIASES_RECSIZ;
- buffer[len-1] = '\n';
+ len += NET_ALIASES_RECSIZ;
+ buffer[len-1] = '\n';
- pos=begin+len;
- if(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- }
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
}
/*
- * notifier for devices events
+ * Notifier for devices events
*/
int net_alias_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{
- struct device *dev = ptr;
+ struct device *dev = ptr;
- if (event == NETDEV_DOWN)
- {
+ if (event == NETDEV_DOWN)
+ {
#ifdef ALIAS_USER_LAND_DEBUG
- printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
+ printk("net_alias: NETDEV_DOWN for %s received\n", dev->name);
#endif
- if (net_alias_has(dev))
- net_alias_free(dev);
- }
+ if (net_alias_has(dev))
+ net_alias_free(dev);
+ }
- if (event == NETDEV_UP)
- {
+ if (event == NETDEV_UP)
+ {
#ifdef ALIAS_USER_LAND_DEBUG
- printk("net_alias: NETDEV_UP for %s received\n", dev->name);
+ printk("net_alias: NETDEV_UP for %s received\n", dev->name);
#endif
- dev->alias_info = 0;
- }
+ dev->alias_info = 0;
+ }
- return NOTIFY_DONE;
+ return NOTIFY_DONE;
}
/*
- * device aliases address comparison workhorse
- * no checks for nat and alias_info, must be !NULL
+ * Device aliases address comparison workhorse
+ * No checks for nat and alias_info, must be !NULL
*/
-static __inline__ struct device *
-nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
+extern __inline__ struct device *nat_addr_chk(struct net_alias_type *nat,
+ struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
{
- struct net_alias *alias;
- for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
- alias; alias = alias->next)
- {
- if (alias->dev.family != sa->sa_family) continue;
-
- /*
- * nat_dev_addr_chk_1 will call type specific address cmp function.
- */
+ struct net_alias *alias;
+ for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+ alias; alias = alias->next)
+ {
+ if (alias->dev.family != sa->sa_family)
+ continue;
+
+ /*
+ * Nat_dev_addr_chk_1 will call type specific address
+ * cmp function.
+ */
- if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
- nat_dev_addr_chk_1(nat,&alias->dev,sa))
- return &alias->dev;
- }
- return NULL;
+ if (alias->dev.flags & flags_on &&
+ !(alias->dev.flags & flags_off) &&
+ nat_dev_addr_chk_1(nat,&alias->dev,sa))
+ return &alias->dev;
+ }
+ return NULL;
}
/*
- * nat_addr_chk enough for protocols whose addr is (fully) stored at pa_addr.
- * note that nat pointer is ignored because of static comparison.
+ * Nat_addr_chk enough for protocols whose addr is (fully) stored at
+ * pa_addr. Note that nat pointer is ignored because of static comparison.
*/
-static __inline__ struct device *
-nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
+extern __inline__ struct device *nat_addr_chk32(struct net_alias_type *nat,
+ struct net_alias_info *alias_info, int family, __u32 addr32,
+ int flags_on, int flags_off)
{
- struct net_alias *alias;
- for (alias=alias_info->hash_tab[HASH(addr32,family)];
- alias; alias=alias->next)
- {
- if (alias->dev.family != family) continue;
-
- /*
- * "hard" (static) comparison between addr32 and pa_addr.
- */
+ struct net_alias *alias;
+ for (alias=alias_info->hash_tab[HASH(addr32,family)];
+ alias; alias=alias->next)
+ {
+ if (alias->dev.family != family)
+ continue;
+ /*
+ * "hard" (static) comparison between addr32 and pa_addr.
+ */
- if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
- addr32 == alias->dev.pa_addr)
- return &alias->dev;
- }
- return NULL;
+ if (alias->dev.flags & flags_on && !(alias->dev.flags & flags_off) &&
+ addr32 == alias->dev.pa_addr)
+ return &alias->dev;
+ }
+ return NULL;
}
/*
- * returns alias device with specified address AND flags_on AND flags_off,
- * else NULL.
- * intended for main devices.
+ * Returns alias device with specified address AND flags_on AND flags_off,
+ * else NULL.
+ * Intended for main devices.
*/
-struct device *
-net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int flags_off)
+struct device *net_alias_dev_chk(struct device *main_dev,
+ struct sockaddr *sa,int flags_on, int flags_off)
{
- struct net_alias_info *alias_info = main_dev->alias_info;
- struct net_alias_type *nat;
+ struct net_alias_info *alias_info = main_dev->alias_info;
+ struct net_alias_type *nat;
- /*
- * only if main_dev has aliases
- */
+ /*
+ * Only if main_dev has aliases
+ */
- if (!alias_info) return NULL;
+ if (!alias_info)
+ return NULL;
- /*
- * get alias_type object for sa->sa_family.
- */
+ /*
+ * Get alias_type object for sa->sa_family.
+ */
- nat = nat_getbytype(sa->sa_family);
- if (!nat)
- return NULL;
+ nat = nat_getbytype(sa->sa_family);
+ if (!nat)
+ return NULL;
- return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
+ return nat_addr_chk(nat, alias_info, sa, flags_on, flags_off);
}
/*
@@ -1102,180 +1144,199 @@ net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa,int flags_on, int
* at pa_addr.
*/
-struct device *
-net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32,
- int flags_on, int flags_off)
+struct device *net_alias_dev_chk32(struct device *main_dev, int family,
+ __u32 addr32, int flags_on, int flags_off)
{
- struct net_alias_info *alias_info = main_dev->alias_info;
+ struct net_alias_info *alias_info = main_dev->alias_info;
- /*
- * only if main_dev has aliases
- */
+ /*
+ * only if main_dev has aliases
+ */
- if (!alias_info) return NULL;
-
- return nat_addr_chk32(NULL, alias_info, family, addr32, flags_on, flags_off);
+ if (!alias_info)
+ return NULL;
+ return nat_addr_chk32(NULL, alias_info, family, addr32,
+ flags_on, flags_off);
}
/*
- * select closest (main or alias) device to <src,dst> addresses given. if no
- * further info is available, return main_dev (for easier calling arrangement).
+ * Select closest (main or alias) device to <src,dst> addresses given. If
+ * there is no further info available, return main_dev (for easier
+ * calling arrangement).
*
- * Should be called early at xxx_rcv() time for device selection
+ * Should be called early at xxx_rcv() time for device selection
*/
-struct device *
-net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+struct device *net_alias_dev_rcv_sel(struct device *main_dev,
+ struct sockaddr *sa_src, struct sockaddr *sa_dst)
{
- int family;
- struct net_alias_type *nat;
- struct net_alias_info *alias_info;
- struct device *dev;
+ int family;
+ struct net_alias_type *nat;
+ struct net_alias_info *alias_info;
+ struct device *dev;
- if (main_dev == NULL) return NULL;
+ if (main_dev == NULL)
+ return NULL;
- /*
- * if not aliased, don't bother any more
- */
+ /*
+ * If not aliased, don't bother any more
+ */
- if ((alias_info = main_dev->alias_info) == NULL)
- return main_dev;
+ if ((alias_info = main_dev->alias_info) == NULL)
+ return main_dev;
- /*
- * find out family
- */
+ /*
+ * Find out family
+ */
- family = (sa_src)? sa_src->sa_family : ((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
- if (family == AF_UNSPEC) return main_dev;
+ family = (sa_src)? sa_src->sa_family :
+ ((sa_dst)? sa_dst->sa_family : AF_UNSPEC);
- /*
- * get net_alias_type object for this family
- */
+ if (family == AF_UNSPEC)
+ return main_dev;
- if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+ /*
+ * Get net_alias_type object for this family
+ */
+
+ if ( (nat = nat_getbytype(family)) == NULL )
+ return main_dev;
- /*
- * first step: find out if dst addr is main_dev's or one of its aliases'
- */
+ /*
+ * First step: find out if dst addr is main_dev's or one of its
+ * aliases'
+ */
- if (sa_dst)
- {
- if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
- return main_dev;
+ if (sa_dst)
+ {
+ if (nat_dev_addr_chk_1(nat, main_dev,sa_dst))
+ return main_dev;
- dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
+ dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
- if (dev != NULL) return dev;
- }
+ if (dev != NULL)
+ return dev;
+ }
+
+ /*
+ * Second step: find the rcv addr 'closest' alias through nat
+ * method call
+ */
- /*
- * second step: find the rcv addr 'closest' alias through nat method call
- */
+ if ( sa_src == NULL || nat->dev_select == NULL)
+ return main_dev;
- if ( sa_src == NULL || nat->dev_select == NULL) return main_dev;
- dev = nat->dev_select(nat, main_dev, sa_src);
+ dev = nat->dev_select(nat, main_dev, sa_src);
- if (dev == NULL || dev->family != family) return main_dev;
+ if (dev == NULL || dev->family != family)
+ return main_dev;
- /*
- * dev ok only if it is alias of main_dev
- */
+ /*
+ * Dev ok only if it is alias of main_dev
+ */
- dev = net_alias_is(dev)?
- ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+ dev = net_alias_is(dev)?
+ ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
- /*
- * do not return NULL.
- */
+ /*
+ * Do not return NULL.
+ */
- return (dev)? dev : main_dev;
+ return (dev)? dev : main_dev;
}
/*
- * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
*/
-struct device *
-net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+struct device *net_alias_dev_rcv_sel32(struct device *main_dev, int family,
+ __u32 src, __u32 dst)
{
- struct net_alias_type *nat;
- struct net_alias_info *alias_info;
- struct sockaddr_in sin_src;
- struct device *dev;
+ struct net_alias_type *nat;
+ struct net_alias_info *alias_info;
+ struct sockaddr_in sin_src;
+ struct device *dev;
- if (main_dev == NULL) return NULL;
+ if (main_dev == NULL)
+ return NULL;
- /*
- * if not aliased, don't bother any more
- */
+ /*
+ * If not aliased, don't bother any more
+ */
- if ((alias_info = main_dev->alias_info) == NULL)
- return main_dev;
+ if ((alias_info = main_dev->alias_info) == NULL)
+ return main_dev;
- /*
- * early return if dst is main_dev's address
- */
+ /*
+ * Early return if dst is main_dev's address
+ */
- if (dst == main_dev->pa_addr)
- return main_dev;
+ if (dst == main_dev->pa_addr)
+ return main_dev;
- if (family == AF_UNSPEC) return main_dev;
+ if (family == AF_UNSPEC)
+ return main_dev;
- /*
- * get net_alias_type object for this family
- */
+ /*
+ * Get net_alias_type object for this family
+ */
- if ( (nat = nat_getbytype(family)) == NULL ) return main_dev;
+ if ( (nat = nat_getbytype(family)) == NULL )
+ return main_dev;
- /*
- * first step: find out if dst address one of main_dev aliases'
- */
+ /*
+ * First step: find out if dst address one of main_dev aliases'
+ */
- if (dst)
- {
- dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
- if (dev) return dev;
- }
+ if (dst)
+ {
+ dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
+ if (dev)
+ return dev;
+ }
- /*
- * second step: find the rcv addr 'closest' alias through nat method call
- */
+ /*
+ * Second step: find the rcv addr 'closest' alias through nat
+ * method call
+ */
- if ( src == 0 || nat->dev_select == NULL) return main_dev;
+ if ( src == 0 || nat->dev_select == NULL)
+ return main_dev;
- sin_src.sin_family = family;
- sin_src.sin_addr.s_addr = src;
+ sin_src.sin_family = family;
+ sin_src.sin_addr.s_addr = src;
- dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
+ dev = nat->dev_select(nat, main_dev, (struct sockaddr *)&sin_src);
- if (dev == NULL || dev->family != family) return main_dev;
+ if (dev == NULL || dev->family != family)
+ return main_dev;
- /*
- * dev ok only if it is alias of main_dev
- */
+ /*
+ * Dev ok only if it is alias of main_dev
+ */
- dev = net_alias_is(dev)?
- ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+ dev = net_alias_is(dev)?
+ ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
- /*
- * do not return NULL.
- */
-
- return (dev)? dev : main_dev;
+ /*
+ * Do not return NULL.
+ */
+ return (dev)? dev : main_dev;
}
/*
- * device event hook
+ * Device event hook
*/
-static struct notifier_block net_alias_dev_notifier = {
- net_alias_device_event,
- NULL,
- 0
+static struct notifier_block net_alias_dev_notifier =
+{
+ net_alias_device_event,
+ NULL,
+ 0
};
#ifndef ALIAS_USER_LAND_DEBUG
@@ -1296,93 +1357,95 @@ static struct proc_dir_entry proc_net_aliases = {
#endif
/*
- * net_alias initialisation
- * called from net_dev_init().
+ * Net_alias initialisation called from net_dev_init().
*/
-void net_alias_init(void)
+__initfunc(void net_alias_init(void))
{
- /*
- * register dev events notifier
- */
+ /*
+ * Register device events notifier
+ */
- register_netdevice_notifier(&net_alias_dev_notifier);
+ register_netdevice_notifier(&net_alias_dev_notifier);
- /*
- * register /proc/net entries
- */
+ /*
+ * Register /proc/net entries
+ */
#ifndef ALIAS_USER_LAND_DEBUG
#ifdef CONFIG_PROC_FS
- proc_net_register(&proc_net_alias_types);
- proc_net_register(&proc_net_aliases);
+ proc_net_register(&proc_net_alias_types);
+ proc_net_register(&proc_net_aliases);
#endif
#endif
}
/*
- * net_alias type object registering func.
+ * Net_alias type object registering func.
*/
+
int register_net_alias_type(struct net_alias_type *nat, int type)
{
- unsigned hash;
- unsigned long flags;
- if (!nat)
- {
- printk(KERN_ERR "register_net_alias_type(): NULL arg\n");
- return -EINVAL;
- }
- nat->type = type;
- nat->n_attach = 0;
- hash = nat->type & 0x0f;
- save_flags(flags);
- cli();
- nat->next = nat_base[hash];
- nat_base[hash] = nat;
- restore_flags(flags);
- return 0;
+ unsigned hash;
+ unsigned long flags;
+ if (!nat)
+ {
+ printk(KERN_ERR "register_net_alias_type(): NULL arg\n");
+ return -EINVAL;
+ }
+ nat->type = type;
+ nat->n_attach = 0;
+ hash = nat->type & 0x0f;
+ save_flags(flags);
+ cli();
+ nat->next = nat_base[hash];
+ nat_base[hash] = nat;
+ restore_flags(flags);
+ return 0;
}
/*
- * net_alias type object unreg.
+ * Net_alias type object unreg.
*/
+
int unregister_net_alias_type(struct net_alias_type *nat)
{
- struct net_alias_type **natp;
- unsigned hash;
- unsigned long flags;
-
- if (!nat)
- {
- printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n");
- return -EINVAL;
- }
-
- /*
- * only allow unregistration if it has no attachments
- */
- if (nat->n_attach)
- {
- printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n",
- nat->n_attach);
- return -EINVAL;
- }
- hash = nat->type & 0x0f;
- save_flags(flags);
- cli();
- for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
- {
- if (nat==(*natp))
- {
- *natp = nat->next;
- restore_flags(flags);
- return 0;
- }
- }
- restore_flags(flags);
- printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type);
- return -EINVAL;
+ struct net_alias_type **natp;
+ unsigned hash;
+ unsigned long flags;
+
+ if (!nat)
+ {
+ printk(KERN_ERR "unregister_net_alias_type(): NULL arg\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Only allow unregistration if it has no attachments
+ */
+
+ if (nat->n_attach)
+ {
+ printk(KERN_ERR "unregister_net_alias_type(): has %d attachments. failed\n",
+ nat->n_attach);
+ return -EINVAL;
+ }
+ hash = nat->type & 0x0f;
+ save_flags(flags);
+ cli();
+ for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+ {
+ if (nat==(*natp))
+ {
+ *natp = nat->next;
+ restore_flags(flags);
+ return 0;
+ }
+ }
+ restore_flags(flags);
+ printk(KERN_ERR "unregister_net_alias_type(type=%d): not found!\n", nat->type);
+ return -EINVAL;
}
diff --git a/net/core/scm.c b/net/core/scm.c
new file mode 100644
index 000000000..3aa0c7b17
--- /dev/null
+++ b/net/core/scm.c
@@ -0,0 +1,308 @@
+/* scm.c - Socket level control messages processing.
+ *
+ * Author: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/rarp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/scm.h>
+
+
+/*
+ * Only allow a user to send credentials, that they could set with
+ * setu(g)id.
+ */
+
+static __inline__ int scm_check_creds(struct ucred *creds)
+{
+ if (suser())
+ return 0;
+ if (creds->pid != current->pid ||
+ (creds->uid != current->uid && creds->uid != current->euid &&
+ creds->uid != current->suid) ||
+ (creds->gid != current->gid && creds->gid != current->egid &&
+ creds->gid != current->sgid))
+ return -EPERM;
+ return 0;
+}
+
+
+static int scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+{
+ int num;
+ struct scm_fp_list *fpl = *fplp;
+ struct file **fpp;
+ int *fdp = (int*)cmsg->cmsg_data;
+ int i;
+
+ num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int);
+
+ if (!num)
+ return 0;
+
+ if (num > SCM_MAX_FD)
+ return -EINVAL;
+
+ if (!fpl)
+ {
+ fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
+ if (!fpl)
+ return -ENOMEM;
+ *fplp = fpl;
+ fpl->count = 0;
+ }
+ fpp = &fpl->fp[fpl->count];
+
+ if (fpl->count + num > SCM_MAX_FD)
+ return -EINVAL;
+
+ /*
+ * Verify the descriptors.
+ */
+
+ for (i=0; i< num; i++)
+ {
+ int fd;
+
+ fd = fdp[i];
+ if (fd < 0 || fd >= NR_OPEN)
+ return -EBADF;
+ if (current->files->fd[fd]==NULL)
+ return -EBADF;
+ fpp[i] = current->files->fd[fd];
+ }
+
+ /* add another reference to these files */
+ for (i=0; i< num; i++, fpp++)
+ (*fpp)->f_count++;
+ fpl->count += num;
+
+ return num;
+}
+
+void __scm_destroy(struct scm_cookie *scm)
+{
+ int i;
+ struct scm_fp_list *fpl = scm->fp;
+
+ if (!fpl)
+ return;
+
+ for (i=fpl->count-1; i>=0; i--)
+ close_fp(fpl->fp[i]);
+
+ kfree(fpl);
+}
+
+
+
+extern __inline__ int not_one_bit(unsigned val)
+{
+ return (val-1) & val;
+}
+
+
+int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
+{
+ int err;
+ struct cmsghdr *cmsg;
+ struct file *file;
+ int acc_fd;
+ unsigned scm_flags=0;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
+ {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+
+ err = -EINVAL;
+
+ switch (cmsg->cmsg_type)
+ {
+ case SCM_RIGHTS:
+ err=scm_fp_copy(cmsg, &p->fp);
+ if (err<0)
+ goto error;
+ break;
+ case SCM_CREDENTIALS:
+ if (cmsg->cmsg_len < sizeof(*cmsg) + sizeof(struct ucred))
+ goto error;
+ memcpy(&p->creds, cmsg->cmsg_data, sizeof(struct ucred));
+ err = scm_check_creds(&p->creds);
+ if (err)
+ goto error;
+ break;
+ case SCM_CONNECT:
+ if (scm_flags)
+ goto error;
+ if (cmsg->cmsg_len < sizeof(*cmsg) + sizeof(int))
+ goto error;
+ memcpy(&acc_fd, cmsg->cmsg_data, sizeof(int));
+ p->sock = NULL;
+ if (acc_fd != -1) {
+ if (acc_fd < 0 || acc_fd >= NR_OPEN ||
+ (file=current->files->fd[acc_fd])==NULL)
+ return -EBADF;
+ if (!file->f_inode || !file->f_inode->i_sock)
+ return -ENOTSOCK;
+ p->sock = &file->f_inode->u.socket_i;
+ if (p->sock->state != SS_UNCONNECTED)
+ return -EINVAL;
+ }
+ scm_flags |= MSG_SYN;
+ break;
+ default:
+ goto error;
+ }
+ }
+
+ if (p->fp && !p->fp->count)
+ {
+ kfree(p->fp);
+ p->fp = NULL;
+ }
+
+ err = -EINVAL;
+ msg->msg_flags |= scm_flags;
+ scm_flags = msg->msg_flags&MSG_CTLFLAGS;
+ if (not_one_bit(scm_flags))
+ goto error;
+
+ if (!(scm_flags && p->fp))
+ return 0;
+
+error:
+ scm_destroy(p);
+ return err;
+}
+
+void put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
+{
+ struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+ int cmlen = sizeof(*cm) + len;
+ int err;
+
+ if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
+ msg->msg_flags |= MSG_CTRUNC;
+ return;
+ }
+ if (msg->msg_controllen < cmlen) {
+ msg->msg_flags |= MSG_CTRUNC;
+ cmlen = msg->msg_controllen;
+ }
+ err = put_user(level, &cm->cmsg_level);
+ if (!err)
+ err = put_user(type, &cm->cmsg_type);
+ if (!err)
+ err = put_user(cmlen, &cm->cmsg_len);
+ if (!err)
+ err = copy_to_user(cm->cmsg_data, data, cmlen - sizeof(*cm));
+ if (!err) {
+ cmlen = CMSG_ALIGN(cmlen);
+ msg->msg_control += cmlen;
+ msg->msg_controllen -= cmlen;
+ }
+}
+
+void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
+{
+ struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+
+ int fdmax = (msg->msg_controllen - sizeof(struct cmsghdr))/sizeof(int);
+ int fdnum = scm->fp->count;
+ int *cmfptr;
+ int err = 0;
+ int i;
+ struct file **fp = scm->fp->fp;
+
+ if (fdnum > fdmax)
+ fdmax = fdnum;
+
+ for (i=0, cmfptr=(int*)cm->cmsg_data; i<fdmax; i++, cmfptr++)
+ {
+ int new_fd = get_unused_fd();
+ if (new_fd < 0)
+ break;
+ current->files->fd[new_fd] = fp[i];
+ err = put_user(new_fd, cmfptr);
+ cmfptr++;
+ }
+
+ if (i > 0)
+ {
+ int cmlen = i*sizeof(int) + sizeof(struct cmsghdr);
+ if (!err)
+ err = put_user(SOL_SOCKET, &cm->cmsg_level);
+ if (!err)
+ err = put_user(SCM_RIGHTS, &cm->cmsg_type);
+ if (!err)
+ err = put_user(cmlen, &cm->cmsg_len);
+ if (!err) {
+ cmlen = CMSG_ALIGN(cmlen);
+ msg->msg_control += cmlen;
+ msg->msg_controllen -= cmlen;
+ }
+ }
+
+ if (err)
+ i = 0;
+
+ /*
+ * Dump those that don't fit.
+ */
+ for ( ; i < fdnum; i++) {
+ msg->msg_flags |= MSG_CTRUNC;
+ close_fp(fp[i]);
+ }
+
+ kfree (scm->fp);
+ scm->fp = NULL;
+}
+
+struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl)
+{
+ int i;
+ struct scm_fp_list *new_fpl;
+
+ if (!fpl)
+ return NULL;
+
+ new_fpl = kmalloc(fpl->count*sizeof(int) + sizeof(*fpl), GFP_KERNEL);
+ if (!new_fpl)
+ return NULL;
+
+ memcpy(new_fpl, fpl, fpl->count*sizeof(int) + sizeof(*fpl));
+
+ for (i=fpl->count-1; i>=0; i--)
+ fpl->fp[i]->f_count++;
+
+ return new_fpl;
+}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c90d8d4e2..00a87e0e2 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -15,12 +15,13 @@
* Alan Cox : Added all the changed routines Linus
* only put in the headers
* Ray VanTassle : Fixed --skb->lock in free
+ * Alan Cox : skb_copy copy arp field
*
- * TO FIX:
- * The __skb_ routines ought to check interrupts are disabled
- * when called, and bitch like crazy if not. Unfortunately I don't think
- * we currently have a portable way to check if interrupts are off -
- * Linus ???
+ * NOTE:
+ * The __skb_ routines should be called with interrupts
+ * disabled, or you better be *real* sure that the operation is atomic
+ * with respect to whatever list is being frobbed (e.g. via lock_sock()
+ * or via disabling bottom half handlers, etc).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -46,9 +47,8 @@
#include <linux/skbuff.h>
#include <net/ip.h>
-#include <net/ipv6.h>
#include <net/protocol.h>
-#include <net/route.h>
+#include <net/dst.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <net/sock.h>
@@ -60,11 +60,9 @@
* Resource tracking variables
*/
-atomic_t net_skbcount = 0;
-atomic_t net_locked = 0;
-atomic_t net_allocs = 0;
-atomic_t net_fails = 0;
-atomic_t net_free_locked = 0;
+static atomic_t net_skbcount = ATOMIC_INIT(0);
+static atomic_t net_allocs = ATOMIC_INIT(0);
+static atomic_t net_fails = ATOMIC_INIT(0);
extern atomic_t ip_frag_mem;
@@ -77,602 +75,80 @@ char *skb_put_errstr ="skput:over: %p:%d";
void show_net_buffers(void)
{
- printk(KERN_INFO "Networking buffers in use : %u\n",net_skbcount);
- printk(KERN_INFO "Network buffers locked by drivers : %u\n",net_locked);
- printk(KERN_INFO "Total network buffer allocations : %u\n",net_allocs);
- printk(KERN_INFO "Total failed network buffer allocs : %u\n",net_fails);
- printk(KERN_INFO "Total free while locked events : %u\n",net_free_locked);
+ printk(KERN_INFO "Networking buffers in use : %u\n",
+ atomic_read(&net_skbcount));
+ printk(KERN_INFO "Total network buffer allocations : %u\n",
+ atomic_read(&net_allocs));
+ printk(KERN_INFO "Total failed network buffer allocs : %u\n",
+ atomic_read(&net_fails));
#ifdef CONFIG_INET
- printk(KERN_INFO "IP fragment buffer size : %u\n",ip_frag_mem);
+ printk(KERN_INFO "IP fragment buffer size : %u\n",
+ atomic_read(&ip_frag_mem));
#endif
}
-#if CONFIG_SKB_CHECK
-
-/*
- * Debugging paranoia. Can go later when this crud stack works
- */
-
-int skb_check(struct sk_buff *skb, int head, int line, char *file)
-{
- if (head) {
- if (skb->magic_debug_cookie != SK_HEAD_SKB) {
- printk("File: %s Line %d, found a bad skb-head\n",
- file,line);
- return -1;
- }
- if (!skb->next || !skb->prev) {
- printk("skb_check: head without next or prev\n");
- return -1;
- }
- if (skb->next->magic_debug_cookie != SK_HEAD_SKB
- && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
- printk("File: %s Line %d, bad next head-skb member\n",
- file,line);
- return -1;
- }
- if (skb->prev->magic_debug_cookie != SK_HEAD_SKB
- && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
- printk("File: %s Line %d, bad prev head-skb member\n",
- file,line);
- return -1;
- }
-#if 0
- {
- struct sk_buff *skb2 = skb->next;
- int i = 0;
- while (skb2 != skb && i < 5) {
- if (skb_check(skb2, 0, line, file) < 0) {
- printk("bad queue element in whole queue\n");
- return -1;
- }
- i++;
- skb2 = skb2->next;
- }
- }
-#endif
- return 0;
- }
- if (skb->next != NULL && skb->next->magic_debug_cookie != SK_HEAD_SKB
- && skb->next->magic_debug_cookie != SK_GOOD_SKB) {
- printk("File: %s Line %d, bad next skb member\n",
- file,line);
- return -1;
- }
- if (skb->prev != NULL && skb->prev->magic_debug_cookie != SK_HEAD_SKB
- && skb->prev->magic_debug_cookie != SK_GOOD_SKB) {
- printk("File: %s Line %d, bad prev skb member\n",
- file,line);
- return -1;
- }
-
-
- if(skb->magic_debug_cookie==SK_FREED_SKB)
- {
- printk("File: %s Line %d, found a freed skb lurking in the undergrowth!\n",
- file,line);
- printk("skb=%p, real size=%d, free=%d\n",
- skb,skb->truesize,skb->free);
- return -1;
- }
- if(skb->magic_debug_cookie!=SK_GOOD_SKB)
- {
- printk("File: %s Line %d, passed a non skb!\n", file,line);
- printk("skb=%p, real size=%d, free=%d\n",
- skb,skb->truesize,skb->free);
- return -1;
- }
- if(skb->head>skb->data)
- {
- printk("File: %s Line %d, head > data !\n", file,line);
- printk("skb=%p, head=%p, data=%p\n",
- skb,skb->head,skb->data);
- return -1;
- }
- if(skb->tail>skb->end)
- {
- printk("File: %s Line %d, tail > end!\n", file,line);
- printk("skb=%p, tail=%p, end=%p\n",
- skb,skb->tail,skb->end);
- return -1;
- }
- if(skb->data>skb->tail)
- {
- printk("File: %s Line %d, data > tail!\n", file,line);
- printk("skb=%p, data=%p, tail=%p\n",
- skb,skb->data,skb->tail);
- return -1;
- }
- if(skb->tail-skb->data!=skb->len)
- {
- printk("File: %s Line %d, wrong length\n", file,line);
- printk("skb=%p, data=%p, end=%p len=%ld\n",
- skb,skb->data,skb->end,skb->len);
- return -1;
- }
- if((unsigned long) skb->end > (unsigned long) skb)
- {
- printk("File: %s Line %d, control overrun\n", file,line);
- printk("skb=%p, end=%p\n",
- skb,skb->end);
- return -1;
- }
-
- /* Guess it might be acceptable then */
- return 0;
-}
-#endif
-
-
-#if CONFIG_SKB_CHECK
-void skb_queue_head_init(struct sk_buff_head *list)
-{
- list->prev = (struct sk_buff *)list;
- list->next = (struct sk_buff *)list;
- list->qlen = 0;
- list->magic_debug_cookie = SK_HEAD_SKB;
-}
-
-
-/*
- * Insert an sk_buff at the start of a list.
- */
-void skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
-{
- unsigned long flags;
- struct sk_buff *list = (struct sk_buff *)list_;
-
- save_flags(flags);
- cli();
-
- IS_SKB(newsk);
- IS_SKB_HEAD(list);
- if (newsk->next || newsk->prev)
- printk("Suspicious queue head: sk_buff on list!\n");
-
- newsk->next = list->next;
- newsk->prev = list;
-
- newsk->next->prev = newsk;
- newsk->prev->next = newsk;
- newsk->list = list_;
- list_->qlen++;
-
- restore_flags(flags);
-}
-
-void __skb_queue_head(struct sk_buff_head *list_,struct sk_buff *newsk)
-{
- struct sk_buff *list = (struct sk_buff *)list_;
-
-
- IS_SKB(newsk);
- IS_SKB_HEAD(list);
- if (newsk->next || newsk->prev)
- printk("Suspicious queue head: sk_buff on list!\n");
-
- newsk->next = list->next;
- newsk->prev = list;
-
- newsk->next->prev = newsk;
- newsk->prev->next = newsk;
- newsk->list = list_;
- list_->qlen++;
-
-}
-
-/*
- * Insert an sk_buff at the end of a list.
- */
-void skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
-{
- unsigned long flags;
- struct sk_buff *list = (struct sk_buff *)list_;
-
- save_flags(flags);
- cli();
-
- if (newsk->next || newsk->prev)
- printk("Suspicious queue tail: sk_buff on list!\n");
- IS_SKB(newsk);
- IS_SKB_HEAD(list);
-
- newsk->next = list;
- newsk->prev = list->prev;
-
- newsk->next->prev = newsk;
- newsk->prev->next = newsk;
-
- newsk->list = list_;
- list_->qlen++;
-
- restore_flags(flags);
-}
-
-void __skb_queue_tail(struct sk_buff_head *list_, struct sk_buff *newsk)
-{
- struct sk_buff *list = (struct sk_buff *)list_;
-
- if (newsk->next || newsk->prev)
- printk("Suspicious queue tail: sk_buff on list!\n");
- IS_SKB(newsk);
- IS_SKB_HEAD(list);
-
- newsk->next = list;
- newsk->prev = list->prev;
-
- newsk->next->prev = newsk;
- newsk->prev->next = newsk;
-
- newsk->list = list_;
- list_->qlen++;
-}
-
-/*
- * Remove an sk_buff from a list. This routine is also interrupt safe
- * so you can grab read and free buffers as another process adds them.
- */
-
-struct sk_buff *skb_dequeue(struct sk_buff_head *list_)
-{
- unsigned long flags;
- struct sk_buff *result;
- struct sk_buff *list = (struct sk_buff *)list_;
-
- save_flags(flags);
- cli();
-
- IS_SKB_HEAD(list);
-
- result = list->next;
- if (result == list) {
- restore_flags(flags);
- return NULL;
- }
-
- result->next->prev = list;
- list->next = result->next;
-
- result->next = NULL;
- result->prev = NULL;
- list_->qlen--;
- result->list = NULL;
-
- restore_flags(flags);
-
- IS_SKB(result);
- return result;
-}
-
-struct sk_buff *__skb_dequeue(struct sk_buff_head *list_)
-{
- struct sk_buff *result;
- struct sk_buff *list = (struct sk_buff *)list_;
-
- IS_SKB_HEAD(list);
-
- result = list->next;
- if (result == list) {
- return NULL;
- }
-
- result->next->prev = list;
- list->next = result->next;
-
- result->next = NULL;
- result->prev = NULL;
- list_->qlen--;
- result->list = NULL;
-
- IS_SKB(result);
- return result;
-}
-
-/*
- * Insert a packet before another one in a list.
- */
-void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
-{
- unsigned long flags;
-
- IS_SKB(old);
- IS_SKB(newsk);
-
- if(!old->next || !old->prev)
- printk("insert before unlisted item!\n");
- if(newsk->next || newsk->prev)
- printk("inserted item is already on a list.\n");
-
- save_flags(flags);
- cli();
- newsk->next = old;
- newsk->prev = old->prev;
- old->prev = newsk;
- newsk->prev->next = newsk;
- newsk->list = old->list;
- newsk->list->qlen++;
-
- restore_flags(flags);
-}
-
-/*
- * Insert a packet before another one in a list.
- */
-
-void __skb_insert(struct sk_buff *newsk,
- struct sk_buff * prev, struct sk_buff *next,
- struct sk_buff_head * list)
-{
- IS_SKB(prev);
- IS_SKB(newsk);
- IS_SKB(next);
-
- if(!prev->next || !prev->prev)
- printk("insert after unlisted item!\n");
- if(!next->next || !next->prev)
- printk("insert before unlisted item!\n");
- if(newsk->next || newsk->prev)
- printk("inserted item is already on a list.\n");
-
- newsk->next = next;
- newsk->prev = prev;
- next->prev = newsk;
- prev->next = newsk;
- newsk->list = list;
- list->qlen++;
-
-}
-
-/*
- * Place a packet after a given packet in a list.
- */
-void skb_append(struct sk_buff *old, struct sk_buff *newsk)
-{
- unsigned long flags;
-
- IS_SKB(old);
- IS_SKB(newsk);
-
- if(!old->next || !old->prev)
- printk("append before unlisted item!\n");
- if(newsk->next || newsk->prev)
- printk("append item is already on a list.\n");
-
- save_flags(flags);
- cli();
-
- newsk->prev = old;
- newsk->next = old->next;
- newsk->next->prev = newsk;
- old->next = newsk;
- newsk->list = old->list;
- newsk->list->qlen++;
-
- restore_flags(flags);
-}
-
-/*
- * Remove an sk_buff from its list. Works even without knowing the list it
- * is sitting on, which can be handy at times. It also means that THE LIST
- * MUST EXIST when you unlink. Thus a list must have its contents unlinked
- * _FIRST_.
- */
-void skb_unlink(struct sk_buff *skb)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- IS_SKB(skb);
-
- if(skb->list)
- {
- skb->list->qlen--;
- skb->next->prev = skb->prev;
- skb->prev->next = skb->next;
- skb->next = NULL;
- skb->prev = NULL;
- skb->list = NULL;
- }
-#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
- else
- printk("skb_unlink: not a linked element\n");
-#endif
- restore_flags(flags);
-}
-
-void __skb_unlink(struct sk_buff *skb)
-{
- IS_SKB(skb);
-
- if(skb->list)
- {
- skb->list->qlen--;
- skb->next->prev = skb->prev;
- skb->prev->next = skb->next;
- skb->next = NULL;
- skb->prev = NULL;
- skb->list = NULL;
- }
-#ifdef PARANOID_BUGHUNT_MODE /* This is legal but we sometimes want to watch it */
- else
- printk("skb_unlink: not a linked element\n");
-#endif
-}
-
/*
- * Add data to an sk_buff
+ * Free an sk_buff. Release anything attached to the buffer.
*/
-
-unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
-{
- unsigned char *tmp=skb->tail;
- IS_SKB(skb);
- skb->tail+=len;
- skb->len+=len;
- IS_SKB(skb);
- if(skb->tail>skb->end)
- panic("skput:over: %p:%d", return_address(),len);
- return tmp;
-}
-unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
+void __kfree_skb(struct sk_buff *skb)
{
- IS_SKB(skb);
- skb->data-=len;
- skb->len+=len;
- IS_SKB(skb);
- if(skb->data<skb->head)
- panic("skpush:under: %p:%d", return_address(),len);
- return skb->data;
-}
-
-unsigned char * skb_pull(struct sk_buff *skb, unsigned int len)
-{
- IS_SKB(skb);
- if(len>skb->len)
- return 0;
- skb->data+=len;
- skb->len-=len;
- return skb->data;
-}
-
-int skb_headroom(struct sk_buff *skb)
-{
- IS_SKB(skb);
- return skb->data-skb->head;
-}
-
-int skb_tailroom(struct sk_buff *skb)
-{
- IS_SKB(skb);
- return skb->end-skb->tail;
-}
-
-void skb_reserve(struct sk_buff *skb, unsigned int len)
-{
- IS_SKB(skb);
- skb->data+=len;
- skb->tail+=len;
- if(skb->tail>skb->end)
- panic("sk_res: over");
- if(skb->data<skb->head)
- panic("sk_res: under");
- IS_SKB(skb);
-}
-
-void skb_trim(struct sk_buff *skb, unsigned int len)
-{
- IS_SKB(skb);
- if(skb->len>len)
- {
- skb->len=len;
- skb->tail=skb->data+len;
- }
-}
-
-
-
-#endif
-
-/*
- * Free an sk_buff. This still knows about things it should
- * not need to like protocols and sockets.
- */
-
-void kfree_skb(struct sk_buff *skb, int rw)
-{
- if (skb == NULL)
- {
- printk(KERN_CRIT "kfree_skb: skb = NULL (from %p)\n",
- return_address());
- return;
- }
-#if CONFIG_SKB_CHECK
- IS_SKB(skb);
-#endif
- if (skb->lock)
- {
- skb->free = 3; /* Free when unlocked */
- net_free_locked++;
- return;
- }
- if (skb->free == 2)
- printk(KERN_WARNING "Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
- return_address());
if (skb->list)
- printk(KERN_WARNING "Warning: kfree_skb passed an skb still on a list (from %p).\n",
- return_address());
+ printk(KERN_WARNING "Warning: kfree_skb passed an skb still "
+ "on a list (from %p).\n", __builtin_return_address(0));
+ dst_release(skb->dst);
if(skb->destructor)
skb->destructor(skb);
- if (skb->sk)
- {
- struct sock * sk = skb->sk;
- if(sk->prot!=NULL)
- {
- if (rw)
- sock_rfree(sk, skb);
- else
- sock_wfree(sk, skb);
-
- }
- else
- {
- if (rw)
- atomic_sub(skb->truesize, &sk->rmem_alloc);
- else {
- if(!sk->dead)
- sk->write_space(sk);
- atomic_sub(skb->truesize, &sk->wmem_alloc);
- }
- kfree_skbmem(skb);
- }
- }
- else
- kfree_skbmem(skb);
+ kfree_skbmem(skb);
}
/*
* Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
* fields and also do memory statistics to find all the [BEEP] leaks.
+ *
+ * Note: For now we put the header after the data to get better cache
+ * usage. Once we have a good cache aware kmalloc this will cease
+ * to be a good idea.
*/
+
struct sk_buff *alloc_skb(unsigned int size,int priority)
{
struct sk_buff *skb;
- int len;
unsigned char *bptr;
+ int len;
- if (intr_count && priority!=GFP_ATOMIC)
- {
+ if (in_interrupt() && priority!=GFP_ATOMIC) {
static int count = 0;
if (++count < 5) {
- printk(KERN_ERR "alloc_skb called nonatomically from interrupt %p\n",
- return_address());
+ printk(KERN_ERR "alloc_skb called nonatomically "
+ "from interrupt %p\n", __builtin_return_address(0));
priority = GFP_ATOMIC;
}
}
- size=(size+15)&~15; /* Allow for alignments. Make a multiple of 16 bytes */
+ /*
+ * FIXME: We could do with an architecture dependant
+ * 'alignment mask'.
+ */
+
+ /* Allow for alignments. Make a multiple of 16 bytes */
+ size = (size + 15) & ~15;
len = size;
- size+=sizeof(struct sk_buff); /* And stick the control itself on the end */
+ /* And stick the control itself on the end */
+ size += sizeof(struct sk_buff);
/*
* Allocate some space
*/
- bptr=(unsigned char *)kmalloc(size,priority);
- if (bptr == NULL)
- {
- net_fails++;
+ bptr = kmalloc(size,priority);
+ if (bptr == NULL) {
+ atomic_inc(&net_fails);
return NULL;
}
-#ifdef PARANOID_BUGHUNT_MODE
- if(skb->magic_debug_cookie == SK_GOOD_SKB)
- printk("Kernel kmalloc handed us an existing skb (%p)\n",skb);
-#endif
+
/*
* Now we play a little game with the caches. Linux kmalloc is
* a bit cache dumb, in fact its just about maximally non
@@ -680,38 +156,35 @@ struct sk_buff *alloc_skb(unsigned int size,int priority)
* by doing the following. Which is to deliberately put the
* skb at the _end_ not the start of the memory block.
*/
- net_allocs++;
+ atomic_inc(&net_allocs);
- skb=(struct sk_buff *)(bptr+size)-1;
+ skb = (struct sk_buff *)(bptr + size) - 1;
- skb->count = 1; /* only one reference to this */
- skb->data_skb = NULL; /* and we're our own data skb */
+ atomic_set(&skb->count, 1); /* only one reference to this */
+ skb->data_skb = skb; /* and we're our own data skb */
- skb->free = 2; /* Invalid so we pick up forgetful users */
- skb->lock = 0;
skb->pkt_type = PACKET_HOST; /* Default type */
skb->pkt_bridged = 0; /* Not bridged */
- skb->prev = skb->next = skb->link3 = NULL;
+ skb->prev = skb->next = NULL;
skb->list = NULL;
skb->sk = NULL;
skb->truesize=size;
- skb->localroute=0;
skb->stamp.tv_sec=0; /* No idea about time */
- skb->localroute = 0;
skb->ip_summed = 0;
- memset(skb->proto_priv, 0, sizeof(skb->proto_priv));
- net_skbcount++;
-#if CONFIG_SKB_CHECK
- skb->magic_debug_cookie = SK_GOOD_SKB;
-#endif
- skb->users = 0;
- /* Load the data pointers */
- skb->head=bptr;
- skb->data=bptr;
- skb->tail=bptr;
- skb->end=bptr+len;
- skb->len=0;
- skb->destructor=NULL;
+ skb->security = 0; /* By default packets are insecure */
+ skb->dst = NULL;
+ skb->destructor = NULL;
+ memset(skb->cb, 0, sizeof(skb->cb));
+ skb->priority = SOPRI_NORMAL;
+ atomic_inc(&net_skbcount);
+ atomic_set(&skb->users, 1);
+
+ /* Load the data pointers. */
+ skb->head = bptr;
+ skb->data = bptr;
+ skb->tail = bptr;
+ skb->end = bptr + len;
+ skb->len = 0;
skb->inclone = 0;
return skb;
}
@@ -720,7 +193,7 @@ struct sk_buff *alloc_skb(unsigned int size,int priority)
* Free an skbuff by memory
*/
-static inline void __kfree_skbmem(struct sk_buff *skb)
+extern inline void __kfree_skbmem(struct sk_buff *skb)
{
/* don't do anything if somebody still uses us */
if (atomic_dec_and_test(&skb->count)) {
@@ -735,13 +208,10 @@ void kfree_skbmem(struct sk_buff *skb)
/* don't do anything if somebody still uses us */
if (atomic_dec_and_test(&skb->count)) {
-
- int free_head;
-
- free_head = (skb->inclone != SKB_CLONE_INLINE);
+ int free_head = (skb->inclone != SKB_CLONE_INLINE);
/* free the skb that contains the actual data if we've clone()'d */
- if (skb->data_skb) {
+ if (skb->data_skb != skb) {
addr = skb;
__kfree_skbmem(skb->data_skb);
}
@@ -752,8 +222,7 @@ void kfree_skbmem(struct sk_buff *skb)
}
/*
- * Duplicate an sk_buff. The new one is not owned by a socket or locked
- * and will be freed on deletion.
+ * Duplicate an sk_buff. The new one is not owned by a socket.
*/
struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
@@ -761,36 +230,31 @@ struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
struct sk_buff *n;
int inbuff = 0;
- IS_SKB(skb);
- if (skb_tailroom(skb) >= sizeof(struct sk_buff))
- {
+ if (!skb->inclone && skb_tailroom(skb) >= sizeof(struct sk_buff)) {
n = ((struct sk_buff *) skb->end) - 1;
skb->end -= sizeof(struct sk_buff);
skb->inclone = SKB_CLONE_ORIG;
inbuff = SKB_CLONE_INLINE;
- }
- else
- {
+ } else {
n = kmalloc(sizeof(*n), priority);
if (!n)
return NULL;
}
memcpy(n, skb, sizeof(*n));
- n->count = 1;
- if (skb->data_skb)
- skb = skb->data_skb;
+ atomic_set(&n->count, 1);
+ skb = skb->data_skb;
atomic_inc(&skb->count);
atomic_inc(&net_allocs);
atomic_inc(&net_skbcount);
+ dst_clone(n->dst);
n->data_skb = skb;
- n->next = n->prev = n->link3 = NULL;
+ n->next = n->prev = NULL;
n->list = NULL;
n->sk = NULL;
- n->free = 1;
n->tries = 0;
- n->lock = 0;
- n->users = 0;
+ atomic_set(&n->users, 1);
n->inclone = inbuff;
+ n->destructor = NULL;
return n;
}
@@ -807,8 +271,6 @@ struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
* Allocate the copy buffer
*/
- IS_SKB(skb);
-
n=alloc_skb(skb->end - skb->head, priority);
if(n==NULL)
return NULL;
@@ -825,82 +287,83 @@ struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
skb_put(n,skb->len);
/* Copy the bytes */
memcpy(n->head,skb->head,skb->end-skb->head);
- n->link3=NULL;
n->list=NULL;
n->sk=NULL;
n->when=skb->when;
n->dev=skb->dev;
+ n->priority=skb->priority;
+ n->protocol=skb->protocol;
+ n->dst=dst_clone(skb->dst);
n->h.raw=skb->h.raw+offset;
+ n->nh.raw=skb->nh.raw+offset;
n->mac.raw=skb->mac.raw+offset;
- n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- n->ipv6_hdr=(struct ipv6hdr *)(((char *)skb->ipv6_hdr)+offset);
- n->nexthop = skb->nexthop;
-#endif
- n->saddr=skb->saddr;
- n->daddr=skb->daddr;
- n->raddr=skb->raddr;
n->seq=skb->seq;
n->end_seq=skb->end_seq;
n->ack_seq=skb->ack_seq;
- n->acked=skb->acked;
- memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+ memcpy(n->cb, skb->cb, sizeof(skb->cb));
n->used=skb->used;
- n->free=1;
n->arp=skb->arp;
n->tries=0;
- n->lock=0;
- n->users=0;
+ atomic_set(&n->users, 1);
n->pkt_type=skb->pkt_type;
n->stamp=skb->stamp;
-
- IS_SKB(n);
+ n->destructor = NULL;
+ n->security=skb->security;
return n;
}
-/*
- * Skbuff device locking
- */
-
-void skb_device_lock(struct sk_buff *skb)
+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom)
{
- if(skb->lock)
- printk("double lock on device queue, lock=%d caller=%p\n",
- skb->lock, (&skb)[-1]);
- else
- net_locked++;
- skb->lock++;
-}
+ struct sk_buff *n;
+ unsigned long offset;
+ int headroom = skb_headroom(skb);
-void skb_device_unlock(struct sk_buff *skb)
-{
- if(skb->lock==0)
- printk("double unlock on device queue!\n");
- skb->lock--;
- if(skb->lock==0)
- net_locked--;
-}
+ /*
+ * Allocate the copy buffer
+ */
+
+ n=alloc_skb(skb->truesize+newheadroom-headroom-sizeof(struct sk_buff), GFP_ATOMIC);
+ if(n==NULL)
+ return NULL;
-void dev_kfree_skb(struct sk_buff *skb, int mode)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if(skb->lock)
- {
- net_locked--;
- skb->lock--;
- }
- if (!skb->lock && (skb->free == 1 || skb->free == 3))
- {
- restore_flags(flags);
- kfree_skb(skb,mode);
- }
- else
- restore_flags(flags);
-}
+ skb_reserve(n,newheadroom);
+ /*
+ * Shift between the two data areas in bytes
+ */
+
+ offset=n->data-skb->data;
+
+ /* Set the tail pointer and length */
+ skb_put(n,skb->len);
+ /* Copy the bytes */
+ memcpy(n->data,skb->data,skb->len);
+ n->list=NULL;
+ n->sk=NULL;
+ n->when=skb->when;
+ n->priority=skb->priority;
+ n->protocol=skb->protocol;
+ n->dev=skb->dev;
+ n->dst=dst_clone(skb->dst);
+ n->h.raw=skb->h.raw+offset;
+ n->nh.raw=skb->nh.raw+offset;
+ n->mac.raw=skb->mac.raw+offset;
+ memcpy(n->cb, skb->cb, sizeof(skb->cb));
+ n->seq=skb->seq;
+ n->end_seq=skb->end_seq;
+ n->ack_seq=skb->ack_seq;
+ n->used=skb->used;
+ n->arp=skb->arp;
+ n->tries=0;
+ atomic_set(&n->users, 1);
+ n->pkt_type=skb->pkt_type;
+ n->stamp=skb->stamp;
+ n->destructor = NULL;
+ n->security=skb->security;
+
+ return n;
+}
+
struct sk_buff *dev_alloc_skb(unsigned int length)
{
struct sk_buff *skb;
@@ -910,8 +373,3 @@ struct sk_buff *dev_alloc_skb(unsigned int length)
skb_reserve(skb,16);
return skb;
}
-
-int skb_device_locked(struct sk_buff *skb)
-{
- return skb->lock? 1 : 0;
-}
diff --git a/net/core/sock.c b/net/core/sock.c
index 28c3eb897..8c008c0f2 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -95,6 +95,7 @@
#include <linux/net.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
@@ -113,6 +114,7 @@
#include <net/sock.h>
#include <net/raw.h>
#include <net/icmp.h>
+#include <linux/ipsec.h>
#define min(a,b) ((a)<(b)?(a):(b))
@@ -121,15 +123,16 @@
* at the socket level. Everything here is generic.
*/
-int sock_setsockopt(struct sock *sk, int level, int optname,
+int sock_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
+ struct sock *sk=sock->sk;
int val;
int valbool;
int err;
struct linger ling;
int ret = 0;
-
+
/*
* Options without arguments
*/
@@ -143,8 +146,13 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
}
#endif
- if (optval == NULL)
+ if(optlen<sizeof(int)) {
+#if 1 /* DaveM Debugging */
+ printk("sock_setsockopt: optlen is %d, going on anyways.\n", optlen);
+#else
return(-EINVAL);
+#endif
+ }
err = get_user(val, (int *)optval);
if (err)
@@ -157,7 +165,7 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
case SO_DEBUG:
if(val && !suser())
{
- ret = -EPERM;
+ ret = -EACCES;
}
else
sk->debug=valbool;
@@ -176,23 +184,34 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
sk->broadcast=valbool;
break;
case SO_SNDBUF:
+ /*
+ * The spec isnt clear if ENOBUFS or EINVAL
+ * is best
+ */
+
if(val > SK_WMEM_MAX*2)
- val = SK_WMEM_MAX*2;
- if(val < 256)
- val = 256;
+ return -EINVAL;
+ /*
+ * Once this is all 32bit values we can
+ * drop this check.
+ */
if(val > 65535)
- val = 65535;
- sk->sndbuf = val;
+ return -EINVAL;
+ sk->sndbuf = max(val,2048);
+ /*
+ * Wake up sending tasks if we
+ * upped the value.
+ */
+ sk->write_space(sk);
break;
case SO_RCVBUF:
if(val > SK_RMEM_MAX*2)
- val = SK_RMEM_MAX*2;
- if(val < 256)
- val = 256;
+ return -EINVAL;
+ /* Can go soon: FIXME */
if(val > 65535)
- val = 65535;
- sk->rcvbuf = val;
+ return -EINVAL;
+ sk->rcvbuf = max(val,256);
break;
case SO_KEEPALIVE:
@@ -215,17 +234,15 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
case SO_PRIORITY:
if (val >= 0 && val < DEV_NUMBUFFS)
- {
sk->priority = val;
- }
else
- {
return(-EINVAL);
- }
break;
case SO_LINGER:
+ if(optlen<sizeof(ling))
+ return -EINVAL; /* 1003.1g */
err = copy_from_user(&ling,optval,sizeof(ling));
if (err)
{
@@ -244,8 +261,55 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
case SO_BSDCOMPAT:
sk->bsdism = valbool;
break;
+
+ case SO_PASSCRED:
+ sock->passcred = valbool;
+ break;
+
+
+#ifdef CONFIG_NET_SECURITY
+ /*
+ * FIXME: make these error things that are not
+ * available!
+ */
+
+ case SO_SECURITY_AUTHENTICATION:
+ if(val<=IPSEC_LEVEL_DEFAULT)
+ {
+ sk->authentication=val;
+ return 0;
+ }
+ if(net_families[sock->ops->family]->authentication)
+ sk->authentication=val;
+ else
+ return -EINVAL;
+ break;
+
+ case SO_SECURITY_ENCRYPTION_TRANSPORT:
+ if(val<=IPSEC_LEVEL_DEFAULT)
+ {
+ sk->encryption=val;
+ return 0;
+ }
+ if(net_families[sock->ops->family]->encryption)
+ sk->encryption = val;
+ else
+ return -EINVAL;
+ break;
- /* We implementation the SO_SNDLOWAT etc to
+ case SO_SECURITY_ENCRYPTION_NETWORK:
+ if(val<=IPSEC_LEVEL_DEFAULT)
+ {
+ sk->encrypt_net=val;
+ return 0;
+ }
+ if(net_families[sock->ops->family]->encrypt_net)
+ sk->encrypt_net = val;
+ else
+ return -EINVAL;
+ break;
+#endif
+ /* We implement the SO_SNDLOWAT etc to
not be settable (1003.1g 5.3) */
default:
return(-ENOPROTOOPT);
@@ -254,141 +318,218 @@ int sock_setsockopt(struct sock *sk, int level, int optname,
}
-int sock_getsockopt(struct sock *sk, int level, int optname,
- char *optval, int *optlen)
-{
- int val;
- int err;
- struct linger ling;
+int sock_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ struct sock *sk = sock->sk;
+
+ union
+ {
+ int val;
+ struct linger ling;
+ struct timeval tm;
+ } v;
+
+ int lv=sizeof(int),len;
+
+ if(get_user(len,optlen))
+ return -EFAULT;
switch(optname)
{
case SO_DEBUG:
- val = sk->debug;
+ v.val = sk->debug;
break;
case SO_DONTROUTE:
- val = sk->localroute;
+ v.val = sk->localroute;
break;
case SO_BROADCAST:
- val= sk->broadcast;
+ v.val= sk->broadcast;
break;
case SO_SNDBUF:
- val=sk->sndbuf;
+ v.val=sk->sndbuf;
break;
case SO_RCVBUF:
- val =sk->rcvbuf;
+ v.val =sk->rcvbuf;
break;
case SO_REUSEADDR:
- val = sk->reuse;
+ v.val = sk->reuse;
break;
case SO_KEEPALIVE:
- val = sk->keepopen;
+ v.val = sk->keepopen;
break;
case SO_TYPE:
- val = sk->type;
+ v.val = sk->type;
break;
case SO_ERROR:
- val = -sock_error(sk);
- if(val==0)
- val=xchg(&sk->err_soft,0);
+ v.val = -sock_error(sk);
+ if(v.val==0)
+ v.val=xchg(&sk->err_soft,0);
break;
case SO_OOBINLINE:
- val = sk->urginline;
+ v.val = sk->urginline;
break;
case SO_NO_CHECK:
- val = sk->no_check;
+ v.val = sk->no_check;
break;
case SO_PRIORITY:
- val = sk->priority;
+ v.val = sk->priority;
break;
case SO_LINGER:
- err = put_user(sizeof(ling), optlen);
- if (!err) {
- ling.l_onoff=sk->linger;
- ling.l_linger=sk->lingertime;
- err = copy_to_user(optval,&ling,sizeof(ling));
- if (err)
- err = -EFAULT;
- }
- return err;
-
+ lv=sizeof(v.ling);
+ v.ling.l_onoff=sk->linger;
+ v.ling.l_linger=sk->lingertime;
+ break;
+
case SO_BSDCOMPAT:
- val = sk->bsdism;
+ v.val = sk->bsdism;
break;
case SO_RCVTIMEO:
case SO_SNDTIMEO:
- {
- static struct timeval tm={0,0};
- return copy_to_user(optval,&tm,sizeof(tm));
- }
+ lv=sizeof(struct timeval);
+ v.tm.tv_sec=0;
+ v.tm.tv_usec=0;
+ break;
+
case SO_RCVLOWAT:
case SO_SNDLOWAT:
- val=1;
+ v.val=1;
+
+ case SO_PASSCRED:
+ v.val = sock->passcred;
+ break;
+ case SO_PEERCRED:
+ lv=sizeof(sk->peercred);
+ len=min(len, lv);
+ if(copy_to_user((void*)optval, &sk->peercred, len))
+ return -EFAULT;
+ goto lenout;
+
+#ifdef CONFIG_NET_SECURITY
+
+ case SO_SECURITY_AUTHENTICATION:
+ v.val = sk->authentication;
+ break;
+
+ case SO_SECURITY_ENCRYPTION_TRANSPORT:
+ v.val = sk->encryption;
+ break;
+
+ case SO_SECURITY_ENCRYPTION_NETWORK:
+ v.val = sk->encrypt_net;
+ break;
+#endif
default:
return(-ENOPROTOOPT);
}
- err = put_user(sizeof(int), optlen);
- if (!err)
- err = put_user(val,(unsigned int *)optval);
-
- return err;
+ len=min(len,lv);
+ if(copy_to_user(optval,&v,len))
+ return -EFAULT;
+lenout:
+ if(put_user(len, optlen))
+ return -EFAULT;
+ return 0;
}
+static kmem_cache_t *sk_cachep;
+
+/*
+ * All socket objects are allocated here. This is for future
+ * usage.
+ */
+
struct sock *sk_alloc(int priority)
{
- struct sock *sk=(struct sock *)kmalloc(sizeof(*sk), priority);
- if(!sk)
- return NULL;
- memset(sk, 0, sizeof(*sk));
+ struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
+
+ if(sk)
+ memset(sk, 0, sizeof(struct sock));
return sk;
}
void sk_free(struct sock *sk)
{
- kfree_s(sk,sizeof(*sk));
+ kmem_cache_free(sk_cachep, sk);
+}
+
+void sk_init(void)
+{
+ sk_cachep = kmem_cache_create("sock", sizeof(struct sock), 0,
+ SLAB_HWCACHE_ALIGN, 0, 0);
+}
+
+/*
+ * Simple resource managers for sockets.
+ */
+
+void sock_wfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+#if 1
+ if (!sk) {
+ printk(KERN_DEBUG "sock_wfree: sk==NULL\n");
+ return;
+ }
+#endif
+ /* In case it might be waiting for more memory. */
+ atomic_sub(skb->truesize, &sk->wmem_alloc);
+ sk->write_space(sk);
+}
+
+
+void sock_rfree(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+#if 1
+ if (!sk) {
+ printk(KERN_DEBUG "sock_rfree: sk==NULL\n");
+ return;
+ }
+#endif
+ atomic_sub(skb->truesize, &sk->rmem_alloc);
}
struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
{
- if (sk) {
- if (force || sk->wmem_alloc < sk->sndbuf) {
- struct sk_buff * skb = alloc_skb(size, priority);
- if (skb)
- atomic_add(skb->truesize, &sk->wmem_alloc);
- return skb;
+ if (force || atomic_read(&sk->wmem_alloc) < sk->sndbuf) {
+ struct sk_buff * skb = alloc_skb(size, priority);
+ if (skb) {
+ atomic_add(skb->truesize, &sk->wmem_alloc);
+ skb->destructor = sock_wfree;
+ skb->sk = sk;
}
- return NULL;
+ return skb;
}
- return alloc_skb(size, priority);
+ return NULL;
}
struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
{
- if (sk) {
- if (force || sk->rmem_alloc < sk->rcvbuf) {
- struct sk_buff *skb = alloc_skb(size, priority);
- if (skb)
- atomic_add(skb->truesize, &sk->rmem_alloc);
- return skb;
+ if (force || atomic_read(&sk->rmem_alloc) < sk->rcvbuf) {
+ struct sk_buff *skb = alloc_skb(size, priority);
+ if (skb) {
+ atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb->destructor = sock_rfree;
+ skb->sk = sk;
}
- return NULL;
+ return skb;
}
- return alloc_skb(size, priority);
+ return NULL;
}
@@ -398,9 +539,9 @@ unsigned long sock_rspace(struct sock *sk)
if (sk != NULL)
{
- if (sk->rmem_alloc >= sk->rcvbuf-2*MIN_WINDOW)
+ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf-2*MIN_WINDOW)
return(0);
- amt = min((sk->rcvbuf-sk->rmem_alloc)/2-MIN_WINDOW, MAX_WINDOW);
+ amt = min((sk->rcvbuf-atomic_read(&sk->rmem_alloc))/2-MIN_WINDOW, MAX_WINDOW);
if (amt < 0)
return(0);
return(amt);
@@ -415,42 +556,14 @@ unsigned long sock_wspace(struct sock *sk)
{
if (sk->shutdown & SEND_SHUTDOWN)
return(0);
- if (sk->wmem_alloc >= sk->sndbuf)
+ if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf)
return(0);
- return sk->sndbuf - sk->wmem_alloc;
+ return sk->sndbuf - atomic_read(&sk->wmem_alloc);
}
return(0);
}
-void sock_wfree(struct sock *sk, struct sk_buff *skb)
-{
- int s=skb->truesize;
-#if CONFIG_SKB_CHECK
- IS_SKB(skb);
-#endif
- kfree_skbmem(skb);
- if (sk)
- {
- /* In case it might be waiting for more memory. */
- sk->write_space(sk);
- atomic_sub(s, &sk->wmem_alloc);
- }
-}
-
-
-void sock_rfree(struct sock *sk, struct sk_buff *skb)
-{
- int s=skb->truesize;
-#if CONFIG_SKB_CHECK
- IS_SKB(skb);
-#endif
- kfree_skbmem(skb);
- if (sk)
- {
- atomic_sub(s, &sk->rmem_alloc);
- }
-}
/*
* Generic send/receive buffer handlers
@@ -459,22 +572,21 @@ void sock_rfree(struct sock *sk, struct sk_buff *skb)
struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode)
{
struct sk_buff *skb;
- int err;
do
{
if(sk->err!=0)
{
- cli();
- err= -sk->err;
- sk->err=0;
- sti();
- *errcode=err;
+ *errcode=xchg(&sk->err,0);
return NULL;
}
if(sk->shutdown&SEND_SHUTDOWN)
- {
+ {
+ /*
+ * FIXME: Check 1003.1g should we deliver
+ * a signal here ???
+ */
*errcode=-EPIPE;
return NULL;
}
@@ -509,7 +621,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne
*errcode=-EPIPE;
return NULL;
}
- tmp = sk->wmem_alloc;
+ tmp = atomic_read(&sk->wmem_alloc);
cli();
if(sk->shutdown&SEND_SHUTDOWN)
{
@@ -519,7 +631,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne
}
#if 1
- if( tmp <= sk->wmem_alloc)
+ if( tmp <= atomic_read(&sk->wmem_alloc))
#else
/* ANK: Line above seems either incorrect
* or useless. sk->wmem_alloc has a tiny chance to change
@@ -529,7 +641,7 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigne
* In any case I'd delete this check at all, or
* change it to:
*/
- if (sk->wmem_alloc + size >= sk->sndbuf)
+ if (atomic_read(&sk->wmem_alloc) + size >= sk->sndbuf)
#endif
{
sk->socket->flags &= ~SO_NOSPACE;
@@ -566,3 +678,216 @@ void __release_sock(struct sock *sk)
end_bh_atomic();
#endif
}
+
+
+/*
+ * Generic socket manager library. Most simpler socket families
+ * use this to manage their socket lists. At some point we should
+ * hash these. By making this generic we get the lot hashed for free.
+ */
+
+void sklist_remove_socket(struct sock **list, struct sock *sk)
+{
+ unsigned long flags;
+ struct sock *s;
+
+ save_flags(flags);
+ cli();
+
+ s= *list;
+ if(s==sk)
+ {
+ *list = s->next;
+ restore_flags(flags);
+ return;
+ }
+ while(s && s->next)
+ {
+ if(s->next==sk)
+ {
+ s->next=sk->next;
+ restore_flags(flags);
+ return;
+ }
+ s=s->next;
+ }
+ restore_flags(flags);
+}
+
+void sklist_insert_socket(struct sock **list, struct sock *sk)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ sk->next= *list;
+ *list=sk;
+ restore_flags(flags);
+}
+
+/*
+ * This is only called from user mode. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+
+void sklist_destroy_socket(struct sock **list, struct sock *sk);
+
+/*
+ * Handler for deferred kills.
+ */
+
+static void sklist_destroy_timer(unsigned long data)
+{
+ struct sock *sk=(struct sock *)data;
+ sklist_destroy_socket(NULL,sk);
+}
+
+/*
+ * Destroy a socket. We pass NULL for a list if we know the
+ * socket is not on a list.
+ */
+
+void sklist_destroy_socket(struct sock **list,struct sock *sk)
+{
+ struct sk_buff *skb;
+ if(list)
+ sklist_remove_socket(list, sk);
+
+ while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+ {
+ kfree_skb(skb,FREE_READ);
+ }
+
+ if(atomic_read(&sk->wmem_alloc) == 0 &&
+ atomic_read(&sk->rmem_alloc) == 0 &&
+ sk->dead)
+ {
+ sk_free(sk);
+ }
+ else
+ {
+ /*
+ * Someone is using our buffers still.. defer
+ */
+ init_timer(&sk->timer);
+ sk->timer.expires=jiffies+10*HZ;
+ sk->timer.function=sklist_destroy_timer;
+ sk->timer.data = (unsigned long)sk;
+ add_timer(&sk->timer);
+ }
+}
+
+/*
+ * Support routines for general vectors
+ */
+
+/*
+ * Socket with no special fcntl calls.
+ */
+
+int sock_no_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct sock *sk = sock->sk;
+
+ switch(cmd)
+ {
+ case F_SETOWN:
+ /*
+ * This is a little restrictive, but it's the only
+ * way to make sure that you can't send a sigurg to
+ * another process.
+ */
+ if (!suser() && current->pgrp != -arg &&
+ current->pid != arg) return(-EPERM);
+ sk->proc = arg;
+ return(0);
+ case F_GETOWN:
+ return(sk->proc);
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * Default socket getsockopt / setsockopt
+ */
+
+int sock_no_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+int sock_no_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Default Socket Callbacks
+ */
+
+void sock_def_callback1(struct sock *sk)
+{
+ if(!sk->dead)
+ wake_up_interruptible(sk->sleep);
+}
+
+void sock_def_callback2(struct sock *sk, int len)
+{
+ if(!sk->dead)
+ {
+ wake_up_interruptible(sk->sleep);
+ sock_wake_async(sk->socket,1);
+ }
+}
+
+void sock_def_callback3(struct sock *sk)
+{
+ if(!sk->dead)
+ {
+ wake_up_interruptible(sk->sleep);
+ sock_wake_async(sk->socket, 2);
+ }
+}
+
+void sock_init_data(struct socket *sock, struct sock *sk)
+{
+ skb_queue_head_init(&sk->receive_queue);
+ skb_queue_head_init(&sk->write_queue);
+ skb_queue_head_init(&sk->back_log);
+ skb_queue_head_init(&sk->error_queue);
+
+ init_timer(&sk->timer);
+
+ sk->allocation = GFP_KERNEL;
+ sk->rcvbuf = SK_RMEM_MAX;
+ sk->sndbuf = SK_WMEM_MAX;
+ sk->priority = SOPRI_NORMAL;
+ sk->state = TCP_CLOSE;
+ sk->zapped = 1;
+ sk->socket = sock;
+ if(sock)
+ {
+ sk->type = sock->type;
+ sk->sleep = &sock->wait;
+ sock->sk = sk;
+ }
+
+ sk->state_change = sock_def_callback1;
+ sk->data_ready = sock_def_callback2;
+ sk->write_space = sock_def_callback3;
+ sk->error_report = sock_def_callback1;
+
+ sk->peercred.pid = 0;
+ sk->peercred.uid = -1;
+ sk->peercred.gid = -1;
+
+}
diff --git a/net/ethernet/Makefile b/net/ethernet/Makefile
index a1e61cdda..8c9041b4c 100644
--- a/net/ethernet/Makefile
+++ b/net/ethernet/Makefile
@@ -9,7 +9,11 @@
O_TARGET := ethernet.o
-OBJS := eth.o sysctl_net_ether.o
+OBJS := eth.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+OBJS += sysctl_net_ether.o
+endif
ifdef CONFIG_IPX
OBJ2 := pe2.o
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 4872fd2b5..9f4477807 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -46,28 +46,21 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
+#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
#include <linux/config.h>
+#include <net/dst.h>
#include <net/arp.h>
#include <net/sock.h>
#include <net/ipv6.h>
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-#include <linux/in6.h>
-#include <net/ndisc.h>
-#endif
#include <asm/checksum.h>
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-int (*ndisc_eth_hook) (unsigned char *, struct device *,
- struct sk_buff *) = NULL;
-#endif
-
void eth_setup(char *str, int *ints)
{
struct device *d = dev_base;
@@ -150,40 +143,30 @@ int eth_header(struct sk_buff *skb, struct device *dev, unsigned short type,
* sk_buff. We now let ARP fill in the other fields.
*/
-int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
- struct sk_buff *skb)
+int eth_rebuild_header(struct sk_buff *skb)
{
- struct ethhdr *eth = (struct ethhdr *)buff;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+ struct device *dev = skb->dev;
+ struct neighbour *neigh = NULL;
/*
* Only ARP/IP and NDISC/IPv6 are currently supported
*/
+ if (skb->dst)
+ neigh = skb->dst->neighbour;
+
+ if (neigh)
+ return neigh->ops->resolve(eth->h_dest, skb);
+
switch (eth->h_proto)
{
#ifdef CONFIG_INET
case __constant_htons(ETH_P_IP):
-
- /*
- * Try to get ARP to resolve the header.
- */
-
- return (arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ?
- 1 : 0);
- break;
-#endif
-
-#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
- case __constant_htons(ETH_P_IPV6):
-#ifdef CONFIG_IPV6
- return (ndisc_eth_resolv(eth->h_dest, dev, skb));
-#else
- if (ndisc_eth_hook)
- return (ndisc_eth_hook(eth->h_dest, dev, skb));
-#endif
+ return arp_find(eth->h_dest, skb);
#endif
default:
- printk(KERN_DEBUG
+ printk(KERN_DEBUG
"%s: unable to resolve type %X addresses.\n",
dev->name, (int)eth->h_proto);
@@ -250,30 +233,39 @@ unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev)
return htons(ETH_P_802_2);
}
-/*
- * Upper level calls this function to bind hardware header cache entry.
- * If the call is successful, then corresponding Address Resolution Protocol
- * (maybe, not ARP) takes responsibility for updating cache content.
- */
-
-void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
- unsigned short htype, __u32 daddr)
+int eth_header_cache(struct dst_entry *dst, struct neighbour *neigh,
+ struct hh_cache *hh)
{
- struct hh_cache *hh;
+ unsigned short type = hh->hh_type;
+ struct ethhdr *eth = (struct ethhdr*)hh->hh_data;
+ struct device *dev = dst->dev;
- if (htype != ETH_P_IP)
- {
- printk(KERN_DEBUG "eth_header_cache_bind: %04x cache is not implemented\n", htype);
- return;
+ if (type == ETH_P_802_3)
+ return -1;
+
+ eth->h_proto = htons(type);
+
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+
+ if (dev->flags & IFF_LOOPBACK) {
+ memset(eth->h_dest, 0, dev->addr_len);
+ hh->hh_uptodate = 1;
+ return 0;
}
- if (arp_bind_cache(hhp, dev, htype, daddr))
- return;
- if ((hh=*hhp) != NULL)
+
+ if (type != ETH_P_IP)
{
- memcpy(hh->hh_data+6, dev->dev_addr, ETH_ALEN);
- hh->hh_data[12] = htype>>8;
- hh->hh_data[13] = htype&0xFF;
+ printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",dev->name,(int)eth->h_proto);
+ hh->hh_uptodate = 0;
+ return 0;
}
+
+#ifdef CONFIG_INET
+ hh->hh_uptodate = arp_find_1(eth->h_dest, dst, neigh);
+#else
+ hh->hh_uptodate = 0;
+#endif
+ return 0;
}
/*
@@ -291,20 +283,18 @@ void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned c
hh->hh_uptodate = 1;
}
+#ifndef CONFIG_IP_ROUTER
+
/*
* Copy from an ethernet device memory space to an sk_buff while checksumming if IP
*/
void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int base)
{
-#ifdef CONFIG_IP_ROUTER
- memcpy(dest->data,src,length);
-#else
struct ethhdr *eth;
struct iphdr *iph;
int ip_length;
- IS_SKB(dest);
eth=(struct ethhdr *)src;
if(eth->h_proto!=htons(ETH_P_IP))
{
@@ -328,5 +318,6 @@ void eth_copy_and_sum(struct sk_buff *dest, unsigned char *src, int length, int
dest->csum=csum_partial_copy(src+sizeof(struct iphdr)+ETH_HLEN,dest->data+sizeof(struct iphdr)+ETH_HLEN,length,base);
dest->ip_summed=1;
-#endif
}
+
+#endif /* !(CONFIG_IP_ROUTER) */
diff --git a/net/ipv4/Config.in b/net/ipv4/Config.in
index 25596cc4f..489598994 100644
--- a/net/ipv4/Config.in
+++ b/net/ipv4/Config.in
@@ -1,7 +1,6 @@
#
# IP configuration
#
-bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD
bool 'IP: multicasting' CONFIG_IP_MULTICAST
if [ "$CONFIG_FIREWALL" = "y" ]; then
bool 'IP: firewalling' CONFIG_IP_FIREWALL
@@ -10,25 +9,19 @@ if [ "$CONFIG_FIREWALL" = "y" ]; then
bool 'IP: firewall packet netlink device' CONFIG_IP_FIREWALL_NETLINK
fi
bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE
- if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_IP_FORWARD" = "y" ]; then
- bool 'IP: masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE
- if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
- comment 'Protocol-specific masquerading support will be built as modules.'
- fi
- bool 'IP: transparent proxy support (EXPERIMENTAL)' CONFIG_IP_TRANSPARENT_PROXY
- bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
+ bool 'IP: masquerading' CONFIG_IP_MASQUERADE
+ if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
+ comment 'Protocol-specific masquerading support will be built as modules.'
fi
+ bool 'IP: transparent proxy support' CONFIG_IP_TRANSPARENT_PROXY
+ bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
fi
fi
bool 'IP: accounting' CONFIG_IP_ACCT
-if [ "$CONFIG_IP_FORWARD" = "y" ]; then
- bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
- tristate 'IP: tunneling' CONFIG_NET_IPIP
- if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
- if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
- bool 'IP: multicast routing (EXPERIMENTAL)' CONFIG_IP_MROUTE
- fi
- fi
+bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
+tristate 'IP: tunneling' CONFIG_NET_IPIP
+if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
+ bool 'IP: multicast routing' CONFIG_IP_MROUTE
fi
if [ "$CONFIG_NET_ALIAS" = "y" ]; then
tristate 'IP: aliasing support' CONFIG_IP_ALIAS
@@ -41,7 +34,7 @@ fi
comment '(it is safe to leave these untouched)'
bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
tristate 'IP: Reverse ARP' CONFIG_INET_RARP
-bool 'IP: Disable Path MTU Discovery (normally enabled)' CONFIG_NO_PATH_MTU_DISCOVERY
+bool 'IP: Path MTU Discovery (normally enabled)' CONFIG_PATH_MTU_DISCOVERY
#bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 2ca338c04..9ce538dc4 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -13,7 +13,8 @@ IPV4_OBJS := utils.o route.o proc.o timer.o protocol.o packet.o \
ip_output.o ip_sockglue.o \
tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o\
raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o ip_fw.o \
- sysctl_net_ipv4.o
+ sysctl_net_ipv4.o fib.o ip_nat_dumb.o
+IPV4X_OBJS :=
MOD_LIST_NAME := IPV4_MODULES
M_OBJS :=
@@ -39,8 +40,8 @@ else
endif
ifeq ($(CONFIG_IP_MASQUERADE),y)
-IPV4_OBJS += ip_masq.o ip_masq_app.o
-M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o
+IPV4X_OBJS += ip_masq.o ip_masq_app.o
+M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_quake.o
endif
ifeq ($(CONFIG_IP_ALIAS),y)
@@ -53,6 +54,7 @@ endif
ifdef CONFIG_INET
O_OBJS := $(IPV4_OBJS)
+OX_OBJS := $(IPV4X_OBJS)
endif
include $(TOPDIR)/Rules.make
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 34379849d..d96910bb0 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -50,6 +50,8 @@
* Alan Cox : Loosened bind a little.
* Mike McLagan : ADD/DEL DLCI Ioctls
* Willy Konynenberg : Transparent proxying support.
+ * David S. Miller : New socket lookup architecture.
+ * Some other random speedups.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -91,6 +93,7 @@
#include <net/sock.h>
#include <net/raw.h>
#include <net/icmp.h>
+#include <net/inet_common.h>
#include <linux/ip_fw.h>
#ifdef CONFIG_IP_MASQUERADE
#include <net/ip_masq.h>
@@ -104,6 +107,9 @@
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
#define min(a,b) ((a)<(b)?(a):(b))
@@ -115,10 +121,6 @@ extern int tcp_get_info(char *, char **, off_t, int, int);
extern int udp_get_info(char *, char **, off_t, int, int);
-struct sock * tcp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * udp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * raw_sock_array[SOCK_ARRAY_SIZE];
-
#ifdef CONFIG_DLCI
extern int dlci_ioctl(unsigned int, void*);
#endif
@@ -130,279 +132,94 @@ int (*dlci_ioctl_hook)(unsigned int, void *) = NULL;
int (*rarp_ioctl_hook)(unsigned int,void*) = NULL;
/*
- * See if a socket number is in use.
+ * Destroy an AF_INET socket
*/
-static int sk_inuse(struct proto *prot, int num)
+static __inline__ void kill_sk_queues(struct sock *sk)
{
- struct sock *sk;
+ struct sk_buff *skb;
- for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
- sk != NULL; sk=sk->next)
- {
- if (sk->num == num)
- return(1);
+ /* First the read buffer. */
+ while((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ /* This will take care of closing sockets that were
+ * listening and didn't accept everything.
+ */
+ if (skb->sk != NULL && skb->sk != sk)
+ skb->sk->prot->close(skb->sk, 0);
+ kfree_skb(skb, FREE_READ);
}
- return(0);
-}
+ /* Next, the error queue. */
+ while((skb = skb_dequeue(&sk->error_queue)) != NULL)
+ kfree_skb(skb, FREE_READ);
-/*
- * Pick a new socket number
- */
+ /* Now the backlog. */
+ while((skb=skb_dequeue(&sk->back_log)) != NULL)
+ kfree_skb(skb, FREE_READ);
+}
-unsigned short get_new_socknum(struct proto *prot, unsigned short base)
+static __inline__ void kill_sk_now(struct sock *sk)
{
- static int start=0;
-
- /*
- * Used to cycle through the port numbers so the
- * chances of a confused connection drop.
- */
-
- int i, j;
- int best = 0;
- int size = 32767; /* a big num. */
- struct sock *sk;
-
- if (base == 0)
- base = PROT_SOCK+1+(start & 1023);
- if (base <= PROT_SOCK)
- {
- base += PROT_SOCK+(start & 1023);
- }
-
- /*
- * Now look through the entire array and try to find an empty ptr.
- */
-
- for(i=0; i < SOCK_ARRAY_SIZE; i++)
- {
- j = 0;
- sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
- while(sk != NULL)
- {
- sk = sk->next;
- j++;
- }
- if (j == 0)
- {
- start =(i+1+start )&1023;
- return(i+base+1);
- }
- if (j < size)
- {
- best = i;
- size = j;
- }
- }
+ /* No longer exists. */
+ del_from_prot_sklist(sk);
- /* Now make sure the one we want is not in use. */
+ /* This is gross, but needed for SOCK_PACKET -DaveM */
+ if(sk->prot->unhash)
+ sk->prot->unhash(sk);
- while(sk_inuse(prot, base +best+1))
- {
- best += SOCK_ARRAY_SIZE;
- }
- return(best+base+1);
+ if(sk->opt)
+ kfree(sk->opt);
+ dst_release(sk->dst_cache);
+ sk_free(sk);
}
-/*
- * Add a socket into the socket tables by number.
- */
-
-void inet_put_sock(unsigned short num, struct sock *sk)
+static __inline__ void kill_sk_later(struct sock *sk)
{
- struct sock **skp, *tmp;
- int mask;
- unsigned long flags;
-
- if(sk->type==SOCK_PACKET)
- return;
-
- sk->num = num;
- sk->next = NULL;
- num = num &(SOCK_ARRAY_SIZE -1);
-
+ /* this should never happen. */
+ /* actually it can if an ack has just been sent. */
/*
- * We can't have an interrupt re-enter here.
+ * It's more normal than that...
+ * It can happen because a skb is still in the device queues
+ * [PR]
*/
-
- save_flags(flags);
- cli();
-
- sk->prot->inuse += 1;
- if (sk->prot->highestinuse < sk->prot->inuse)
- sk->prot->highestinuse = sk->prot->inuse;
-
- if (sk->prot->sock_array[num] == NULL)
- {
- sk->prot->sock_array[num] = sk;
- restore_flags(flags);
- return;
- }
-
- restore_flags(flags);
- for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask)
- {
- if ((mask & sk->rcv_saddr) &&
- (mask & sk->rcv_saddr) != (mask & 0xffffffff))
- {
- mask = mask << 8;
- break;
- }
- }
-
- /*
- * add the socket to the sock_array[]..
- */
- skp = sk->prot->sock_array + num;
- cli();
- while ((tmp = *skp) != NULL) {
- if (!(tmp->rcv_saddr & mask))
- break;
- skp = &tmp->next;
- }
- sk->next = tmp;
- *skp = sk;
- sti();
-}
-
-/*
- * Remove a socket from the socket tables.
- */
-
-void inet_remove_sock(struct sock *sk1)
-{
- struct sock **p;
- unsigned long flags;
-
- if (sk1->type==SOCK_PACKET)
- return;
-
- if (!sk1->prot)
- {
- NETDEBUG(printk("sock.c: remove_sock: sk1->prot == NULL\n"));
- return;
- }
+
+ printk(KERN_DEBUG "Socket destroy delayed (r=%d w=%d)\n",
+ atomic_read(&sk->rmem_alloc), atomic_read(&sk->wmem_alloc));
- /* We can't have this changing out from under us. */
- save_flags(flags);
- cli();
-
- p=&(sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)]);
-
- while(*p!=NULL)
- {
- if(*p==sk1)
- {
- sk1->prot->inuse--;
- *p=sk1->next;
- break;
- }
- p=&((*p)->next);
- }
- restore_flags(flags);
+ sk->destroy = 1;
+ sk->ack_backlog = 0;
+ release_sock(sk);
+ net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
}
-/*
- * Destroy an AF_INET socket
- */
-
void destroy_sock(struct sock *sk)
{
- struct sk_buff *skb;
-
lock_sock(sk); /* just to be safe. */
-
- /*
- * Now we can no longer get new packets or once the
- * timers are killed, send them.
+ /* Now we can no longer get new packets or once the
+ * timers are killed, send them.
*/
-
net_delete_timer(sk);
if (sk->prot->destroy)
sk->prot->destroy(sk);
-
- /*
- * Clean up the read buffer.
- */
-
- while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
- {
- /*
- * This will take care of closing sockets that were
- * listening and didn't accept everything.
- */
- if (skb->sk != NULL && skb->sk != sk)
- {
- IS_SKB(skb);
- skb->sk->prot->close(skb->sk, 0);
- }
- IS_SKB(skb);
- kfree_skb(skb, FREE_READ);
- }
- /*
- * Now the backlog.
- */
-
- while((skb=skb_dequeue(&sk->back_log))!=NULL)
- {
- /* this should [almost] never happen. */
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- }
+ kill_sk_queues(sk);
- /*
- * Now if it has a half accepted/ closed socket.
- */
-
- if (sk->pair)
- {
+ /* Now if it has a half accepted/ closed socket. */
+ if (sk->pair) {
sk->pair->prot->close(sk->pair, 0);
sk->pair = NULL;
}
- /*
- * Now if everything is gone we can free the socket
+ /* Now if everything is gone we can free the socket
* structure, otherwise we need to keep it around until
* everything is gone.
*/
-
- if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0)
- {
- inet_remove_sock(sk);
-
- if(sk->opt)
- kfree(sk->opt);
- ip_rt_put(sk->ip_route_cache);
- /*
- * This one is pure paranoia. I'll take it out
- * later once I know the bug is buried.
- */
- tcp_cache_zap();
- sk_free(sk);
- }
- else
- {
- /* this should never happen. */
- /* actually it can if an ack has just been sent. */
- /*
- * It's more normal than that...
- * It can happen because a skb is still in the device queues
- * [PR]
- */
-
- printk("Socket destroy delayed (r=%d w=%d)\n",
- sk->rmem_alloc, sk->wmem_alloc);
-
- sk->destroy = 1;
- sk->ack_backlog = 0;
- release_sock(sk);
- net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
- }
+ if (atomic_read(&sk->rmem_alloc) == 0 && atomic_read(&sk->wmem_alloc) == 0)
+ kill_sk_now(sk);
+ else
+ kill_sk_later(sk);
}
/*
@@ -411,30 +228,6 @@ void destroy_sock(struct sock *sk)
* the work.
*/
-int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- struct sock *sk;
-
- sk = (struct sock *) sock->data;
-
- switch(cmd)
- {
- case F_SETOWN:
- /*
- * This is a little restrictive, but it's the only
- * way to make sure that you can't send a sigurg to
- * another process.
- */
- if (!suser() && current->pgrp != -arg &&
- current->pid != arg) return(-EPERM);
- sk->proc = arg;
- return(0);
- case F_GETOWN:
- return(sk->proc);
- default:
- return(-EINVAL);
- }
-}
/*
* Set socket options on an inet socket.
@@ -443,13 +236,10 @@ int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
int inet_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
- struct sock *sk = (struct sock *) sock->data;
- if (level == SOL_SOCKET)
- return sock_setsockopt(sk,level,optname,optval,optlen);
+ struct sock *sk=sock->sk;
if (sk->prot->setsockopt==NULL)
return(-EOPNOTSUPP);
- else
- return sk->prot->setsockopt(sk,level,optname,optval,optlen);
+ return sk->prot->setsockopt(sk,level,optname,optval,optlen);
}
/*
@@ -463,13 +253,10 @@ int inet_setsockopt(struct socket *sock, int level, int optname,
int inet_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
- struct sock *sk = (struct sock *) sock->data;
- if (level == SOL_SOCKET)
- return sock_getsockopt(sk,level,optname,optval,optlen);
- if(sk->prot->getsockopt==NULL)
- return(-EOPNOTSUPP);
- else
- return sk->prot->getsockopt(sk,level,optname,optval,optlen);
+ struct sock *sk=sock->sk;
+ if (sk->prot->getsockopt==NULL)
+ return(-EOPNOTSUPP);
+ return sk->prot->getsockopt(sk,level,optname,optval,optlen);
}
/*
@@ -479,15 +266,13 @@ int inet_getsockopt(struct socket *sock, int level, int optname,
static int inet_autobind(struct sock *sk)
{
/* We may need to bind the socket. */
- if (sk->num == 0)
- {
- sk->num = get_new_socknum(sk->prot, 0);
+ if (sk->num == 0) {
+ sk->num = sk->prot->good_socknum();
if (sk->num == 0)
return(-EAGAIN);
- udp_cache_zap();
- tcp_cache_zap();
- inet_put_sock(sk->num, sk);
- sk->dummy_th.source = ntohs(sk->num);
+ sk->dummy_th.source = htons(sk->num);
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
}
return 0;
}
@@ -498,9 +283,12 @@ static int inet_autobind(struct sock *sk)
int inet_listen(struct socket *sock, int backlog)
{
- struct sock *sk = (struct sock *) sock->data;
+ struct sock *sk = sock->sk;
+
+ if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
+ return(-EINVAL);
- if(inet_autobind(sk)!=0)
+ if (inet_autobind(sk) != 0)
return -EAGAIN;
/* We might as well re use these. */
@@ -515,44 +303,17 @@ int inet_listen(struct socket *sock, int backlog)
if ((unsigned) backlog > SOMAXCONN)
backlog = SOMAXCONN;
sk->max_ack_backlog = backlog;
- if (sk->state != TCP_LISTEN)
- {
+ if (sk->state != TCP_LISTEN) {
sk->ack_backlog = 0;
sk->state = TCP_LISTEN;
+ sk->prot->rehash(sk);
+ add_to_prot_sklist(sk);
}
+ sk->socket->flags |= SO_ACCEPTCON;
return(0);
}
/*
- * Default callbacks for user INET sockets. These just wake up
- * the user owning the socket.
- */
-
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk,int len)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 1);
- }
-}
-
-static void def_callback3(struct sock *sk)
-{
- if(!sk->dead && sk->wmem_alloc*2 <= sk->sndbuf)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 2);
- }
-}
-
-/*
* Create an inet socket.
*
* FIXME: Gcc would generate much better code if we set the parameters
@@ -563,105 +324,61 @@ static int inet_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
- int err;
+ sock->state = SS_UNCONNECTED;
sk = sk_alloc(GFP_KERNEL);
if (sk == NULL)
- return(-ENOBUFS);
- memset(sk,0,sizeof(*sk)); /* Efficient way to set most fields to zero */
- /*
- * Note for tcp that also wiped the dummy_th block for us.
- */
- switch(sock->type)
- {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- if (protocol && protocol != IPPROTO_TCP)
- {
- sk_free(sk);
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_TCP;
- sk->no_check = TCP_NO_CHECK;
- prot = &tcp_prot;
- break;
-
- case SOCK_DGRAM:
- if (protocol && protocol != IPPROTO_UDP)
- {
- sk_free(sk);
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_UDP;
- sk->no_check = UDP_NO_CHECK;
- prot=&udp_prot;
- break;
-
- case SOCK_RAW:
- if (!suser())
- {
- sk_free(sk);
- return(-EPERM);
- }
- if (!protocol)
- {
- sk_free(sk);
- return(-EPROTONOSUPPORT);
- }
- prot = &raw_prot;
- sk->reuse = 1;
- sk->num = protocol;
- break;
-
- case SOCK_PACKET:
- if (!suser())
- {
- sk_free(sk);
- return(-EPERM);
- }
- if (!protocol)
- {
- sk_free(sk);
- return(-EPROTONOSUPPORT);
- }
- prot = &packet_prot;
- sk->reuse = 1;
- sk->num = protocol;
- break;
-
- default:
- sk_free(sk);
- return(-ESOCKTNOSUPPORT);
+ goto do_oom;
+
+ /* Note for tcp that also wiped the dummy_th block for us. */
+ if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+ if (protocol && protocol != IPPROTO_TCP)
+ goto free_and_noproto;
+ protocol = IPPROTO_TCP;
+ sk->no_check = TCP_NO_CHECK;
+ if (ipv4_config.no_pmtu_disc)
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ else
+ sk->ip_pmtudisc = IP_PMTUDISC_WANT;
+ prot = &tcp_prot;
+ sock->ops = &inet_stream_ops;
+ } else if(sock->type == SOCK_DGRAM) {
+ if (protocol && protocol != IPPROTO_UDP)
+ goto free_and_noproto;
+ protocol = IPPROTO_UDP;
+ sk->no_check = UDP_NO_CHECK;
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ prot=&udp_prot;
+ sock->ops = &inet_dgram_ops;
+ } else if(sock->type == SOCK_RAW || sock->type == SOCK_PACKET) {
+ if (!suser())
+ goto free_and_badperm;
+ if (!protocol)
+ goto free_and_noproto;
+ prot = (sock->type == SOCK_RAW) ? &raw_prot : &packet_prot;
+ sk->reuse = 1;
+ sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+ sk->num = protocol;
+ sock->ops = &inet_dgram_ops;
+ } else {
+ goto free_and_badtype;
}
- sk->socket = sock;
+
+ sock_init_data(sock,sk);
+
+ sk->zapped=0;
#ifdef CONFIG_TCP_NAGLE_OFF
sk->nonagle = 1;
#endif
sk->family = AF_INET;
- sk->type = sock->type;
sk->protocol = protocol;
- sk->allocation = GFP_KERNEL;
- sk->sndbuf = SK_WMEM_MAX;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->priority = 1;
sk->prot = prot;
sk->backlog_rcv = prot->backlog_rcv;
- sk->sleep = sock->wait;
- sock->data =(void *) sk;
-
- sk->state = TCP_CLOSE;
-
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->back_log);
-
-
sk->timer.data = (unsigned long)sk;
sk->timer.function = &net_timer;
- sock->data =(void *) sk;
sk->ip_ttl=ip_statistics.IpDefaultTTL;
if(sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
@@ -669,44 +386,52 @@ static int inet_create(struct socket *sock, int protocol)
else
sk->ip_hdrincl=0;
-#ifdef CONFIG_IP_MULTICAST
sk->ip_mc_loop=1;
sk->ip_mc_ttl=1;
- *sk->ip_mc_name=0;
+ sk->ip_mc_index=0;
sk->ip_mc_list=NULL;
-#endif
- /*
- * Speed up by setting some standard state for the dummy_th
+
+ /* Speed up by setting some standard state for the dummy_th
* if TCP uses it (maybe move to tcp_init later)
*/
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback3;
- sk->error_report = def_callback1;
-
- if (sk->num)
- {
- /*
- * It assumes that any protocol which allows
- * the user to assign a number at socket
- * creation time automatically
- * shares.
- */
- inet_put_sock(sk->num, sk);
+ if (sk->num) {
+ /* It assumes that any protocol which allows
+ * the user to assign a number at socket
+ * creation time automatically
+ * shares.
+ */
sk->dummy_th.source = ntohs(sk->num);
+
+ /* This is gross, but needed for SOCK_PACKET -DaveM */
+ if(sk->prot->hash)
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
}
- if (sk->prot->init)
- {
- err = sk->prot->init(sk);
- if (err != 0)
- {
+ if (sk->prot->init) {
+ int err = sk->prot->init(sk);
+ if (err != 0) {
destroy_sock(sk);
return(err);
}
}
return(0);
+
+free_and_badtype:
+ sk_free(sk);
+ return -ESOCKTNOSUPPORT;
+
+free_and_badperm:
+ sk_free(sk);
+ return -EPERM;
+
+free_and_noproto:
+ sk_free(sk);
+ return -EPROTONOSUPPORT;
+
+do_oom:
+ return -ENOBUFS;
}
@@ -716,7 +441,7 @@ static int inet_create(struct socket *sock, int protocol)
static int inet_dup(struct socket *newsock, struct socket *oldsock)
{
- return(inet_create(newsock,((struct sock *)(oldsock->data))->protocol));
+ return inet_create(newsock, oldsock->sk->protocol);
}
/*
@@ -725,195 +450,120 @@ static int inet_dup(struct socket *newsock, struct socket *oldsock)
* should refer to it.
*/
-int inet_release(struct socket *sock, struct socket *peer)
+int inet_release(struct socket *sock, struct socket *peersock)
{
- unsigned long timeout;
- struct sock *sk = (struct sock *) sock->data;
+ struct sock *sk = sock->sk;
- if (sk == NULL)
- return(0);
+ if (sk) {
+ unsigned long timeout;
- sk->state_change(sk);
+ /* Begin closedown and wake up sleepers. */
+ if (sock->state != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+ sk->state_change(sk);
- /* Start closing the connection. This may take a while. */
+ /* Applications forget to leave groups before exiting */
+ ip_mc_drop_socket(sk);
-#ifdef CONFIG_IP_MULTICAST
- /* Applications forget to leave groups before exiting */
- ip_mc_drop_socket(sk);
-#endif
- /*
- * If linger is set, we don't return until the close
- * is complete. Otherwise we return immediately. The
- * actually closing is done the same either way.
- *
- * If the close is due to the process exiting, we never
- * linger..
- */
- timeout = 0;
- if (sk->linger) {
- timeout = ~0UL;
- if (!sk->lingertime)
- timeout = jiffies + HZ*sk->lingertime;
- }
- if (current->flags & PF_EXITING)
+ /* If linger is set, we don't return until the close
+ * is complete. Otherwise we return immediately. The
+ * actually closing is done the same either way.
+ *
+ * If the close is due to the process exiting, we never
+ * linger..
+ */
timeout = 0;
+ if (sk->linger && !(current->flags & PF_EXITING)) {
+ timeout = ~0UL;
- sock->data = NULL;
- sk->socket = NULL;
-
- sk->prot->close(sk, timeout);
+ /* XXX This makes no sense whatsoever... -DaveM */
+ if (!sk->lingertime)
+ timeout = jiffies + HZ*sk->lingertime;
+ }
+ sock->sk = NULL;
+ sk->socket = NULL;
+ sk->prot->close(sk, timeout);
+ }
return(0);
}
-
-static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
- int addr_len)
+static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
- struct sock *sk=(struct sock *)sock->data, *sk2;
- unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
+ struct sock *sk=sock->sk;
+ unsigned short snum;
int chk_addr_ret;
- /*
- * If the socket has its own bind function then use it.
- */
-
+ /* If the socket has its own bind function then use it. (RAW and PACKET) */
if(sk->prot->bind)
- return sk->prot->bind(sk,uaddr, addr_len);
+ return sk->prot->bind(sk, uaddr, addr_len);
- /* check this error. */
- if (sk->state != TCP_CLOSE)
- return(-EINVAL);
- if(addr_len<sizeof(struct sockaddr_in))
+ /* Check these errors (active socket, bad address length, double bind). */
+ if ((sk->state != TCP_CLOSE) ||
+ (addr_len < sizeof(struct sockaddr_in)) ||
+ (sk->num != 0))
return -EINVAL;
- if(sock->type != SOCK_RAW)
- {
- if (sk->num != 0)
- return(-EINVAL);
-
- snum = ntohs(addr->sin_port);
-
+ snum = ntohs(addr->sin_port);
#ifdef CONFIG_IP_MASQUERADE
- /*
- * The kernel masquerader needs some ports
- */
- if(snum>=PORT_MASQ_BEGIN && snum<=PORT_MASQ_END)
- return -EADDRINUSE;
+ /* The kernel masquerader needs some ports. */
+ if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+ return -EADDRINUSE;
#endif
-
- if (snum == 0)
- snum = get_new_socknum(sk->prot, 0);
- if (snum < PROT_SOCK && !suser())
- return(-EACCES);
- }
+ if (snum == 0)
+ snum = sk->prot->good_socknum();
+ if (snum < PROT_SOCK && !suser())
+ return(-EACCES);
- chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
+ chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+ if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+ chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /*
- * Superuser may bind to any address to allow transparent proxying.
- */
- if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST && !suser())
-#else
- if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST)
+ /* Superuser may bind to any address to allow transparent proxying. */
+ if(!suser())
#endif
- return(-EADDRNOTAVAIL); /* Source address MUST be ours! */
+ return -EADDRNOTAVAIL; /* Source address MUST be ours! */
+ }
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
- /*
- * Am I just thick or is this test really always true after the one
- * above? Just taking the test out appears to be the easiest way to
- * make binds to remote addresses for transparent proxying work.
+ /* We keep a pair of addresses. rcv_saddr is the one
+ * used by hash lookups, and saddr is used for transmit.
+ *
+ * In the BSD API these are the same except where it
+ * would be illegal to use them (multicast/broadcast) in
+ * which case the sending device address is used.
*/
- if (chk_addr_ret || addr->sin_addr.s_addr == 0)
- {
-#endif
- /*
- * We keep a pair of addresses. rcv_saddr is the one
- * used by get_sock_*(), and saddr is used for transmit.
- *
- * In the BSD API these are the same except where it
- * would be illegal to use them (multicast/broadcast) in
- * which case the sending device address is used.
- */
- sk->rcv_saddr = addr->sin_addr.s_addr;
- if(chk_addr_ret==IS_MULTICAST||chk_addr_ret==IS_BROADCAST)
- sk->saddr = 0; /* Use device */
- else
- sk->saddr = addr->sin_addr.s_addr;
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
- }
-#endif
- if(sock->type != SOCK_RAW)
- {
- /* Make sure we are allowed to bind here. */
- cli();
- for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
- sk2 != NULL; sk2 = sk2->next)
- {
- /*
- * Hash collision or real match ?
- */
-
- if (sk2->num != snum)
- continue;
-
- /*
- * Either bind on the port is wildcard means
- * they will overlap and thus be in error
- */
-
- if (!sk2->rcv_saddr || !sk->rcv_saddr)
- {
- /*
- * Allow only if both are setting reuse.
- */
- if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
- continue;
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Two binds match ?
- */
-
- if (sk2->rcv_saddr != sk->rcv_saddr)
- continue;
- /*
- * Reusable port ?
- */
-
- if (!sk->reuse)
- {
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Reuse ?
- */
-
- if (!sk2->reuse || sk2->state==TCP_LISTEN)
- {
- sti();
- return(-EADDRINUSE);
- }
- }
- sti();
-
- inet_remove_sock(sk);
- if(sock->type==SOCK_DGRAM)
- udp_cache_zap();
- if(sock->type==SOCK_STREAM)
- tcp_cache_zap();
- inet_put_sock(snum, sk);
- sk->dummy_th.source = ntohs(sk->num);
- sk->daddr = 0;
- sk->dummy_th.dest = 0;
- }
- ip_rt_put(sk->ip_route_cache);
- sk->ip_route_cache=NULL;
+ sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+ if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+ sk->saddr = 0; /* Use device */
+
+ /* Make sure we are allowed to bind here. */
+ if(sk->prot->verify_bind(sk, snum))
+ return -EADDRINUSE;
+
+ sk->num = snum;
+ sk->dummy_th.source = ntohs(snum);
+ sk->daddr = 0;
+ sk->dummy_th.dest = 0;
+ sk->prot->rehash(sk);
+ add_to_prot_sklist(sk);
+ dst_release(sk->dst_cache);
+ sk->dst_cache=NULL;
+ return(0);
+}
+
+int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
+ int addr_len, int flags)
+{
+ struct sock *sk=sock->sk;
+ int err;
+
+ if (inet_autobind(sk) != 0)
+ return(-EAGAIN);
+ if (sk->prot->connect == NULL)
+ return(-EOPNOTSUPP);
+ err = sk->prot->connect(sk, (struct sockaddr *)uaddr, addr_len);
+ if (err < 0)
+ return(err);
return(0);
}
@@ -922,166 +572,159 @@ static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
* TCP 'magic' in here.
*/
-int inet_connect(struct socket *sock, struct sockaddr * uaddr,
- int addr_len, int flags)
+int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
+ int addr_len, int flags)
{
- struct sock *sk=(struct sock *)sock->data;
+ struct sock *sk=sock->sk;
int err;
- sock->conn = NULL;
- if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
- {
- sock->state = SS_CONNECTED;
- /* Connection completing after a connect/EINPROGRESS/select/connect */
- return 0; /* Rock and roll */
+ if(sock->state != SS_UNCONNECTED && sock->state != SS_CONNECTING) {
+ if(sock->state == SS_CONNECTED)
+ return -EISCONN;
+ return -EINVAL;
}
- if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK))
- {
- if(sk->err!=0)
- return sock_error(sk);
- return -EALREADY; /* Connecting is currently in progress */
- }
- if (sock->state != SS_CONNECTING)
- {
+ if(sock->state == SS_CONNECTING) {
+ if(tcp_connected(sk->state)) {
+ sock->state = SS_CONNECTED;
+ return 0;
+ }
+ if(sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) {
+ if(sk->err)
+ return sock_error(sk);
+ return -EALREADY;
+ }
+ } else {
/* We may need to bind the socket. */
- if(inet_autobind(sk)!=0)
+ if (inet_autobind(sk) != 0)
return(-EAGAIN);
if (sk->prot->connect == NULL)
return(-EOPNOTSUPP);
err = sk->prot->connect(sk, uaddr, addr_len);
- if (err < 0)
+ if (err < 0)
return(err);
sock->state = SS_CONNECTING;
}
- if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING)
- {
- sock->state=SS_UNCONNECTED;
+ if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING) {
+ sock->state = SS_UNCONNECTED;
return sock_error(sk);
}
- if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK))
- return(-EINPROGRESS);
+ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+ return (-EINPROGRESS);
- cli(); /* avoid the race condition */
- while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- {
+ cli();
+ while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
interruptible_sleep_on(sk->sleep);
- if (current->signal & ~current->blocked)
- {
+ if (current->signal & ~current->blocked) {
sti();
return(-ERESTARTSYS);
}
/* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
icmp error packets wanting to close a tcp or udp socket. */
- if(sk->err && sk->protocol == IPPROTO_TCP)
- {
+ if (sk->err && sk->protocol == IPPROTO_TCP) {
sock->state = SS_UNCONNECTED;
sti();
return sock_error(sk); /* set by tcp_err() */
}
}
sti();
- sock->state = SS_CONNECTED;
- if (sk->state != TCP_ESTABLISHED && sk->err)
- {
+ sock->state = SS_CONNECTED;
+ if ((sk->state != TCP_ESTABLISHED) && sk->err) {
sock->state = SS_UNCONNECTED;
return sock_error(sk);
}
return(0);
}
-
-static int inet_socketpair(struct socket *sock1, struct socket *sock2)
-{
- return(-EOPNOTSUPP);
-}
-
-
/*
* Accept a pending connection. The TCP layer now gives BSD semantics.
*/
int inet_accept(struct socket *sock, struct socket *newsock, int flags)
{
- struct sock *sk1, *sk2;
- int err;
+ struct sock *sk1 = sock->sk, *sk2;
+ struct sock *newsk = newsock->sk;
+ int err = -EINVAL;
- sk1 = (struct sock *) sock->data;
+ if (sock->state != SS_UNCONNECTED || !(sock->flags & SO_ACCEPTCON))
+ goto do_err;
+
+ err = -EOPNOTSUPP;
+ if (sk1->prot->accept == NULL)
+ goto do_err;
+
+ /* Restore the state if we have been interrupted, and then returned. */
+ if (sk1->pair != NULL) {
+ sk2 = sk1->pair;
+ sk1->pair = NULL;
+ } else {
+ if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
+ goto do_sk1_err;
+ }
/*
* We've been passed an extra socket.
* We need to free it up because the tcp module creates
* its own when it accepts one.
*/
-
- if (newsock->data)
- {
- struct sock *sk=(struct sock *)newsock->data;
- newsock->data=NULL;
- destroy_sock(sk);
- }
-
- if (sk1->prot->accept == NULL)
- return(-EOPNOTSUPP);
+ sk2->sleep = newsk->sleep;
- /*
- * Restore the state if we have been interrupted, and then returned.
- */
-
- if (sk1->pair != NULL )
- {
- sk2 = sk1->pair;
- sk1->pair = NULL;
- }
- else
- {
- sk2 = sk1->prot->accept(sk1,flags);
- if (sk2 == NULL)
- {
- return sock_error(sk1);
- }
- }
- newsock->data = (void *)sk2;
- sk2->sleep = newsock->wait;
+ newsock->sk = sk2;
sk2->socket = newsock;
- newsock->conn = NULL;
- if (flags & O_NONBLOCK)
- return(0);
+ newsk->socket = NULL;
- cli(); /* avoid the race. */
- while(sk2->state == TCP_SYN_RECV)
- {
+ if (flags & O_NONBLOCK)
+ goto do_half_success;
+
+ cli();
+ while (sk2->state == TCP_SYN_RECV) {
interruptible_sleep_on(sk2->sleep);
- if (current->signal & ~current->blocked)
- {
- sti();
- sk1->pair = sk2;
- sk2->sleep = NULL;
- sk2->socket=NULL;
- newsock->data = NULL;
- return(-ERESTARTSYS);
- }
+ if (current->signal & ~current->blocked)
+ goto do_interrupted;
}
sti();
-
- if (sk2->state != TCP_ESTABLISHED && sk2->err > 0)
- {
- err = sock_error(sk2);
- destroy_sock(sk2);
- newsock->data = NULL;
- return err;
- }
+ if(sk2->state == TCP_ESTABLISHED)
+ goto do_full_success;
+ if(sk2->err > 0)
+ goto do_connect_err;
+ err = -ECONNABORTED;
if (sk2->state == TCP_CLOSE)
- {
- destroy_sock(sk2);
- newsock->data=NULL;
- return -ECONNABORTED;
- }
+ goto do_bad_connection;
+do_full_success:
+ destroy_sock(newsk);
newsock->state = SS_CONNECTED;
+ return 0;
+
+do_half_success:
+ destroy_sock(newsk);
return(0);
+
+do_connect_err:
+ err = sock_error(sk2);
+do_bad_connection:
+ sk2->sleep = NULL;
+ sk2->socket = NULL;
+ destroy_sock(sk2);
+ newsock->sk = newsk;
+ newsk->socket = newsock;
+ return err;
+
+do_interrupted:
+ sti();
+ sk1->pair = sk2;
+ sk2->sleep = NULL;
+ sk2->socket = NULL;
+ newsock->sk = newsk;
+ newsk->socket = newsock;
+ err = -ERESTARTSYS;
+do_err:
+ return err;
+do_sk1_err:
+ err = sock_error(sk1);
+ return err;
}
@@ -1092,26 +735,19 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
- struct sockaddr_in *sin=(struct sockaddr_in *)uaddr;
- struct sock *sk;
+ struct sock *sk = sock->sk;
+ struct sockaddr_in *sin = (struct sockaddr_in *)uaddr;
sin->sin_family = AF_INET;
- sk = (struct sock *) sock->data;
- if (peer)
- {
+ if (peer) {
if (!tcp_connected(sk->state))
return(-ENOTCONN);
sin->sin_port = sk->dummy_th.dest;
sin->sin_addr.s_addr = sk->daddr;
- }
- else
- {
+ } else {
__u32 addr = sk->rcv_saddr;
- if (!addr) {
+ if (!addr)
addr = sk->saddr;
- if (!addr)
- addr = ip_my_addr();
- }
sin->sin_port = sk->dummy_th.source;
sin->sin_addr.s_addr = addr;
}
@@ -1121,28 +757,36 @@ static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
-int inet_recvmsg(struct socket *sock, struct msghdr *ubuf, int size,
- int noblock, int flags, int *addr_len)
+int inet_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *) sock->data;
+ struct sock *sk = sock->sk;
+ int addr_len = 0;
+ int err;
+ if (sock->flags & SO_ACCEPTCON)
+ return(-EINVAL);
if (sk->prot->recvmsg == NULL)
return(-EOPNOTSUPP);
- if(sk->err)
+ if (sk->err)
return sock_error(sk);
/* We may need to bind the socket. */
- if(inet_autobind(sk)!=0)
+ if (inet_autobind(sk) != 0)
return(-EAGAIN);
- return(sk->prot->recvmsg(sk, ubuf, size, noblock, flags,addr_len));
+ err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT,
+ flags&~MSG_DONTWAIT, &addr_len);
+ if (err >= 0)
+ msg->msg_namelen = addr_len;
+ return err;
}
-int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
- int noblock, int flags)
+int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
+ struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *) sock->data;
- if (sk->shutdown & SEND_SHUTDOWN)
- {
+ struct sock *sk = sock->sk;
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 1);
return(-EPIPE);
}
@@ -1150,20 +794,20 @@ int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
return(-EOPNOTSUPP);
if(sk->err)
return sock_error(sk);
+
/* We may need to bind the socket. */
- if(inet_autobind(sk)!=0)
+ if(inet_autobind(sk) != 0)
return -EAGAIN;
- return(sk->prot->sendmsg(sk, msg, size, noblock, flags));
-
+
+ return sk->prot->sendmsg(sk, msg, size);
}
int inet_shutdown(struct socket *sock, int how)
{
- struct sock *sk=(struct sock*)sock->data;
+ struct sock *sk = sock->sk;
- /*
- * This should really check to make sure
+ /* This should really check to make sure
* the socket is a TCP socket. (WHY AC...)
*/
how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
@@ -1182,14 +826,13 @@ int inet_shutdown(struct socket *sock, int how)
}
-int inet_select(struct socket *sock, int sel_type, select_table *wait )
+unsigned int inet_poll(struct socket *sock, poll_table *wait)
{
- struct sock *sk=(struct sock *) sock->data;
- if (sk->prot->select == NULL)
- {
+ struct sock *sk = sock->sk;
+
+ if (sk->prot->poll == NULL)
return(0);
- }
- return(sk->prot->select(sk, sel_type, wait));
+ return sk->prot->poll(sock, wait);
}
/*
@@ -1204,7 +847,7 @@ int inet_select(struct socket *sock, int sel_type, select_table *wait )
static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk=(struct sock *)sock->data;
+ struct sock *sk = sock->sk;
int err;
int pid;
@@ -1215,7 +858,6 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
err = get_user(pid, (int *) arg);
if (err)
return err;
- /* see inet_fcntl */
if (current->pid != pid && current->pgrp != -pid && !suser())
return -EPERM;
sk->proc = pid;
@@ -1223,17 +865,17 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case FIOGETOWN:
case SIOCGPGRP:
return put_user(sk->proc, (int *)arg);
+ return(0);
case SIOCGSTAMP:
if(sk->stamp.tv_sec==0)
return -ENOENT;
err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
if (err)
- {
err = -EFAULT;
- }
return err;
case SIOCADDRT:
case SIOCDELRT:
+ case SIOCRTMSG:
return(ip_rt_ioctl(cmd,(void *) arg));
case SIOCDARP:
case SIOCGARP:
@@ -1251,19 +893,20 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#endif
if (rarp_ioctl_hook != NULL)
return(rarp_ioctl_hook(cmd,(void *) arg));
- case SIOCGIFCONF:
- case SIOCGIFFLAGS:
- case SIOCSIFFLAGS:
case SIOCGIFADDR:
case SIOCSIFADDR:
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- case SIOCGIFDSTADDR:
- case SIOCSIFDSTADDR:
case SIOCGIFBRDADDR:
case SIOCSIFBRDADDR:
case SIOCGIFNETMASK:
case SIOCSIFNETMASK:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ return(devinet_ioctl(cmd,(void *) arg));
+ case SIOCGIFCONF:
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
case SIOCGIFMETRIC:
case SIOCSIFMETRIC:
case SIOCGIFMEM:
@@ -1277,6 +920,7 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCGIFMAP:
case SIOCSIFSLAVE:
case SIOCGIFSLAVE:
+ case SIOGIFINDEX:
return(dev_ioctl(cmd,(void *) arg));
case SIOCGIFBR:
@@ -1286,6 +930,7 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#else
return -ENOPKG;
#endif
+
case SIOCADDDLCI:
case SIOCDELDLCI:
#ifdef CONFIG_DLCI
@@ -1303,12 +948,17 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return((*dlci_ioctl_hook)(cmd, (void *) arg));
#endif
return -ENOPKG;
-
+
default:
if ((cmd >= SIOCDEVPRIVATE) &&
- (cmd <= (SIOCDEVPRIVATE + 15)))
+ (cmd <= (SIOCDEVPRIVATE + 15)))
return(dev_ioctl(cmd,(void *) arg));
+#ifdef CONFIG_NET_RADIO
+ if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
+ return(dev_ioctl(cmd,(void *) arg));
+#endif
+
if (sk->prot->ioctl==NULL)
return(-EINVAL);
return(sk->prot->ioctl(sk, cmd, arg));
@@ -1317,244 +967,53 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return(0);
}
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-/*
- * Some routines for the for loop in get_sock which sometimes needs to walk
- * two linked lists in sequence. Could use macros as well.
- * Does anyone know a nicer way to code this?
- */
-static __inline__ struct sock *secondlist(unsigned short hpnum, struct sock *s,
- int *pfirstpass, struct proto *prot)
-{
- if (hpnum && s == NULL && (*pfirstpass)-- )
- return prot->sock_array[hpnum & (SOCK_ARRAY_SIZE - 1)];
- else
- return s;
-}
-static __inline__ struct sock *get_sock_loop_init(unsigned short hnum,
- unsigned short hpnum, struct sock *s,
- int *pfirstpass, struct proto *prot)
-{
- s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
- return secondlist(hpnum, s, pfirstpass, prot);
-}
-static __inline__ struct sock *get_sock_loop_next(unsigned short hnum,
- unsigned short hpnum, struct sock *s,
- int *pfirstpass, struct proto *prot)
-{
- s = s->next;
- return secondlist(hpnum, s, pfirstpass, prot);
-}
-#endif
-
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *get_sock(struct proto *prot, unsigned short num,
- unsigned long raddr, unsigned short rnum,
- unsigned long laddr, unsigned long paddr,
- unsigned short pnum)
-{
- struct sock *s = 0;
- struct sock *result = NULL;
- int badness = -1;
- unsigned short hnum;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- unsigned short hpnum;
- int firstpass = 1;
-#endif
-
- hnum = ntohs(num);
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- hpnum = ntohs(pnum);
-#endif
-
- /*
- * SOCK_ARRAY_SIZE must be a power of two. This will work better
- * than a prime unless 3 or more sockets end up using the same
- * array entry. This should not be a problem because most
- * well known sockets don't overlap that much, and for
- * the other ones, we can just be careful about picking our
- * socket number when we choose an arbitrary one.
- */
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
- s != NULL;
- s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
-#else
- for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
- s != NULL; s = s->next)
-#endif
- {
- int score = 0;
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /* accept the addressed port or the redirect (proxy) port */
- if (s->num != hnum && (hpnum == 0 || s->num != hpnum))
-#else
- if (s->num != hnum)
-#endif
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
- /* local address matches? */
- if (s->rcv_saddr) {
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /*
- * If this is redirected traffic, it must either
- * match on the redirected port/ip-address or on
- * the actual destination, not on a mixture.
- * There must be a simpler way to express this...
- */
- if (hpnum
- ? ((s->num != hpnum || s->rcv_saddr != paddr)
- && (s->num != hnum || s->rcv_saddr != laddr))
- : (s->rcv_saddr != laddr))
-#else
- if (s->rcv_saddr != laddr)
-#endif
- continue;
- score++;
- }
- /* remote address matches? */
- if (s->daddr) {
- if (s->daddr != raddr)
- continue;
- score++;
- }
- /* remote port matches? */
- if (s->dummy_th.dest) {
- if (s->dummy_th.dest != rnum)
- continue;
- score++;
- }
- /* perfect match? */
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- if (score == 3 && s->num == hnum)
-#else
- if (score == 3)
-#endif
- return s;
- /* no, check if this is the best so far.. */
- if (score <= badness)
- continue;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /* don't accept near matches on the actual destination
- * port with IN_ADDR_ANY for redirected traffic, but do
- * allow explicit remote address listens. (disputable)
- */
- if (hpnum && s->num != hpnum && !s->rcv_saddr)
- continue;
-#endif
- result = s;
- badness = score;
- }
- return result;
-}
-
-/*
- * Deliver a datagram to raw sockets.
- */
-
-struct sock *get_sock_raw(struct sock *sk,
- unsigned short num,
- unsigned long raddr,
- unsigned long laddr)
-{
- struct sock *s;
-
- s=sk;
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != num)
- continue;
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
- if(s->daddr && s->daddr!=raddr)
- continue;
- if(s->rcv_saddr && s->rcv_saddr != laddr)
- continue;
- return(s);
- }
- return(NULL);
-}
-
-#ifdef CONFIG_IP_MULTICAST
-/*
- * Deliver a datagram to broadcast/multicast sockets.
- */
-
-struct sock *get_sock_mcast(struct sock *sk,
- unsigned short num,
- unsigned long raddr,
- unsigned short rnum, unsigned long laddr)
-{
- struct sock *s;
- unsigned short hnum;
-
- hnum = ntohs(num);
-
- /*
- * SOCK_ARRAY_SIZE must be a power of two. This will work better
- * than a prime unless 3 or more sockets end up using the same
- * array entry. This should not be a problem because most
- * well known sockets don't overlap that much, and for
- * the other ones, we can just be careful about picking our
- * socket number when we choose an arbitrary one.
- */
-
- s=sk;
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != hnum)
- continue;
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
- if(s->daddr && s->daddr!=raddr)
- continue;
- if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0)
- continue;
- if(s->rcv_saddr && s->rcv_saddr != laddr)
- continue;
- return(s);
- }
- return(NULL);
-}
-
-#endif
-
-struct proto_ops inet_proto_ops = {
+struct proto_ops inet_stream_ops = {
AF_INET,
- inet_create,
inet_dup,
inet_release,
inet_bind,
- inet_connect,
- inet_socketpair,
+ inet_stream_connect,
+ NULL,
inet_accept,
inet_getname,
- inet_select,
+ inet_poll,
inet_ioctl,
inet_listen,
inet_shutdown,
inet_setsockopt,
inet_getsockopt,
- inet_fcntl,
+ sock_no_fcntl,
inet_sendmsg,
inet_recvmsg
};
+struct proto_ops inet_dgram_ops = {
+ AF_INET,
+
+ inet_dup,
+ inet_release,
+ inet_bind,
+ inet_dgram_connect,
+ NULL,
+ NULL,
+ inet_getname,
+ datagram_poll,
+ inet_ioctl,
+ sock_no_listen,
+ inet_shutdown,
+ inet_setsockopt,
+ inet_getsockopt,
+ sock_no_fcntl,
+ inet_sendmsg,
+ inet_recvmsg
+};
+
+struct net_proto_family inet_family_ops = {
+ AF_INET,
+ inet_create
+};
+
extern unsigned long seq_offset;
#ifdef CONFIG_PROC_FS
@@ -1596,62 +1055,40 @@ static struct proc_dir_entry proc_net_udp = {
0, &proc_net_inode_operations,
udp_get_info
};
-static struct proc_dir_entry proc_net_route = {
- PROC_NET_ROUTE, 5, "route",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt_get_info
-};
-static struct proc_dir_entry proc_net_rtcache = {
- PROC_NET_RTCACHE, 8, "rt_cache",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt_cache_get_info
-};
#endif /* CONFIG_PROC_FS */
+extern void tcp_init(void);
+
/*
* Called by socket.c on kernel startup.
*/
void inet_proto_init(struct net_proto *pro)
{
+ struct sk_buff *dummy_skb;
struct inet_protocol *p;
- int i;
+ printk(KERN_INFO "Swansea University Computer Society TCP/IP for NET3.037\n");
- printk("Swansea University Computer Society TCP/IP for NET3.037\n");
+ if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb))
+ {
+ printk(KERN_CRIT "inet_proto_init: panic\n");
+ return;
+ }
/*
* Tell SOCKET that we are alive...
*/
- (void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+ (void) sock_register(&inet_family_ops);
seq_offset = CURRENT_TIME*250;
/*
* Add all the protocols.
*/
-
- for(i = 0; i < SOCK_ARRAY_SIZE; i++)
- {
- tcp_sock_array[i] = NULL;
- udp_sock_array[i] = NULL;
- raw_sock_array[i] = NULL;
- }
- tcp_prot.inuse = 0;
- tcp_prot.highestinuse = 0;
- tcp_prot.sock_array = tcp_sock_array;
- udp_prot.inuse = 0;
- udp_prot.highestinuse = 0;
- udp_prot.sock_array = udp_sock_array;
- raw_prot.inuse = 0;
- raw_prot.highestinuse = 0;
- raw_prot.sock_array = raw_sock_array;
-
- printk("IP Protocols: ");
+ printk(KERN_INFO "IP Protocols: ");
for(p = inet_protocol_base; p != NULL;)
{
struct inet_protocol *tmp = (struct inet_protocol *) p->next;
@@ -1660,35 +1097,44 @@ void inet_proto_init(struct net_proto *pro)
p = tmp;
}
-
/*
* Set the ARP module up
*/
+
arp_init();
/*
* Set the IP module up
*/
+
ip_init();
+ /* Setup TCP slab cache for open requests. */
+ tcp_init();
+
/*
* Set the ICMP layer up
*/
- icmp_init(&inet_proto_ops);
+
+ icmp_init(&inet_family_ops);
+
/*
* Set the firewalling up
*/
-#if defined(CONFIG_IP_ACCT)||defined(CONFIG_IP_FIREWALL)|| \
- defined(CONFIG_IP_MASQUERADE)
+#if defined(CONFIG_IP_ACCT)||defined(CONFIG_IP_FIREWALL)
ip_fw_init();
#endif
+#ifdef CONFIG_IP_MASQUERADE
+ ip_masq_init();
+#endif
+
/*
* Initialise the multicast router
*/
#if defined(CONFIG_IP_MROUTE)
ip_mr_init();
#endif
-
+
/*
* Initialise AF_INET alias type (register net_alias_type)
*/
@@ -1705,17 +1151,13 @@ void inet_proto_init(struct net_proto *pro)
*/
#ifdef CONFIG_PROC_FS
-
#ifdef CONFIG_INET_RARP
proc_net_register(&proc_net_rarp);
#endif /* RARP */
-
proc_net_register(&proc_net_raw);
proc_net_register(&proc_net_snmp);
proc_net_register(&proc_net_sockstat);
proc_net_register(&proc_net_tcp);
proc_net_register(&proc_net_udp);
- proc_net_register(&proc_net_route);
- proc_net_register(&proc_net_rtcache);
#endif /* CONFIG_PROC_FS */
}
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 090808b68..8ef0be2af 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -7,13 +7,6 @@
* high-level addresses) into a low-level hardware address (like an Ethernet
* address).
*
- * FIXME:
- * Experiment with better retransmit timers
- * Clean up the timer deletions
- * If you create a proxy entry, set your interface address to the address
- * and then delete it, proxies may get out of sync with reality -
- * check this.
- *
* This program 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
@@ -60,6 +53,11 @@
* Mike Shaver : /proc/sys/net/ipv4/arp_* support
* Stuart Cheshire : Metricom and grat arp fixes
* *** FOR 2.1 clean this up ***
+ * Lawrence V. Stefani: (08/12/96) Added FDDI support.
+ * Alan Cox : Took the AP1000 nasty FDDI hack and
+ * folded into the mainstream FDDI code.
+ * Ack spit, Linus how did you allow that
+ * one in...
*/
/* RFC1122 Status:
@@ -86,6 +84,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/fddidevice.h>
#include <linux/if_arp.h>
#include <linux/trdevice.h>
#include <linux/skbuff.h>
@@ -105,9 +104,7 @@
#include <net/netrom.h>
#endif
#endif
-#ifdef CONFIG_NET_ALIAS
#include <linux/net_alias.h>
-#endif
#ifdef CONFIG_ARPD
#include <net/netlink.h>
#endif
@@ -115,8 +112,6 @@
#include <asm/system.h>
#include <asm/uaccess.h>
-#include <stdarg.h>
-
/*
* Configurable Parameters
*/
@@ -142,9 +137,6 @@ int sysctl_arp_check_interval = ARP_CHECK_INTERVAL;
/*
* Soft limit on ARP cache size.
- * Note that this number should be greater than
- * number of simultaneously opened sockets, or else
- * hardware header cache will not be efficient.
*/
#if RT_CACHE_DEBUG >= 2
@@ -158,6 +150,16 @@ int sysctl_arp_check_interval = ARP_CHECK_INTERVAL;
#endif
/*
+ * Limit on unresolved ARP cache entries.
+ */
+#define ARP_MAX_UNRES (ARP_MAXSIZE/2)
+
+/*
+ * Maximal number of skb's queued for resolution.
+ */
+#define ARP_MAX_UNRES_PACKETS 3
+
+/*
* If an arp request is send, ARP_RES_TIME is the timeout value until the
* next request is send.
* RFC1122: OK. Throttles ARPing, as per 2.3.2.1. (MUST)
@@ -228,58 +230,31 @@ int sysctl_arp_max_pings = ARP_MAX_PINGS;
int sysctl_arp_dead_res_time = ARP_DEAD_RES_TIME;
+static void arp_neigh_destroy(struct neighbour *neigh);
+
/*
- * This structure defines the ARP mapping cache.
+ * Interface to generic neighbour cache.
*/
-struct arp_table
-{
- struct arp_table *next; /* Linked entry list */
- unsigned long last_used; /* For expiry */
- unsigned long last_updated; /* For expiry */
- unsigned int flags; /* Control status */
- u32 ip; /* ip address of entry */
- u32 mask; /* netmask - used for generalised proxy arps (tridge) */
- unsigned char ha[MAX_ADDR_LEN]; /* Hardware address */
- struct device *dev; /* Device the entry is tied to */
- struct hh_cache *hh; /* Hardware headers chain */
-
- /*
- * The following entries are only used for unresolved hw addresses.
- */
-
- struct timer_list timer; /* expire timer */
- int retries; /* remaining retries */
- struct sk_buff_head skb; /* list of queued packets */
+struct neigh_ops arp_neigh_ops = {
+ AF_INET,
+ NULL,
+ arp_find,
+ arp_neigh_destroy
};
-static atomic_t arp_size = 0;
+static atomic_t arp_size = ATOMIC_INIT(0);
+static atomic_t arp_unres_size = ATOMIC_INIT(0);
#ifdef CONFIG_ARPD
static int arpd_not_running;
static int arpd_stamp;
#endif
-static unsigned int arp_bh_mask;
-
-#define ARP_BH_BACKLOG 1
-
-/*
- * Backlog for ARP updates.
- */
-static struct arp_table *arp_backlog;
-
-/*
- * Backlog for incomplete entries.
- */
-static struct arp_table *arp_req_backlog;
-
-
-static void arp_run_bh(void);
static void arp_check_expire (unsigned long);
static int arp_update (u32 sip, char *sha, struct device * dev,
- unsigned long updated, struct arp_table *ientry, int grat);
+ unsigned long updated, int grat);
static struct timer_list arp_timer =
{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -313,93 +288,41 @@ struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
#define HASH(paddr) (htonl(paddr) & (ARP_TABLE_SIZE - 1))
/*
- * ARP cache semaphore.
- *
- * Every time when someone wants to traverse arp table,
- * he MUST call arp_fast_lock.
- * It will guarantee that arp cache list will not change
- * by interrupts and the entry that you found will not
- * disappear unexpectedly.
- *
- * If you want to modify arp cache lists, you MUST
- * call arp_fast_lock, and check that you are the only
- * owner of semaphore (arp_lock == 1). If it is not the case
- * you can defer your operation or forgot it,
- * but DO NOT TOUCH lists.
- *
- * However, you are allowed to change arp entry contents.
+ * Hardware header cache.
*
- * Assumptions:
- * -- interrupt code MUST have lock/unlock balanced,
- * you cannot lock cache on interrupt and defer unlocking
- * to callback.
- * In particular, it means that lock/unlock are allowed
- * to be non-atomic. They are made atomic, but it was not
- * necessary.
- * -- nobody is allowed to sleep while
- * it keeps arp locked. (route cache has similar locking
- * scheme, but allows sleeping)
- *
*/
-static atomic_t arp_lock;
-
-#define ARP_LOCKED() (arp_lock != 1)
-
-static __inline__ void arp_fast_lock(void)
-{
- atomic_inc(&arp_lock);
-}
-
-static __inline__ void arp_unlock(void)
-{
- if (atomic_dec_and_test(&arp_lock) && arp_bh_mask)
- arp_run_bh();
-}
-
/*
- * Enqueue to FIFO list.
+ * Signal to device layer, that hardware address may be changed.
*/
-static void arp_enqueue(struct arp_table **q, struct arp_table *entry)
+static __inline__ void arp_update_hhs(struct arp_table * entry)
{
- unsigned long flags;
- struct arp_table * tail;
-
- save_flags(flags);
- cli();
- tail = *q;
- if (!tail)
- entry->next = entry;
- else
+ struct hh_cache *hh;
+ void (*update)(struct hh_cache*, struct device*, unsigned char*) =
+ entry->u.neigh.dev->header_cache_update;
+
+#if RT_CACHE_DEBUG >= 1
+ if (!update && entry->u.neigh.hh)
{
- entry->next = tail->next;
- tail->next = entry;
+ printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.neigh.dev->name);
+ return;
}
- *q = entry;
- restore_flags(flags);
- return;
+#endif
+ for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
+ update(hh, entry->u.neigh.dev, entry->u.neigh.ha);
}
/*
- * Dequeue from FIFO list,
- * caller should mask interrupts.
+ * Invalidate all hh's, so that higher level will not try to use it.
*/
-static struct arp_table * arp_dequeue(struct arp_table **q)
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
{
- struct arp_table * entry;
+ struct hh_cache *hh;
- if (*q)
- {
- entry = (*q)->next;
- (*q)->next = entry->next;
- if (entry->next == entry)
- *q = NULL;
- entry->next = NULL;
- return entry;
- }
- return NULL;
+ for (hh=entry->u.neigh.hh; hh; hh=hh->hh_next)
+ hh->hh_uptodate = 0;
}
/*
@@ -409,185 +332,55 @@ static struct arp_table * arp_dequeue(struct arp_table **q)
static void arp_purge_send_q(struct arp_table *entry)
{
struct sk_buff *skb;
- unsigned long flags;
- save_flags(flags);
- cli();
/* Release the list of `skb' pointers. */
- while ((skb = skb_dequeue(&entry->skb)) != NULL)
- {
- skb_device_lock(skb);
- restore_flags(flags);
- dev_kfree_skb(skb, FREE_WRITE);
- cli();
+ while ((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+}
+
+static void arp_free(struct arp_table **entryp)
+{
+ struct arp_table *entry = *entryp;
+ *entryp = entry->u.next;
+
+ if (!(entry->flags&ATF_PUBL)) {
+ atomic_dec(&arp_size);
+ if (!(entry->flags&ATF_COM))
+ atomic_dec(&arp_unres_size);
}
- restore_flags(flags);
- return;
+ del_timer(&entry->timer);
+ arp_purge_send_q(entry);
+ arp_invalidate_hhs(entry);
+
+ neigh_destroy(&entry->u.neigh);
}
-/*
- * Release the entry and all resources linked to it: skb's, hh's, timer
- * and certainly memory.
- * The entry should be already removed from lists.
- */
-static void arp_free_entry(struct arp_table *entry)
+static void arp_neigh_destroy(struct neighbour *neigh)
{
- unsigned long flags;
+ struct arp_table *entry = (struct arp_table*)neigh;
struct hh_cache *hh, *next;
del_timer(&entry->timer);
arp_purge_send_q(entry);
- save_flags(flags);
- cli();
- hh = entry->hh;
- entry->hh = NULL;
- restore_flags(flags);
+ hh = entry->u.neigh.hh;
+ entry->u.neigh.hh = NULL;
for ( ; hh; hh = next)
{
next = hh->hh_next;
hh->hh_uptodate = 0;
hh->hh_next = NULL;
- hh->hh_arp = NULL;
if (atomic_dec_and_test(&hh->hh_refcnt))
- kfree_s(hh, sizeof(struct(struct hh_cache)));
- }
-
- kfree_s(entry, sizeof(struct arp_table));
- atomic_dec(&arp_size);
- return;
-}
-
-/*
- * Hardware header cache.
- *
- * BEWARE! Hardware header cache has no locking, so that
- * it requires especially careful handling.
- * It is the only part of arp+route, where a list
- * should be traversed with masked interrupts.
- * Luckily, this list contains one element 8), as rule.
- */
-
-/*
- * How many users has this entry?
- * The answer is reliable only when interrupts are masked.
- */
-
-static __inline__ int arp_count_hhs(struct arp_table * entry)
-{
- struct hh_cache *hh;
- int count = 0;
-
- for (hh = entry->hh; hh; hh = hh->hh_next)
- count += hh->hh_refcnt-1;
-
- return count;
-}
-
-/*
- * Signal to device layer, that hardware address may be changed.
- */
-
-static __inline__ void arp_update_hhs(struct arp_table * entry)
-{
- struct hh_cache *hh;
-
- for (hh=entry->hh; hh; hh=hh->hh_next)
- entry->dev->header_cache_update(hh, entry->dev, entry->ha);
-}
-
-/*
- * Invalidate all hh's, so that higher level will not try to use it.
- */
-
-static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
-{
- struct hh_cache *hh;
-
- for (hh=entry->hh; hh; hh=hh->hh_next)
- hh->hh_uptodate = 0;
-}
-
-/*
- * Atomic attaching new hh entry.
- * Return 1, if entry has been freed, rather than attached.
- */
-
-static int arp_set_hh(struct hh_cache **hhp, struct hh_cache *hh)
-{
- unsigned long flags;
- struct hh_cache *hh1;
- struct arp_table *entry;
-
- atomic_inc(&hh->hh_refcnt);
-
- save_flags(flags);
- cli();
- if ((hh1 = *hhp) == NULL)
- {
- *hhp = hh;
- restore_flags(flags);
- return 0;
- }
-
- entry = (struct arp_table*)hh->hh_arp;
-
- /*
- * An hh1 entry is already attached to this point.
- * Is it not linked to arp entry? Link it!
- */
- if (!hh1->hh_arp && entry)
- {
- atomic_inc(&hh1->hh_refcnt);
- hh1->hh_next = entry->hh;
- entry->hh = hh1;
- hh1->hh_arp = (void*)entry;
- restore_flags(flags);
-
- if (entry->flags & ATF_COM)
- entry->dev->header_cache_update(hh1, entry->dev, entry->ha);
-#if RT_CACHE_DEBUG >= 1
- printk("arp_set_hh: %08x is reattached. Good!\n", entry->ip);
-#endif
- }
-#if RT_CACHE_DEBUG >= 1
- else if (entry)
- printk("arp_set_hh: %08x rr1 ok!\n", entry->ip);
+ {
+#if RT_CACHE_DEBUG >= 2
+ extern atomic_t hh_count;
+ atomic_dec(&hh_count);
#endif
- restore_flags(flags);
- if (atomic_dec_and_test(&hh->hh_refcnt))
- kfree_s(hh, sizeof(struct hh_cache));
- return 1;
-}
-
-static __inline__ struct hh_cache * arp_alloc_hh(int htype)
-{
- struct hh_cache *hh;
- hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
- if (hh)
- {
- memset(hh, 0, sizeof(struct hh_cache));
- hh->hh_type = htype;
- }
- return hh;
-}
-
-/*
- * Test if a hardware address is all zero
- */
-
-static __inline__ int empty(unsigned char * addr, int len)
-{
- while (len > 0)
- {
- if (*addr)
- return 0;
- len--;
- addr++;
+ kfree_s(hh, sizeof(struct(struct hh_cache)));
+ }
}
- return 1;
}
@@ -610,7 +403,6 @@ static void arpd_send(int req, u32 addr, struct device * dev, char *ha,
if (skb == NULL)
return;
- skb->free=1;
arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request));
arpreq->req = req;
arpreq->ip = addr;
@@ -633,14 +425,14 @@ static void arpd_send(int req, u32 addr, struct device * dev, char *ha,
* Send ARPD update message.
*/
-static __inline__ void arpd_update(struct arp_table * entry)
+static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
{
if (arpd_not_running)
return;
- arpd_send(ARPD_UPDATE, entry->ip, entry->dev, entry->ha,
- entry->last_updated);
+ arpd_send(ARPD_UPDATE, ip, dev, ha, jiffies);
}
+
/*
* Send ARPD lookup request.
*/
@@ -664,7 +456,7 @@ static __inline__ void arpd_flush(struct device * dev)
}
-static int arpd_callback(struct sk_buff *skb)
+static int arpd_callback(int minor, struct sk_buff *skb)
{
struct device * dev;
struct arpd_request *retreq;
@@ -686,7 +478,7 @@ static int arpd_callback(struct sk_buff *skb)
return -EINVAL;
}
- if (!retreq->updated || empty(retreq->ha, sizeof(retreq->ha)))
+ if (!retreq->updated)
{
/*
* Invalid mapping: drop it and send ARP broadcast.
@@ -696,9 +488,9 @@ static int arpd_callback(struct sk_buff *skb)
}
else
{
- arp_fast_lock();
- arp_update(retreq->ip, retreq->ha, dev, retreq->updated, NULL, 0);
- arp_unlock();
+ start_bh_atomic();
+ arp_update(retreq->ip, retreq->ha, dev, retreq->updated, 0);
+ end_bh_atomic();
}
kfree_skb(skb, FREE_READ);
@@ -707,7 +499,7 @@ static int arpd_callback(struct sk_buff *skb)
#else
-static __inline__ void arpd_update(struct arp_table * entry)
+static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
{
return;
}
@@ -732,16 +524,10 @@ static int arp_force_expire(void)
struct arp_table *entry, **pentry;
struct arp_table **oldest_entry = NULL;
unsigned long oldest_used = ~0;
- unsigned long flags;
unsigned long now = jiffies;
int result = 0;
- static last_index;
-
- if (ARP_LOCKED())
- return 0;
-
- save_flags(flags);
+ static int last_index;
if (last_index >= ARP_TABLE_SIZE)
last_index = 0;
@@ -754,31 +540,26 @@ static int arp_force_expire(void)
{
if (!(entry->flags & ATF_PERM))
{
- int users;
- cli();
- users = arp_count_hhs(entry);
-
- if (!users && now - entry->last_used > sysctl_arp_timeout)
+ if (!atomic_read(&entry->u.neigh.refcnt) &&
+ now - entry->u.neigh.lastused > sysctl_arp_timeout)
{
- *pentry = entry->next;
- restore_flags(flags);
#if RT_CACHE_DEBUG >= 2
printk("arp_force_expire: %08x expired\n", entry->ip);
#endif
- arp_free_entry(entry);
+ arp_free(pentry);
result++;
- if (arp_size < ARP_MAXSIZE)
+ if (atomic_read(&arp_size) < ARP_MAXSIZE)
goto done;
continue;
}
- restore_flags(flags);
- if (!users && entry->last_used < oldest_used)
+ if (!atomic_read(&entry->u.neigh.refcnt) &&
+ entry->u.neigh.lastused < oldest_used)
{
oldest_entry = pentry;
- oldest_used = entry->last_used;
+ oldest_used = entry->u.neigh.lastused;
}
}
- pentry = &entry->next;
+ pentry = &entry->u.next;
}
}
@@ -786,15 +567,42 @@ done:
if (result || !oldest_entry)
return result;
- entry = *oldest_entry;
- *oldest_entry = entry->next;
#if RT_CACHE_DEBUG >= 2
- printk("arp_force_expire: expiring %08x\n", entry->ip);
+ printk("arp_force_expire: expiring %08x\n", (*oldest_entry)->ip);
#endif
- arp_free_entry(entry);
+ arp_free(oldest_entry);
return 1;
}
+static void arp_unres_expire(void)
+{
+ int i;
+ struct arp_table *entry, **pentry;
+ unsigned long now = jiffies;
+
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ pentry = &arp_tables[i & (ARP_TABLE_SIZE-1)];
+
+ while ((entry = *pentry) != NULL) {
+ if (!(entry->flags & (ATF_PERM|ATF_COM)) &&
+ (entry->retries < sysctl_arp_max_tries ||
+ entry->timer.expires - now <
+ sysctl_arp_res_time - sysctl_arp_res_time/32)) {
+ if (!atomic_read(&entry->u.neigh.refcnt)) {
+#if RT_CACHE_DEBUG >= 2
+ printk("arp_unres_expire: %08x discarded\n", entry->ip);
+#endif
+ arp_free(pentry);
+ continue;
+ }
+ arp_purge_send_q(entry);
+ }
+ pentry = &entry->u.next;
+ }
+ }
+}
+
+
/*
* Check if there are entries that are too old and remove them. If the
* ATF_PERM flag is set, they are always left in the arp cache (permanent
@@ -817,61 +625,48 @@ static void arp_check_expire(unsigned long dummy)
ip_rt_check_expire();
- arp_fast_lock();
-
- if (!ARP_LOCKED())
+ for (i = 0; i < ARP_TABLE_SIZE; i++)
{
-
- for (i = 0; i < ARP_TABLE_SIZE; i++)
- {
- struct arp_table *entry, **pentry;
+ struct arp_table *entry, **pentry;
- pentry = &arp_tables[i];
+ pentry = &arp_tables[i];
- while ((entry = *pentry) != NULL)
+ while ((entry = *pentry) != NULL)
+ {
+ if (entry->flags & ATF_PERM)
{
- if (entry->flags & ATF_PERM)
- {
- pentry = &entry->next;
- continue;
- }
+ pentry = &entry->u.next;
+ continue;
+ }
- cli();
- if (now - entry->last_used > sysctl_arp_timeout
- && !arp_count_hhs(entry))
- {
- *pentry = entry->next;
- sti();
+ if (!atomic_read(&entry->u.neigh.refcnt) &&
+ now - entry->u.neigh.lastused > sysctl_arp_timeout)
+ {
#if RT_CACHE_DEBUG >= 2
- printk("arp_expire: %08x expired\n", entry->ip);
+ printk("arp_expire: %08x expired\n", entry->ip);
#endif
- arp_free_entry(entry);
- continue;
- }
- sti();
- if (entry->last_updated
- && now - entry->last_updated > sysctl_arp_confirm_interval
- && !(entry->flags & ATF_PERM))
- {
- struct device * dev = entry->dev;
- entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings;
- del_timer(&entry->timer);
- entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
- add_timer(&entry->timer);
- arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
- dev, dev->pa_addr, entry->ha,
- dev->dev_addr, NULL);
+ arp_free(pentry);
+ continue;
+ }
+ if (entry->last_updated &&
+ now - entry->last_updated > sysctl_arp_confirm_interval)
+ {
+ struct device * dev = entry->u.neigh.dev;
+ entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings;
+ del_timer(&entry->timer);
+ entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
+ add_timer(&entry->timer);
+ arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
+ dev, dev->pa_addr, entry->u.neigh.ha,
+ dev->dev_addr, NULL);
#if RT_CACHE_DEBUG >= 2
- printk("arp_expire: %08x requires confirmation\n", entry->ip);
+ printk("arp_expire: %08x requires confirmation\n", entry->ip);
#endif
- }
- pentry = &entry->next; /* go to next entry */
}
+ pentry = &entry->u.next; /* go to next entry */
}
}
- arp_unlock();
-
/*
* Set the timer again.
*/
@@ -891,35 +686,10 @@ static void arp_expire_request (unsigned long arg)
struct arp_table *entry = (struct arp_table *) arg;
struct arp_table **pentry;
unsigned long hash;
- unsigned long flags;
- arp_fast_lock();
-
- save_flags(flags);
- cli();
del_timer(&entry->timer);
- /*
- * If arp table is locked, defer expire processing.
- */
- if (ARP_LOCKED())
- {
-#if RT_CACHE_DEBUG >= 1
- printk(KERN_DEBUG "arp_expire_request: %08x deferred\n", entry->ip);
-#endif
- entry->timer.expires = jiffies + HZ/10;
- add_timer(&entry->timer);
- restore_flags(flags);
- arp_unlock();
- return;
- }
-
- /*
- * Since all timeouts are handled with interrupts enabled, there is a
- * small chance, that this entry has just been resolved by an incoming
- * packet. This is the only race condition, but it is handled...
- *
- * One exception: if entry is COMPLETE but old,
+ /* If entry is COMPLETE but old,
* it means that point-to-point ARP ping has been failed
* (It really occurs with Cisco 4000 routers)
* We should reconfirm it.
@@ -927,17 +697,11 @@ static void arp_expire_request (unsigned long arg)
if ((entry->flags & ATF_COM) && entry->last_updated
&& jiffies - entry->last_updated <= sysctl_arp_confirm_interval)
- {
- restore_flags(flags);
- arp_unlock();
return;
- }
-
- restore_flags(flags);
if (entry->last_updated && --entry->retries > 0)
{
- struct device *dev = entry->dev;
+ struct device *dev = entry->u.neigh.dev;
#if RT_CACHE_DEBUG >= 2
printk("arp_expire_request: %08x timed out\n", entry->ip);
@@ -946,9 +710,8 @@ static void arp_expire_request (unsigned long arg)
entry->timer.expires = jiffies + sysctl_arp_res_time;
add_timer(&entry->timer);
arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
- entry->retries > sysctl_arp_max_tries ? entry->ha : NULL,
+ entry->retries > sysctl_arp_max_tries ? entry->u.neigh.ha : NULL,
dev->dev_addr, NULL);
- arp_unlock();
return;
}
@@ -958,8 +721,7 @@ static void arp_expire_request (unsigned long arg)
arp_purge_send_q(entry);
- cli();
- if (arp_count_hhs(entry))
+ if (atomic_read(&entry->u.neigh.refcnt))
{
/*
* The host is dead, but someone refers to it.
@@ -969,32 +731,29 @@ static void arp_expire_request (unsigned long arg)
* to ARP_DEAD_RES_TIME.
*/
- struct device *dev = entry->dev;
+ struct device *dev = entry->u.neigh.dev;
#if RT_CACHE_DEBUG >= 2
printk("arp_expire_request: %08x is dead\n", entry->ip);
#endif
entry->retries = sysctl_arp_max_tries;
+ if (entry->flags&ATF_COM)
+ atomic_inc(&arp_unres_size);
entry->flags &= ~ATF_COM;
arp_invalidate_hhs(entry);
- restore_flags(flags);
/*
* Declare the entry dead.
*/
entry->last_updated = 0;
- arpd_update(entry);
entry->timer.expires = jiffies + sysctl_arp_dead_res_time;
add_timer(&entry->timer);
arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
NULL, dev->dev_addr, NULL);
- arp_unlock();
return;
}
- restore_flags(flags);
entry->last_updated = 0;
- arpd_update(entry);
hash = HASH(entry->ip);
@@ -1004,48 +763,51 @@ static void arp_expire_request (unsigned long arg)
{
if (*pentry != entry)
{
- pentry = &(*pentry)->next;
+ pentry = &(*pentry)->u.next;
continue;
}
- *pentry = entry->next;
#if RT_CACHE_DEBUG >= 2
printk("arp_expire_request: %08x is killed\n", entry->ip);
#endif
- arp_free_entry(entry);
+ arp_free(pentry);
}
- arp_unlock();
}
/*
* Allocate memory for a new entry. If we are at the maximum limit
- * of the internal ARP cache, arp_force_expire() an entry. NOTE:
- * arp_force_expire() needs the cache to be locked, so therefore
- * arp_alloc_entry() should only be called with the cache locked too!
+ * of the internal ARP cache, arp_force_expire() an entry.
*/
-static struct arp_table * arp_alloc_entry(void)
+static struct arp_table * arp_alloc(int how)
{
struct arp_table * entry;
-
- if (arp_size >= ARP_MAXSIZE)
+ if (how && atomic_read(&arp_size) >= ARP_MAXSIZE)
arp_force_expire();
+ if (how > 1 && atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) {
+ arp_unres_expire();
+ if (atomic_read(&arp_unres_size) >= ARP_MAX_UNRES) {
+ printk(KERN_DEBUG "arp_unres_size=%d\n",
+ atomic_read(&arp_unres_size));
+ return NULL;
+ }
+ }
- entry = (struct arp_table *)
- kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+ entry = (struct arp_table *)neigh_alloc(sizeof(struct arp_table),
+ &arp_neigh_ops);
+ atomic_set(&entry->u.neigh.refcnt, 1);
if (entry != NULL)
{
- atomic_inc(&arp_size);
- memset(entry, 0, sizeof(struct arp_table));
+ if (how)
+ atomic_inc(&arp_size);
entry->mask = DEF_ARP_NETMASK;
init_timer(&entry->timer);
entry->timer.function = arp_expire_request;
entry->timer.data = (unsigned long)entry;
- entry->last_updated = entry->last_used = jiffies;
- skb_queue_head_init(&entry->skb);
+ entry->last_updated = jiffies;
}
return entry;
}
@@ -1069,29 +831,25 @@ int arp_device_event(struct notifier_block *this, unsigned long event, void *ptr
arpd_stamp++;
#endif
- arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
- if (ARP_LOCKED())
- printk("arp_device_event: impossible\n");
-#endif
-
for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
{
struct arp_table *entry;
struct arp_table **pentry = &arp_tables[i];
+ start_bh_atomic();
+
while ((entry = *pentry) != NULL)
{
- if (entry->dev == dev)
+ if (entry->u.neigh.dev != dev)
{
- *pentry = entry->next; /* remove from list */
- arp_free_entry(entry);
+ pentry = &entry->u.next;
+ continue;
}
- else
- pentry = &entry->next; /* go to next entry */
+ arp_free(pentry);
}
+
+ end_bh_atomic();
}
- arp_unlock();
return NOTIFY_DONE;
}
@@ -1105,63 +863,28 @@ static void arp_send_q(struct arp_table *entry)
{
struct sk_buff *skb;
- unsigned long flags;
-
- /*
- * Empty the entire queue, building its data up ready to send
- */
-
- if(!(entry->flags&ATF_COM))
- {
- printk(KERN_ERR "arp_send_q: incomplete entry for %s\n",
- in_ntoa(entry->ip));
- /* Can't flush the skb, because RFC1122 says to hang on to */
- /* at least one from any unresolved entry. --MS */
- /* What's happened is that someone has 'unresolved' the entry
- as we got to use it - this 'can't happen' -- AC */
- return;
- }
-
- save_flags(flags);
-
- cli();
- while((skb = skb_dequeue(&entry->skb)) != NULL)
- {
- IS_SKB(skb);
- skb_device_lock(skb);
- restore_flags(flags);
- if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb))
- {
- skb->arp = 1;
- if(skb->sk==NULL)
- dev_queue_xmit(skb, skb->dev, 0);
- else
- dev_queue_xmit(skb,skb->dev,skb->sk->priority);
- }
- cli();
- }
- restore_flags(flags);
+ while((skb = skb_dequeue(&entry->u.neigh.arp_queue)) != NULL)
+ dev_queue_xmit(skb);
}
static int
arp_update (u32 sip, char *sha, struct device * dev,
- unsigned long updated, struct arp_table *ientry, int grat)
+ unsigned long updated, int grat)
{
struct arp_table * entry;
unsigned long hash;
- int do_arpd = 0;
if (updated == 0)
{
updated = jiffies;
- do_arpd = 1;
+ arpd_update(sip, dev, sha);
}
hash = HASH(sip);
- for (entry=arp_tables[hash]; entry; entry = entry->next)
- if (entry->ip == sip && entry->dev == dev)
+ for (entry=arp_tables[hash]; entry; entry = entry->u.next)
+ if (entry->ip == sip && entry->u.neigh.dev == dev)
break;
if (entry)
@@ -1173,28 +896,24 @@ arp_update (u32 sip, char *sha, struct device * dev,
{
del_timer(&entry->timer);
entry->last_updated = updated;
- if (memcmp(entry->ha, sha, dev->addr_len)!=0)
+ if (memcmp(entry->u.neigh.ha, sha, dev->addr_len) != 0)
{
- memcpy(entry->ha, sha, dev->addr_len);
+ memcpy(entry->u.neigh.ha, sha, dev->addr_len);
if (entry->flags & ATF_COM)
arp_update_hhs(entry);
}
- if (do_arpd)
- arpd_update(entry);
}
if (!(entry->flags & ATF_COM))
{
/*
- * This entry was incomplete. Delete the retransmit timer
- * and switch to complete status.
+ * Switch to complete status.
*/
entry->flags |= ATF_COM;
+ atomic_dec(&arp_unres_size);
arp_update_hhs(entry);
/*
- * Send out waiting packets. We might have problems, if someone is
- * manually removing entries right now -- entry might become invalid
- * underneath us.
+ * Send out waiting packets.
*/
arp_send_q(entry);
}
@@ -1204,39 +923,23 @@ arp_update (u32 sip, char *sha, struct device * dev,
/*
* No entry found. Need to add a new entry to the arp table.
*/
- entry = ientry;
-
- if (grat && !entry)
+ if (grat)
return 0;
+ entry = arp_alloc(1);
if (!entry)
- {
- entry = arp_alloc_entry();
- if (!entry)
- return 0;
-
- entry->ip = sip;
- entry->flags = ATF_COM;
- memcpy(entry->ha, sha, dev->addr_len);
- entry->dev = dev;
- }
+ return 0;
+ entry->ip = sip;
+ entry->flags = ATF_COM;
+ memcpy(entry->u.neigh.ha, sha, dev->addr_len);
+ entry->u.neigh.dev = dev;
+ entry->hatype = dev->type;
entry->last_updated = updated;
- entry->last_used = jiffies;
- if (do_arpd)
- arpd_update(entry);
- if (!ARP_LOCKED())
- {
- entry->next = arp_tables[hash];
- arp_tables[hash] = entry;
- return 0;
- }
-#if RT_CACHE_DEBUG >= 2
- printk("arp_update: %08x backlogged\n", entry->ip);
-#endif
- arp_enqueue(&arp_backlog, entry);
- arp_bh_mask |= ARP_BH_BACKLOG;
+ entry->u.next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ neigh_release(&entry->u.neigh);
return 0;
}
@@ -1246,39 +949,12 @@ static __inline__ struct arp_table *arp_lookup(u32 paddr, struct device * dev)
{
struct arp_table *entry;
- for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
- if (entry->ip == paddr && (!dev || entry->dev == dev))
+ for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->u.next)
+ if (entry->ip == paddr && entry->u.neigh.dev == dev)
return entry;
return NULL;
}
-/*
- * Find an arp mapping in the cache. If not found, return false.
- */
-
-int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
-{
- struct arp_table *entry;
-
- arp_fast_lock();
-
- entry = arp_lookup(paddr, dev);
-
- if (entry != NULL)
- {
- entry->last_used = jiffies;
- if (entry->flags & ATF_COM)
- {
- memcpy(haddr, entry->ha, dev->addr_len);
- arp_unlock();
- return 1;
- }
- }
- arp_unlock();
- return 0;
-}
-
-
static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
{
switch (addr_hint)
@@ -1287,9 +963,9 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s
printk(KERN_DEBUG "ARP: arp called for own IP address\n");
memcpy(haddr, dev->dev_addr, dev->addr_len);
return 1;
-#ifdef CONFIG_IP_MULTICAST
case IS_MULTICAST:
- if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+ if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802
+ || dev->type==ARPHRD_FDDI)
{
u32 taddr;
haddr[0]=0x01;
@@ -1306,7 +982,6 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s
/*
* If a device does not support multicast broadcast the stuff (eg AX.25 for now)
*/
-#endif
case IS_BROADCAST:
memcpy(haddr, dev->broadcast, dev->addr_len);
@@ -1315,57 +990,49 @@ static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, s
return 0;
}
+
+static void arp_start_resolution(struct arp_table *entry)
+{
+ struct device * dev = entry->u.neigh.dev;
+
+ del_timer(&entry->timer);
+ entry->timer.expires = jiffies + sysctl_arp_res_time;
+ entry->retries = sysctl_arp_max_tries;
+ add_timer(&entry->timer);
+#ifdef CONFIG_ARPD
+ if (!arpd_not_running)
+ arpd_lookup(entry->ip, dev);
+ else
+#endif
+ arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev,
+ dev->pa_addr, NULL, dev->dev_addr, NULL);
+}
+
/*
* Create a new unresolved entry.
*/
-struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache *hh, struct sk_buff *skb)
+struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct sk_buff *skb)
{
struct arp_table *entry;
+ unsigned long hash = HASH(paddr);
- entry = arp_alloc_entry();
+ entry = arp_alloc(2);
if (entry != NULL)
{
entry->ip = paddr;
- entry->dev = dev;
- if (hh)
- {
- entry->hh = hh;
- atomic_inc(&hh->hh_refcnt);
- hh->hh_arp = (void*)entry;
- }
- entry->timer.expires = jiffies + sysctl_arp_res_time;
+ entry->u.neigh.dev = dev;
+ entry->hatype = dev->type;
if (skb != NULL)
- {
- skb_queue_tail(&entry->skb, skb);
- skb_device_unlock(skb);
- }
+ skb_queue_tail(&entry->u.neigh.arp_queue, skb);
- if (!ARP_LOCKED())
- {
- unsigned long hash = HASH(paddr);
- entry->next = arp_tables[hash];
- arp_tables[hash] = entry;
- add_timer(&entry->timer);
- entry->retries = sysctl_arp_max_tries;
-#ifdef CONFIG_ARPD
- if (!arpd_not_running)
- arpd_lookup(paddr, dev);
- else
-#endif
- arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL,
- dev->dev_addr, NULL);
- }
- else
- {
-#if RT_CACHE_DEBUG >= 2
- printk("arp_new_entry: %08x backlogged\n", entry->ip);
-#endif
- arp_enqueue(&arp_req_backlog, entry);
- arp_bh_mask |= ARP_BH_BACKLOG;
- }
+ atomic_inc(&arp_unres_size);
+ entry->u.next = arp_tables[hash];
+ arp_tables[hash] = entry;
+ arp_start_resolution(entry);
+ neigh_release(&entry->u.neigh);
}
return entry;
}
@@ -1375,21 +1042,29 @@ struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache
* Find an arp mapping in the cache. If not found, post a request.
*/
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
- u32 saddr, struct sk_buff *skb)
+int arp_find(unsigned char *haddr, struct sk_buff *skb)
{
+ struct device *dev = skb->dev;
+ u32 paddr;
struct arp_table *entry;
unsigned long hash;
- if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
- {
+ if (!skb->dst) {
+ printk(KERN_DEBUG "arp_find called with dst==NULL\n");
+ return 1;
+ }
+
+ paddr = ((struct rtable*)skb->dst)->rt_gateway;
+
+ if (arp_set_predefined(__ip_chk_addr(paddr), haddr, paddr, dev)) {
if (skb)
skb->arp = 1;
return 0;
}
hash = HASH(paddr);
- arp_fast_lock();
+
+ start_bh_atomic();
/*
* Find an entry
@@ -1400,11 +1075,11 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
{
if (entry->flags & ATF_COM)
{
- entry->last_used = jiffies;
- memcpy(haddr, entry->ha, dev->addr_len);
+ entry->u.neigh.lastused = jiffies;
+ memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
if (skb)
skb->arp = 1;
- arp_unlock();
+ end_bh_atomic();
return 0;
}
@@ -1417,8 +1092,10 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
{
if (entry->last_updated)
{
- skb_queue_tail(&entry->skb, skb);
- skb_device_unlock(skb);
+ if (entry->u.neigh.arp_queue.qlen < ARP_MAX_UNRES_PACKETS)
+ skb_queue_tail(&entry->u.neigh.arp_queue, skb);
+ else
+ kfree_skb(skb, FREE_WRITE);
}
/*
* If last_updated==0 host is dead, so
@@ -1426,236 +1103,127 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
*/
else
{
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
- dev_kfree_skb(skb, FREE_WRITE);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ kfree_skb(skb, FREE_WRITE);
}
}
- arp_unlock();
+ end_bh_atomic();
return 1;
}
- entry = arp_new_entry(paddr, dev, NULL, skb);
+ entry = arp_new_entry(paddr, dev, skb);
if (skb != NULL && !entry)
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
- arp_unlock();
+ end_bh_atomic();
return 1;
}
-/*
- * Binding hardware header cache entry.
- * It is the only really complicated part of arp code.
- * We have no locking for hh records, so that
- * all possible race conditions should be resolved by
- * cli()/sti() pairs.
- *
- * Important note: hhs never disappear from lists, if ARP_LOCKED,
- * this fact allows to scan hh lists with enabled interrupts,
- * but results in generating duplicate hh entries.
- * It is harmless. (and I've never seen such event)
- *
- * Returns 0, if hh has been just created, so that
- * caller should fill it.
- */
-
-int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
+int arp_find_1(unsigned char *haddr, struct dst_entry *dst,
+ struct neighbour *neigh)
{
+ struct rtable *rt = (struct rtable*)dst;
+ struct device *dev = dst->dev;
+ u32 paddr = rt->rt_gateway;
struct arp_table *entry;
- struct hh_cache *hh;
- int addr_hint;
- unsigned long flags;
-
- save_flags(flags);
+ unsigned long hash;
- if ((addr_hint = ip_chk_addr(paddr)) != 0)
+ if (!neigh)
{
- unsigned char haddr[MAX_ADDR_LEN];
- if (*hhp)
+ if ((rt->rt_flags & RTF_MULTICAST) &&
+ (dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802))
+ {
+ u32 taddr;
+ haddr[0]=0x01;
+ haddr[1]=0x00;
+ haddr[2]=0x5e;
+ taddr=ntohl(paddr);
+ haddr[5]=taddr&0xff;
+ taddr=taddr>>8;
+ haddr[4]=taddr&0xff;
+ taddr=taddr>>8;
+ haddr[3]=taddr&0x7f;
return 1;
- hh = arp_alloc_hh(htype);
- if (!hh)
+ }
+ if (rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST))
+ {
+ memcpy(haddr, dev->broadcast, dev->addr_len);
return 1;
- arp_set_predefined(addr_hint, haddr, paddr, dev);
- dev->header_cache_update(hh, dev, haddr);
- return arp_set_hh(hhp, hh);
- }
-
- arp_fast_lock();
-
- entry = arp_lookup(paddr, dev);
-
- if (entry)
- {
- for (hh = entry->hh; hh; hh=hh->hh_next)
- if (hh->hh_type == htype)
- break;
-
- if (hh)
+ }
+ if (rt->rt_flags & RTF_LOCAL)
{
- arp_set_hh(hhp, hh);
- arp_unlock();
+ printk(KERN_DEBUG "ARP: arp called for own IP address\n");
+ memcpy(haddr, dev->dev_addr, dev->addr_len);
return 1;
}
+ return 0;
}
- hh = arp_alloc_hh(htype);
- if (!hh)
- {
- arp_unlock();
- return 1;
- }
-
- if (entry)
- {
-
- cli();
- hh->hh_arp = (void*)entry;
- hh->hh_next = entry->hh;
- entry->hh = hh;
- atomic_inc(&hh->hh_refcnt);
- restore_flags(flags);
-
- if (entry->flags & ATF_COM)
- dev->header_cache_update(hh, dev, entry->ha);
+ hash = HASH(paddr);
- if (arp_set_hh(hhp, hh))
- {
- arp_unlock();
- return 0;
- }
+ start_bh_atomic();
- entry->last_used = jiffies;
- arp_unlock();
- return 0;
- }
+ entry = (struct arp_table*)neigh;
- entry = arp_new_entry(paddr, dev, hh, NULL);
- if (entry == NULL)
+ if (entry->flags & ATF_COM)
{
- kfree_s(hh, sizeof(struct hh_cache));
- arp_unlock();
+ entry->u.neigh.lastused = jiffies;
+ memcpy(haddr, entry->u.neigh.ha, dev->addr_len);
+ end_bh_atomic();
return 1;
}
- if (!arp_set_hh(hhp, hh))
- {
- arp_unlock();
- return 0;
- }
- arp_unlock();
- return 1;
+ end_bh_atomic();
+ return 0;
}
-static void arp_run_bh()
+
+struct neighbour* arp_find_neighbour(struct dst_entry *dst, int resolve)
{
- unsigned long flags;
- struct arp_table *entry, *entry1;
- struct device * dev;
+ struct rtable *rt = (struct rtable*)dst;
+ struct device *dev = rt->u.dst.dev;
+ u32 paddr = rt->rt_gateway;
+ struct arp_table *entry;
unsigned long hash;
- struct hh_cache *hh;
- u32 sip;
- save_flags(flags);
- cli();
- arp_fast_lock();
+ if (dst->ops->family != AF_INET)
+ return NULL;
- while (arp_bh_mask)
- {
- arp_bh_mask &= ~ARP_BH_BACKLOG;
+ if ((dev->flags & (IFF_LOOPBACK|IFF_NOARP)) ||
+ (rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST)))
+ return NULL;
- while ((entry = arp_dequeue(&arp_backlog)) != NULL)
- {
- restore_flags(flags);
- if (arp_update(entry->ip, entry->ha, entry->dev, 0, entry, 0))
- arp_free_entry(entry);
- cli();
- }
+ hash = HASH(paddr);
- cli();
- while ((entry = arp_dequeue(&arp_req_backlog)) != NULL)
- {
- restore_flags(flags);
+ start_bh_atomic();
+
+ /*
+ * Find an entry
+ */
+ entry = arp_lookup(paddr, dev);
+
+ if (entry != NULL) /* It exists */
+ {
+ atomic_inc(&entry->u.neigh.refcnt);
+ end_bh_atomic();
+ entry->u.neigh.lastused = jiffies;
+ return (struct neighbour*)entry;
+ }
- dev = entry->dev;
- sip = entry->ip;
- hash = HASH(sip);
+ if (!resolve)
+ return NULL;
- for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
- if (entry1->ip == sip && entry1->dev == dev)
- break;
+ entry = arp_new_entry(paddr, dev, NULL);
- if (!entry1)
- {
- cli();
- entry->next = arp_tables[hash];
- arp_tables[hash] = entry;
- restore_flags(flags);
- entry->timer.expires = jiffies + sysctl_arp_res_time;
- entry->retries = sysctl_arp_max_tries;
- entry->last_used = jiffies;
- if (!(entry->flags & ATF_COM))
- {
- add_timer(&entry->timer);
-#ifdef CONFIG_ARPD
- if (!arpd_not_running)
- arpd_lookup(sip, dev);
- else
-#endif
- arp_send(ARPOP_REQUEST, ETH_P_ARP, sip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
- }
-#if RT_CACHE_DEBUG >= 1
- printk(KERN_DEBUG "arp_run_bh: %08x reinstalled\n", sip);
-#endif
- }
- else
- {
- struct sk_buff * skb;
- struct hh_cache * next;
-
- /* Discard entry, but preserve its hh's and
- * skb's.
- */
- cli();
- for (hh=entry->hh; hh; hh=next)
- {
- next = hh->hh_next;
- hh->hh_next = entry1->hh;
- entry1->hh = hh;
- hh->hh_arp = (void*)entry1;
- }
- entry->hh = NULL;
+ if (entry)
+ atomic_inc(&entry->u.neigh.refcnt);
- /* Prune skb list from entry
- * and graft it to entry1.
- */
- while ((skb = skb_dequeue(&entry->skb)) != NULL)
- {
- skb_device_lock(skb);
- restore_flags(flags);
- skb_queue_tail(&entry1->skb, skb);
- skb_device_unlock(skb);
- cli();
- }
- restore_flags(flags);
-
- arp_free_entry(entry);
+ end_bh_atomic();
- if (entry1->flags & ATF_COM)
- {
- arp_update_hhs(entry1);
- arp_send_q(entry1);
- }
- }
- cli();
- }
- cli();
- }
- arp_unlock();
- restore_flags(flags);
+ return (struct neighbour*)entry;
}
-
/*
* Interface to link layer: send routine and receive handler.
*/
@@ -1696,17 +1264,28 @@ void arp_send(int type, int ptype, u32 dest_ip,
arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
skb->arp = 1;
skb->dev = dev;
- skb->free = 1;
skb->protocol = htons (ETH_P_IP);
/*
* Fill the device header for the ARP frame
*/
-
dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
- /* Fill out the arp protocol part. */
+ /*
+ * Fill out the arp protocol part.
+ *
+ * The arp hardware type should match the device type, except for FDDI,
+ * which (according to RFC 1390) should always equal 1 (Ethernet).
+ */
+#ifdef CONFIG_FDDI
+ arp->ar_hrd = (dev->type == ARPHRD_FDDI) ? htons(ARPHRD_ETHER) : htons(dev->type);
+#else
arp->ar_hrd = htons(dev->type);
+#endif
+ /*
+ * Exceptions everywhere. AX.25 uses the AX.25 PID value not the
+ * DIX code for the protocol. Make these device structure fields.
+ */
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
@@ -1732,8 +1311,10 @@ void arp_send(int type, int ptype, u32 dest_ip,
memset(arp_ptr, 0, dev->addr_len);
arp_ptr+=dev->addr_len;
memcpy(arp_ptr, &dest_ip, 4);
+ skb->dev = dev;
+ skb->priority = 0;
- dev_queue_xmit(skb, dev, 0);
+ dev_queue_xmit(skb);
}
@@ -1743,15 +1324,12 @@ void arp_send(int type, int ptype, u32 dest_ip,
int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
-/*
- * We shouldn't use this type conversion. Check later.
- */
-
- struct arphdr *arp = (struct arphdr *)skb->h.raw;
+ struct arphdr *arp = skb->nh.arph;
unsigned char *arp_ptr= (unsigned char *)(arp+1);
- unsigned char *sha,*tha;
- u32 sip,tip;
-
+ struct rtable *rt;
+ unsigned char *sha, *tha;
+ u32 sip, tip;
+
/*
* The hardware length of the packet should match the hardware length
* of the device. Similarly, the hardware types should match. The
@@ -1759,16 +1337,46 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
* is not from an IP number. We can't currently handle this, so toss
* it.
*/
+#if defined(CONFIG_FDDI) || defined(CONFIG_AP1000)
+ if (dev->type == ARPHRD_FDDI)
+ {
+ /*
+ * According to RFC 1390, FDDI devices should accept ARP hardware types
+ * of 1 (Ethernet). However, to be more robust, we'll accept hardware
+ * types of either 1 (Ethernet) or 6 (IEEE 802.2).
+ */
+ if (arp->ar_hln != dev->addr_len ||
+ ((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) ||
+ dev->flags & IFF_NOARP ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ arp->ar_pln != 4)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+ else
+ {
+ if (arp->ar_hln != dev->addr_len ||
+ dev->type != ntohs(arp->ar_hrd) ||
+ dev->flags & IFF_NOARP ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ arp->ar_pln != 4)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+#else
if (arp->ar_hln != dev->addr_len ||
- dev->type != ntohs(arp->ar_hrd) ||
+ dev->type != ntohs(arp->ar_hrd) ||
dev->flags & IFF_NOARP ||
- arp->ar_pln != 4)
- {
+ skb->pkt_type == PACKET_OTHERHOST ||
+ arp->ar_pln != 4) {
kfree_skb(skb, FREE_READ);
return 0;
- /* Should this be an error/printk? Seems like something */
- /* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
}
+#endif
/*
* Another test.
@@ -1776,7 +1384,6 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
* match the protocol the device speaks. If it doesn't, there is a
* problem, so toss the packet.
*/
-/* Again, should this be an error/printk? -- MS */
switch (dev->type)
{
@@ -1801,14 +1408,8 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
case ARPHRD_ETHER:
case ARPHRD_ARCNET:
case ARPHRD_METRICOM:
- if(arp->ar_pro != htons(ETH_P_IP))
- {
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- break;
-
case ARPHRD_IEEE802:
+ case ARPHRD_FDDI:
if(arp->ar_pro != htons(ETH_P_IP))
{
kfree_skb(skb, FREE_READ);
@@ -1833,13 +1434,22 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
tha=arp_ptr;
arp_ptr += dev->addr_len;
memcpy(&tip, arp_ptr, 4);
-
/*
* Check for bad requests for 127.x.x.x and requests for multicast
* addresses. If this is one such, delete it.
*/
- if (LOOPBACK(tip) || MULTICAST(tip))
- {
+ if (LOOPBACK(tip) || MULTICAST(tip)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ if (ip_route_input(skb, tip, sip, 0, dev)) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ dev = skb->dev;
+ rt = (struct rtable*)skb->dst;
+ if (dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP ||
+ rt->rt_flags&RTF_BROADCAST) {
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -1861,84 +1471,30 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
* cache.
*/
-/*
- * try to switch to alias device whose addr is tip or closest to sip.
- */
-
-#ifdef CONFIG_NET_ALIAS
- if (tip != dev->pa_addr && net_alias_has(skb->dev))
- {
- /*
- * net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
- */
- dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+ if (arp->ar_op == htons(ARPOP_REQUEST)) {
+ struct arp_table *entry;
- if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
- {
- kfree_skb(skb, FREE_READ);
- return 0;
+ for (entry = arp_proxy_list; entry; entry = entry->u.next) {
+ if (!((entry->ip^tip)&entry->mask) &&
+ ((!entry->u.neigh.dev &&
+ (!(entry->flags & ATF_COM) || entry->hatype == dev->type))
+ || entry->u.neigh.dev == dev) )
+ break;
}
- }
-#endif
- if (arp->ar_op == htons(ARPOP_REQUEST))
- {
-
-/*
- * Only reply for the real device address or when it's in our proxy tables
- */
- if (tip != dev->pa_addr)
- {
- struct arp_table *proxy_entry;
+ if (entry && !(entry->flags & ATF_DONTPUB)) {
+ char *ha = (entry->flags & ATF_COM) ? entry->u.neigh.ha : dev->dev_addr;
-/*
- * To get in here, it is a request for someone else. We need to
- * check if that someone else is one of our proxies. If it isn't,
- * we can toss it.
- *
- * Make "longest match" lookup, a la routing.
- */
-
- arp_fast_lock();
-
- for (proxy_entry = arp_proxy_list; proxy_entry;
- proxy_entry = proxy_entry->next)
- {
- if (proxy_entry->dev == dev &&
- !((proxy_entry->ip^tip)&proxy_entry->mask))
- break;
- }
-
- if (proxy_entry && (proxy_entry->mask || ((dev->pa_addr^tip)&dev->pa_mask)))
- {
- char ha[MAX_ADDR_LEN];
- struct rtable * rt;
-
- /* Unlock arp tables to make life for
- * ip_rt_route easy. Note, that we are obliged
- * to make local copy of hardware address.
- */
-
- memcpy(ha, proxy_entry->ha, dev->addr_len);
- arp_unlock();
-
- rt = ip_rt_route(tip, 0);
- if (rt && rt->rt_dev != dev)
- arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
- ip_rt_put(rt);
-
- }
- else
- arp_unlock();
+ if (rt->rt_flags&(RTF_LOCAL|RTF_NAT) ||
+ (!(rt->rt_flags&RTCF_DOREDIRECT) &&
+ rt->u.dst.dev != dev))
+ arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
}
- else
- arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
-
}
- arp_fast_lock();
- arp_update(sip, sha, dev, 0, NULL, ip_chk_addr(tip) != IS_MYADDR && dev->type != ARPHRD_METRICOM);
- arp_unlock();
+ start_bh_atomic();
+ arp_update(sip, sha, dev, 0, !RT_LOCALADDR(rt->rt_flags) && dev->type != ARPHRD_METRICOM);
+ end_bh_atomic();
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -1953,14 +1509,13 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
* Set (create) an ARP cache entry.
*/
-static int arp_req_set(struct arpreq *r, struct device * dev)
+int arp_req_set(struct arpreq *r, struct device * dev)
{
struct arp_table *entry, **entryp;
struct sockaddr_in *si;
- unsigned char *ha;
+ unsigned char *ha = NULL;
u32 ip;
u32 mask = DEF_ARP_NETMASK;
- unsigned long flags;
/*
* Extract netmask (if supplied).
@@ -1979,44 +1534,50 @@ static int arp_req_set(struct arpreq *r, struct device * dev)
si = (struct sockaddr_in *) &r->arp_pa;
ip = si->sin_addr.s_addr;
-
if (r->arp_flags&ATF_PUBL)
{
- if (!mask && ip)
+ if (ip & ~mask)
return -EINVAL;
- if (!dev) {
- dev = dev_getbytype(r->arp_ha.sa_family);
+ if (!dev && (r->arp_flags & ATF_COM))
+ {
+ dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data);
if (!dev)
return -ENODEV;
}
}
else
{
+ struct rtable * rt;
+ int err;
+
+ if ((r->arp_flags & ATF_PERM) && !(r->arp_flags & ATF_COM))
+ return -EINVAL;
+ err = ip_route_output(&rt, ip, 0, 1, dev);
+ if (err)
+ return err;
if (!dev)
- {
- struct rtable * rt;
- rt = ip_rt_route(ip, 0);
- if (!rt)
- return -ENETUNREACH;
- dev = rt->rt_dev;
+ dev = rt->u.dst.dev;
+ if (rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTCF_NAT)) {
+ if (rt->rt_flags&RTF_BROADCAST &&
+ dev->type == ARPHRD_METRICOM &&
+ r->arp_ha.sa_family == ARPHRD_METRICOM) {
+ memcpy(dev->broadcast, r->arp_ha.sa_data, dev->addr_len);
+ ip_rt_put(rt);
+ return 0;
+ }
ip_rt_put(rt);
- if (!dev)
- return -ENODEV;
- }
- if (dev->type != ARPHRD_METRICOM && ip_chk_addr(ip))
return -EINVAL;
+ }
+ ip_rt_put(rt);
}
- if (dev->flags & (IFF_LOOPBACK | IFF_NOARP))
+
+ if (dev && (dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
return -ENODEV;
- if (r->arp_ha.sa_family != dev->type)
+ if (dev && r->arp_ha.sa_family != dev->type)
return -EINVAL;
- arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
- if (ARP_LOCKED())
- printk("arp_req_set: bug\n");
-#endif
+ start_bh_atomic();
if (!(r->arp_flags & ATF_PUBL))
entryp = &arp_tables[HASH(ip)];
@@ -2025,49 +1586,100 @@ static int arp_req_set(struct arpreq *r, struct device * dev)
while ((entry = *entryp) != NULL)
{
- /* User supplied arp entries are definitive - RHP 960603 */
+ if (entry->mask == mask)
+ break;
+ if ((entry->mask & mask) != mask)
+ break;
+ entryp = &entry->u.next;
+ }
+ while ((entry = *entryp) != NULL && entry->mask == mask)
+ {
+ if (entry->ip == ip)
+ break;
+ entryp = &entry->u.next;
+ }
+ while ((entry = *entryp) != NULL && entry->mask == mask &&
+ entry->ip == ip)
+ {
+ if (!entry->u.neigh.dev || entry->u.neigh.dev == dev)
+ break;
+ entryp = &entry->u.next;
+ }
- if (entry->ip == ip && entry->mask == mask && entry->dev == dev) {
- *entryp=entry->next;
- arp_free_entry(entry);
- continue;
+ while ((entry = *entryp) != NULL)
+ {
+ if (entry->ip != ip || entry->mask != mask ||
+ entry->u.neigh.dev != dev)
+ {
+ entry = NULL;
+ break;
}
- if ((entry->mask & mask) != mask)
+ if (entry->hatype == r->arp_ha.sa_family &&
+ (!(r->arp_flags & ATF_MAGIC) ||
+ entry->flags == r->arp_flags))
break;
- entryp = &entry->next;
+ entryp = &entry->u.next;
}
- entry = arp_alloc_entry();
- if (entry == NULL)
+ if (entry)
+ atomic_inc(&entry->u.neigh.refcnt);
+ else
{
- arp_unlock();
- return -ENOMEM;
+ entry = arp_alloc(r->arp_flags&ATF_PUBL ? 0 : 1);
+ if (entry == NULL)
+ {
+ end_bh_atomic();
+ return -ENOMEM;
+ }
+ entry->ip = ip;
+ entry->u.neigh.dev = dev;
+ entry->mask = mask;
+
+ if (dev)
+ entry->hatype = dev->type;
+
+ entry->u.next = *entryp;
+ *entryp = entry;
}
- entry->ip = ip;
- entry->dev = dev;
- entry->mask = mask;
entry->flags = r->arp_flags;
+ if (!(entry->flags&(ATF_PUBL|ATF_COM)))
+ atomic_inc(&arp_unres_size);
- entry->next = *entryp;
- *entryp = entry;
-
- ha = r->arp_ha.sa_data;
- if (empty(ha, dev->addr_len))
- ha = dev->dev_addr;
-
- save_flags(flags);
- cli();
- memcpy(entry->ha, ha, dev->addr_len);
- entry->last_updated = entry->last_used = jiffies;
- entry->flags |= ATF_COM;
- restore_flags(flags);
- arpd_update(entry);
- arp_update_hhs(entry);
- arp_unlock();
- return 0;
-}
+ if (entry->flags & ATF_PUBL)
+ {
+ if (entry->flags & ATF_COM)
+ {
+ entry->hatype = r->arp_ha.sa_family;
+ ha = r->arp_ha.sa_data;
+ }
+ else if (dev)
+ ha = dev->dev_addr;
+ }
+ else
+ ha = r->arp_ha.sa_data;
+ if (ha)
+ memcpy(entry->u.neigh.ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN);
+ else
+ memset(entry->u.neigh.ha, 0, MAX_ADDR_LEN);
+ entry->last_updated = entry->u.neigh.lastused = jiffies;
+
+ if (!(entry->flags & ATF_PUBL))
+ {
+ if (entry->flags & ATF_COM)
+ {
+ arpd_update(entry->ip, entry->u.neigh.dev, ha);
+ arp_update_hhs(entry);
+ }
+ else
+ arp_start_resolution(entry);
+ }
+
+ neigh_release(&entry->u.neigh);
+ end_bh_atomic();
+ return 0;
+}
/*
* Get an ARP cache entry.
@@ -2087,37 +1699,43 @@ static int arp_req_get(struct arpreq *r, struct device *dev)
si = (struct sockaddr_in *) &r->arp_pa;
- arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
- if (ARP_LOCKED())
- printk("arp_req_set: impossible\n");
-#endif
+ start_bh_atomic();
if (!(r->arp_flags & ATF_PUBL))
entry = arp_tables[HASH(si->sin_addr.s_addr)];
else
entry = arp_proxy_list;
- for ( ; entry ;entry = entry->next)
+ for ( ; entry ;entry = entry->u.next)
{
- if (entry->ip == si->sin_addr.s_addr
- && (!dev || entry->dev == dev)
- && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+ if (entry->ip == si->sin_addr.s_addr &&
+ (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) &&
+ ( (r->arp_flags&ATF_PUBL) ?
+ (entry->u.neigh.dev == dev && entry->hatype == r->arp_ha.sa_family)
+ : (entry->u.neigh.dev == dev || !dev)))
{
- memcpy(r->arp_ha.sa_data, entry->ha, entry->dev->addr_len);
- r->arp_ha.sa_family = entry->dev->type;
+ if (entry->u.neigh.dev)
+ {
+ memcpy(r->arp_ha.sa_data, entry->u.neigh.ha, entry->u.neigh.dev->addr_len);
+ r->arp_ha.sa_family = entry->u.neigh.dev->type;
+ strncpy(r->arp_dev, entry->u.neigh.dev->name, sizeof(r->arp_dev));
+ }
+ else
+ {
+ r->arp_ha.sa_family = entry->hatype;
+ memset(r->arp_ha.sa_data, 0, sizeof(r->arp_ha.sa_data));
+ }
r->arp_flags = entry->flags;
- strncpy(r->arp_dev, entry->dev->name, sizeof(r->arp_dev));
- arp_unlock();
+ end_bh_atomic();
return 0;
}
}
- arp_unlock();
+ end_bh_atomic();
return -ENXIO;
}
-static int arp_req_delete(struct arpreq *r, struct device * dev)
+int arp_req_delete(struct arpreq *r, struct device * dev)
{
struct sockaddr_in *si;
struct arp_table *entry, **entryp;
@@ -2132,11 +1750,7 @@ static int arp_req_delete(struct arpreq *r, struct device * dev)
si = (struct sockaddr_in *) &r->arp_pa;
- arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
- if (ARP_LOCKED())
- printk("arp_req_delete: impossible\n");
-#endif
+ start_bh_atomic();
if (!(r->arp_flags & ATF_PUBL))
entryp = &arp_tables[HASH(si->sin_addr.s_addr)];
@@ -2145,19 +1759,24 @@ static int arp_req_delete(struct arpreq *r, struct device * dev)
while ((entry = *entryp) != NULL)
{
- if (entry->ip == si->sin_addr.s_addr
- && (!dev || entry->dev == dev)
- && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+ if (entry->ip == si->sin_addr.s_addr
+ && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)
+ && (entry->u.neigh.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev))
+ && (!(r->arp_flags&ATF_MAGIC) || r->arp_flags == entry->flags))
{
- *entryp = entry->next;
- arp_free_entry(entry);
- retval = 0;
- continue;
+ if (!atomic_read(&entry->u.neigh.refcnt))
+ {
+ arp_free(entryp);
+ retval = 0;
+ continue;
+ }
+ if (retval)
+ retval = -EBUSY;
}
- entryp = &entry->next;
+ entryp = &entry->u.next;
}
- arp_unlock();
+ end_bh_atomic();
return retval;
}
@@ -2169,7 +1788,6 @@ int arp_ioctl(unsigned int cmd, void *arg)
{
int err;
struct arpreq r;
-
struct device * dev = NULL;
switch(cmd)
@@ -2181,17 +1799,7 @@ int arp_ioctl(unsigned int cmd, void *arg)
case SIOCGARP:
err = copy_from_user(&r, arg, sizeof(struct arpreq));
if (err)
- return -EFAULT;
- break;
- case OLD_SIOCDARP:
- case OLD_SIOCSARP:
- if (!suser())
- return -EPERM;
- case OLD_SIOCGARP:
- err = copy_from_user(&r, arg, sizeof(struct arpreq_old));
- if (err)
- return -EFAULT;
- memset(&r.arp_dev, 0, sizeof(r.arp_dev));
+ return -EFAULT;
break;
default:
return -EINVAL;
@@ -2200,8 +1808,9 @@ int arp_ioctl(unsigned int cmd, void *arg)
if (r.arp_pa.sa_family != AF_INET)
return -EPFNOSUPPORT;
- if (!(r.arp_flags & ATF_PUBL))
- r.arp_flags &= ~ATF_NETMASK;
+ if (!(r.arp_flags & ATF_PUBL) &&
+ (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB|ATF_MAGIC)))
+ return -EINVAL;
if (!(r.arp_flags & ATF_NETMASK))
((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK;
@@ -2212,7 +1821,7 @@ int arp_ioctl(unsigned int cmd, void *arg)
if (!r.arp_ha.sa_family)
r.arp_ha.sa_family = dev->type;
- else if (r.arp_ha.sa_family != dev->type)
+ if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
return -EINVAL;
}
@@ -2222,54 +1831,10 @@ int arp_ioctl(unsigned int cmd, void *arg)
return arp_req_delete(&r, dev);
case SIOCSARP:
return arp_req_set(&r, dev);
- case OLD_SIOCDARP:
- /* old SIOCDARP destroys both
- * normal and proxy mappings
- */
- r.arp_flags &= ~ATF_PUBL;
- err = arp_req_delete(&r, dev);
- r.arp_flags |= ATF_PUBL;
- if (!err)
- arp_req_delete(&r, dev);
- else
- err = arp_req_delete(&r, dev);
- return err;
- case OLD_SIOCSARP:
- err = arp_req_set(&r, dev);
- /* old SIOCSARP works so funny,
- * that its behaviour can be emulated
- * only approximately 8).
- * It should work. --ANK
- */
- if (r.arp_flags & ATF_PUBL)
- {
- r.arp_flags &= ~ATF_PUBL;
- arp_req_delete(&r, dev);
- }
- return err;
case SIOCGARP:
err = arp_req_get(&r, dev);
if (!err)
- {
err = copy_to_user(arg, &r, sizeof(r));
- if (err)
- err = -EFAULT;
- }
- return err;
- case OLD_SIOCGARP:
- r.arp_flags &= ~ATF_PUBL;
- err = arp_req_get(&r, dev);
- if (err < 0)
- {
- r.arp_flags |= ATF_PUBL;
- err = arp_req_get(&r, dev);
- }
- if (!err)
- {
- err = copy_to_user(arg, &r, sizeof(struct arpreq_old));
- if (err)
- err = -EFAULT;
- }
return err;
}
/*NOTREACHED*/
@@ -2297,56 +1862,66 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
pos+=size;
len+=size;
- arp_fast_lock();
for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
{
- for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+ start_bh_atomic();
+
+ for(entry=arp_tables[i]; entry!=NULL; entry=entry->u.next)
{
/*
* Convert hardware address to XX:XX:XX:XX ... form.
*/
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
- if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
- strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+ if (entry->hatype == ARPHRD_AX25 || entry->hatype == ARPHRD_NETROM)
+ strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha));
else {
#else
- if(entry->dev->type==ARPHRD_AX25)
- strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+ if(entry->hatype==ARPHRD_AX25)
+ strcpy(hbuffer,ax2asc((ax25_address *)entry->u.neigh.ha));
else {
#endif
#endif
-
- for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+
+ if (entry->u.neigh.dev)
{
- hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
- hbuffer[k++]=hexbuf[ entry->ha[j]&15 ];
- hbuffer[k++]=':';
+ for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.neigh.dev->addr_len;j++)
+ {
+ hbuffer[k++]=hexbuf[ (entry->u.neigh.ha[j]>>4)&15 ];
+ hbuffer[k++]=hexbuf[ entry->u.neigh.ha[j]&15 ];
+ hbuffer[k++]=':';
+ }
+ hbuffer[--k]=0;
}
- hbuffer[--k]=0;
+ else
+ strcpy(hbuffer, "00:00:00:00:00:00");
#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
}
#endif
+
size = sprintf(buffer+len,
"%-17s0x%-10x0x%-10x%s",
in_ntoa(entry->ip),
- (unsigned int)entry->dev->type,
+ entry->hatype,
entry->flags,
hbuffer);
#if RT_CACHE_DEBUG < 2
size += sprintf(buffer+len+size,
" %-17s %s\n",
entry->mask==DEF_ARP_NETMASK ?
- "*" : in_ntoa(entry->mask), entry->dev->name);
+ "*" : in_ntoa(entry->mask),
+ entry->u.neigh.dev ? entry->u.neigh.dev->name : "*");
#else
size += sprintf(buffer+len+size,
- " %-17s %s\t%d\t%1d\n",
+ " %-17s %s\t%d\t%d\t%1d\n",
entry->mask==DEF_ARP_NETMASK ?
- "*" : in_ntoa(entry->mask), entry->dev->name,
- entry->hh ? entry->hh->hh_refcnt : -1,
- entry->hh ? entry->hh->hh_uptodate : 0);
+ "*" : in_ntoa(entry->mask),
+ entry->u.neigh.dev ? entry->u.neigh.dev->name : "*",
+ atomic_read(&entry->u.neigh.refcnt),
+ entry->u.neigh.hh ? atomic_read(&entry->u.neigh.hh->hh_refcnt) : -1,
+ entry->u.neigh.hh ? entry->u.neigh.hh->hh_uptodate : 0);
#endif
len += size;
@@ -2355,11 +1930,14 @@ int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy
if (pos <= offset)
len=0;
if (pos >= offset+length)
+ {
+ end_bh_atomic();
goto done;
+ }
}
+ end_bh_atomic();
}
done:
- arp_unlock();
*start = buffer+len-(pos-offset); /* Start of wanted data */
len = pos-offset; /* Start slop */
@@ -2376,7 +1954,7 @@ done:
static struct packet_type arp_packet_type =
{
- 0, /* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */
+ __constant_htons(ETH_P_ARP),
NULL, /* All devices */
arp_rcv,
NULL,
@@ -2400,8 +1978,6 @@ static struct proc_dir_entry proc_net_arp = {
void arp_init (void)
{
- /* Register the packet type */
- arp_packet_type.type=htons(ETH_P_ARP);
dev_add_pack(&arp_packet_type);
/* Start with the regular checks for expired arp entries. */
add_timer(&arp_timer);
@@ -2417,6 +1993,7 @@ void arp_init (void)
#endif
}
+
#ifdef CONFIG_AX25_MODULE
/*
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 0c2d70cae..da0685340 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -34,6 +34,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/protocol.h>
@@ -41,16 +42,23 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/arp.h>
+#include <linux/notifier.h>
+#include <linux/net_alias.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+extern struct notifier_block *netdev_chain;
/*
* Determine a default network mask, based on the IP address.
*/
-
-unsigned long ip_get_mask(unsigned long addr)
+
+static unsigned long ip_get_mask(unsigned long addr)
{
unsigned long dst;
- if (addr == 0L)
+ if (ZERONET(addr))
return(0L); /* special case */
dst = ntohl(addr);
@@ -68,184 +76,222 @@ unsigned long ip_get_mask(unsigned long addr)
return(0);
}
-/*
- * Check the address for our address, broadcasts, etc.
- *
- * I intend to fix this to at the very least cache the last
- * resolved entry.
+
+/*
+ * This checks bitmasks for the ioctl calls for devices.
*/
-int ip_chk_addr(unsigned long addr)
+static inline int bad_mask(unsigned long mask, unsigned long addr)
{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+
+int devinet_ioctl(unsigned int cmd, void *arg)
+{
+ struct ifreq ifr;
struct device *dev;
-#ifndef CONFIG_IP_CLASSLESS
- unsigned long mask;
+ __u32 addr;
+#ifdef CONFIG_NET_ALIAS
+ int err;
#endif
- /*
- * Accept both `all ones' and `all zeros' as BROADCAST.
- * (Support old BSD in other words). This old BSD
- * support will go very soon as it messes other things
- * up.
- * Also accept `loopback broadcast' as BROADCAST.
+ /*
+ * Fetch the caller's info block into kernel space
*/
- if (addr == INADDR_ANY || addr == INADDR_BROADCAST ||
- addr == htonl(0x7FFFFFFFL))
- return IS_BROADCAST;
-
-#ifndef CONFIG_IP_CLASSLESS
- mask = ip_get_mask(addr);
+ if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+ return -EFAULT;
/*
- * Accept all of the `loopback' class A net.
+ * See which interface the caller is talking about.
*/
- if ((addr & mask) == htonl(0x7F000000L))
- return IS_MYADDR;
-#else
- if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
- return IS_MYADDR;
-#endif
-
/*
- * OK, now check the interface addresses. We could
- * speed this by keeping a dev and a dev_up chain.
+ *
+ * net_alias_dev_get(): dev_get() with added alias naming magic.
+ * only allow alias creation/deletion if (getset==SIOCSIFADDR)
+ *
*/
- for (dev = dev_base; dev != NULL; dev = dev->next)
+#ifdef CONFIG_KERNELD
+ dev_load(ifr.ifr_name);
+#endif
+
+#ifdef CONFIG_NET_ALIAS
+ if ((dev = net_alias_dev_get(ifr.ifr_name, cmd == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+ return(err);
+#else
+ if ((dev = dev_get(ifr.ifr_name)) == NULL)
+ return(-ENODEV);
+#endif
+
+ if (cmd != SIOCSIFADDR && dev->family != AF_INET)
+ return(-EINVAL);
+
+ switch(cmd)
{
- if ((!(dev->flags & IFF_UP)) || dev->family!=AF_INET)
- continue;
- /*
- * If the protocol address of the device is 0 this is special
- * and means we are address hunting (eg bootp).
- */
-
- if (dev->pa_addr == 0)
- return IS_MYADDR;
- /*
- * Is it the exact IP address?
- */
-
- if (addr == dev->pa_addr)
- return IS_MYADDR;
- /*
- * Is it our broadcast address?
- */
-
- if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
- return IS_BROADCAST;
- /*
- * Nope. Check for a subnetwork broadcast.
- */
-
- if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0)
- {
- if ((addr & ~dev->pa_mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
- return IS_BROADCAST;
- }
+ case SIOCGIFADDR: /* Get interface address (and family) */
+ if (ifr.ifr_addr.sa_family == AF_UNSPEC)
+ {
+ memcpy(ifr.ifr_hwaddr.sa_data, dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family = dev->type;
+ }
+ else
+ {
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ }
+ break;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
-#ifndef CONFIG_IP_CLASSLESS
- /*
- * Nope. Check for Network broadcast.
- */
-
- if (((addr ^ dev->pa_addr) & mask) == 0)
- {
- if ((addr & ~mask) == 0)
- return IS_BROADCAST;
- if ((addr & ~mask) == ~mask)
- return IS_BROADCAST;
- }
-#endif
- }
- if(IN_MULTICAST(ntohl(addr)))
- return IS_MULTICAST;
- return 0; /* no match at all */
-}
+ if (!suser())
+ return -EPERM;
+ /*
+ * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
+ * physical address. We can cope with this now.
+ */
+
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ int ret;
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ ret = dev->set_mac_address(dev,&ifr.ifr_addr);
+ if (!ret)
+ notifier_call_chain(&netdev_chain, NETDEV_CHANGEADDR, dev);
+ return ret;
+ }
+ if(ifr.ifr_addr.sa_family!=AF_INET)
+ return -EINVAL;
-/*
- * Retrieve our own address.
- *
- * Because the loopback address (127.0.0.1) is already recognized
- * automatically, we can use the loopback interface's address as
- * our "primary" interface. This is the address used by IP et
- * al when it doesn't know which address to use (i.e. it does not
- * yet know from or to which interface to go...).
- */
-
-unsigned long ip_my_addr(void)
-{
- struct device *dev;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr;
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->flags & IFF_LOOPBACK)
- return(dev->pa_addr);
- }
- return(0);
-}
+ dev_lock_wait();
+ dev_lock_list();
-/*
- * Find an interface that can handle addresses for a certain address.
- */
+ if (dev->family == AF_INET && addr == dev->pa_addr) {
+ dev_unlock_list();
+ return 0;
+ }
-struct device * ip_dev_bynet(unsigned long addr, unsigned long mask)
-{
- struct device *dev;
- struct device *best_dev = NULL;
- __u32 best_mask = mask;
+ if (dev->flags & IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
- for (dev = dev_base; dev; dev = dev->next)
- {
- if (!(dev->flags & IFF_UP))
- continue;
- if (dev->flags & IFF_POINTOPOINT)
- {
+ /*
+ * if dev is an alias, must rehash to update
+ * address change
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ net_alias_dev_rehash(dev, &ifr.ifr_addr);
+#endif
+ dev->pa_addr = addr;
+ dev->ip_flags |= IFF_IP_ADDR_OK;
+ dev->ip_flags &= ~(IFF_IP_BRD_OK|IFF_IP_MASK_OK);
+ dev->family = AF_INET;
+ if (dev->flags & IFF_POINTOPOINT) {
+ dev->pa_mask = 0xFFFFFFFF;
+ dev->pa_brdaddr = 0xFFFFFFFF;
+ } else {
+ dev->pa_mask = ip_get_mask(dev->pa_addr);
+ dev->pa_brdaddr = dev->pa_addr|~dev->pa_mask;
+ }
+ if (dev->flags & IFF_UP)
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+ dev_unlock_list();
+ return 0;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ break;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ if (!suser())
+ return -EPERM;
+
+ addr = (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr.s_addr;
+
+ if (dev->flags & IFF_UP)
+ ip_rt_change_broadcast(dev, addr);
+ dev->pa_brdaddr = addr;
+ dev->ip_flags |= IFF_IP_BRD_OK;
+ return 0;
+
+ case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_port = 0;
+ break;
+
+ case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
+ if (!suser())
+ return -EPERM;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_dstaddr).sin_addr.s_addr;
if (addr == dev->pa_dstaddr)
- return dev;
- continue;
- }
- if (dev->pa_mask & (addr ^ dev->pa_addr))
- continue;
- if (mask == dev->pa_mask)
- return dev;
- if (best_dev && (best_mask & dev->pa_mask) != best_mask)
- continue;
- best_dev = dev;
- best_mask = dev->pa_mask;
- }
- return best_dev;
-}
+ return 0;
+ if (dev->flags & IFF_UP)
+ ip_rt_change_dstaddr(dev, addr);
+ dev->pa_dstaddr = addr;
+ return 0;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ break;
-/*
- * Find the first device with a given source address.
- */
-
-struct device *ip_dev_find(unsigned long addr)
-{
- struct device *dev;
- for(dev = dev_base; dev; dev=dev->next)
- {
- if((dev->flags&IFF_UP) && dev->pa_addr==addr)
- return dev;
- }
- return NULL;
-}
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ if (!suser())
+ return -EPERM;
+ addr = (*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr.s_addr;
-struct device *dev_getbytype(unsigned short type)
-{
- struct device *dev;
+ if (addr == dev->pa_mask) {
+ dev->ip_flags |= IFF_IP_MASK_OK;
+ return 0;
+ }
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
- return(dev);
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(addr, 0))
+ return -EINVAL;
+ if (addr == htonl(0xFFFFFFFE))
+ return -EINVAL;
+ if (dev->flags & IFF_UP)
+ ip_rt_change_netmask(dev, addr);
+ dev->pa_mask = addr;
+ dev->ip_flags |= IFF_IP_MASK_OK;
+ dev->ip_flags &= ~IFF_IP_BRD_OK;
+ return 0;
+ default:
+ return -EINVAL;
+
}
- return(NULL);
+ if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+ return -EFAULT;
+ return 0;
}
-
diff --git a/net/ipv4/fib.c b/net/ipv4/fib.c
new file mode 100644
index 000000000..c2182728c
--- /dev/null
+++ b/net/ipv4/fib.c
@@ -0,0 +1,2076 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * IPv4 Forwarding Information Base.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ *
+ * NOTE: This file is scheduled to be removed from kernel.
+ * The natural place for router FIB is user level
+ * routing daemon (it has to keep its copy in any case)
+ *
+ * Kernel should keep only interface routes and,
+ * if host is not router, default gateway.
+ *
+ * We have good proof that it is feasible and efficient -
+ * multicast routing.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/arp.h>
+#include <net/netlink.h>
+#include <net/ip_fib.h>
+#include <net/dst.h>
+#include <linux/net_alias.h>
+
+static struct fib_class local_class = {RT_CLASS_LOCAL, };
+static struct fib_class default_class = {RT_CLASS_DEFAULT, };
+static struct fib_class main_class = {RT_CLASS_MAIN, };
+static struct fib_class *fib_classes[RT_CLASS_MAX+1];
+
+static struct fib_rule *fib_rules;
+
+static struct fib_info *fib_info_list;
+
+static int fib_stamp;
+
+static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r);
+
+
+#ifdef CONFIG_RTNETLINK
+
+static unsigned rt_nl_flags;
+static int rt_nl_owner = -1;
+
+/*
+ * Default mode is delayed for 0.5sec batch delivery.
+ * If someone starts to use user->level calls,
+ * we turn on synchronous message passing.
+ */
+
+#define RTMSG_DELAY (HZ/2)
+
+static struct nlmsg_ctl rtmsg_ctl = {
+ { NULL, NULL, 0, 0L, NULL },
+ NULL,
+ NETLINK_ROUTE,
+ RTMSG_DELAY,
+ NLMSG_GOODSIZE,
+ 0, 0, 0, 0
+};
+
+static void __rtmsg_ack(struct nlmsghdr *n, int err);
+
+static __inline__ void rtmsg_ack(struct nlmsghdr *n, int err)
+{
+ if (n->nlmsg_seq && rt_nl_flags&RTCTL_ACK)
+ __rtmsg_ack(n, err);
+}
+
+static void rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
+ struct fib_class *class, struct nlmsghdr *n);
+static void rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n);
+#define rtmsg_kick() ({ if (rtmsg_ctl.nlmsg_skb) nlmsg_transmit(&rtmsg_ctl); })
+
+#else
+#define rtmsg_fib(a,b,c,d,e)
+#define rtmsg_dev(a,b,c)
+#define rtmsg_ack(a,b)
+#define rtmsg_kick()
+#endif
+
+
+/*
+ * FIB locking.
+ */
+
+static struct wait_queue *fib_wait;
+static atomic_t fib_users = ATOMIC_INIT(0);
+
+static void fib_lock(void)
+{
+ while (atomic_read(&fib_users))
+ sleep_on(&fib_wait);
+ atomic_inc(&fib_users);
+ dev_lock_list();
+}
+
+static void fib_unlock(void)
+{
+ dev_unlock_list();
+ if (atomic_dec_and_test(&fib_users)) {
+ rtmsg_kick();
+ wake_up(&fib_wait);
+ }
+}
+
+/*
+ * Check if a mask is acceptable.
+ */
+
+static __inline__ int bad_mask(u32 mask, u32 addr)
+{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+/*
+ * Evaluate mask length.
+ */
+
+static __inline__ int fib_logmask(u32 mask)
+{
+ if (!(mask = ntohl(mask)))
+ return 32;
+ return ffz(~mask);
+}
+
+/*
+ * Create mask from mask length.
+ */
+
+static __inline__ u32 fib_mask(int logmask)
+{
+ if (logmask >= 32)
+ return 0;
+ return htonl(~((1<<logmask)-1));
+}
+
+static __inline__ u32 fib_netmask(int logmask)
+{
+ return fib_mask(32-logmask);
+}
+
+
+static struct fib_class *fib_alloc_class(int id)
+{
+ struct fib_class *class;
+
+ if (fib_classes[id])
+ return fib_classes[id];
+
+ class = kmalloc(sizeof(*class), GFP_KERNEL);
+ if (!class)
+ return NULL;
+ memset(class, 0, sizeof(*class));
+ class->cl_id = id;
+ fib_classes[id] = class;
+ return class;
+}
+
+static struct fib_class *fib_empty_class(void)
+{
+ int id;
+ for (id = 1; id <= RT_CLASS_MAX; id++)
+ if (fib_classes[id] == NULL)
+ return fib_alloc_class(id);
+ return NULL;
+}
+
+static int fib_rule_delete(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
+{
+ u32 src = r->rtrmsg_src.s_addr;
+ u32 dst = r->rtrmsg_dst.s_addr;
+ u32 srcmask = fib_netmask(r->rtrmsg_srclen);
+ u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
+ struct fib_rule *cl, **clp;
+
+ for (clp=&fib_rules; (cl=*clp) != NULL; clp=&cl->cl_next) {
+ if (src == cl->cl_src &&
+ srcmask == cl->cl_srcmask &&
+ dst == cl->cl_dst &&
+ dstmask == cl->cl_dstmask &&
+ r->rtrmsg_tos == cl->cl_tos &&
+ dev == cl->cl_dev &&
+ r->rtrmsg_action == cl->cl_action &&
+ (!r->rtrmsg_preference || r->rtrmsg_preference == cl->cl_preference) &&
+ (!r->rtrmsg_class || (cl && r->rtrmsg_class == cl->cl_class->cl_id))) {
+ cli();
+ *clp = cl->cl_next;
+ sti();
+ if (cl->cl_class)
+ cl->cl_class->cl_users--;
+ kfree(cl);
+ return 0;
+ }
+ }
+ return -ESRCH;
+}
+
+static int fib_rule_add(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
+{
+ u32 src = r->rtrmsg_src.s_addr;
+ u32 dst = r->rtrmsg_dst.s_addr;
+ u32 srcmask = fib_netmask(r->rtrmsg_srclen);
+ u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
+
+ struct fib_rule *cl, *new_cl, **clp;
+ struct fib_class *class = NULL;
+
+ if ((src&~srcmask) || (dst&~dstmask))
+ return -EINVAL;
+ if (dev && net_alias_main_dev(dev) != dev)
+ return -ENODEV;
+
+ if (!r->rtrmsg_class) {
+ if (r->rtrmsg_action==RTP_GO || r->rtrmsg_action==RTP_NAT
+ || r->rtrmsg_action==RTP_MASQUERADE) {
+ if ((class = fib_empty_class()) == NULL)
+ return -ENOMEM;
+ class->cl_auto = 1;
+ } else if (r->rtrmsg_rtmsgs)
+ return -EINVAL;
+ } else if ((class = fib_alloc_class(r->rtrmsg_class)) == NULL)
+ return -ENOMEM;
+
+ new_cl = kmalloc(sizeof(*new_cl), GFP_KERNEL);
+ if (!new_cl)
+ return -ENOMEM;
+ new_cl->cl_src = src;
+ new_cl->cl_srcmask = srcmask;
+ new_cl->cl_dst = dst;
+ new_cl->cl_dstmask = dstmask;
+ new_cl->cl_dev = dev;
+ new_cl->cl_srcmap = r->rtrmsg_srcmap.s_addr;
+ new_cl->cl_tos = r->rtrmsg_tos;
+ new_cl->cl_action = r->rtrmsg_action;
+ new_cl->cl_flags = r->rtrmsg_flags;
+ new_cl->cl_preference = r->rtrmsg_preference;
+ new_cl->cl_class = class;
+ if (class)
+ class->cl_users++;
+
+ clp = &fib_rules;
+
+ if (!new_cl->cl_preference) {
+ cl = fib_rules;
+ if (cl && (cl = cl->cl_next) != NULL) {
+ clp = &fib_rules->cl_next;
+ if (cl->cl_preference)
+ new_cl->cl_preference = cl->cl_preference - 1;
+ }
+ }
+
+ while ( (cl = *clp) != NULL ) {
+ if (cl->cl_preference >= new_cl->cl_preference)
+ break;
+ clp = &cl->cl_next;
+ }
+
+ new_cl->cl_next = cl;
+ cli();
+ *clp = new_cl;
+ sti();
+
+ if (r->rtrmsg_rtmsgs) {
+ n->nlmsg_type = RTMSG_NEWROUTE;
+ r->rtrmsg_rtmsg->rtmsg_class = class->cl_id;
+ return rtmsg_process(n, r->rtrmsg_rtmsg);
+ }
+ return 0;
+}
+
+
+#define FZ_MAX_DIVISOR 1024
+
+static __inline__ u32 fib_hash(u32 key, u32 mask)
+{
+ u32 h;
+ h = key^(key>>20);
+ h = h^(h>>10);
+ h = h^(h>>5);
+ return h & mask;
+}
+
+static __inline__ struct fib_node ** fz_hash_p(u32 key, struct fib_zone *fz)
+{
+ return &fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
+}
+
+static __inline__ struct fib_node * fz_hash(u32 key, struct fib_zone *fz)
+{
+ return fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
+}
+
+/*
+ * Free FIB node.
+ */
+
+static void fib_free_node(struct fib_node * f)
+{
+ struct fib_info * fi = f->fib_info;
+ if (fi && !--fi->fib_refcnt) {
+#if RT_CACHE_DEBUG >= 2
+ printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null");
+#endif
+ if (fi->fib_next)
+ fi->fib_next->fib_prev = fi->fib_prev;
+ if (fi->fib_prev)
+ fi->fib_prev->fib_next = fi->fib_next;
+ if (fi == fib_info_list)
+ fib_info_list = fi->fib_next;
+ }
+ kfree_s(f, sizeof(struct fib_node));
+}
+
+static __inline__ int fib_flags_trans(unsigned flags)
+{
+ if (flags & RTF_BROADCAST)
+ return IS_BROADCAST;
+ if (flags & RTF_MULTICAST)
+ return IS_MULTICAST;
+ if (flags & RTF_LOCAL)
+ return IS_MYADDR;
+ return 0;
+}
+
+unsigned ip_fib_chk_addr(u32 addr)
+{
+ struct fib_zone * fz;
+ struct fib_node * f;
+
+ /*
+ * Accept both `all ones' and `all zeros' as BROADCAST.
+ * (Support old BSD in other words). This old BSD
+ * support will go very soon as it messes other things
+ * up.
+ */
+
+ if (addr == INADDR_ANY || addr == 0xFFFFFFFF)
+ return RTF_LOCAL|RTF_BROADCAST;
+
+ if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
+ return RTF_LOCAL|RTF_INTERFACE;
+
+ if (MULTICAST(addr))
+ return RTF_MULTICAST;
+
+ addr = ntohl(addr);
+ for (fz = local_class.fib_zone_list; fz; fz = fz->fz_next) {
+ u32 key = (addr&fz->fz_mask)>>fz->fz_logmask;
+ for (f = fz_hash(key, fz); f; f = f->fib_next) {
+ if (key != f->fib_key || (f->fib_flag & FIBFLG_DOWN))
+ continue;
+ if (!f->fib_info)
+ return 0;
+ return f->fib_info->fib_flags&RTF_ADDRCLASSMASK;
+ }
+ }
+
+ return 0;
+}
+
+int __ip_chk_addr(unsigned long addr)
+{
+ return fib_flags_trans(ip_fib_chk_addr(addr));
+}
+
+/*
+ * Find the first device with a given source address.
+ */
+
+struct device *ip_dev_find(unsigned long addr, char *name)
+{
+ struct fib_zone * fz = local_class.fib_zones[0];
+ u32 key;
+ struct fib_node * f;
+
+ key = (ntohl(addr)&fz->fz_mask)>>fz->fz_logmask;
+ for (f = fz_hash(key, fz); f; f = f->fib_next) {
+ if (key == f->fib_key &&
+ !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+ f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
+ if (!name || strcmp(name, f->fib_info->fib_dev->name) == 0)
+ return f->fib_info->fib_dev;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find tunnel with a given source and destination.
+ */
+
+struct device *ip_dev_find_tunnel(u32 daddr, u32 saddr)
+{
+ struct fib_zone * fz = local_class.fib_zones[0];
+ u32 key;
+ struct fib_node * f;
+
+ key = (ntohl(daddr)&fz->fz_mask)>>fz->fz_logmask;
+ for (f = fz_hash(key, fz); f; f = f->fib_next) {
+ if (key == f->fib_key &&
+ !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+ f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
+ struct device *dev = f->fib_info->fib_dev;
+ if (dev->type == ARPHRD_TUNNEL &&
+ dev->pa_dstaddr == saddr)
+ return dev;
+ }
+ if (!f->fib_info)
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+int ip_fib_chk_default_gw(u32 addr, struct device *dev)
+{
+ struct fib_rule *cl;
+ struct fib_node * f;
+
+ for (cl = fib_rules; cl; cl = cl->cl_next) {
+ if (cl->cl_srcmask || cl->cl_dstmask || cl->cl_tos ||
+ cl->cl_dev || cl->cl_action != RTP_GO || !cl->cl_class ||
+ !cl->cl_class->fib_zones[32])
+ continue;
+ for (f = cl->cl_class->fib_zones[32]->fz_hash[0]; f; f = f->fib_next) {
+ struct fib_info *fi = f->fib_info;
+ if (!(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+ fi->fib_gateway == addr &&
+ fi->fib_dev == dev &&
+ fi->fib_flags&RTF_GATEWAY)
+ return 0;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * Main lookup routine.
+ */
+
+
+int
+fib_lookup(struct fib_result *res, u32 daddr, u32 src, u8 tos,
+ struct device *devin, struct device *devout)
+{
+ struct fib_node * f;
+ struct fib_rule * cl;
+ u32 dst;
+ int local = tos & 1;
+
+ tos &= IPTOS_TOS_MASK;
+ dst = ntohl(daddr);
+
+ for (cl = fib_rules; cl; cl=cl->cl_next) {
+ struct fib_zone * fz;
+
+ if (((src^cl->cl_src) & cl->cl_srcmask) ||
+ ((daddr^cl->cl_dst) & cl->cl_dstmask) ||
+ (cl->cl_tos && cl->cl_tos != tos) ||
+ (cl->cl_dev && cl->cl_dev != devin))
+ continue;
+
+ switch (cl->cl_action) {
+ case RTP_GO:
+ case RTP_NAT:
+ case RTP_MASQUERADE:
+ default:
+ break;
+ case RTP_UNREACHABLE:
+ return -ENETUNREACH;
+ case RTP_DROP:
+ return -EINVAL;
+ case RTP_PROHIBIT:
+ return -EACCES;
+ }
+
+ for (fz = cl->cl_class->fib_zone_list; fz; fz = fz->fz_next) {
+ u32 key = (dst&fz->fz_mask)>>fz->fz_logmask;
+
+ for (f = fz_hash(key, fz); f; f = f->fib_next) {
+ if (key != f->fib_key ||
+ (f->fib_flag & FIBFLG_DOWN) ||
+ (f->fib_tos && f->fib_tos != tos))
+ continue;
+ if (f->fib_flag & FIBFLG_THROW)
+ goto next_class;
+ if (f->fib_flag & FIBFLG_REJECT)
+ return -ENETUNREACH;
+ if (devout && f->fib_info->fib_dev != devout)
+ continue;
+ if (!local || !(f->fib_info->fib_flags&RTF_GATEWAY)) {
+ res->f = f;
+ res->fr = cl;
+ res->fm = fz->fz_logmask;
+ return 0;
+ }
+ }
+ }
+next_class:
+ }
+ return -ENETUNREACH;
+}
+
+static int fib_autopublish(int op, struct fib_node *f, int logmask)
+{
+ struct fib_zone *fz;
+ struct fib_node *f1;
+ struct arpreq r;
+ u32 addr = htonl(f->fib_key<<logmask);
+
+ if (f->fib_flag || LOOPBACK(addr) ||
+ (!RT_LOCALADDR(f->fib_info->fib_flags) &&
+ !(f->fib_info->fib_flags&RTF_NAT)))
+ return 0;
+
+ memset(&r, 0, sizeof(struct arpreq));
+ r.arp_flags = ATF_PUBL|ATF_PERM|ATF_MAGIC;
+ if (logmask)
+ r.arp_flags |= ATF_NETMASK;
+ ((struct sockaddr_in*)&r.arp_pa)->sin_family = AF_INET;
+ ((struct sockaddr_in*)&r.arp_pa)->sin_addr.s_addr = addr;
+ ((struct sockaddr_in*)&r.arp_netmask)->sin_family = AF_INET;
+ ((struct sockaddr_in*)&r.arp_netmask)->sin_addr.s_addr = fib_mask(logmask);
+
+ if (op)
+ return arp_req_set(&r, NULL);
+
+ fz = local_class.fib_zones[logmask];
+
+ for (f1 = fz_hash(f->fib_key, fz); f1; f1=f1->fib_next) {
+ if (f->fib_key != f1->fib_key || f1->fib_flag ||
+ (!RT_LOCALADDR(f1->fib_info->fib_flags) &&
+ !(f1->fib_info->fib_flags&RTF_NAT)))
+ continue;
+ return 0;
+ }
+
+ return arp_req_delete(&r, NULL);
+}
+
+#define FIB_SCAN(f, fp) \
+for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fib_next)
+
+#define FIB_SCAN_KEY(f, fp, key) \
+for ( ; ((f) = *(fp)) != NULL && (f)->fib_key == (key); (fp) = &(f)->fib_next)
+
+#define FIB_CONTINUE(f, fp) \
+{ \
+ fp = &f->fib_next; \
+ continue; \
+}
+
+static int fib_delete(struct in_rtmsg * r, struct device *dev,
+ struct fib_class *class, struct nlmsghdr *n)
+{
+ struct fib_node **fp, *f;
+ struct fib_zone *fz = class->fib_zones[32-r->rtmsg_prefixlen];
+ int logmask = 32 - r->rtmsg_prefixlen;
+ u32 dst = ntohl(r->rtmsg_prefix.s_addr);
+ u32 gw = r->rtmsg_gateway.s_addr;
+ short metric = r->rtmsg_metric;
+ u8 tos = r->rtmsg_tos;
+ u8 fibflg = 0;
+ int found=0;
+ unsigned flags;
+ u32 key;
+
+ flags = r->rtmsg_flags;
+ if (flags & RTF_REJECT)
+ fibflg |= FIBFLG_REJECT;
+ else if (flags & RTF_THROW)
+ fibflg |= FIBFLG_THROW;
+ flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
+
+ if (fz != NULL) {
+ key = (dst&fz->fz_mask)>>logmask;
+ fp = fz_hash_p(key, fz);
+
+ FIB_SCAN(f, fp) {
+ if (f->fib_key == key)
+ break;
+ }
+ FIB_SCAN_KEY(f, fp, key) {
+ if (f->fib_tos == tos)
+ break;
+ }
+
+ while ((f = *fp) != NULL && f->fib_key == key && f->fib_tos == tos) {
+ struct fib_info * fi = f->fib_info;
+
+ /*
+ * If metric was not specified (<0), match all metrics.
+ */
+ if (metric >= 0 && f->fib_metric != metric)
+ FIB_CONTINUE(f, fp);
+
+ if (flags & RTF_MAGIC) {
+ /* "Magic" deletions require exact match */
+ if (!fi || (fi->fib_flags^flags) ||
+ fi->fib_dev != dev ||
+ fi->fib_gateway != gw)
+ FIB_CONTINUE(f, fp);
+ } else {
+ /*
+ * Device, gateway, reject and throw are
+ * also checked if specified.
+ */
+ if ((dev && fi && fi->fib_dev != dev) ||
+ (gw && fi && fi->fib_gateway != gw) ||
+ (fibflg && (f->fib_flag^fibflg)&~FIBFLG_DOWN))
+ FIB_CONTINUE(f, fp);
+ }
+ cli();
+ /* It's interesting, can this operation be not atomic? */
+ *fp = f->fib_next;
+ sti();
+ if (class == &local_class)
+ fib_autopublish(0, f, logmask);
+ rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, n);
+ fib_free_node(f);
+ found++;
+ }
+ fz->fz_nent -= found;
+ }
+
+ if (found) {
+ fib_stamp++;
+ rt_cache_flush(0);
+ rtmsg_ack(n, 0);
+ return 0;
+ }
+ rtmsg_ack(n, ESRCH);
+ return -ESRCH;
+}
+
+static struct fib_info * fib_create_info(struct device * dev, struct in_rtmsg *r)
+{
+ struct fib_info * fi;
+ unsigned flags = r->rtmsg_flags;
+ u32 gw = r->rtmsg_gateway.s_addr;
+ unsigned short mtu;
+ unsigned short irtt;
+ unsigned long window;
+
+ mtu = dev ? dev->mtu : 0;
+ if (flags&RTF_MSS && r->rtmsg_mtu < mtu && r->rtmsg_mtu >= 68)
+ mtu = r->rtmsg_mtu;
+ window = (flags & RTF_WINDOW) ? r->rtmsg_window : 0;
+ irtt = (flags & RTF_IRTT) ? r->rtmsg_rtt : TCP_TIMEOUT_INIT;
+
+ flags &= RTF_FIB;
+
+ for (fi=fib_info_list; fi; fi = fi->fib_next) {
+ if (fi->fib_gateway != gw ||
+ fi->fib_dev != dev ||
+ fi->fib_flags != flags ||
+ fi->fib_mtu != mtu ||
+ fi->fib_window != window ||
+ fi->fib_irtt != irtt)
+ continue;
+ fi->fib_refcnt++;
+#if RT_CACHE_DEBUG >= 2
+ printk("fib_create_info: fi %08x/%s/%04x is duplicate\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
+#endif
+ return fi;
+ }
+ fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
+ if (!fi)
+ return NULL;
+ memset(fi, 0, sizeof(struct fib_info));
+ fi->fib_flags = flags;
+ fi->fib_dev = dev;
+ fi->fib_gateway = gw;
+ fi->fib_mtu = mtu;
+ fi->fib_window = window;
+ fi->fib_refcnt++;
+ fi->fib_next = fib_info_list;
+ fi->fib_prev = NULL;
+ fi->fib_irtt = irtt;
+ if (fib_info_list)
+ fib_info_list->fib_prev = fi;
+ fib_info_list = fi;
+#if RT_CACHE_DEBUG >= 2
+ printk("fib_create_info: fi %08x/%s/%04x is created\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
+#endif
+ return fi;
+}
+
+static __inline__ void fib_rebuild_zone(struct fib_zone *fz,
+ struct fib_node **old_ht,
+ int old_divisor)
+{
+ int i;
+ struct fib_node **ht = fz->fz_hash;
+ u32 hashmask = fz->fz_hashmask;
+ struct fib_node *f, **fp, *next;
+ unsigned hash;
+
+ for (i=0; i<old_divisor; i++) {
+ for (f=old_ht[i]; f; f=next) {
+ next = f->fib_next;
+ f->fib_next = NULL;
+ hash = fib_hash(f->fib_key, hashmask);
+ for (fp = &ht[hash]; *fp; fp = &(*fp)->fib_next)
+ /* NONE */;
+ *fp = f;
+ }
+ }
+}
+
+static void fib_rehash_zone(struct fib_zone *fz)
+{
+ struct fib_node **ht, **old_ht;
+ int old_divisor, new_divisor;
+ u32 new_hashmask;
+
+ old_divisor = fz->fz_divisor;
+
+ switch (old_divisor) {
+ case 16:
+ new_divisor = 256;
+ new_hashmask = 0xFF;
+ break;
+ case 256:
+ new_divisor = 1024;
+ new_hashmask = 0x3FF;
+ break;
+ default:
+ printk(KERN_CRIT "route.c: bad divisor %d!\n", old_divisor);
+ return;
+ }
+#if RT_CACHE_DEBUG >= 2
+ printk("fib_rehash_zone: hash for zone %d grows from %d\n", fz->fz_logmask, old_divisor);
+#endif
+
+ ht = kmalloc(new_divisor*sizeof(struct rtable*), GFP_KERNEL);
+
+ if (ht) {
+ memset(ht, 0, new_divisor*sizeof(struct fib_node*));
+ start_bh_atomic();
+ old_ht = fz->fz_hash;
+ fz->fz_hash = ht;
+ fz->fz_hashmask = new_hashmask;
+ fz->fz_divisor = new_divisor;
+ fib_rebuild_zone(fz, old_ht, old_divisor);
+ fib_stamp++;
+ end_bh_atomic();
+ kfree(old_ht);
+ }
+}
+
+static struct fib_zone *
+fib_new_zone(struct fib_class *class, int logmask)
+{
+ int i;
+ struct fib_zone *fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
+ if (!fz)
+ return NULL;
+
+ memset(fz, 0, sizeof(struct fib_zone));
+ if (logmask < 32) {
+ fz->fz_divisor = 16;
+ fz->fz_hashmask = 0xF;
+ } else {
+ fz->fz_divisor = 1;
+ fz->fz_hashmask = 0;
+ }
+ fz->fz_hash = kmalloc(fz->fz_divisor*sizeof(struct fib_node*), GFP_KERNEL);
+ if (!fz->fz_hash) {
+ kfree(fz);
+ return NULL;
+ }
+ memset(fz->fz_hash, 0, fz->fz_divisor*sizeof(struct fib_node*));
+ fz->fz_logmask = logmask;
+ fz->fz_mask = ntohl(fib_mask(logmask));
+ for (i=logmask-1; i>=0; i--)
+ if (class->fib_zones[i])
+ break;
+ start_bh_atomic();
+ if (i<0) {
+ fz->fz_next = class->fib_zone_list;
+ class->fib_zone_list = fz;
+ } else {
+ fz->fz_next = class->fib_zones[i]->fz_next;
+ class->fib_zones[i]->fz_next = fz;
+ }
+ class->fib_zones[logmask] = fz;
+ fib_stamp++;
+ end_bh_atomic();
+ return fz;
+}
+
+static int fib_create(struct in_rtmsg *r, struct device *dev,
+ struct fib_class *class, struct nlmsghdr *n)
+{
+ struct fib_node *f, *f1, **fp;
+ struct fib_node **dup_fp = NULL;
+ struct fib_zone * fz;
+ struct fib_info * fi;
+
+ long logmask = 32L - r->rtmsg_prefixlen; /* gcc bug work-around: must be "L" and "long" */
+ u32 dst = ntohl(r->rtmsg_prefix.s_addr);
+ u32 gw = r->rtmsg_gateway.s_addr;
+ short metric = r->rtmsg_metric;
+ unsigned flags = r->rtmsg_flags;
+ u8 tos = r->rtmsg_tos;
+ u8 fibflg = 0;
+ u32 key;
+
+ /*
+ * Allocate an entry and fill it in.
+ */
+
+ f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
+ if (f == NULL) {
+ rtmsg_ack(n, ENOMEM);
+ return -ENOMEM;
+ }
+
+ memset(f, 0, sizeof(struct fib_node));
+
+ if (!(flags & RTF_UP))
+ fibflg = FIBFLG_DOWN;
+ if (flags & RTF_REJECT)
+ fibflg |= FIBFLG_REJECT;
+ else if (flags & RTF_THROW)
+ fibflg |= FIBFLG_THROW;
+
+ flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
+ r->rtmsg_flags = flags;
+
+ fi = NULL;
+ if (!(fibflg & (FIBFLG_REJECT|FIBFLG_THROW))) {
+ if ((fi = fib_create_info(dev, r)) == NULL) {
+ kfree_s(f, sizeof(struct fib_node));
+ rtmsg_ack(n, ENOMEM);
+ return -ENOMEM;
+ }
+ f->fib_info = fi;
+ flags = fi->fib_flags;
+ }
+
+ f->fib_key = key = dst>>logmask;
+ f->fib_metric = metric;
+ f->fib_tos = tos;
+ f->fib_flag = fibflg;
+ fz = class->fib_zones[logmask];
+
+ if (!fz && !(fz = fib_new_zone(class, logmask))) {
+ fib_free_node(f);
+ rtmsg_ack(n, ENOMEM);
+ return -ENOMEM;
+ }
+
+ if (fz->fz_nent > (fz->fz_divisor<<2) &&
+ fz->fz_divisor < FZ_MAX_DIVISOR &&
+ (!logmask || (1<<(32-logmask)) > fz->fz_divisor))
+ fib_rehash_zone(fz);
+
+ fp = fz_hash_p(key, fz);
+
+ /*
+ * Scan list to find the first route with the same destination
+ */
+ FIB_SCAN(f1, fp) {
+ if (f1->fib_key == key)
+ break;
+ }
+
+ /*
+ * Find route with the same destination and tos.
+ */
+ FIB_SCAN_KEY(f1, fp, dst) {
+ if (f1->fib_tos <= tos)
+ break;
+ }
+
+ /*
+ * Find route with the same destination/tos and less (or equal) metric.
+ * "Magic" additions go to the end of list.
+ */
+ for ( ; (f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos;
+ fp = &f1->fib_next) {
+ if (f1->fib_metric >= metric && metric != MAGIC_METRIC)
+ break;
+
+ /*
+ * Record route with the same destination/tos/gateway/dev,
+ * but less metric.
+ */
+ if (!dup_fp) {
+ struct fib_info *fi1 = f1->fib_info;
+
+ if ((fibflg^f1->fib_flag) & ~FIBFLG_DOWN)
+ continue;
+ if (fi == fi1 ||
+ (fi && fi1 &&
+ fi->fib_dev == fi1->fib_dev &&
+ fi->fib_gateway == fi1->fib_gateway &&
+ !(flags&RTF_MAGIC)))
+ dup_fp = fp;
+ }
+ }
+
+ /*
+ * Is it already present?
+ */
+
+ if (f1 && f1->fib_key == key && f1->fib_tos == tos &&
+ f1->fib_metric == metric && f1->fib_info == fi) {
+ fib_free_node(f);
+
+ if (fibflg == f1->fib_flag) {
+ rtmsg_ack(n, EEXIST);
+ return -EEXIST;
+ } else {
+ fib_stamp++;
+ f1->fib_flag = fibflg;
+ rt_cache_flush(0);
+ rtmsg_ack(n, 0);
+ return 0;
+ }
+ }
+
+ /*
+ * Do not add "magic" route, if better one is already present.
+ */
+ if ((flags & RTF_MAGIC) && dup_fp) {
+ fib_free_node(f);
+ rtmsg_ack(n, EEXIST);
+ return -EEXIST;
+ }
+
+ /*
+ * Insert new entry to the list.
+ */
+
+ cli();
+ f->fib_next = f1;
+ *fp = f;
+ sti();
+ fz->fz_nent++;
+ if (class == &local_class && !dup_fp)
+ fib_autopublish(1, f, logmask);
+ rtmsg_fib(RTMSG_NEWROUTE, f, logmask, class, n);
+
+ if (flags & RTF_MAGIC) {
+ fib_stamp++;
+ rt_cache_flush(0);
+ rtmsg_ack(n, 0);
+ return 0;
+ }
+
+ /*
+ * Clean routes with the same destination,tos,gateway and device,
+ * but different metric.
+ */
+ fp = dup_fp ? : &f->fib_next;
+
+ while ((f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos) {
+ if (f1 == f || ((f1->fib_flag^fibflg)&~FIBFLG_DOWN))
+ FIB_CONTINUE(f1, fp);
+
+ if (f1->fib_info != fi &&
+ (!fi || !f1->fib_info ||
+ f1->fib_info->fib_gateway != gw ||
+ f1->fib_info->fib_dev != dev))
+ FIB_CONTINUE(f1, fp);
+
+ cli();
+ *fp = f1->fib_next;
+ sti();
+ fz->fz_nent--;
+ rtmsg_fib(RTMSG_DELROUTE, f1, logmask, class, n);
+ fib_free_node(f1);
+ }
+ fib_stamp++;
+ rt_cache_flush(0);
+ rtmsg_ack(n, 0);
+ return 0;
+}
+
+static int fib_flush_list(struct fib_node ** fp, struct device *dev,
+ int logmask, struct fib_class *class)
+{
+ int found = 0;
+ struct fib_node *f;
+
+ while ((f = *fp) != NULL) {
+ if (!f->fib_info || f->fib_info->fib_dev != dev)
+ FIB_CONTINUE(f, fp);
+ cli();
+ *fp = f->fib_next;
+ sti();
+ if (class == &local_class)
+ fib_autopublish(0, f, logmask);
+#ifdef CONFIG_RTNETLINK
+ if (rt_nl_flags&RTCTL_FLUSH)
+ rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, 0);
+#endif
+ fib_free_node(f);
+ found++;
+ }
+ return found;
+}
+
+static void fib_flush(struct device *dev)
+{
+ struct fib_class *class;
+ struct fib_rule *cl, **clp;
+ struct fib_zone *fz;
+ int found = 0;
+ int i, tmp, cl_id;
+
+
+ for (cl_id = RT_CLASS_MAX; cl_id>=0; cl_id--) {
+ if ((class = fib_classes[cl_id])==NULL)
+ continue;
+ for (fz = class->fib_zone_list; fz; fz = fz->fz_next) {
+ tmp = 0;
+ for (i=fz->fz_divisor-1; i>=0; i--)
+ tmp += fib_flush_list(&fz->fz_hash[i], dev,
+ fz->fz_logmask, class);
+ fz->fz_nent -= tmp;
+ found += tmp;
+ }
+ }
+
+ clp = &fib_rules;
+ while ( (cl=*clp) != NULL) {
+ if (cl->cl_dev != dev) {
+ clp = &cl->cl_next;
+ continue;
+ }
+ found++;
+ cli();
+ *clp = cl->cl_next;
+ sti();
+ kfree(cl);
+ }
+
+ if (found) {
+ fib_stamp++;
+ rt_cache_flush(1);
+ }
+}
+
+#ifdef CONFIG_PROC_FS
+
+static unsigned __inline__ fib_flag_trans(u8 fibflg)
+{
+ unsigned ret = RTF_UP;
+ if (!fibflg)
+ return ret;
+ if (fibflg & FIBFLG_DOWN)
+ ret &= ~RTF_UP;
+ if (fibflg & FIBFLG_REJECT)
+ ret |= RTF_REJECT;
+ if (fibflg & FIBFLG_THROW)
+ ret |= RTF_THROW;
+ return ret;
+}
+
+/*
+ * Called from the PROCfs module. This outputs /proc/net/route.
+ *
+ * We preserve the old format but pad the buffers out. This means that
+ * we can spin over the other entries as we read them. Remember the
+ * gated BGP4 code could need to read 60,000+ routes on occasion (that's
+ * about 7Mb of data). To do that ok we will need to also cache the
+ * last route we got to (reads will generally be following on from
+ * one another without gaps).
+ */
+
+static int fib_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct fib_class *class;
+ struct fib_zone *fz;
+ struct fib_node *f;
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ int i;
+ int cl_id;
+
+ pos = 128;
+
+ if (offset<128)
+ {
+ sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
+ len = 128;
+ }
+
+ fib_lock();
+
+ for (cl_id=RT_CLASS_MAX-1; cl_id >= 0; cl_id--) {
+ class = fib_classes[cl_id];
+ if (!class)
+ continue;
+ for (fz=class->fib_zone_list; fz; fz = fz->fz_next)
+ {
+ int maxslot;
+ struct fib_node ** fp;
+
+ if (fz->fz_nent == 0)
+ continue;
+
+ if (pos + 128*fz->fz_nent <= offset) {
+ pos += 128*fz->fz_nent;
+ len = 0;
+ continue;
+ }
+
+ maxslot = fz->fz_divisor;
+ fp = fz->fz_hash;
+
+ for (i=0; i < maxslot; i++, fp++) {
+
+ for (f = *fp; f; f = f->fib_next)
+ {
+ struct fib_info * fi;
+ unsigned flags;
+
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
+
+ if (pos <= offset)
+ {
+ len=0;
+ continue;
+ }
+
+ fi = f->fib_info;
+ flags = fib_flag_trans(f->fib_flag);
+
+ if (fi)
+ flags |= fi->fib_flags;
+ sprintf(temp, "%s\t%08lX\t%08X\t%04X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
+ fi && fi->fib_dev ? fi->fib_dev->name : "*", htonl(f->fib_key<<fz->fz_logmask), fi ? fi->fib_gateway : 0,
+ flags, 0, 0, f->fib_metric,
+ htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, class->cl_id);
+ sprintf(buffer+len,"%-127s\n",temp);
+
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+ }
+ }
+
+done:
+ fib_unlock();
+
+ *start = buffer+len-(pos-offset);
+ len = pos - offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+
+static int fib_local_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct fib_zone *fz;
+ struct fib_node *f;
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ int i;
+
+ pos = 128;
+
+ if (offset<128)
+ {
+ sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
+ len = 128;
+ }
+
+ fib_lock();
+
+ for (fz=local_class.fib_zone_list; fz; fz = fz->fz_next)
+ {
+ int maxslot;
+ struct fib_node ** fp;
+
+ if (fz->fz_nent == 0)
+ continue;
+
+ if (pos + 128*fz->fz_nent <= offset)
+ {
+ pos += 128*fz->fz_nent;
+ len = 0;
+ continue;
+ }
+
+ maxslot = fz->fz_divisor;
+ fp = fz->fz_hash;
+
+ for (i=0; i < maxslot; i++, fp++)
+ {
+
+ for (f = *fp; f; f = f->fib_next)
+ {
+ unsigned flags;
+ struct fib_info * fi;
+
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
+
+ if (pos <= offset)
+ {
+ len=0;
+ continue;
+ }
+
+ fi = f->fib_info;
+ flags = fib_flag_trans(f->fib_flag);
+
+ if (fi)
+ flags |= fi->fib_flags;
+ sprintf(temp, "%s\t%08lX\t%08X\t%X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
+ fi && fi->fib_dev ? fi->fib_dev->name : "*",
+ htonl(f->fib_key<<fz->fz_logmask),
+ fi ? fi->fib_gateway : 0,
+ flags, 0, 0, f->fib_metric,
+ htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, RT_CLASS_LOCAL);
+ sprintf(buffer+len,"%-127s\n",temp);
+
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
+ }
+ }
+ }
+
+done:
+ fib_unlock();
+
+ *start = buffer+len-(pos-offset);
+ len = pos - offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+
+static int fib_rules_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ struct fib_rule *cl;
+
+ pos = 128;
+
+ if (offset<128) {
+ sprintf(buffer,"%-127s\n","Pref\tSource\t\tSrcMask\t\tDst\t\tDstMask\t\tIface\tTOS\tClass\tFlags\tSrcMap\n");
+ len = 128;
+ }
+
+
+ fib_lock();
+
+ for (cl = fib_rules; cl; cl = cl->cl_next) {
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
+
+ if (pos <= offset) {
+ len = 0;
+ continue;
+ }
+
+ sprintf(temp, "%d\t%08X\t%08X\t%08X\t%08X\t%s\t%02X\t%02x\t%02X\t%02X\t%08X",
+ cl->cl_preference,
+ cl->cl_src, cl->cl_srcmask,
+ cl->cl_dst, cl->cl_dstmask,
+ cl->cl_dev ? cl->cl_dev->name : "*",
+ cl->cl_tos, cl->cl_class ? cl->cl_class->cl_id : 0,
+ cl->cl_flags, cl->cl_action, cl->cl_srcmap
+ );
+ sprintf(buffer+len,"%-127s\n",temp);
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
+ }
+
+done:
+ fib_unlock();
+
+ *start = buffer+len-(pos-offset);
+ len = pos-offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+
+static int fib_class_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ int i;
+ struct fib_class *cl;
+
+ pos = 128;
+
+ if (offset<128)
+ {
+ sprintf(buffer,"%-127s\n","Class\tSize\n");
+ len = 128;
+ }
+
+
+ fib_lock();
+
+ for (i = RT_CLASS_MAX; i>=0; i--)
+ {
+ int sz = 0;
+ struct fib_zone *fz;
+
+ if ((cl=fib_classes[i])==NULL)
+ continue;
+
+ for (fz=cl->fib_zone_list; fz; fz=fz->fz_next)
+ sz += fz->fz_nent;
+
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
+
+ if (pos <= offset)
+ {
+ len = 0;
+ continue;
+ }
+
+ sprintf(temp, "%d\t%d\n", cl->cl_id, sz);
+ sprintf(buffer+len,"%-127s\n",temp);
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
+ }
+
+done:
+ fib_unlock();
+
+ *start = buffer+len-(pos-offset);
+ len = pos-offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+
+#endif
+
+static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r)
+{
+ unsigned long cmd=n->nlmsg_type;
+ struct device * dev = NULL;
+ struct fib_class *class;
+
+ if ((cmd != RTMSG_NEWROUTE && cmd != RTMSG_DELROUTE) ||
+ (r->rtmsg_flags & (RTF_MAGIC|RTF_XRESOLVE|RTF_REINSTATE)) ||
+ r->rtmsg_prefixlen > 32 ||
+ (r->rtmsg_tos & ~IPTOS_TOS_MASK)) {
+ rtmsg_ack(n, EINVAL);
+ return -EINVAL;
+ }
+
+ /* Reject/throw directives have no interface/gateway specification */
+
+ if (r->rtmsg_flags & (RTF_REJECT|RTF_THROW)) {
+ r->rtmsg_ifindex = 0;
+ r->rtmsg_gateway.s_addr = 0;
+ r->rtmsg_flags &= ~RTF_GATEWAY;
+ }
+
+ /* Silly metric hack, it is preserved for "compatibility",
+ * though I do not know any program using it.
+ */
+
+ r->rtmsg_metric--;
+ if (cmd == RTMSG_NEWROUTE && r->rtmsg_metric < 0)
+ r->rtmsg_metric = 0;
+
+ if (cmd == RTMSG_DELROUTE)
+ r->rtmsg_flags &= RTF_FIB;
+
+ if (r->rtmsg_ifindex) {
+ dev = dev_get_by_index(r->rtmsg_ifindex);
+ if (!dev) {
+ rtmsg_ack(n, ENODEV);
+ return -ENODEV;
+ }
+ }
+
+ if (r->rtmsg_gateway.s_addr && !(r->rtmsg_flags&RTF_NAT)) {
+ struct fib_info *fi;
+
+ fi = fib_lookup_info(r->rtmsg_gateway.s_addr, 0, 1,
+ &loopback_dev, dev);
+ if (fi) {
+ if (fi->fib_flags&(RTF_BROADCAST|RTF_MULTICAST) &&
+ cmd != RTMSG_DELROUTE)
+ return -EINVAL;
+ dev = fi->fib_dev;
+ if (fi->fib_flags&RTF_LOCAL) {
+ r->rtmsg_flags &= ~RTF_GATEWAY;
+ r->rtmsg_gateway.s_addr = 0;
+ }
+ } else if (cmd != RTMSG_DELROUTE)
+ return -ENETUNREACH;
+
+ /* If gateway is not found in routing table,
+ * we could assume that user knows that he does.
+ * It is link layer problem to decide reachable
+ * this gateway or not. Good example is tunnel interface.
+ * Another example is ethernet, ARP could (in theory)
+ * resolve addresses, even if we had no routes.
+ */
+ }
+
+ if (dev && (dev->flags&IFF_LOOPBACK)) {
+ if (r->rtmsg_flags&RTF_GATEWAY)
+ return -EINVAL;
+ /*
+ * Loopback routes: we declare them local addresses.
+ * It is the only reasonable solution to avoid
+ * loopback routing loops.
+ */
+ r->rtmsg_flags |= RTF_LOCAL|RTF_INTERFACE;
+ }
+
+ if (r->rtmsg_flags&RTF_GATEWAY) {
+ if (!dev && cmd != RTMSG_DELROUTE) {
+ rtmsg_ack(n, ENETUNREACH);
+ return -ENETUNREACH;
+ }
+ } else {
+ if (!dev && !(r->rtmsg_flags & (RTF_NAT|RTF_REJECT|RTF_THROW)) &&
+ cmd != RTMSG_DELROUTE) {
+ rtmsg_ack(n, ENODEV);
+ return -ENODEV;
+ }
+ }
+
+ if (dev && dev->family != AF_INET)
+ {
+ rtmsg_ack(n, ENODEV);
+ return -ENODEV;
+ }
+
+ if (r->rtmsg_class == 0) {
+ if (r->rtmsg_flags&(RTF_LOCAL|RTF_NAT))
+ r->rtmsg_class = RT_CLASS_LOCAL;
+ else if ((r->rtmsg_flags&RTF_GATEWAY) &&
+ (ipv4_config.fib_model==2 ||
+ (ipv4_config.fib_model==1 && !r->rtmsg_prefixlen)))
+ r->rtmsg_class = RT_CLASS_DEFAULT;
+ else
+ r->rtmsg_class = RT_CLASS_MAIN;
+ }
+
+ if ((class = fib_classes[r->rtmsg_class]) == NULL)
+ {
+ rtmsg_ack(n, EINVAL);
+ return -EINVAL;
+ }
+
+ return (cmd == RTMSG_NEWROUTE ? fib_create : fib_delete)(r, dev, class, n);
+}
+
+
+static int rtrulemsg_process(struct nlmsghdr *n, struct in_rtrulemsg *r)
+{
+ unsigned long cmd=n->nlmsg_type;
+ struct device * dev = NULL;
+
+ if ((cmd != RTMSG_NEWRULE && cmd != RTMSG_DELRULE) ||
+ r->rtrmsg_srclen > 32 || r->rtrmsg_dstlen > 32 ||
+ (r->rtrmsg_tos & ~IPTOS_TOS_MASK))
+ return -EINVAL;
+
+ if (r->rtrmsg_ifindex) {
+ dev = dev_get_by_index(r->rtrmsg_ifindex);
+ if (!dev)
+ return -ENODEV;
+ if (dev->family != AF_INET)
+ return -ENODEV;
+ }
+
+ if (cmd == RTMSG_DELRULE)
+ return fib_rule_delete(r, dev, n);
+
+ return fib_rule_add(r, dev, n);
+}
+
+
+static int ifmsg_process(struct nlmsghdr *n, struct in_ifmsg *r)
+{
+ unsigned long cmd=n->nlmsg_type;
+
+ if (cmd != RTMSG_NEWDEVICE && cmd != RTMSG_DELDEVICE) {
+ rtmsg_ack(n, EINVAL);
+ return -EINVAL;
+ }
+ rtmsg_ack(n, EINVAL);
+ return -EINVAL;
+}
+
+static int rtcmsg_process(struct nlmsghdr *n, struct in_rtctlmsg *r)
+{
+#ifdef CONFIG_RTNETLINK
+ if (r->rtcmsg_flags&RTCTL_DELAY)
+ rtmsg_ctl.nlmsg_delay = r->rtcmsg_delay;
+ if (r->rtcmsg_flags&RTCTL_OWNER)
+ rt_nl_owner = n->nlmsg_pid;
+ rt_nl_flags = r->rtcmsg_flags;
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+static int get_rt_from_user(struct in_rtmsg *rtm, void *arg)
+{
+ struct rtentry r;
+
+ if (copy_from_user(&r, arg, sizeof(struct rtentry)))
+ return -EFAULT;
+ if (r.rt_dev) {
+ struct device *dev;
+ char devname[16];
+
+ if (copy_from_user(devname, r.rt_dev, 15))
+ return -EFAULT;
+ devname[15] = 0;
+ dev = dev_get(devname);
+ if (!dev)
+ return -ENODEV;
+ rtm->rtmsg_ifindex = dev->ifindex;
+ }
+
+ rtm->rtmsg_flags = r.rt_flags;
+
+ if (r.rt_dst.sa_family != AF_INET)
+ return -EAFNOSUPPORT;
+ rtm->rtmsg_prefix = ((struct sockaddr_in*)&r.rt_dst)->sin_addr;
+
+ if (rtm->rtmsg_flags&RTF_HOST) {
+ rtm->rtmsg_flags &= ~RTF_HOST;
+ rtm->rtmsg_prefixlen = 32;
+ } else {
+ u32 mask = ((struct sockaddr_in*)&r.rt_genmask)->sin_addr.s_addr;
+ if (r.rt_genmask.sa_family != AF_INET) {
+ printk(KERN_DEBUG "%s forgot to specify route netmask.\n", current->comm);
+ if (r.rt_genmask.sa_family)
+ return -EAFNOSUPPORT;
+ }
+ if (bad_mask(mask, rtm->rtmsg_prefix.s_addr))
+ return -EINVAL;
+ rtm->rtmsg_prefixlen = 32 - fib_logmask(mask);
+ }
+ if ((rtm->rtmsg_flags & RTF_GATEWAY) &&
+ r.rt_gateway.sa_family != AF_INET)
+ return -EAFNOSUPPORT;
+ rtm->rtmsg_gateway = ((struct sockaddr_in*)&r.rt_gateway)->sin_addr;
+ rtm->rtmsg_rtt = r.rt_irtt;
+ rtm->rtmsg_window = r.rt_window;
+ rtm->rtmsg_mtu = r.rt_mtu;
+ rtm->rtmsg_class = r.rt_class;
+ rtm->rtmsg_metric = r.rt_metric;
+ rtm->rtmsg_tos = r.rt_tos;
+ return 0;
+}
+
+
+/*
+ * Handle IP routing ioctl calls. These are used to manipulate the routing tables
+ */
+
+int ip_rt_ioctl(unsigned int cmd, void *arg)
+{
+ int err;
+ union
+ {
+ struct in_rtmsg rtmsg;
+ struct in_ifmsg ifmsg;
+ struct in_rtrulemsg rtrmsg;
+ struct in_rtctlmsg rtcmsg;
+ } m;
+ struct nlmsghdr dummy_nlh;
+
+ memset(&m, 0, sizeof(m));
+ dummy_nlh.nlmsg_seq = 0;
+ dummy_nlh.nlmsg_pid = current->pid;
+
+ switch (cmd)
+ {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!suser())
+ return -EPERM;
+ err = get_rt_from_user(&m.rtmsg, arg);
+ if (err)
+ return err;
+ fib_lock();
+ dummy_nlh.nlmsg_type = cmd == SIOCDELRT ? RTMSG_DELROUTE
+ : RTMSG_NEWROUTE;
+ err = rtmsg_process(&dummy_nlh, &m.rtmsg);
+ fib_unlock();
+ return err;
+ case SIOCRTMSG:
+ if (!suser())
+ return -EPERM;
+ if (copy_from_user(&dummy_nlh, arg, sizeof(dummy_nlh)))
+ return -EFAULT;
+ switch (dummy_nlh.nlmsg_type)
+ {
+ case RTMSG_NEWROUTE:
+ case RTMSG_DELROUTE:
+ if (dummy_nlh.nlmsg_len < sizeof(m.rtmsg) + sizeof(dummy_nlh))
+ return -EINVAL;
+ if (copy_from_user(&m.rtmsg, arg+sizeof(dummy_nlh), sizeof(m.rtmsg)))
+ return -EFAULT;
+ fib_lock();
+ err = rtmsg_process(&dummy_nlh, &m.rtmsg);
+ fib_unlock();
+ return err;
+ case RTMSG_NEWRULE:
+ case RTMSG_DELRULE:
+ if (dummy_nlh.nlmsg_len < sizeof(m.rtrmsg) + sizeof(dummy_nlh))
+ return -EINVAL;
+ if (copy_from_user(&m.rtrmsg, arg+sizeof(dummy_nlh), sizeof(m.rtrmsg)))
+ return -EFAULT;
+ fib_lock();
+ err = rtrulemsg_process(&dummy_nlh, &m.rtrmsg);
+ fib_unlock();
+ return err;
+ case RTMSG_NEWDEVICE:
+ case RTMSG_DELDEVICE:
+ if (dummy_nlh.nlmsg_len < sizeof(m.ifmsg) + sizeof(dummy_nlh))
+ return -EINVAL;
+ if (copy_from_user(&m.ifmsg, arg+sizeof(dummy_nlh), sizeof(m.ifmsg)))
+ return -EFAULT;
+ fib_lock();
+ err = ifmsg_process(&dummy_nlh, &m.ifmsg);
+ fib_unlock();
+ return err;
+ case RTMSG_CONTROL:
+ if (dummy_nlh.nlmsg_len < sizeof(m.rtcmsg) + sizeof(dummy_nlh))
+ return -EINVAL;
+ if (copy_from_user(&m.rtcmsg, arg+sizeof(dummy_nlh), sizeof(m.rtcmsg)))
+ return -EFAULT;
+ fib_lock();
+ err = rtcmsg_process(&dummy_nlh, &m.rtcmsg);
+ fib_unlock();
+ return err;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+/*
+ * Netlink hooks for IP
+ */
+
+
+static void
+rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
+ struct fib_class *class, struct nlmsghdr *n)
+{
+ struct in_rtmsg *r;
+ struct fib_info *fi;
+
+ if (n && !(rt_nl_flags&RTCTL_ECHO) && rt_nl_owner == n->nlmsg_pid)
+ return;
+
+ start_bh_atomic();
+ r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
+ n ? n->nlmsg_pid : 0);
+ if (r) {
+ r->rtmsg_prefix.s_addr = htonl(f->fib_key<<logmask);
+ r->rtmsg_prefixlen = 32 - logmask;
+ r->rtmsg_metric= f->fib_metric;
+ r->rtmsg_tos = f->fib_tos;
+ r->rtmsg_class=class->cl_id;
+ r->rtmsg_flags = fib_flag_trans(f->fib_flag);
+
+ if ((fi = f->fib_info) != NULL) {
+ r->rtmsg_gateway.s_addr = fi->fib_gateway;
+ r->rtmsg_flags |= fi->fib_flags;
+ r->rtmsg_mtu = fi->fib_mtu;
+ r->rtmsg_window = fi->fib_window;
+ r->rtmsg_rtt = fi->fib_irtt;
+ r->rtmsg_ifindex = fi->fib_dev ? fi->fib_dev->ifindex : 0;
+ }
+ }
+ end_bh_atomic();
+}
+
+static void
+__rtmsg_ack(struct nlmsghdr *n, int err)
+{
+ nlmsg_ack(&rtmsg_ctl, n->nlmsg_seq, n->nlmsg_pid, err);
+}
+
+
+static void
+rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n)
+{
+ struct in_ifmsg *r;
+
+ start_bh_atomic();
+ r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
+ n ? n->nlmsg_pid : 0);
+ if (r)
+ {
+ memset(r, 0, sizeof(*r));
+ r->ifmsg_lladdr.sa_family = dev->type;
+ memcpy(&r->ifmsg_lladdr.sa_data, dev->dev_addr, dev->addr_len);
+ r->ifmsg_prefix.s_addr = dev->pa_addr;
+ if (dev->flags & IFF_POINTOPOINT || dev->type == ARPHRD_TUNNEL)
+ r->ifmsg_brd.s_addr = dev->pa_dstaddr;
+ else
+ r->ifmsg_brd.s_addr = dev->pa_brdaddr;
+ r->ifmsg_flags = dev->flags;
+ r->ifmsg_mtu = dev->mtu;
+ r->ifmsg_metric = dev->metric;
+ r->ifmsg_prefixlen = 32 - fib_logmask(dev->pa_mask);
+ r->ifmsg_index = dev->ifindex;
+ strcpy(r->ifmsg_name, dev->name);
+ }
+ end_bh_atomic();
+}
+
+static int fib_netlink_call(int minor, struct sk_buff *skb)
+{
+ struct nlmsghdr *nlh;
+ int totlen = 0;
+ int err = 0;
+
+ fib_lock();
+ while (skb->len >= sizeof(*nlh)) {
+ int rlen;
+ nlh = (struct nlmsghdr *)skb->data;
+ rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+ if (skb->len < rlen)
+ break;
+ totlen += rlen;
+ err = 0;
+ skb_pull(skb, rlen);
+ switch (nlh->nlmsg_type) {
+ case RTMSG_NEWROUTE:
+ case RTMSG_DELROUTE:
+ if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtmsg)) {
+ rtmsg_ack(nlh, EINVAL);
+ err = -EINVAL;
+ break;
+ }
+ err = rtmsg_process(nlh, (struct in_rtmsg*)nlh->nlmsg_data);
+ break;
+ case RTMSG_NEWRULE:
+ case RTMSG_DELRULE:
+ if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtrulemsg)) {
+ rtmsg_ack(nlh, EINVAL);
+ err = -EINVAL;
+ break;
+ }
+ err = rtrulemsg_process(nlh, (struct in_rtrulemsg*)nlh->nlmsg_data);
+ break;
+ case RTMSG_NEWDEVICE:
+ case RTMSG_DELDEVICE:
+ if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_ifmsg)) {
+ rtmsg_ack(nlh, EINVAL);
+ err = -EINVAL;
+ break;
+ }
+ err = ifmsg_process(nlh, (struct in_ifmsg*)nlh->nlmsg_data);
+ break;
+ case RTMSG_CONTROL:
+ if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtctlmsg)) {
+ rtmsg_ack(nlh, EINVAL);
+ err = -EINVAL;
+ break;
+ }
+ err = rtcmsg_process(nlh, (struct in_rtctlmsg*)nlh->nlmsg_data);
+ break;
+ default:
+ break;
+ }
+ }
+ kfree_skb(skb, FREE_READ);
+ fib_unlock();
+ if (!err || rt_nl_flags&RTCTL_ACK)
+ return totlen;
+ return err;
+}
+
+#endif
+
+
+static int fib_magic(int op, unsigned flags, u32 dst, u32 mask, struct device *dev)
+{
+ struct nlmsghdr n;
+ struct in_rtmsg r;
+ memset(&r, 0, sizeof(r));
+ n.nlmsg_seq=0;
+ n.nlmsg_pid=0;
+ r.rtmsg_metric = MAGIC_METRIC;
+ r.rtmsg_prefix.s_addr = dst;
+ if (dev->flags&IFF_LOOPBACK)
+ flags |= RTF_LOCAL;
+ r.rtmsg_flags = flags;
+ r.rtmsg_prefixlen = 32 - fib_logmask(mask);
+
+ return (op == RTMSG_NEWROUTE ? fib_create : fib_delete)
+ (&r, dev, (flags&RTF_LOCAL) ? &local_class : &main_class, &n);
+}
+
+static void ip_rt_del_broadcasts(struct device *dev)
+{
+ u32 net = dev->pa_addr&dev->pa_mask;
+
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
+}
+
+static void ip_rt_add_broadcasts(struct device *dev, u32 brd, u32 mask)
+{
+ u32 net = dev->pa_addr&mask;
+
+ if (dev->flags&IFF_BROADCAST)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, brd, ~0, dev);
+
+ if (net && !(mask&htonl(1))) {
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~mask, ~0, dev);
+ }
+}
+
+void ip_rt_change_broadcast(struct device *dev, u32 new_brd)
+{
+ fib_lock();
+ printk(KERN_DEBUG "%s changes brd %08lX -> %08X\n",
+ dev->name, dev->pa_brdaddr, new_brd);
+ if (!ZERONET(dev->pa_addr) && dev->flags&IFF_BROADCAST) {
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+ rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+ rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+ ip_rt_add_broadcasts(dev, new_brd, dev->pa_mask);
+ }
+ fib_unlock();
+}
+
+void ip_rt_change_dstaddr(struct device *dev, u32 dstaddr)
+{
+ fib_lock();
+ if (!ZERONET(dev->pa_addr) && (dev->flags&IFF_POINTOPOINT) && dev->type != ARPHRD_TUNNEL) {
+ printk(KERN_DEBUG "%s changes dst %08lX -> %08X\n",
+ dev->name, dev->pa_dstaddr, dstaddr);
+ fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+ rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+ rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+ if (dstaddr)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dstaddr, ~0, dev);
+ }
+ fib_unlock();
+}
+
+void ip_rt_change_netmask(struct device *dev, u32 mask)
+{
+ u32 net;
+
+ fib_lock();
+ printk(KERN_DEBUG "%s changes netmask %08lX -> %08X\n",
+ dev->name, dev->pa_mask, mask);
+ if (ZERONET(dev->pa_addr)) {
+ fib_unlock();
+ return;
+ }
+ net = dev->pa_addr&dev->pa_mask;
+ fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+ ip_rt_del_broadcasts(dev);
+ if (mask != 0xFFFFFFFF && dev->flags&IFF_POINTOPOINT)
+ fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+ rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+
+ if (mask != 0xFFFFFFFF)
+ dev->flags &= ~IFF_POINTOPOINT;
+
+ rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+ net = dev->pa_addr&mask;
+ if (net)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, mask, dev);
+ ip_rt_add_broadcasts(dev, dev->pa_addr, mask);
+ fib_unlock();
+}
+
+int ip_rt_event(int event, struct device *dev)
+{
+ fib_lock();
+ if (event == NETDEV_DOWN) {
+ fib_flush(dev);
+ rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+ fib_unlock();
+ return NOTIFY_DONE;
+ }
+ if (event == NETDEV_CHANGE) {
+ printk(KERN_DEBUG "%s(%s) changes state fl=%08x pa=%08lX/%08lX brd=%08lX dst=%08lX\n",
+ dev->name, current->comm, dev->flags, dev->pa_addr, dev->pa_mask,
+ dev->pa_brdaddr, dev->pa_dstaddr);
+ if (!(dev->flags&IFF_BROADCAST))
+ fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+ if (!(dev->flags&IFF_POINTOPOINT))
+ fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+ else {
+ u32 net = dev->pa_addr&dev->pa_mask;
+ fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+ ip_rt_del_broadcasts(dev);
+ }
+ rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+ }
+
+ if ((event == NETDEV_UP || event == NETDEV_CHANGE) && !ZERONET(dev->pa_addr)) {
+ if (dev->flags&IFF_POINTOPOINT) {
+ dev->pa_mask = 0xFFFFFFFF;
+ dev->ip_flags &= ~IFF_IP_MASK_OK;
+ dev->flags &= ~IFF_BROADCAST;
+ dev->pa_brdaddr = 0;
+ }
+
+ if (event == NETDEV_UP)
+ printk(KERN_DEBUG "%s UP fl=%08x pa=%08lX/%08lX brd=%08lX dst=%08lX\n",
+ dev->name, dev->flags, dev->pa_addr,
+ dev->pa_mask, dev->pa_brdaddr, dev->pa_dstaddr);
+
+ rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+
+ if (dev->flags&IFF_POINTOPOINT) {
+ if (dev->pa_dstaddr && dev->type != ARPHRD_TUNNEL)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+ } else {
+ u32 net = dev->pa_addr&dev->pa_mask;
+
+ if (net)
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+ ip_rt_add_broadcasts(dev, dev->pa_brdaddr, dev->pa_mask);
+ }
+ fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL, dev->pa_addr, ~0, dev);
+ if (dev == &loopback_dev) {
+ if (dev->pa_addr != htonl(INADDR_LOOPBACK)) {
+ u32 mask = htonl(0xFF000000);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX,
+ htonl(INADDR_LOOPBACK)&mask,
+ mask, dev);
+ fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL,
+ htonl(INADDR_LOOPBACK),
+ mask, dev);
+ }
+ }
+ }
+ if (event == NETDEV_CHANGEMTU || event == NETDEV_CHANGEADDR)
+ rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+ fib_unlock();
+ return NOTIFY_DONE;
+}
+
+
+void ip_fib_init()
+{
+ struct in_rtrulemsg r;
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_ROUTE, 5, "route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ fib_get_info
+ });
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_RTCLASSES, 10, "rt_classes",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ fib_class_get_info
+ });
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_RTRULES, 8, "rt_local",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ fib_local_get_info
+ });
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_RTRULES, 8, "rt_rules",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ fib_rules_get_info
+ });
+#endif /* CONFIG_PROC_FS */
+
+ fib_classes[RT_CLASS_LOCAL] = &local_class;
+ fib_classes[RT_CLASS_MAIN] = &main_class;
+ fib_classes[RT_CLASS_DEFAULT] = &default_class;
+
+ memset(&r, 0, sizeof(r));
+ r.rtrmsg_class = RT_CLASS_LOCAL;
+ r.rtrmsg_preference = 0;
+ fib_rule_add(&r, NULL, NULL);
+
+ memset(&r, 0, sizeof(r));
+ r.rtrmsg_class = RT_CLASS_DEFAULT;
+ r.rtrmsg_preference = 255;
+ fib_rule_add(&r, NULL, NULL);
+
+ memset(&r, 0, sizeof(r));
+ r.rtrmsg_class = RT_CLASS_MAIN;
+ r.rtrmsg_preference = 254;
+ fib_rule_add(&r, NULL, NULL);
+
+#ifdef CONFIG_RTNETLINK
+ netlink_attach(NETLINK_ROUTE, fib_netlink_call);
+#endif
+}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 787f69d7f..6b697d001 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -250,6 +250,7 @@
#include <net/icmp.h>
#include <net/tcp.h>
#include <net/udp.h>
+#include <net/raw.h>
#include <net/snmp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
@@ -275,18 +276,18 @@ struct icmp_err icmp_err_convert[] = {
{ EHOSTUNREACH, 0 }, /* ICMP_HOST_UNREACH */
{ ENOPROTOOPT, 1 }, /* ICMP_PROT_UNREACH */
{ ECONNREFUSED, 1 }, /* ICMP_PORT_UNREACH */
- { EOPNOTSUPP, 0 }, /* ICMP_FRAG_NEEDED */
+ { EMSGSIZE, 0 }, /* ICMP_FRAG_NEEDED */
{ EOPNOTSUPP, 0 }, /* ICMP_SR_FAILED */
{ ENETUNREACH, 1 }, /* ICMP_NET_UNKNOWN */
{ EHOSTDOWN, 1 }, /* ICMP_HOST_UNKNOWN */
{ ENONET, 1 }, /* ICMP_HOST_ISOLATED */
{ ENETUNREACH, 1 }, /* ICMP_NET_ANO */
{ EHOSTUNREACH, 1 }, /* ICMP_HOST_ANO */
- { EOPNOTSUPP, 0 }, /* ICMP_NET_UNR_TOS */
- { EOPNOTSUPP, 0 }, /* ICMP_HOST_UNR_TOS */
- { EOPNOTSUPP, 1 }, /* ICMP_PKT_FILTERED */
- { EOPNOTSUPP, 1 }, /* ICMP_PREC_VIOLATION */
- { EOPNOTSUPP, 1 } /* ICMP_PREC_CUTOFF */
+ { ENETUNREACH, 0 }, /* ICMP_NET_UNR_TOS */
+ { EHOSTUNREACH, 0 }, /* ICMP_HOST_UNR_TOS */
+ { EHOSTUNREACH, 1 }, /* ICMP_PKT_FILTERED */
+ { EHOSTUNREACH, 1 }, /* ICMP_PREC_VIOLATION */
+ { EHOSTUNREACH, 1 } /* ICMP_PREC_CUTOFF */
};
/*
@@ -326,17 +327,17 @@ struct icmp_xrlim
/*
* ICMP control array. This specifies what to do with each ICMP.
*/
-
+
struct icmp_control
{
unsigned long *output; /* Address to increment on output */
unsigned long *input; /* Address to increment on input */
- void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len);
+ void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len);
unsigned long error; /* This ICMP is classed as an error message */
struct icmp_xrlim *xrlim; /* Transmit rate limit control structure or NULL for no limits */
};
-static struct icmp_control icmp_pointers[19];
+static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1];
/*
* Build xmit assembly blocks
@@ -348,7 +349,7 @@ struct icmp_bxm
int data_len;
struct icmphdr icmph;
unsigned long csum;
- struct options replyopts;
+ struct ip_options replyopts;
unsigned char optbuf[40];
};
@@ -358,7 +359,8 @@ struct icmp_bxm
* all layers. All Socketless IP sends will soon be gone.
*/
-struct socket icmp_socket;
+struct inode icmp_inode;
+struct socket *icmp_socket=&icmp_inode.u.socket_i;
/*
* Send an ICMP frame.
@@ -376,7 +378,7 @@ static void xrlim_init(void)
int type, entry;
struct icmp_xrlim *xr;
- for (type=0; type<=18; type++) {
+ for (type=0; type<=NR_ICMP_TYPES; type++) {
xr = icmp_pointers[type].xrlim;
if (xr) {
for (entry=0; entry<XRLIM_CACHE_SIZE; entry++)
@@ -399,7 +401,7 @@ static int xrlim_allow(int type, __u32 addr)
struct icmp_xrl_cache *c;
unsigned long now;
- if (type > 18) /* No time limit present */
+ if (type > NR_ICMP_TYPES) /* No time limit present */
return 1;
r = icmp_pointers[type].xrlim;
if (!r)
@@ -464,7 +466,7 @@ static int xrlim_allow(int type, __u32 addr)
static void icmp_out_count(int type)
{
- if(type>18)
+ if (type>NR_ICMP_TYPES)
return;
(*icmp_pointers[type].output)++;
icmp_statistics.IcmpOutMsgs++;
@@ -474,14 +476,13 @@ static void icmp_out_count(int type)
* Checksum each fragment, and on the first include the headers and final checksum.
*/
-static int icmp_glue_bits(const void *p, __u32 saddr, char *to, unsigned int offset, unsigned int fraglen)
+static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
struct icmphdr *icmph;
unsigned long csum;
- if (offset)
- {
+ if (offset) {
icmp_param->csum=csum_partial_copy(icmp_param->data_ptr+offset-sizeof(struct icmphdr),
to, fraglen,icmp_param->csum);
return 0;
@@ -500,24 +501,38 @@ static int icmp_glue_bits(const void *p, __u32 saddr, char *to, unsigned int off
fraglen-sizeof(struct icmphdr), csum);
icmph=(struct icmphdr *)to;
icmph->checksum = csum_fold(csum);
-
- return 0;
+ return 0;
}
/*
* Driving logic for building and sending ICMP messages.
*/
-static void icmp_build_xmit(struct icmp_bxm *icmp_param, __u32 saddr, __u32 daddr, __u8 tos)
+static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
{
- struct sock *sk=icmp_socket.data;
+ struct sock *sk=icmp_socket->sk;
+ struct ipcm_cookie ipc;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ u32 daddr;
+
+ if (ip_options_echo(&icmp_param->replyopts, skb))
+ return;
+
icmp_param->icmph.checksum=0;
icmp_param->csum=0;
icmp_out_count(icmp_param->icmph.type);
- sk->ip_tos = tos;
+
+ sk->ip_tos = skb->nh.iph->tos;
+ daddr = ipc.addr = rt->rt_src;
+ ipc.opt = &icmp_param->replyopts;
+ if (ipc.opt->srr)
+ daddr = icmp_param->replyopts.faddr;
+ if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), NULL))
+ return;
ip_build_xmit(sk, icmp_glue_bits, icmp_param,
icmp_param->data_len+sizeof(struct icmphdr),
- daddr, saddr, &icmp_param->replyopts, 0, IPPROTO_ICMP, 1);
+ &ipc, rt, MSG_DONTWAIT);
+ ip_rt_put(rt);
}
@@ -531,61 +546,63 @@ static void icmp_build_xmit(struct icmp_bxm *icmp_param, __u32 saddr, __u32 dadd
* MUST reply to only the first fragment.
*/
-void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
+void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info)
{
struct iphdr *iph;
struct icmphdr *icmph;
- int atype, room;
+ int room;
struct icmp_bxm icmp_param;
- __u32 saddr;
+ struct rtable *rt = (struct rtable*)skb_in->dst;
+ struct ipcm_cookie ipc;
+ u32 saddr;
+ u8 tos;
/*
* Find the original header
*/
- iph = skb_in->ip_hdr;
+ iph = skb_in->nh.iph;
/*
* No replies to physical multicast/broadcast
*/
- if(skb_in->pkt_type!=PACKET_HOST)
+ if (skb_in->pkt_type!=PACKET_HOST)
return;
/*
* Now check at the protocol level
*/
-
- atype=ip_chk_addr(iph->daddr);
- if(atype==IS_BROADCAST||atype==IS_MULTICAST)
+ if (!rt)
return;
+ if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST))
+ return;
+
/*
* Only reply to fragment 0. We byte re-order the constant
* mask for efficiency.
*/
- if(iph->frag_off&htons(IP_OFFSET))
+ if (iph->frag_off&htons(IP_OFFSET))
return;
/*
* If we send an ICMP error to an ICMP error a mess would result..
*/
- if(icmp_pointers[type].error)
- {
+ if (icmp_pointers[type].error) {
/*
* We are an error, check if we are replying to an ICMP error
*/
- if(iph->protocol==IPPROTO_ICMP)
- {
+ if (iph->protocol==IPPROTO_ICMP) {
icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
/*
* Assume any unknown ICMP type is an error. This isn't
* specified by the RFC, but think about it..
*/
- if(icmph->type>18 || icmp_pointers[icmph->type].error)
+ if (icmph->type>NR_ICMP_TYPES || icmp_pointers[icmph->type].error)
return;
}
}
@@ -597,17 +614,27 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, s
#ifndef CONFIG_NO_ICMP_LIMIT
if (!xrlim_allow(type, iph->saddr))
return;
-#endif
+#endif
/*
* Construct source address and options.
*/
-
- saddr=iph->daddr;
- if(saddr!=dev->pa_addr && ip_chk_addr(saddr)!=IS_MYADDR)
- saddr=dev->pa_addr;
- if(ip_options_echo(&icmp_param.replyopts, NULL, saddr, iph->saddr, skb_in))
+
+ saddr = iph->daddr;
+ if (!(rt->rt_flags&RTF_LOCAL))
+ saddr = 0;
+
+ tos = icmp_pointers[type].error ?
+ ((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :
+ iph->tos;
+
+ if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), NULL))
+ return;
+
+ if (ip_options_echo(&icmp_param.replyopts, skb_in)) {
+ ip_rt_put(rt);
return;
+ }
/*
* Prepare data for ICMP header.
@@ -616,136 +643,97 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, s
icmp_param.icmph.type=type;
icmp_param.icmph.code=code;
icmp_param.icmph.un.gateway = info;
+ icmp_param.icmph.checksum=0;
+ icmp_param.csum=0;
icmp_param.data_ptr=iph;
- room = 576 - sizeof(struct iphdr) - icmp_param.replyopts.optlen;
- icmp_param.data_len=(iph->ihl<<2)+skb_in->len; /* RFC says return as much as we can without exceeding 576 bytes */
+ icmp_out_count(icmp_param.icmph.type);
+ icmp_socket->sk->ip_tos = tos;
+ ipc.addr = iph->saddr;
+ ipc.opt = &icmp_param.replyopts;
+ if (icmp_param.replyopts.srr) {
+ ip_rt_put(rt);
+ if (ip_route_output(&rt, icmp_param.replyopts.faddr, saddr, RT_TOS(tos), NULL))
+ return;
+ }
+
+ /* RFC says return as much as we can without exceeding 576 bytes. */
+
+ room = rt->u.dst.pmtu;
+ if (room > 576)
+ room = 576;
+ room -= sizeof(struct iphdr) - icmp_param.replyopts.optlen;
+
+ icmp_param.data_len=(iph->ihl<<2)+skb_in->len;
if (icmp_param.data_len > room)
icmp_param.data_len = room;
- /*
- * Build and send the packet.
- */
+ ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param,
+ icmp_param.data_len+sizeof(struct icmphdr),
+ &ipc, rt, MSG_DONTWAIT);
- icmp_build_xmit(&icmp_param, saddr, iph->saddr,
- icmp_pointers[type].error ?
- (iph->tos & 0x1E) | 0xC0 : iph->tos);
+ ip_rt_put(rt);
}
/*
* Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH.
*/
-
-static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
struct iphdr *iph;
int hash;
struct inet_protocol *ipprot;
- unsigned char *dp;
- __u32 info = 0;
+ unsigned char *dp;
+ struct sock *raw_sk;
- if(len<sizeof(struct iphdr))
- goto flush_it;
-
- iph = (struct iphdr *) (icmph + 1);
-
- len-=iph->ihl<<2;
- if(len<0)
- goto flush_it;
+ /*
+ * Incomplete header ?
+ */
+
+ if(skb->len<sizeof(struct iphdr)+8)
+ {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
- dp= ((unsigned char *)iph)+(iph->ihl<<2);
+ iph = (struct iphdr *) (icmph + 1);
+ dp = (unsigned char*)iph;
- if(icmph->type==ICMP_DEST_UNREACH)
- {
- switch(icmph->code & 15)
- {
+ if(icmph->type==ICMP_DEST_UNREACH) {
+ switch(icmph->code & 15) {
case ICMP_NET_UNREACH:
break;
case ICMP_HOST_UNREACH:
break;
case ICMP_PROT_UNREACH:
-/* printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
- in_ntoa(iph->daddr), (int)iph->protocol);*/
break;
case ICMP_PORT_UNREACH:
break;
case ICMP_FRAG_NEEDED:
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
- printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
- in_ntoa(iph->daddr));
- break;
-#else
- {
- unsigned short old_mtu = ntohs(iph->tot_len);
- unsigned short new_mtu = ntohs(icmph->un.echo.sequence);
-
- /*
- * RFC1191 5. 4.2BSD based router can return incorrect
- * Total Length. If current mtu is unknown or old_mtu
- * is not less than current mtu, reduce old_mtu by 4 times
- * the header length.
- */
-
- if (skb->sk == NULL /* can this happen? */
- || skb->sk->ip_route_cache == NULL
- || skb->sk->ip_route_cache->rt_mtu <= old_mtu)
- {
- NETDEBUG(printk(KERN_INFO "4.2BSD based fragmenting router between here and %s, mtu corrected from %d", in_ntoa(iph->daddr), old_mtu));
- old_mtu -= 4 * iph->ihl;
- NETDEBUG(printk(" to %d\n", old_mtu));
- }
-
- if (new_mtu < 68 || new_mtu >= old_mtu)
- {
- /*
- * It is either dumb router, which does not
- * understand Path MTU Disc. protocol
- * or broken (f.e. Linux<=1.3.37 8) router.
- * Try to guess...
- * The table is taken from RFC-1191.
- */
- if (old_mtu > 32000)
- new_mtu = 32000;
- else if (old_mtu > 17914)
- new_mtu = 17914;
- else if (old_mtu > 8166)
- new_mtu = 8166;
- else if (old_mtu > 4352)
- new_mtu = 4352;
- else if (old_mtu > 2002)
- new_mtu = 2002;
- else if (old_mtu > 1492)
- new_mtu = 1492;
- else if (old_mtu > 576)
- new_mtu = 576;
- else if (old_mtu > 296)
- new_mtu = 296;
- /*
- * These two are not from the RFC but
- * are needed for AMPRnet AX.25 paths.
- */
- else if (old_mtu > 216)
- new_mtu = 216;
- else if (old_mtu > 128)
- new_mtu = 128;
- else
- /*
- * Despair..
- */
- new_mtu = 68;
+ if (ipv4_config.no_pmtu_disc)
+ printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
+ in_ntoa(iph->daddr));
+ else {
+ unsigned short new_mtu;
+ new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
+ if (!new_mtu) {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+ icmph->un.frag.mtu = htons(new_mtu);
}
- info = new_mtu;
break;
- }
-#endif
case ICMP_SR_FAILED:
printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
break;
default:
break;
}
- if(icmph->code>NR_ICMP_UNREACH) /* Invalid type */
+ if (icmph->code>NR_ICMP_UNREACH) {
+ kfree_skb(skb, FREE_READ);
return;
+ }
}
/*
@@ -755,22 +743,44 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
* RFC 1122: 3.2.2.1 MUST pass ICMP unreach messages to the transport layer.
* RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
*/
-
+
/*
- * Get the protocol(s).
+ * Check the other end isnt violating RFC 1122. Some routers send
+ * bogus responses to broadcast frames. If you see this message
+ * first check your netmask matches at both ends, if it does then
+ * get the other vendor to fix their kit.
*/
- hash = iph->protocol & (MAX_INET_PROTOS -1);
+ if(__ip_chk_addr(iph->daddr)==IS_BROADCAST)
+ {
+ printk("%s sent an invalid ICMP error to a broadcast.\n",
+ in_ntoa(skb->nh.iph->saddr));
+ kfree_skb(skb, FREE_READ);
+ }
+
+ /*
+ * Deliver ICMP message to raw sockets. Pretty useless feature?
+ */
+
+ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+ hash = iph->protocol & (MAX_INET_PROTOS - 1);
+ if ((raw_sk = raw_v4_htable[hash]) != NULL)
+ {
+ raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+ while (raw_sk)
+ {
+ raw_err(raw_sk, skb);
+ raw_sk = raw_v4_lookup(raw_sk->next, iph->protocol,
+ iph->saddr, iph->daddr);
+ }
+ }
/*
* This can't change while we are doing it.
- *
- * FIXME: Deliver to appropriate raw sockets too.
*/
-
+
ipprot = (struct inet_protocol *) inet_protos[hash];
- while(ipprot != NULL)
- {
+ while(ipprot != NULL) {
struct inet_protocol *nextip;
nextip = (struct inet_protocol *) ipprot->next;
@@ -782,15 +792,12 @@ static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct devi
/* RFC1122: OK. Passes appropriate ICMP errors to the */
/* appropriate protocol layer (MUST), as per 3.2.2. */
- if (iph->protocol == ipprot->protocol && ipprot->err_handler)
- {
- ipprot->err_handler(icmph->type, icmph->code, dp, info,
- iph->daddr, iph->saddr, ipprot, len);
- }
+ if (iph->protocol == ipprot->protocol && ipprot->err_handler)
+ ipprot->err_handler(skb, dp);
ipprot = nextip;
}
-flush_it:
+
kfree_skb(skb, FREE_READ);
}
@@ -799,7 +806,7 @@ flush_it:
* Handle ICMP_REDIRECT.
*/
-static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 source, __u32 daddr, int len)
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
struct iphdr *iph;
unsigned long ip;
@@ -807,65 +814,30 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct dev
/*
* Get the copied header of the packet that caused the redirect
*/
-
- if(len<=sizeof(struct iphdr))
- goto flush_it;
iph = (struct iphdr *) (icmph + 1);
ip = iph->daddr;
- /*
- * If we are a router and we run a routing protocol, we MUST NOT follow redirects.
- * When using no routing protocol, we MAY follow redirects. (RFC 1812, 5.2.7.2)
- */
-#if defined(CONFIG_IP_FORWARD) && !defined(CONFIG_IP_DUMB_ROUTER)
- NETDEBUG(printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %lX, "
- "orig gw = %lX, \"new\" gw = %lX, device = %s.\n", ntohl(ip),
- ntohl(source), ntohl(icmph->un.gateway), dev->name));
-#else
- switch(icmph->code & 7)
- {
+ switch(icmph->code & 7) {
case ICMP_REDIR_NET:
- /*
- * This causes a problem with subnetted networks. What we should do
- * is use ICMP_ADDRESS to get the subnet mask of the problem route
- * and set both. But we don't.. [RFC1812 says routers MUST NOT
- * generate Network Redirects]
- */
-#ifdef not_a_good_idea
- ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
- ip, 0, icmph->un.gateway, dev,0, 0, 0);
-#endif
+ case ICMP_REDIR_NETTOS:
/*
* As per RFC recommendations now handle it as
* a host redirect.
*/
case ICMP_REDIR_HOST:
- /*
- * Add better route to host.
- * But first check that the redirect
- * comes from the old gateway..
- * And make sure it's an ok host address
- * (not some confused thing sending our
- * address)
- */
- printk(KERN_INFO "ICMP redirect from %s\n", in_ntoa(source));
- ip_rt_redirect(source, ip, icmph->un.gateway, dev);
- break;
- case ICMP_REDIR_NETTOS:
case ICMP_REDIR_HOSTTOS:
- printk(KERN_INFO "ICMP: cannot handle TOS redirects yet!\n");
+ ip_rt_redirect(skb->nh.iph->saddr, ip, icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
break;
default:
break;
}
-#endif
/*
* Discard the original packet
*/
-flush_it:
+
kfree_skb(skb, FREE_READ);
}
@@ -878,16 +850,16 @@ flush_it:
* See also WRT handling of options once they are done and working.
*/
-static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
#ifndef CONFIG_IP_IGNORE_ECHO_REQUESTS
struct icmp_bxm icmp_param;
+
icmp_param.icmph=*icmph;
icmp_param.icmph.type=ICMP_ECHOREPLY;
icmp_param.data_ptr=(icmph+1);
icmp_param.data_len=len;
- if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
- icmp_build_xmit(&icmp_param, daddr, saddr, skb->ip_hdr->tos);
+ icmp_reply(&icmp_param, skb);
#endif
kfree_skb(skb, FREE_READ);
}
@@ -900,8 +872,9 @@ static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device
* MUST be updated at least at 15Hz.
*/
-static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
+ struct timeval tv;
__u32 times[3]; /* So the new timestamp works on ALPHA's.. */
struct icmp_bxm icmp_param;
@@ -909,8 +882,7 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
* Too short.
*/
- if(len<12)
- {
+ if(len<12) {
icmp_statistics.IcmpInErrors++;
kfree_skb(skb, FREE_READ);
return;
@@ -920,11 +892,8 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
* Fill in the current time as ms since midnight UT:
*/
- {
- struct timeval tv;
- do_gettimeofday(&tv);
- times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
- }
+ do_gettimeofday(&tv);
+ times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
times[2] = times[1];
memcpy((void *)&times[0], icmph+1, 4); /* Incoming stamp */
icmp_param.icmph=*icmph;
@@ -932,8 +901,7 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
icmp_param.icmph.code=0;
icmp_param.data_ptr=&times;
icmp_param.data_len=12;
- if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
- icmp_build_xmit(&icmp_param, daddr, saddr, skb->ip_hdr->tos);
+ icmp_reply(&icmp_param, skb);
kfree_skb(skb,FREE_READ);
}
@@ -946,27 +914,79 @@ static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct de
* agent. Receiving a request doesn't constitute implicit permission to
* act as one. Of course, implementing this correctly requires (SHOULD)
* a way to turn the functionality on and off. Another one for sysctl(),
- * I guess. -- MS
- * Botched with a CONFIG option for now - Linus add scts sysctl please..
+ * I guess. -- MS
+ *
+ * RFC1812 (4.3.3.9). A router MUST implement it.
+ * A router SHOULD have switch turning it on/off.
+ * This switch MUST be ON by default.
+ *
+ * Gratuitous replies, zero-source replies are not implemented,
+ * that complies with RFC. DO NOT implement them!!! All the idea
+ * of broadcast addrmask replies as specified in RFC950 is broken.
+ * The problem is that it is not uncommon to have several prefixes
+ * on one physical interface. Moreover, addrmask agent can even be
+ * not aware of existing another prefixes.
+ * If source is zero, addrmask agent cannot choose correct prefix.
+ * Gratuitous mask announcements suffer from the same problem.
+ * RFC1812 explains it, but still allows to use ADDRMASK,
+ * that is pretty silly. --ANK
*/
-static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
-#ifdef CONFIG_IP_ADDR_AGENT /* Don't use, broken */
struct icmp_bxm icmp_param;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = skb->dev;
+
+ if (!ipv4_config.addrmask_agent ||
+ ZERONET(rt->rt_src) ||
+ rt->rt_src_dev != rt->u.dst.dev ||
+ !(rt->rt_flags&RTCF_DIRECTSRC) ||
+ (rt->rt_flags&RTF_GATEWAY) ||
+ !(dev->ip_flags&IFF_IP_ADDR_OK) ||
+ !(dev->ip_flags&IFF_IP_MASK_OK)) {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
icmp_param.icmph.type=ICMP_ADDRESSREPLY;
icmp_param.icmph.code=0;
- icmp_param.icmph.un.echo.id = icmph->un.echo.id;
- icmp_param.icmph.un.echo.sequence = icmph->un.echo.sequence;
+ icmp_param.icmph.un.echo = icmph->un.echo;
icmp_param.data_ptr=&dev->pa_mask;
icmp_param.data_len=4;
- if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
- icmp_build_xmit(&icmp_param, daddr, saddr, skb->iph->tos);
-#endif
- kfree_skb(skb, FREE_READ);
+ icmp_reply(&icmp_param, skb);
+ kfree_skb(skb, FREE_READ);
}
-static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+/*
+ * RFC1812 (4.3.3.9). A router SHOULD listen all replies, and complain
+ * loudly if an inconsistency is found.
+ */
+
+static void icmp_address_reply(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = skb->dev;
+ u32 mask;
+
+ if (!ipv4_config.log_martians ||
+ len < 4 ||
+ !(rt->rt_flags&RTCF_DIRECTSRC) ||
+ (rt->rt_flags&RTF_GATEWAY) ||
+ !(dev->ip_flags&IFF_IP_ADDR_OK) ||
+ !(dev->ip_flags&IFF_IP_MASK_OK)) {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ mask = *(u32*)&icmph[1];
+ if (mask != dev->pa_mask)
+ printk(KERN_INFO "Wrong address mask %08lX from %08lX/%s\n",
+ ntohl(mask), ntohl(rt->rt_src), dev->name);
+ kfree_skb(skb, FREE_READ);
+}
+
+static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
{
kfree_skb(skb, FREE_READ);
}
@@ -982,11 +1002,15 @@ static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, struct devi
* in udp.c or tcp.c...
*/
+/* This should work with the new hashes now. -DaveM */
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+
int icmp_chkaddr(struct sk_buff *skb)
{
- struct icmphdr *icmph=(struct icmphdr *)(skb->h.raw + skb->h.iph->ihl*4);
+ struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
struct iphdr *iph = (struct iphdr *) (icmph + 1);
- void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len) = icmp_pointers[icmph->type].handler;
+ void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len) = icmp_pointers[icmph->type].handler;
if (handler == icmp_unreach || handler == icmp_redirect) {
struct sock *sk;
@@ -996,8 +1020,7 @@ int icmp_chkaddr(struct sk_buff *skb)
{
struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
- sk = get_sock(&tcp_prot, th->source, iph->daddr,
- th->dest, iph->saddr, 0, 0);
+ sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
if (!sk) return 0;
if (sk->saddr != iph->saddr) return 0;
if (sk->daddr != iph->daddr) return 0;
@@ -1011,10 +1034,9 @@ int icmp_chkaddr(struct sk_buff *skb)
{
struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
- sk = get_sock(&udp_prot, uh->source, iph->daddr,
- uh->dest, iph->saddr, 0, 0);
+ sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
if (!sk) return 0;
- if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
+ if (sk->saddr != iph->saddr && __ip_chk_addr(iph->saddr) != IS_MYADDR)
return 0;
/*
* This packet may have come from us.
@@ -1028,29 +1050,33 @@ int icmp_chkaddr(struct sk_buff *skb)
}
#endif
+
/*
- * Deal with incoming ICMP packets.
+ * Deal with incoming ICMP packets.
*/
-int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo, struct inet_protocol *protocol)
+int icmp_rcv(struct sk_buff *skb, unsigned short len)
{
- struct icmphdr *icmph=(void *)skb->h.raw;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- int r;
-#endif
+ struct icmphdr *icmph = skb->h.icmph;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
icmp_statistics.IcmpInMsgs++;
+ if(len < sizeof(struct icmphdr))
+ {
+ icmp_statistics.IcmpInErrors++;
+ printk(KERN_INFO "ICMP: runt packet\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
/*
* Validate the packet
*/
- if (ip_compute_csum((unsigned char *) icmph, len))
- {
- /* Failed checksum! */
+ if (ip_compute_csum((unsigned char *) icmph, len)) {
icmp_statistics.IcmpInErrors++;
- printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(saddr));
+ printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(skb->nh.iph->saddr));
kfree_skb(skb, FREE_READ);
return(0);
}
@@ -1061,8 +1087,7 @@ int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
* RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently discarded.
*/
- if(icmph->type > 18)
- {
+ if (icmph->type > NR_ICMP_TYPES) {
icmp_statistics.IcmpInErrors++; /* Is this right - or do we ignore ? */
kfree_skb(skb,FREE_READ);
return(0);
@@ -1072,39 +1097,25 @@ int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
* Parse the ICMP message
*/
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /*
- * We may get non-local addresses and still want to handle them
- * locally, due to transparent proxying.
- * Thus, narrow down the test to what is really meant.
- */
- if (daddr!=dev->pa_addr && ((r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST))
-#else
- if (daddr!=dev->pa_addr && ip_chk_addr(daddr) != IS_MYADDR)
-#endif
- {
+ if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST)) {
/*
* RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be silently ignored (we don't as it is used
* by some network mapping tools).
* RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently discarded if to broadcast/multicast.
*/
- if (icmph->type != ICMP_ECHO)
- {
+ if (icmph->type != ICMP_ECHO &&
+ icmph->type != ICMP_TIMESTAMP &&
+ icmph->type != ICMP_ADDRESS &&
+ icmph->type != ICMP_ADDRESSREPLY) {
icmp_statistics.IcmpInErrors++;
kfree_skb(skb, FREE_READ);
return(0);
}
- /*
- * Reply the multicast/broadcast using a legal
- * interface - in this case the device we got
- * it from.
- */
- daddr=dev->pa_addr;
}
-
- len-=sizeof(struct icmphdr);
+
+ len -= sizeof(struct icmphdr);
(*icmp_pointers[icmph->type].input)++;
- (icmp_pointers[icmph->type].handler)(icmph,skb,skb->dev,saddr,daddr,len);
+ (icmp_pointers[icmph->type].handler)(icmph, skb, len);
return 0;
}
@@ -1114,14 +1125,13 @@ int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
static struct icmp_xrlim
xrl_unreach = { 4*HZ, 80, HZ/4 }, /* Host Unreachable */
- xrl_redirect = { 2*HZ, 10, HZ/2 }, /* Redirect */
xrl_generic = { 3*HZ, 30, HZ/4 }; /* All other errors */
/*
* This table is the definition of how we handle ICMP.
*/
-static struct icmp_control icmp_pointers[19] = {
+static struct icmp_control icmp_pointers[NR_ICMP_TYPES+1] = {
/* ECHO REPLY (0) */
{ &icmp_statistics.IcmpOutEchoReps, &icmp_statistics.IcmpInEchoReps, icmp_discard, 0, NULL },
{ &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
@@ -1131,7 +1141,7 @@ static struct icmp_control icmp_pointers[19] = {
/* SOURCE QUENCH (4) */
{ &icmp_statistics.IcmpOutSrcQuenchs, &icmp_statistics.IcmpInSrcQuenchs, icmp_unreach, 1, NULL },
/* REDIRECT (5) */
- { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, &xrl_redirect },
+ { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, NULL },
{ &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
{ &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
/* ECHO (8) */
@@ -1154,20 +1164,27 @@ static struct icmp_control icmp_pointers[19] = {
/* ADDR MASK (17) */
{ &icmp_statistics.IcmpOutAddrMasks, &icmp_statistics.IcmpInAddrMasks, icmp_address, 0, NULL },
/* ADDR MASK REPLY (18) */
- { &icmp_statistics.IcmpOutAddrMaskReps, &icmp_statistics.IcmpInAddrMaskReps, icmp_discard, 0, NULL }
+ { &icmp_statistics.IcmpOutAddrMaskReps, &icmp_statistics.IcmpInAddrMaskReps, icmp_address_reply, 0, NULL }
};
-void icmp_init(struct proto_ops *ops)
+void icmp_init(struct net_proto_family *ops)
{
- struct sock *sk;
int err;
- icmp_socket.type=SOCK_RAW;
- icmp_socket.ops=ops;
- if((err=ops->create(&icmp_socket, IPPROTO_ICMP))<0)
+
+ icmp_inode.i_mode = S_IFSOCK;
+ icmp_inode.i_sock = 1;
+ icmp_inode.i_uid = 0;
+ icmp_inode.i_gid = 0;
+
+ icmp_socket->inode = &icmp_inode;
+ icmp_socket->state = SS_UNCONNECTED;
+ icmp_socket->type=SOCK_RAW;
+
+ if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
panic("Failed to create the ICMP control socket.\n");
- sk=icmp_socket.data;
- sk->allocation=GFP_ATOMIC;
- sk->num = 256; /* Don't receive any data */
+ icmp_socket->sk->allocation=GFP_ATOMIC;
+ icmp_socket->sk->num = 256; /* Don't receive any data */
+ icmp_socket->sk->ip_ttl = MAXTTL;
#ifndef CONFIG_NO_ICMP_LIMIT
xrlim_init();
#endif
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index a5b60f12b..2aaef74de 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -64,6 +64,7 @@
* Alexey Kuznetsov: Wrong group leaving behaviour, backport
* fix from pending 2.1.x patches.
* Alan Cox: Forget to enable FDDI support earlier.
+ * Alexey Kuznetsov: Fixed leaving groups on device down.
*/
@@ -73,7 +74,6 @@
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
-#include <linux/config.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/in.h>
@@ -88,8 +88,6 @@
#include <linux/igmp.h>
#include <net/checksum.h>
-#ifdef CONFIG_IP_MULTICAST
-
/*
* If time expired, change the router type to IGMP_NEW_ROUTER.
@@ -206,14 +204,13 @@ static struct ip_router_info *igmp_set_mrouter_info(struct device *dev,int type,
static void igmp_stop_timer(struct ip_mc_list *im)
{
- if (im->tm_running) {
- del_timer(&im->timer);
- im->tm_running=0;
- }
- else {
- printk(KERN_ERR "igmp_stop_timer() called with timer not running by %p\n",
- return_address());
+ if (im->tm_running)
+ {
+ del_timer(&im->timer);
+ im->tm_running=0;
}
+ else
+ printk(KERN_DEBUG "igmp_stop_timer() called with timer not running by %p\n",__builtin_return_address(0));
}
extern __inline__ unsigned int random(void)
@@ -242,30 +239,55 @@ static void igmp_start_timer(struct ip_mc_list *im,unsigned char max_resp_time)
* Send an IGMP report.
*/
-#define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
+#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
-static void igmp_send_report(struct device *dev, unsigned long address, int type)
+static void igmp_send_report(struct device *dev, u32 group, int type)
{
- struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
- int tmp;
+ struct sk_buff *skb;
+ struct iphdr *iph;
struct igmphdr *ih;
+ struct rtable *rt;
- if(skb==NULL)
+ if (ip_route_output(&rt, group, 0, 0, dev))
return;
- tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
- 28 , 0, 1, NULL);
- if(tmp<0)
- {
- kfree_skb(skb, FREE_WRITE);
+
+ skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
+ if (skb == NULL) {
+ ip_rt_put(rt);
return;
}
- ih=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
+
+ skb->dst = &rt->u.dst;
+
+ skb_reserve(skb, (dev->hard_header_len+15)&~15);
+ ip_ll_header(skb);
+
+ skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+
+ iph->version = 4;
+ iph->ihl = (sizeof(struct iphdr)+4)>>2;
+ iph->tos = 0;
+ iph->frag_off = 0;
+ iph->ttl = 1;
+ iph->daddr = group;
+ iph->saddr = rt->rt_src;
+ iph->protocol = IPPROTO_IGMP;
+ iph->tot_len = htons(IGMP_SIZE);
+ iph->id = htons(ip_id_count++);
+ ((u8*)&iph[1])[0] = IPOPT_RA;
+ ((u8*)&iph[1])[1] = 4;
+ ((u8*)&iph[1])[2] = 0;
+ ((u8*)&iph[1])[3] = 0;
+ ip_send_check(iph);
+
+ ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
ih->type=type;
ih->code=0;
ih->csum=0;
- ih->group=address;
- ih->csum=ip_compute_csum((void *)ih,sizeof(struct igmphdr)); /* Checksum fill */
- ip_queue_xmit(NULL,dev,skb,1);
+ ih->group=group;
+ ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
+
+ skb->dst->output(skb);
}
@@ -282,7 +304,7 @@ static void igmp_timer_expire(unsigned long data)
igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
else
igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
- im->reporter=1;
+ im->reporter = 1;
}
static void igmp_init_timer(struct ip_mc_list *im)
@@ -294,28 +316,27 @@ static void igmp_init_timer(struct ip_mc_list *im)
}
-static void igmp_heard_report(struct device *dev, __u32 address, __u32 src)
+static void igmp_heard_report(struct device *dev, u32 group, u32 source)
{
struct ip_mc_list *im;
- if ((address & IGMP_LOCAL_GROUP_MASK) != IGMP_LOCAL_GROUP)
- {
- /* Timers are only set for non-local groups */
- for(im=dev->ip_mc_list;im!=NULL;im=im->next)
- {
- if(im->multiaddr==address)
- {
- if(im->tm_running)
- igmp_stop_timer(im);
- if(src!=dev->pa_addr)
- im->reporter=0;
- return;
- }
+ /* Timers are only set for non-local groups */
+ if (LOCAL_MCAST(group))
+ return;
+
+ for (im=dev->ip_mc_list; im!=NULL; im=im->next) {
+ if (im->multiaddr == group) {
+ if (im->tm_running)
+ igmp_stop_timer(im);
+ if (source != dev->pa_addr)
+ im->reporter = 0;
+ return;
}
}
}
-static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
+static void igmp_heard_query(struct device *dev, unsigned char max_resp_time,
+ u32 group)
{
struct ip_mc_list *im;
int mrouter_type;
@@ -323,11 +344,10 @@ static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
/*
* The max_resp_time is in units of 1/10 second.
*/
- if(max_resp_time>0)
- {
+ if(max_resp_time>0) {
mrouter_type=IGMP_NEW_ROUTER;
- if(igmp_set_mrouter_info(dev,mrouter_type,0)==NULL)
+ if (igmp_set_mrouter_info(dev,mrouter_type,0)==NULL)
return;
/*
* - Start the timers in all of our membership records
@@ -339,25 +359,18 @@ static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
* - Use the igmp->igmp_code field as the maximum
* delay possible
*/
- for(im=dev->ip_mc_list;im!=NULL;im=im->next)
- {
- if(im->tm_running)
- {
- if(im->timer.expires>jiffies+max_resp_time*HZ/IGMP_TIMER_SCALE)
- {
+ for(im=dev->ip_mc_list;im!=NULL;im=im->next) {
+ if (group && group != im->multiaddr)
+ continue;
+ if(im->tm_running) {
+ if(im->timer.expires>jiffies+max_resp_time*HZ/IGMP_TIMER_SCALE) {
igmp_stop_timer(im);
igmp_start_timer(im,max_resp_time);
}
- }
- else
- {
- if((im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
- igmp_start_timer(im,max_resp_time);
- }
+ } else if (!LOCAL_MCAST(im->multiaddr))
+ igmp_start_timer(im,max_resp_time);
}
- }
- else
- {
+ } else {
mrouter_type=IGMP_OLD_ROUTER;
max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE;
@@ -371,9 +384,8 @@ static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
* "local" group (224.0.0.X).
*/
- for(im=dev->ip_mc_list;im!=NULL;im=im->next)
- {
- if(!im->tm_running && (im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
+ for(im=dev->ip_mc_list;im!=NULL;im=im->next) {
+ if(!im->tm_running && !LOCAL_MCAST(im->multiaddr))
igmp_start_timer(im,max_resp_time);
}
}
@@ -403,6 +415,9 @@ extern __inline__ void ip_mc_map(unsigned long addr, char *buf)
void ip_mc_filter_add(struct device *dev, unsigned long addr)
{
char buf[6];
+ ip_rt_multicast_event(dev);
+ if(!(dev->flags & IFF_MULTICAST))
+ return;
if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
return; /* Only do ethernet or FDDI for now */
ip_mc_map(addr,buf);
@@ -416,6 +431,7 @@ void ip_mc_filter_add(struct device *dev, unsigned long addr)
void ip_mc_filter_del(struct device *dev, unsigned long addr)
{
char buf[6];
+ ip_rt_multicast_event(dev);
if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
return; /* Only do ethernet or FDDI for now */
ip_mc_map(addr,buf);
@@ -425,7 +441,8 @@ void ip_mc_filter_del(struct device *dev, unsigned long addr)
extern __inline__ void igmp_group_dropped(struct ip_mc_list *im)
{
del_timer(&im->timer);
- igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
+ if (im->reporter)
+ igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
ip_mc_filter_del(im->interface, im->multiaddr);
}
@@ -443,51 +460,34 @@ extern __inline__ void igmp_group_added(struct ip_mc_list *im)
igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
}
-int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- __u32 daddr, unsigned short len, __u32 saddr, int redo,
- struct inet_protocol *protocol)
+int igmp_rcv(struct sk_buff *skb, unsigned short len)
{
/* This basically follows the spec line by line -- see RFC1112 */
- struct igmphdr *ih;
+ struct igmphdr *ih = skb->h.igmph;
- /*
- * Mrouted needs to able to query local interfaces. So
- * report for the device this was sent at. (Which can
- * be the loopback this time)
- */
-
- if(dev->flags&IFF_LOOPBACK)
- {
- dev=ip_dev_find(saddr);
- if(dev==NULL)
- dev=&loopback_dev;
- }
- ih=(struct igmphdr *)skb->h.raw;
-
- if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl<1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
- {
+ if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
kfree_skb(skb, FREE_READ);
return 0;
}
- /*
- * I have a report that someone does this!
- */
-
- if(saddr==0)
- {
- printk(KERN_INFO "Broken multicast host using 0.0.0.0 heard on %s\n",
- dev->name);
- kfree_skb(skb, FREE_READ);
- return 0;
+ switch (ih->type) {
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ igmp_heard_query(skb->dev, ih->code, ih->group);
+ break;
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+ igmp_heard_report(skb->dev, ih->group, skb->nh.iph->saddr);
+ break;
+ case IGMP_DVMRP:
+ case IGMP_PIM:
+ case IGMP_TRACE:
+ case IGMP_HOST_LEAVE_MESSAGE:
+ case IGMP_MTRACE:
+ case IGMP_MTRACE_RESP:
+ break;
+ default:
+ NETDEBUG(printk(KERN_DEBUG "Unknown IGMP type=%d\n", ih->type));
}
-
- if(ih->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
- igmp_heard_query(dev,ih->code);
- if(ih->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==ih->group)
- igmp_heard_report(dev,ih->group, saddr);
- if(ih->type==IGMP_HOST_NEW_MEMBERSHIP_REPORT && daddr==ih->group)
- igmp_heard_report(dev,ih->group, saddr);
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -554,12 +554,16 @@ void ip_mc_drop_device(struct device *dev)
{
struct ip_mc_list *i;
struct ip_mc_list *j;
+ start_bh_atomic();
for(i=dev->ip_mc_list;i!=NULL;i=j)
{
j=i->next;
+ if(i->tm_running)
+ del_timer(&i->timer);
kfree_s(i,sizeof(*i));
}
dev->ip_mc_list=NULL;
+ end_bh_atomic();
}
/*
@@ -582,7 +586,6 @@ void ip_mc_allhost(struct device *dev)
i->next=dev->ip_mc_list;
dev->ip_mc_list=i;
ip_mc_filter_add(i->interface, i->multiaddr);
-
}
/*
@@ -595,8 +598,6 @@ int ip_mc_join_group(struct sock *sk , struct device *dev, unsigned long addr)
int i;
if(!MULTICAST(addr))
return -EINVAL;
- if(!(dev->flags&IFF_MULTICAST))
- return -EADDRNOTAVAIL;
if(sk->ip_mc_list==NULL)
{
if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
@@ -628,8 +629,6 @@ int ip_mc_leave_group(struct sock *sk, struct device *dev, unsigned long addr)
int i;
if(!MULTICAST(addr))
return -EINVAL;
- if(!(dev->flags&IFF_MULTICAST))
- return -EADDRNOTAVAIL;
if(sk->ip_mc_list==NULL)
return -EADDRNOTAVAIL;
@@ -668,4 +667,52 @@ void ip_mc_drop_socket(struct sock *sk)
sk->ip_mc_list=NULL;
}
-#endif
+
+/*
+ * Write an multicast group list table for the IGMP daemon to
+ * read.
+ */
+
+int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ off_t pos=0, begin=0;
+ struct ip_mc_list *im;
+ unsigned long flags;
+ int len=0;
+ struct device *dev;
+
+ len=sprintf(buffer,"Device : Count\tGroup Users Timer\tReporter\n");
+ save_flags(flags);
+ cli();
+
+ for(dev = dev_base; dev; dev = dev->next)
+ {
+ if(dev->flags&IFF_UP)
+ {
+ len+=sprintf(buffer+len,"%-10s: %5d\n",
+ dev->name, dev->mc_count);
+ for(im = dev->ip_mc_list; im; im = im->next)
+ {
+ len+=sprintf(buffer+len,
+ "\t\t\t%08lX %5d %d:%08lX\t%d\n",
+ im->multiaddr, im->users,
+ im->tm_running, im->timer.expires-jiffies, im->reporter);
+ pos=begin+len;
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+ }
+ }
+ restore_flags(flags);
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if(len>length)
+ len=length;
+ return len;
+}
+
diff --git a/net/ipv4/ip_alias.c b/net/ipv4/ip_alias.c
index 488de23d4..74ff42a74 100644
--- a/net/ipv4/ip_alias.c
+++ b/net/ipv4/ip_alias.c
@@ -79,7 +79,9 @@ int ip_alias_print_1(struct net_alias_type *this, struct net_alias *alias, char
struct device *ip_alias_dev_select(struct net_alias_type *this, struct device *main_dev, struct sockaddr *sa)
{
__u32 addr;
+#if 0
struct rtable *rt;
+#endif
struct device *dev=NULL;
/*
@@ -102,12 +104,14 @@ struct device *ip_alias_dev_select(struct net_alias_type *this, struct device *m
* net_alias module will check if returned device is main_dev's alias
*/
+#if 0
rt = ip_rt_route(addr, 0);
if(rt)
{
dev=rt->rt_dev;
ip_rt_put(rt);
}
+#endif
return dev;
}
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index 81d90f5de..0726f3bb4 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -16,7 +16,6 @@
* use output device for accounting.
* Jos Vos : Call forward firewall after routing
* (always use output device).
- * Alan Cox : Unshare buffer on forward.
*/
#include <linux/config.h>
@@ -29,6 +28,8 @@
#include <linux/netdevice.h>
#include <net/sock.h>
#include <net/ip.h>
+#include <net/tcp.h>
+#include <net/udp.h>
#include <net/icmp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
@@ -41,534 +42,206 @@
#include <linux/route.h>
#include <net/route.h>
-#ifdef CONFIG_IP_FORWARD
-#ifdef CONFIG_IP_MROUTE
-
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
/*
- * Encapsulate a packet by attaching a valid IPIP header to it.
- * This avoids tunnel drivers and other mess and gives us the speed so
- * important for multicast video.
+ * Check the packet against our socket administration to see
+ * if it is related to a connection on our system.
+ * Needed for transparent proxying.
*/
-
-static void ip_encap(struct sk_buff *skb, int len, struct device *out, __u32 daddr)
-{
- /*
- * There is space for the IPIP header and MAC left.
- *
- * Firstly push down and install the IPIP header.
- */
- struct iphdr *iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
-
- if(len>65515)
- len=65515;
-
-
- iph->version = 4;
- iph->tos = skb->ip_hdr->tos;
- iph->ttl = skb->ip_hdr->ttl;
- iph->frag_off = 0;
- iph->daddr = daddr;
- iph->saddr = out->pa_addr;
- iph->protocol = IPPROTO_IPIP;
- iph->ihl = 5;
- iph->tot_len = htons(skb->len + len); /* Anand, ernet */
- iph->id = htons(ip_id_count++);
- ip_send_check(iph);
-
- skb->dev = out;
- skb->arp = 1;
- skb->raddr=daddr; /* Router address is not destination address. The
- * correct value is given eventually. I have not
- * removed this statement. But could have.
- * Anand, ernet.
- */
- /*
- * Now add the physical header (driver will push it down).
- */
- /* The last parameter of out->hard_header() needed skb->len + len.
- * Anand, ernet.
- */
- if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL,
- skb->len + len)<0)
- skb->arp=0;
- /*
- * Read to queue for transmission.
- */
+int ip_chksock(struct sk_buff *skb)
+{
+ switch (skb->nh.iph->protocol) {
+ case IPPROTO_ICMP:
+ return icmp_chkaddr(skb);
+ case IPPROTO_TCP:
+ return tcp_chkaddr(skb);
+ case IPPROTO_UDP:
+ return udp_chkaddr(skb);
+ default:
+ return 0;
+ }
}
-
#endif
-/*
- * Forward an IP datagram to its next destination.
- */
-int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
- __u32 target_addr)
+int ip_forward(struct sk_buff *skb)
{
struct device *dev2; /* Output device */
struct iphdr *iph; /* Our header */
- struct sk_buff *skb2; /* Output packet */
struct rtable *rt; /* Route we use */
- unsigned char *ptr; /* Data pointer */
- unsigned long raddr; /* Router IP address */
- struct options * opt = (struct options*)skb->proto_priv;
- struct hh_cache *hh = NULL;
- int encap = 0; /* Encap length */
-#ifdef CONFIG_FIREWALL
- int fw_res = 0; /* Forwarding result */
-#ifdef CONFIG_IP_MASQUERADE
- struct sk_buff *skb_in = skb; /* So we can remember if the masquerader did some swaps */
-#endif /* CONFIG_IP_MASQUERADE */
-#endif /* CONFIG_FIREWALL */
+ struct ip_options * opt = &(IPCB(skb)->opt);
+ unsigned short mtu;
+#if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE)
+ int fw_res = 0;
+#endif
- /*
- * We may be sharing the buffer with a snooper. That won't do
- */
-
- if((skb=skb_unshare(skb, GFP_ATOMIC,FREE_READ))==NULL)
- return -1;
+ if (skb->pkt_type != PACKET_HOST) {
+ kfree_skb(skb,FREE_WRITE);
+ return 0;
+ }
/*
* According to the RFC, we must first decrease the TTL field. If
* that reaches zero, we must reply an ICMP control message telling
* that the packet's lifetime expired.
- *
- * Exception:
- * We may not generate an ICMP for an ICMP. icmp_send does the
- * enforcement of this so we can forget it here. It is however
- * sometimes VERY important.
*/
- iph = skb->h.iph;
- if (!(is_frag&IPFWD_NOTTLDEC))
- {
- unsigned long checksum = iph->check;
- iph->ttl--;
+ iph = skb->nh.iph;
+ rt = (struct rtable*)skb->dst;
- /*
- * Re-compute the IP header checksum.
- * This is efficient. We know what has happened to the header
- * and can thus adjust the checksum as Phil Karn does in KA9Q
- * except we do this in "network byte order".
- */
- checksum += htons(0x0100);
- /* carry overflow? */
- checksum += checksum >> 16;
- iph->check = checksum;
- }
+#ifdef CONFIG_TRANSPARENT_PROXY
+ if (ip_chk_sock(skb))
+ return ip_local_deliver(skb);
+#endif
- if (iph->ttl <= 0)
- {
+ if (ip_decrease_ttl(iph) <= 0) {
/* Tell the sender its packet died... */
- icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+ icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
+ kfree_skb(skb, FREE_WRITE);
return -1;
}
- /* If IPFWD_MULTITUNNEL flag is set, then we have to perform routing
- * decision so as to reach the other end of the tunnel. This condition
- * also means that we are dealing with a unicast IP packet "in a way".
- * Anand, ernet.
- */
-
-#ifdef CONFIG_IP_MROUTE
- if(!(is_frag&IPFWD_MULTICASTING) || (is_frag&IPFWD_MULTITUNNEL))
- {
-#endif
+ if (opt->is_strictroute && (rt->rt_flags&RTF_GATEWAY)) {
/*
- * OK, the packet is still valid. Fetch its destination address,
- * and give it to the IP sender for further processing.
+ * Strict routing permits no gatewaying
*/
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
+ kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
- rt = ip_rt_route(target_addr, 0);
- if (rt == NULL)
- {
- /*
- * Tell the sender its packet cannot be delivered. Again
- * ICMP is screened later.
- */
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
- return -1;
- }
-
-
- /*
- * Gosh. Not only is the packet valid; we even know how to
- * forward it onto its final destination. Can we say this
- * is being plain lucky?
- * If the router told us that there is no GW, use the dest.
- * IP address itself- we seem to be connected directly...
- */
+ /*
+ * Having picked a route we can now send the frame out
+ * after asking the firewall permission to do so.
+ */
+
+ skb->priority = rt->u.dst.priority;
+ dev2 = rt->u.dst.dev;
+ mtu = dev2->mtu;
- raddr = rt->rt_gateway;
+#ifdef CONFIG_NET_SECURITY
+ call_fw_firewall(PF_SECURITY, dev2, NULL, &mtu, NULL);
+#endif
- if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
- /*
- * Strict routing permits no gatewaying
- */
+ /*
+ * In IP you never have to forward a frame on the interface that it
+ * arrived upon. We now generate an ICMP HOST REDIRECT giving the route
+ * we calculated.
+ */
+ if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
+ ip_rt_send_redirect(skb);
- ip_rt_put(rt);
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+ /*
+ * We now may allocate a new buffer, and copy the datagram into it.
+ * If the indicated interface is up and running, kick it.
+ */
+
+ if (dev2->flags & IFF_UP) {
+ if (skb->len > mtu && (ntohs(iph->frag_off) & IP_DF)) {
+ ip_statistics.IpFragFails++;
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
+ kfree_skb(skb, FREE_WRITE);
return -1;
}
- /*
- * Having picked a route we can now send the frame out
- * after asking the firewall permission to do so.
- */
-
- dev2 = rt->rt_dev;
- hh = rt->rt_hh;
- /*
- * In IP you never have to forward a frame on the interface that it
- * arrived upon. We now generate an ICMP HOST REDIRECT giving the route
- * we calculated.
- */
-#ifndef CONFIG_IP_NO_ICMP_REDIRECT
- if (dev == dev2 &&
- !((iph->saddr^dev->pa_addr)&dev->pa_mask) &&
- /* The daddr!=raddr test isn't obvious - what it's doing
- is avoiding sending a frame the receiver will not
- believe anyway.. */
- iph->daddr != raddr/*ANK*/ && !opt->srr)
- icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
-#endif
-#ifdef CONFIG_IP_MROUTE
-
- /* This is for ip encap. Anand, ernet.*/
+ if (rt->rt_flags&RTCF_NAT) {
+ if (ip_do_nat(skb)) {
+ kfree_skb(skb, FREE_WRITE);
+ return -1;
+ }
+ }
- if (is_frag&IPFWD_MULTITUNNEL) {
- encap=20;
- }
- }
- else
- {
- /*
- * Multicast route forward. Routing is already done
- */
- dev2=skb->dev;
- raddr=skb->raddr;
- if(is_frag&IPFWD_MULTITUNNEL) /* VIFF_TUNNEL mode */
- encap=20;
- rt=NULL;
- }
-#endif
-
- /*
- * See if we are allowed to forward this.
- * Note: demasqueraded fragments are always 'back'warded.
- */
-
-#ifdef CONFIG_FIREWALL
- if(!(is_frag&IPFWD_MASQUERADED))
- {
#ifdef CONFIG_IP_MASQUERADE
- /*
- * Check that any ICMP packets are not for a
- * masqueraded connection. If so rewrite them
- * and skip the firewall checks
- */
- if (iph->protocol == IPPROTO_ICMP)
- {
- if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0)
- {
- if (rt)
- ip_rt_put(rt);
- /* Problem - ie bad checksum */
+ if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+
+ if (rt->rt_flags&RTCF_VALVE) {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0);
+ kfree_skb(skb, FREE_READ);
return -1;
}
- if (fw_res)
- /* ICMP matched - skip firewall */
+ /*
+ * Check that any ICMP packets are not for a
+ * masqueraded connection. If so rewrite them
+ * and skip the firewall checks
+ */
+ if (iph->protocol == IPPROTO_ICMP) {
+ if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0) {
+ kfree_skb(skb, FREE_READ);
+ return -1;
+ }
+
+ if (fw_res)
+ /* ICMP matched - skip firewall */
+ goto skip_call_fw_firewall;
+ }
+ if (rt->rt_flags&RTCF_MASQ)
goto skip_call_fw_firewall;
- }
#endif
- fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
+#ifdef CONFIG_FIREWALL
+ fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL, &skb);
switch (fw_res) {
case FW_ACCEPT:
case FW_MASQUERADE:
break;
case FW_REJECT:
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
/* fall thru */
default:
+ kfree_skb(skb, FREE_READ);
return -1;
}
-
-#ifdef CONFIG_IP_MASQUERADE
- skip_call_fw_firewall:
-#endif
- }
#endif
- /*
- * We now may allocate a new buffer, and copy the datagram into it.
- * If the indicated interface is up and running, kick it.
- */
-
- if (dev2->flags & IFF_UP)
- {
#ifdef CONFIG_IP_MASQUERADE
+ }
+
+skip_call_fw_firewall:
/*
* If this fragment needs masquerading, make it so...
* (Don't masquerade de-masqueraded fragments)
*/
- if (!(is_frag&IPFWD_MASQUERADED) && fw_res==FW_MASQUERADE)
- if (ip_fw_masquerade(&skb, dev2) < 0)
- {
- /*
- * Masquerading failed; silently discard this packet.
- */
- if (rt)
- ip_rt_put(rt);
+ if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
+ (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
+ if (ip_fw_masquerade(&skb, dev2) < 0) {
+ kfree_skb(skb, FREE_READ);
return -1;
}
+ }
#endif
- IS_SKB(skb);
- if (skb->len+encap > dev2->mtu && (iph->frag_off & htons(IP_DF)))
- {
- ip_statistics.IpFragFails++;
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
- if(rt)
- ip_rt_put(rt);
- return -1;
- }
+ if (skb_headroom(skb) < dev2->hard_header_len || skb_cloned(skb)) {
+ struct sk_buff *skb2;
+ skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
+ kfree_skb(skb, FREE_WRITE);
-#ifdef CONFIG_IP_MROUTE
- if(skb_headroom(skb)-encap<dev2->hard_header_len)
- {
- skb2 = alloc_skb(dev2->hard_header_len + skb->len + encap + 15, GFP_ATOMIC);
-#else
- if(skb_headroom(skb)<dev2->hard_header_len)
- {
- skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
-#endif
- /*
- * This is rare and since IP is tolerant of network failures
- * quite harmless.
- */
-
- if (skb2 == NULL)
- {
- NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
- if(rt)
- ip_rt_put(rt);
+ if (skb2 == NULL) {
+ NETDEBUG(printk(KERN_ERR "\nIP: No memory available for IP forward\n"));
return -1;
}
-
- IS_SKB(skb2);
- /*
- * Add the physical headers.
- */
- skb2->protocol=htons(ETH_P_IP);
-#ifdef CONFIG_IP_MROUTE
- if(is_frag&IPFWD_MULTITUNNEL)
- {
- skb_reserve(skb2,(encap+dev2->hard_header_len+15)&~15); /* 16 byte aligned IP headers are good */
-
-/* We need to pass on IP information of the incoming packet to ip_encap()
- * to fillin ttl, and tos fields.The destination should be target_addr.
- * Anand, ernet.
- */
-
- skb2->ip_hdr = skb->ip_hdr;
-
- ip_encap(skb2,skb->len, dev2, target_addr);
-
-/* The router address is got earlier that to take us to the remote tunnel
- * Anand, ernet.
- */
- skb2->raddr = rt->rt_gateway;
- }
- else
-#endif
- ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr);
-
- /*
- * We have to copy the bytes over as the new header wouldn't fit
- * the old buffer. This should be very rare.
- */
-
- ptr = skb_put(skb2,skb->len);
- skb2->free = 1;
- skb2->h.raw = ptr;
- /*
- * Copy the packet data into the new buffer.
- */
- memcpy(ptr, skb->h.raw, skb->len);
- memcpy(skb2->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
- iph = skb2->ip_hdr = skb2->h.iph;
+ skb = skb2;
+ iph = skb2->nh.iph;
}
- else
- {
- /*
- * Build a new MAC header.
- */
- skb2 = skb;
- skb2->dev=dev2;
-#ifdef CONFIG_IP_MROUTE
- if(is_frag&IPFWD_MULTITUNNEL)
- ip_encap(skb,skb->len, dev2, raddr);
- else
- {
-#endif
- skb->arp=1;
- skb->raddr=raddr;
- if (hh)
- {
- memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len);
- if (!hh->hh_uptodate)
- {
-#if RT_CACHE_DEBUG >= 2
- printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway);
-#endif
- skb->arp = 0;
- }
- }
- else if (dev2->hard_header)
- {
- if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
- skb->arp=0;
- }
-#ifdef CONFIG_IP_MROUTE
- }
-#endif
- }
#ifdef CONFIG_FIREWALL
- if((fw_res = call_out_firewall(PF_INET, skb2->dev, iph, NULL)) < FW_ACCEPT)
- {
+ if ((fw_res = call_out_firewall(PF_INET, dev2, iph, NULL,&skb)) < FW_ACCEPT) {
/* FW_ACCEPT and FW_MASQUERADE are treated equal:
masquerading is only supported via forward rules */
if (fw_res == FW_REJECT)
- icmp_send(skb2, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
- if (skb != skb2)
- kfree_skb(skb2,FREE_WRITE);
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+ kfree_skb(skb,FREE_WRITE);
return -1;
}
#endif
- ip_statistics.IpForwDatagrams++;
-
- if (opt->optlen)
- {
- unsigned char * optptr;
- if (opt->rr_needaddr)
- {
- optptr = (unsigned char *)iph + opt->rr;
- memcpy(&optptr[optptr[2]-5], &dev2->pa_addr, 4);
- opt->is_changed = 1;
- }
- if (opt->srr_is_hit)
- {
- int srrptr, srrspace;
- optptr = (unsigned char *)iph + opt->srr;
-
- for ( srrptr=optptr[2], srrspace = optptr[1];
- srrptr <= srrspace;
- srrptr += 4
- )
- {
- if (srrptr + 3 > srrspace)
- break;
- if (memcmp(&target_addr, &optptr[srrptr-1], 4) == 0)
- break;
- }
- if (srrptr + 3 <= srrspace)
- {
- opt->is_changed = 1;
- memcpy(&optptr[srrptr-1], &dev2->pa_addr, 4);
- iph->daddr = target_addr;
- optptr[2] = srrptr+4;
- }
- else
- printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
- }
- if (opt->ts_needaddr)
- {
- optptr = (unsigned char *)iph + opt->ts;
- memcpy(&optptr[optptr[2]-9], &dev2->pa_addr, 4);
- opt->is_changed = 1;
- }
- if (opt->is_changed)
- {
- opt->is_changed = 0;
- ip_send_check(iph);
- }
- }
-/*
- * ANK: this is point of "no return", we cannot send an ICMP,
- * because we changed SRR option.
- */
+ ip_statistics.IpForwDatagrams++;
- /*
- * See if it needs fragmenting. Note in ip_rcv we tagged
- * the fragment type. This must be right so that
- * the fragmenter does the right thing.
- */
+ if (opt->optlen)
+ ip_forward_options(skb);
- if(skb2->len > dev2->mtu + dev2->hard_header_len)
- {
- ip_fragment(NULL,skb2,dev2, is_frag);
- kfree_skb(skb2,FREE_WRITE);
- }
- else
- {
-#ifdef CONFIG_IP_ACCT
- /*
- * Count mapping we shortcut
- */
-
- ip_fw_chk(iph,dev2,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif
-
- /*
- * Map service types to priority. We lie about
- * throughput being low priority, but it's a good
- * choice to help improve general usage.
- */
- if(iph->tos & IPTOS_LOWDELAY)
- dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
- else if(iph->tos & IPTOS_THROUGHPUT)
- dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
- else
- dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
- }
- }
- else
- {
- if(rt)
- ip_rt_put(rt);
- return -1;
- }
- if(rt)
- ip_rt_put(rt);
-
- /*
- * Tell the caller if their buffer is free.
- */
-
- if(skb==skb2)
- return 0;
-
-#ifdef CONFIG_IP_MASQUERADE
- /*
- * The original is free. Free our copy and
- * tell the caller not to free.
- */
- if(skb!=skb_in)
- {
- kfree_skb(skb_in, FREE_WRITE);
- return 0;
+ ip_send(skb);
}
-#endif
- return 1;
+ return 0;
}
-
-
-#endif
-
-
-
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index c6c33e03b..bf549b047 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -24,6 +24,7 @@
#include <net/icmp.h>
#include <linux/tcp.h>
#include <linux/udp.h>
+#include <linux/inet.h>
#include <linux/firewall.h>
#include <linux/ip_fw.h>
#include <net/checksum.h>
@@ -45,7 +46,9 @@
static struct ipq *ipqueue = NULL; /* IP fragment queue */
-atomic_t ip_frag_mem = 0; /* Memory used for fragments */
+atomic_t ip_frag_mem = ATOMIC_INIT(0); /* Memory used for fragments */
+
+char *in_ntoa(unsigned long in);
/*
* Memory Tracking Functions
@@ -84,7 +87,7 @@ static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, u
fp = (struct ipfrag *) frag_kmalloc(sizeof(struct ipfrag), GFP_ATOMIC);
if (fp == NULL)
{
- NETDEBUG(printk("IP: frag_create: no memory left !\n"));
+ NETDEBUG(printk(KERN_ERR "IP: frag_create: no memory left !\n"));
return(NULL);
}
memset(fp, 0, sizeof(struct ipfrag));
@@ -102,7 +105,7 @@ static struct ipfrag *ip_frag_create(int offset, int end, struct sk_buff *skb, u
save_flags(flags);
cli();
- ip_frag_mem+=skb->truesize;
+ atomic_add(skb->truesize, &ip_frag_mem);
restore_flags(flags);
return(fp);
@@ -174,7 +177,6 @@ static void ip_free(struct ipq *qp)
while (fp != NULL)
{
xp = fp->next;
- IS_SKB(fp->skb);
frag_kfree_skb(fp->skb,FREE_READ);
frag_kfree_s(fp, sizeof(struct ipfrag));
fp = xp;
@@ -208,7 +210,7 @@ static void ip_expire(unsigned long arg)
/* This if is always true... shrug */
if(qp->fragments!=NULL)
icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
- ICMP_EXC_FRAGTIME, 0, qp->dev);
+ ICMP_EXC_FRAGTIME, 0);
/*
* Nuke the fragment queue.
@@ -223,7 +225,7 @@ static void ip_expire(unsigned long arg)
static void ip_evictor(void)
{
- while(ip_frag_mem>IPFRAG_LOW_THRESH)
+ while(atomic_read(&ip_frag_mem)>IPFRAG_LOW_THRESH)
{
if(!ipqueue)
panic("ip_evictor: memcount");
@@ -238,7 +240,7 @@ static void ip_evictor(void)
* will insert the received fragments at their respective positions.
*/
-static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
+static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph)
{
struct ipq *qp;
int ihlen;
@@ -246,7 +248,7 @@ static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct devi
qp = (struct ipq *) frag_kmalloc(sizeof(struct ipq), GFP_ATOMIC);
if (qp == NULL)
{
- NETDEBUG(printk("IP: create: no memory left !\n"));
+ NETDEBUG(printk(KERN_ERR "IP: create: no memory left !\n"));
return(NULL);
}
memset(qp, 0, sizeof(struct ipq));
@@ -259,7 +261,7 @@ static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct devi
qp->iph = (struct iphdr *) frag_kmalloc(64 + 8, GFP_ATOMIC);
if (qp->iph == NULL)
{
- NETDEBUG(printk("IP: create: no memory left !\n"));
+ NETDEBUG(printk(KERN_ERR "IP: create: no memory left !\n"));
frag_kfree_s(qp, sizeof(struct ipq));
return(NULL);
}
@@ -268,7 +270,7 @@ static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct devi
qp->len = 0;
qp->ihlen = ihlen;
qp->fragments = NULL;
- qp->dev = dev;
+ qp->dev = skb->dev;
/* Start a timer for this entry. */
qp->timer.expires = jiffies + IP_FRAG_TIME; /* about 30 seconds */
@@ -337,23 +339,29 @@ static struct sk_buff *ip_glue(struct ipq *qp)
* Allocate a new buffer for the datagram.
*/
len = qp->ihlen + qp->len;
-
+
+ if(len>65535)
+ {
+ printk(KERN_INFO "Oversized IP packet from %s.\n", in_ntoa(qp->iph->saddr));
+ ip_statistics.IpReasmFails++;
+ ip_free(qp);
+ return NULL;
+ }
+
if ((skb = dev_alloc_skb(len)) == NULL)
{
ip_statistics.IpReasmFails++;
- NETDEBUG(printk("IP: queue_glue: no memory for gluing queue %p\n", qp));
+ NETDEBUG(printk(KERN_ERR "IP: queue_glue: no memory for gluing queue %p\n", qp));
ip_free(qp);
return(NULL);
}
/* Fill in the basic details. */
- skb_put(skb,len);
- skb->h.raw = skb->data;
- skb->free = 1;
+ skb->mac.raw = ptr = skb->data;
+ skb->nh.iph = iph = (struct iphdr*)skb_put(skb,len);
/* Copy the original IP headers into the new buffer. */
- ptr = (unsigned char *) skb->h.raw;
- memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+ memcpy(ptr, qp->iph, qp->ihlen);
ptr += qp->ihlen;
count = 0;
@@ -364,13 +372,17 @@ static struct sk_buff *ip_glue(struct ipq *qp)
{
if(count+fp->len > skb->len)
{
- NETDEBUG(printk("Invalid fragment list: Fragment over size.\n"));
+ NETDEBUG(printk(KERN_ERR "Invalid fragment list: Fragment over size.\n"));
ip_free(qp);
kfree_skb(skb,FREE_WRITE);
ip_statistics.IpReasmFails++;
return NULL;
}
memcpy((ptr + fp->offset), fp->ptr, fp->len);
+ if (!count) {
+ skb->dst = dst_clone(fp->skb->dst);
+ skb->dev = fp->skb->dev;
+ }
count += fp->len;
fp = fp->next;
}
@@ -379,10 +391,9 @@ static struct sk_buff *ip_glue(struct ipq *qp)
ip_free(qp);
/* Done with all fragments. Fixup the new IP header. */
- iph = skb->h.iph;
+ iph = skb->nh.iph;
iph->frag_off = 0;
iph->tot_len = htons((iph->ihl * 4) + count);
- skb->ip_hdr = iph;
ip_statistics.IpReasmOKs++;
return(skb);
@@ -393,8 +404,9 @@ static struct sk_buff *ip_glue(struct ipq *qp)
* Process an incoming IP datagram fragment.
*/
-struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
+struct sk_buff *ip_defrag(struct sk_buff *skb)
{
+ struct iphdr *iph = skb->nh.iph;
struct ipfrag *prev, *next, *tmp;
struct ipfrag *tfp;
struct ipq *qp;
@@ -409,7 +421,7 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
* Start by cleaning up the memory
*/
- if(ip_frag_mem>IPFRAG_HIGH_THRESH)
+ if(atomic_read(&ip_frag_mem)>IPFRAG_HIGH_THRESH)
ip_evictor();
/*
* Find the entry of this IP datagram in the "incomplete datagrams" queue.
@@ -424,7 +436,7 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
if (((flags & IP_MF) == 0) && (offset == 0))
{
if (qp != NULL)
- ip_free(qp); /* Huh? How could this exist?? */
+ ip_free(qp); /* Fragmented frame replaced by full unfragmented copy */
return(skb);
}
@@ -458,10 +470,9 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
/*
* If we failed to create it, then discard the frame
*/
- if ((qp = ip_create(skb, iph, dev)) == NULL)
+ if ((qp = ip_create(skb, iph)) == NULL)
{
- skb->sk = NULL;
- frag_kfree_skb(skb, FREE_READ);
+ kfree_skb(skb, FREE_READ);
ip_statistics.IpReasmFails++;
return NULL;
}
@@ -473,7 +484,7 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
if(ntohs(iph->tot_len)+(int)offset>65535)
{
- skb->sk = NULL;
+ printk(KERN_INFO "Oversized packet received from %s\n",in_ntoa(iph->saddr));
frag_kfree_skb(skb, FREE_READ);
ip_statistics.IpReasmFails++;
return NULL;
@@ -550,7 +561,7 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
else
qp->fragments = tmp->next;
- if (tfp->next != NULL)
+ if (tmp->next != NULL)
tmp->next->prev = tmp->prev;
next=tfp; /* We have killed the original next frame */
@@ -573,7 +584,6 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
if (!tfp)
{
- skb->sk = NULL;
frag_kfree_skb(skb, FREE_READ);
return NULL;
}
@@ -600,200 +610,3 @@ struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device
}
return(NULL);
}
-
-
-/*
- * This IP datagram is too large to be sent in one piece. Break it up into
- * smaller pieces (each of size equal to the MAC header plus IP header plus
- * a block of the data of the original IP data part) that will yet fit in a
- * single device frame, and queue such a frame for sending by calling the
- * ip_queue_xmit(). Note that this is recursion, and bad things will happen
- * if this function causes a loop...
- *
- * Yes this is inefficient, feel free to submit a quicker one.
- *
- */
-
-void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
-{
- struct iphdr *iph;
- unsigned char *raw;
- unsigned char *ptr;
- struct sk_buff *skb2;
- int left, mtu, hlen, len;
- int offset;
-
- unsigned short true_hard_header_len;
-
- /*
- * Point into the IP datagram header.
- */
-
- raw = skb->data;
-#if 0
- iph = (struct iphdr *) (raw + dev->hard_header_len);
- skb->ip_hdr = iph;
-#else
- iph = skb->ip_hdr;
-#endif
-
- /*
- * Calculate the length of the link-layer header appended to
- * the IP-packet.
- */
- true_hard_header_len = ((unsigned char *)iph) - raw;
-
- /*
- * Setup starting values.
- */
-
- hlen = iph->ihl * 4;
- left = ntohs(iph->tot_len) - hlen; /* Space per frame */
- hlen += true_hard_header_len;
- mtu = (dev->mtu - hlen); /* Size of data space */
- ptr = (raw + hlen); /* Where to start from */
-
- /*
- * Check for any "DF" flag. [DF means do not fragment]
- */
-
- if (iph->frag_off & htons(IP_DF))
- {
- ip_statistics.IpFragFails++;
- NETDEBUG(printk("ip_queue_xmit: frag needed\n"));
- return;
- }
-
- /*
- * The protocol doesn't seem to say what to do in the case that the
- * frame + options doesn't fit the mtu. As it used to fall down dead
- * in this case we were fortunate it didn't happen
- */
-
- if(mtu<8)
- {
- /* It's wrong but it's better than nothing */
- icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);
- ip_statistics.IpFragFails++;
- return;
- }
-
- /*
- * Fragment the datagram.
- */
-
- /*
- * The initial offset is 0 for a complete frame. When
- * fragmenting fragments it's wherever this one starts.
- */
-
- if (is_frag & 2)
- offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
- else
- offset = 0;
-
-
- /*
- * Keep copying data until we run out.
- */
-
- while(left > 0)
- {
- len = left;
- /* IF: it doesn't fit, use 'mtu' - the data space left */
- if (len > mtu)
- len = mtu;
- /* IF: we are not sending upto and including the packet end
- then align the next start on an eight byte boundary */
- if (len < left)
- {
- len/=8;
- len*=8;
- }
- /*
- * Allocate buffer.
- */
-
- if ((skb2 = alloc_skb(len + hlen+15,GFP_ATOMIC)) == NULL)
- {
- NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
- ip_statistics.IpFragFails++;
- return;
- }
-
- /*
- * Set up data on packet
- */
-
- skb2->arp = skb->arp;
- skb2->protocol = htons(ETH_P_IP); /* Atleast PPP needs this */
-#if 0
- if(skb->free==0)
- printk(KERN_ERR "IP fragmenter: BUG free!=1 in fragmenter\n");
-#endif
- skb2->free = 1;
- skb_put(skb2,len + hlen);
- skb2->h.raw=(char *) skb2->data;
- /*
- * Charge the memory for the fragment to any owner
- * it might possess
- */
-
- if (sk)
- {
- atomic_add(skb2->truesize, &sk->wmem_alloc);
- skb2->sk=sk;
- }
- skb2->raddr = skb->raddr; /* For rebuild_header - must be here */
-
- /*
- * Copy the packet header into the new buffer.
- */
-
- memcpy(skb2->h.raw, raw, hlen);
-
- /*
- * Copy a block of the IP datagram.
- */
- memcpy(skb2->h.raw + hlen, ptr, len);
- left -= len;
-
- skb2->h.raw+=true_hard_header_len;
-
- /*
- * Fill in the new header fields.
- */
- iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
- iph->frag_off = htons((offset >> 3));
- skb2->ip_hdr = iph;
-
- /* ANK: dirty, but effective trick. Upgrade options only if
- * the segment to be fragmented was THE FIRST (otherwise,
- * options are already fixed) and make it ONCE
- * on the initial skb, so that all the following fragments
- * will inherit fixed options.
- */
- if (offset == 0)
- ip_options_fragment(skb);
-
- /*
- * Added AC : If we are fragmenting a fragment that's not the
- * last fragment then keep MF on each bit
- */
- if (left > 0 || (is_frag & 1))
- iph->frag_off |= htons(IP_MF);
- ptr += len;
- offset += len;
-
- /*
- * Put this fragment into the sending queue.
- */
-
- ip_statistics.IpFragCreates++;
-
- ip_queue_xmit(sk, dev, skb2, 2);
- }
- ip_statistics.IpFragOKs++;
-}
-
-
diff --git a/net/ipv4/ip_fw.c b/net/ipv4/ip_fw.c
index f0ae86e36..e516a2baa 100644
--- a/net/ipv4/ip_fw.c
+++ b/net/ipv4/ip_fw.c
@@ -153,7 +153,7 @@ struct ip_fw *ip_acct_chain;
static struct ip_fw **chains[] =
{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain};
#endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
-
+
#ifdef CONFIG_IP_FIREWALL
int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
int ip_fw_in_policy=IP_FW_F_ACCEPT;
@@ -209,9 +209,9 @@ extern inline int port_match(unsigned short *portptr,int nports,unsigned short p
int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode)
{
struct ip_fw *f;
- struct tcphdr *tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl);
- struct udphdr *udp=(struct udphdr *)((unsigned long *)ip+ip->ihl);
- struct icmphdr *icmp=(struct icmphdr *)((unsigned long *)ip+ip->ihl);
+ struct tcphdr *tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+ struct udphdr *udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+ struct icmphdr *icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
__u32 src, dst;
__u16 src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
unsigned short f_prt=0, prt;
@@ -571,7 +571,7 @@ int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_
answer = FW_BLOCK;
#ifdef CONFIG_IP_FIREWALL_NETLINK
- if(answer == FW_REJECT || answer == FW_BLOCK)
+ if((policy&IP_FW_F_PRN) && (answer == FW_REJECT || answer == FW_BLOCK))
{
struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
if(skb)
@@ -1196,17 +1196,17 @@ static int ip_fw_fwd_procinfo(char *buffer, char **start, off_t offset,
* Interface to the generic firewall chains.
*/
-int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_input_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
{
return ip_fw_chk(phdr, dev, arg, ip_fw_in_chain, ip_fw_in_policy, IP_FW_MODE_FW);
}
-int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_output_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
{
return ip_fw_chk(phdr, dev, arg, ip_fw_out_chain, ip_fw_out_policy, IP_FW_MODE_FW);
}
-int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg)
+int ipfw_forward_check(struct firewall_ops *this, int pf, struct device *dev, void *phdr, void *arg, struct sk_buff **pskb)
{
return ip_fw_chk(phdr, dev, arg, ip_fw_fwd_chain, ip_fw_fwd_policy, IP_FW_MODE_FW);
}
@@ -1297,6 +1297,7 @@ static struct proc_dir_entry proc_net_ipfwfwd = {
#endif
#endif
+
void ip_fw_init(void)
{
#ifdef CONFIG_PROC_FS
@@ -1308,24 +1309,19 @@ void ip_fw_init(void)
if(register_firewall(PF_INET,&ipfw_ops)<0)
panic("Unable to register IP firewall.\n");
-
#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_ipfwin);
proc_net_register(&proc_net_ipfwout);
proc_net_register(&proc_net_ipfwfwd);
#endif
#endif
-#ifdef CONFIG_IP_MASQUERADE
-
- /*
- * Initialize masquerading.
- */
-
- ip_masq_init();
-#endif
-
+
#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
/* Register for device up/down reports */
register_netdevice_notifier(&ipfw_dev_notifier);
#endif
+
+#ifdef CONFIG_IP_FIREWALL_NETLINK
+ netlink_attach(NETLINK_FIREWALL, netlink_donothing); /* XXX */
+#endif /* CONFIG_IP_FIREWALL_NETLINK */
}
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 3e286c4d7..2642832e3 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -109,8 +109,6 @@
* output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause
* fragmentation anyway.
*
- * FIXME: copy frag 0 iph to qp->iph
- *
* This program 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
@@ -156,24 +154,15 @@
#include <linux/firewall.h>
#include <linux/mroute.h>
#include <net/netlink.h>
-#ifdef CONFIG_NET_ALIAS
#include <linux/net_alias.h>
-#endif
-
-extern int last_retran;
-extern void sort_send(struct sock *sk);
-
-#define min(a,b) ((a)<(b)?(a):(b))
+#include <linux/ipsec.h>
/*
* SNMP management statistics
*/
-#ifdef CONFIG_IP_FORWARD
-struct ip_mib ip_statistics={1,64,}; /* Forwarding=Yes, Default TTL=64 */
-#else
-struct ip_mib ip_statistics={2,64,}; /* Forwarding=No, Default TTL=64 */
-#endif
+struct ip_mib ip_statistics={2,IPDEFTTL,}; /* Forwarding=No, Default TTL=64 */
+
/*
* Handle the issuing of an ioctl() request
@@ -190,548 +179,291 @@ int ip_ioctl(struct sock *sk, int cmd, unsigned long arg)
}
}
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-/*
- * Check the packet against our socket administration to see
- * if it is related to a connection on our system.
- * Needed for transparent proxying.
- */
-int ip_chksock(struct sk_buff *skb)
-{
- switch (skb->h.iph->protocol) {
- case IPPROTO_ICMP:
- return icmp_chkaddr(skb);
- case IPPROTO_TCP:
- return tcp_chkaddr(skb);
- case IPPROTO_UDP:
- return udp_chkaddr(skb);
- default:
- return 0;
- }
-}
+#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
+#define CONFIG_IP_ALWAYS_DEFRAG 1
#endif
-/*
- * This function receives all incoming IP datagrams.
- *
- * On entry skb->data points to the start of the IP header and
- * the MAC header has been removed.
- */
-
-int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int ip_local_deliver(struct sk_buff *skb)
{
- struct iphdr *iph = skb->h.iph;
+ struct iphdr *iph = skb->nh.iph;
+#ifdef CONFIG_IP_MASQUERADE
+ struct device *dev = skb->dev;
+#endif
+ struct inet_protocol *ipprot;
struct sock *raw_sk=NULL;
unsigned char hash;
- unsigned char flag = 0;
- struct inet_protocol *ipprot;
- int brd=IS_MYADDR;
- struct options * opt = NULL;
- int is_frag=0;
- __u32 daddr;
-
-#ifdef CONFIG_FIREWALL
- int fwres;
- __u16 rport;
-#endif
-#ifdef CONFIG_IP_MROUTE
- int mroute_pkt=0;
-#endif
-
-#ifdef CONFIG_NET_IPV6
- /*
- * Intercept IPv6 frames. We dump ST-II and invalid types just below..
- */
-
- if(iph->version == 6)
- return ipv6_rcv(skb,dev,pt);
-#endif
-
- ip_statistics.IpInReceives++;
+ int flag = 0;
+#ifndef CONFIG_IP_ALWAYS_DEFRAG
/*
- * Account for the packet (even if the packet is
- * not accepted by the firewall!).
+ * Reassemble IP fragments.
*/
-#ifdef CONFIG_IP_ACCT
- ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
-#endif
+ if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ skb = ip_defrag(skb);
+ if (!skb)
+ return 0;
+ iph = skb->nh.iph;
+ }
+#endif
+#ifdef CONFIG_IP_MASQUERADE
/*
- * Tag the ip header of this packet so we can find it
+ * Do we need to de-masquerade this packet?
*/
+ {
+ int ret = ip_fw_demasquerade(&skb, dev);
+ if (ret < 0) {
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
- skb->ip_hdr = iph;
+ if (ret) {
+ iph=skb->nh.iph;
+ IPCB(skb)->flags |= IPSKB_MASQUERADED;
+ dst_release(skb->dst);
+ skb->dst = NULL;
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) {
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+ return skb->dst->input(skb);
+ }
+ }
+#endif
- /*
- * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
- * RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING].
- *
- * Is the datagram acceptable?
- *
- * 1. Length at least the size of an ip header
- * 2. Version of 4
- * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
- * 4. Doesn't have a bogus length
- * (5. We ought to check for IP multicast addresses and undefined types.. does this matter ?)
+ /*
+ * Point into the IP datagram, just past the header.
*/
- if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
- || skb->len < ntohs(iph->tot_len))
- {
- ip_statistics.IpInHdrErrors++;
- kfree_skb(skb, FREE_WRITE);
- return(0);
- }
+ skb->h.raw = skb->nh.raw + iph->ihl*4;
/*
- * Our transport medium may have padded the buffer out. Now we know it
- * is IP we can trim to the true length of the frame.
- * Note this now means skb->len holds ntohs(iph->tot_len).
+ * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
+ *
+ * RFC 1122: SHOULD pass TOS value up to the transport layer.
*/
+
+ /* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+ hash = iph->protocol & (MAX_INET_PROTOS - 1);
- skb_trim(skb,ntohs(iph->tot_len));
-
- /*
- * Try to select closest <src,dst> alias device, if any.
- * net_alias_dev_rcv_sel32 returns main device if it
- * fails to found other.
+ /*
+ * If there maybe a raw socket we must check - if not we don't care less
*/
-
-#ifdef CONFIG_NET_ALIAS
- if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev))
- skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
-#endif
-
- if (iph->ihl > 5)
- {
- skb->ip_summed = 0;
- if (ip_options_compile(NULL, skb))
- return(0);
- opt = (struct options*)skb->proto_priv;
-#ifdef CONFIG_IP_NOSR
- if (opt->srr)
- {
- kfree_skb(skb, FREE_READ);
- return -EINVAL;
+
+ if((raw_sk = raw_v4_htable[hash]) != NULL) {
+ struct sock *sknext = NULL;
+ struct sk_buff *skb1;
+ raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+ if(raw_sk) { /* Any raw sockets */
+ do {
+ /* Find the next */
+ sknext = raw_v4_lookup(raw_sk->next, iph->protocol,
+ iph->saddr, iph->daddr);
+ if(sknext)
+ skb1 = skb_clone(skb, GFP_ATOMIC);
+ else
+ break; /* One pending raw socket left */
+ if(skb1)
+ {
+ if(ipsec_sk_policy(raw_sk,skb1))
+ raw_rcv(raw_sk, skb1);
+ else
+ kfree_skb(skb1, FREE_WRITE);
+ }
+ raw_sk = sknext;
+ } while(raw_sk!=NULL);
+
+ /* Here either raw_sk is the last raw socket, or NULL if
+ * none. We deliver to the last raw socket AFTER the
+ * protocol checks as it avoids a surplus copy.
+ */
}
-#endif
}
-#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
-#define CONFIG_IP_ALWAYS_DEFRAG 1
-#endif
-#ifdef CONFIG_IP_ALWAYS_DEFRAG
/*
- * Defragment all incoming traffic before even looking at it.
- * If you have forwarding enabled, this makes the system a
- * defragmenting router. Not a common thing.
- * You probably DON'T want to enable this unless you have to.
- * You NEED to use this if you want to use transparent proxying,
- * otherwise, we can't vouch for your sanity.
+ * skb->h.raw now points at the protocol beyond the IP header.
*/
-
- /*
- * See if the frame is fragmented.
- */
-
- if(iph->frag_off)
+
+ for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
{
- if (iph->frag_off & htons(IP_MF))
- is_frag|=IPFWD_FRAGMENT;
+ struct sk_buff *skb2;
+
+ if (ipprot->protocol != iph->protocol)
+ continue;
/*
- * Last fragment ?
+ * See if we need to make a copy of it. This will
+ * only be set if more than one protocol wants it.
+ * and then not for the last one. If there is a pending
+ * raw delivery wait for that
*/
- if (iph->frag_off & htons(IP_OFFSET))
- is_frag|=IPFWD_LASTFRAG;
-
+ if (ipprot->copy || raw_sk)
+ {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if(skb2==NULL)
+ continue;
+ }
+ else
+ {
+ skb2 = skb;
+ }
+ flag = 1;
+
/*
- * Reassemble IP fragments.
+ * Pass on the datagram to each protocol that wants it,
+ * based on the datagram protocol. We should really
+ * check the protocol handler's return values here...
*/
- if(is_frag)
- {
- /* Defragment. Obtain the complete packet if there is one */
- skb=ip_defrag(iph,skb,dev);
- if(skb==NULL)
- return 0;
- skb->dev = dev;
- iph=skb->h.iph;
- is_frag = 0;
- /*
- * When the reassembled packet gets forwarded, the ip
- * header checksum should be correct.
- * For better performance, this should actually only
- * be done in that particular case, i.e. set a flag
- * here and calculate the checksum in ip_forward.
- */
- ip_send_check(iph);
- }
+ ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4));
}
-#endif
/*
- * See if the firewall wants to dispose of the packet.
+ * All protocols checked.
+ * If this packet was a broadcast, we may *not* reply to it, since that
+ * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+ * ICMP reply messages get queued up for transmission...)
*/
-
-#ifdef CONFIG_FIREWALL
- if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT)
+ if(raw_sk!=NULL) /* Shift to last raw user */
{
- if(fwres==FW_REJECT)
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
+ if(ipsec_sk_policy(raw_sk, skb))
+ raw_rcv(raw_sk, skb);
+ else
+ kfree_skb(skb, FREE_WRITE);
+ }
+ else if (!flag) /* Free and report errors */
+ {
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);
kfree_skb(skb, FREE_WRITE);
- return 0;
}
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- if (fwres==FW_REDIRECT)
- skb->redirport = rport;
- else
-#endif
- skb->redirport = 0;
-#endif
-
-#ifndef CONFIG_IP_ALWAYS_DEFRAG
- /*
- * Remember if the frame is fragmented.
+ return(0);
+}
+
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct iphdr *iph = skb->nh.iph;
+ struct ip_options * opt = NULL;
+ int err;
+
+#ifdef CONFIG_NET_IPV6
+ /*
+ * Intercept IPv6 frames. We dump ST-II and invalid types just below..
*/
- if(iph->frag_off)
- {
- if (iph->frag_off & htons(IP_MF))
- is_frag|=IPFWD_FRAGMENT;
- /*
- * Last fragment ?
- */
-
- if (iph->frag_off & htons(IP_OFFSET))
- is_frag|=IPFWD_LASTFRAG;
- }
-
+ if(iph->version == 6)
+ return ipv6_rcv(skb,dev,pt);
#endif
+
/*
- * Do any IP forwarding required. chk_addr() is expensive -- avoid it someday.
- *
- * This is inefficient. While finding out if it is for us we could also compute
- * the routing table entry. This is where the great unified cache theory comes
- * in as and when someone implements it
- *
- * For most hosts over 99% of packets match the first conditional
- * and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
- * function entry.
- */
- daddr = iph->daddr;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /*
- * ip_chksock adds still more overhead for forwarded traffic...
+ * When interface is in promisc. mode, drop all the crap
+ * that it receives, do not truing to analyse it.
*/
- if ( iph->daddr == skb->dev->pa_addr || skb->redirport || (brd = ip_chk_addr(iph->daddr)) != 0 || ip_chksock(skb))
-#else
- if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
-#endif
- {
- if (opt && opt->srr)
- {
- int srrspace, srrptr;
- __u32 nexthop;
- unsigned char * optptr = ((unsigned char *)iph) + opt->srr;
-
- if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST)
- {
- kfree_skb(skb, FREE_WRITE);
- return 0;
- }
-
- for ( srrptr=optptr[2], srrspace = optptr[1];
- srrptr <= srrspace;
- srrptr += 4
- )
- {
- int brd2;
- if (srrptr + 3 > srrspace)
- {
- icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2,
- skb->dev);
- kfree_skb(skb, FREE_WRITE);
- return 0;
- }
- memcpy(&nexthop, &optptr[srrptr-1], 4);
- if ((brd2 = ip_chk_addr(nexthop)) == 0)
- break;
- if (brd2 != IS_MYADDR)
- {
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop;
- /*
- * ANK: should we implement weak tunneling of multicasts?
- * Are they obsolete? DVMRP specs (RFC-1075) is old enough...
- * [They are obsolete]
- */
- kfree_skb(skb, FREE_WRITE);
- return -EINVAL;
- }
- memcpy(&daddr, &optptr[srrptr-1], 4);
- }
- if (srrptr <= srrspace)
- {
- opt->srr_is_hit = 1;
- opt->is_changed = 1;
-#ifdef CONFIG_IP_FORWARD
- if (ip_forward(skb, dev, is_frag, nexthop))
- kfree_skb(skb, FREE_WRITE);
-#else
- ip_statistics.IpInAddrErrors++;
- kfree_skb(skb, FREE_WRITE);
-#endif
- return 0;
- }
- }
-
-#ifdef CONFIG_IP_MULTICAST
- if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
- {
- /*
- * Check it is for one of our groups
- */
- struct ip_mc_list *ip_mc=dev->ip_mc_list;
- do
- {
- if(ip_mc==NULL)
- {
- kfree_skb(skb, FREE_WRITE);
- return 0;
- }
- if(ip_mc->multiaddr==iph->daddr)
- break;
- ip_mc=ip_mc->next;
- }
- while(1);
- }
-#endif
-
-#ifndef CONFIG_IP_ALWAYS_DEFRAG
- /*
- * Reassemble IP fragments.
- */
+ ip_statistics.IpInReceives++;
- if(is_frag)
- {
- /* Defragment. Obtain the complete packet if there is one */
- skb=ip_defrag(iph,skb,dev);
- if(skb==NULL)
- return 0;
- skb->dev = dev;
- iph=skb->h.iph;
- }
+ /*
+ * Account for the packet (even if the packet is
+ * not accepted by the firewall!).
+ */
-#endif
+#ifdef CONFIG_IP_ACCT
+ ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
+#endif
-#ifdef CONFIG_IP_MASQUERADE
- /*
- * Do we need to de-masquerade this packet?
- */
- {
- int ret = ip_fw_demasquerade(&skb,dev);
- if (ret < 0) {
- kfree_skb(skb, FREE_WRITE);
- return 0;
- }
+ /*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+ * 4. Doesn't have a bogus length
+ */
- if (ret)
- {
- struct iphdr *iph=skb->h.iph;
- if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr))
- kfree_skb(skb, FREE_WRITE);
- return 0;
- }
- }
-#endif
+ if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4
+ || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
+ || skb->len < ntohs(iph->tot_len))
+ goto inhdr_error;
- /*
- * Point into the IP datagram, just past the header.
- */
+ /*
+ * Our transport medium may have padded the buffer out. Now we know it
+ * is IP we can trim to the true length of the frame.
+ * Note this now means skb->len holds ntohs(iph->tot_len).
+ */
- skb->ip_hdr = iph;
- skb->h.raw += iph->ihl*4;
+ skb_trim(skb, ntohs(iph->tot_len));
-#ifdef CONFIG_IP_MROUTE
- /*
- * Check the state on multicast routing (multicast and not 224.0.0.z)
- */
-
- if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000))
- mroute_pkt=1;
+ if (skb->dst == NULL) {
+ err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev);
+ if (err)
+ goto drop;
+ }
+#ifdef CONFIG_IP_ALWAYS_DEFRAG
+ if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+ skb = ip_defrag(skb);
+ if (!skb)
+ return 0;
+ iph = skb->nh.iph;
+ ip_send_check(iph);
+ }
#endif
- /*
- * Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
- *
- * RFC 1122: SHOULD pass TOS value up to the transport layer.
- */
-
- hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
-
- /*
- * If there maybe a raw socket we must check - if not we don't care less
- */
-
- if((raw_sk=raw_prot.sock_array[hash])!=NULL)
- {
- struct sock *sknext=NULL;
- struct sk_buff *skb1;
- raw_sk=get_sock_raw(raw_sk, iph->protocol, iph->saddr, iph->daddr);
- if(raw_sk) /* Any raw sockets */
- {
- do
- {
- /* Find the next */
- sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
- if(sknext)
- skb1=skb_clone(skb, GFP_ATOMIC);
- else
- break; /* One pending raw socket left */
- if(skb1)
- raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr);
- raw_sk=sknext;
- }
- while(raw_sk!=NULL);
-
- /*
- * Here either raw_sk is the last raw socket, or NULL if none
- */
-
- /*
- * We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy
- */
- }
- }
-
- /*
- * skb->h.raw now points at the protocol beyond the IP header.
- */
-
- hash = iph->protocol & (MAX_INET_PROTOS -1);
- for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
- {
- struct sk_buff *skb2;
-
- if (ipprot->protocol != iph->protocol)
- continue;
- /*
- * See if we need to make a copy of it. This will
- * only be set if more than one protocol wants it.
- * and then not for the last one. If there is a pending
- * raw delivery wait for that
- */
-
-#ifdef CONFIG_IP_MROUTE
- if (ipprot->copy || raw_sk || mroute_pkt)
-#else
- if (ipprot->copy || raw_sk)
-#endif
- {
- skb2 = skb_clone(skb, GFP_ATOMIC);
- if(skb2==NULL)
- continue;
- }
- else
- {
- skb2 = skb;
- }
- flag = 1;
-
- /*
- * Pass on the datagram to each protocol that wants it,
- * based on the datagram protocol. We should really
- * check the protocol handler's return values here...
- */
-
- ipprot->handler(skb2, dev, opt, daddr,
- (ntohs(iph->tot_len) - (iph->ihl * 4)),
- iph->saddr, 0, ipprot);
- }
-
- /*
- * All protocols checked.
- * If this packet was a broadcast, we may *not* reply to it, since that
- * causes (proven, grin) ARP storms and a leakage of memory (i.e. all
- * ICMP reply messages get queued up for transmission...)
- */
-#ifdef CONFIG_IP_MROUTE
- /*
- * Forward the last copy to the multicast router. If
- * there is a pending raw delivery however make a copy
- * and forward that.
- */
-
- if(mroute_pkt)
- {
- flag=1;
- if(raw_sk==NULL)
- ipmr_forward(skb, is_frag);
- else
- {
- struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
- if(skb2)
- {
- skb2->free=1;
- ipmr_forward(skb2, is_frag);
- }
+ if (iph->ihl > 5) {
+ skb->ip_summed = 0;
+ if (ip_options_compile(NULL, skb))
+ goto inhdr_error;
+
+ opt = &(IPCB(skb)->opt);
+ if (opt->srr) {
+ if (!ipv4_config.source_route) {
+ if (ipv4_config.log_martians)
+ printk(KERN_INFO "source route option %08lx -> %08lx\n",
+ ntohl(iph->saddr), ntohl(iph->daddr));
+ goto drop;
}
+ if (RT_LOCALADDR(((struct rtable*)skb->dst)->rt_flags) &&
+ ip_options_rcv_srr(skb))
+ goto drop;
}
-#endif
-
- if(raw_sk!=NULL) /* Shift to last raw user */
- raw_rcv(raw_sk, skb, dev, iph->saddr, daddr);
- else if (!flag) /* Free and report errors */
- {
- if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);
- kfree_skb(skb, FREE_WRITE);
- }
-
- return(0);
}
-
- /*
- * Do any unicast IP forwarding required.
- */
/*
- * Don't forward multicast or broadcast frames.
+ * See if the firewall wants to dispose of the packet.
*/
+
+#ifdef CONFIG_FIREWALL
+ {
+ int fwres;
+ u16 rport;
+
+ if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport, &skb))<FW_ACCEPT) {
+ if (fwres==FW_REJECT)
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+ goto drop;
+ }
- if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
- {
- kfree_skb(skb,FREE_WRITE);
- return 0;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (fwres==FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0)
+ return ip_local_deliver(skb);
+#endif
}
+#endif
- /*
- * The packet is for another target. Forward the frame
- */
+ return skb->dst->input(skb);
-#ifdef CONFIG_IP_FORWARD
- if (opt && opt->is_strictroute)
- {
- icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
- kfree_skb(skb, FREE_WRITE);
- return -1;
- }
- if (ip_forward(skb, dev, is_frag, iph->daddr))
- kfree_skb(skb, FREE_WRITE);
-#else
-/* printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
- iph->saddr,iph->daddr);*/
- ip_statistics.IpInAddrErrors++;
- kfree_skb(skb, FREE_WRITE);
-#endif
- return(0);
+inhdr_error:
+ ip_statistics.IpInHdrErrors++;
+drop:
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
}
-
diff --git a/net/ipv4/ip_masq.c b/net/ipv4/ip_masq.c
index 67e10979f..c5976614e 100644
--- a/net/ipv4/ip_masq.c
+++ b/net/ipv4/ip_masq.c
@@ -16,7 +16,7 @@
* Nigel Metheringham : Checksum checking of masqueraded data
* Nigel Metheringham : Better handling of timeouts of TCP conns
*
- *
+ *
*/
#include <linux/config.h>
@@ -66,7 +66,7 @@ static __u16 masq_port = PORT_MASQ_BEGIN;
*
* Greater values could lower MASQ_EXPIRATION setting as a way to
* manage 'masq_entries resource'.
- *
+ *
*/
int ip_masq_free_ports[2] = {
@@ -74,15 +74,11 @@ int ip_masq_free_ports[2] = {
PORT_MASQ_END - PORT_MASQ_BEGIN /* TCP */
};
-static struct symbol_table ip_masq_syms = {
-#include <linux/symtab_begin.h>
- X(ip_masq_new),
- X(ip_masq_set_expire),
- X(ip_masq_free_ports),
- X(ip_masq_expire),
- X(ip_masq_out_get_2),
-#include <linux/symtab_end.h>
-};
+EXPORT_SYMBOL(ip_masq_new);
+EXPORT_SYMBOL(ip_masq_set_expire);
+EXPORT_SYMBOL(ip_masq_free_ports);
+EXPORT_SYMBOL(ip_masq_expire);
+EXPORT_SYMBOL(ip_masq_out_get_2);
/*
* 2 ip_masq hash tables: for input and output pkts lookups.
@@ -126,7 +122,7 @@ ip_masq_hash(struct ip_masq *ms)
unsigned hash;
if (ms->flags & IP_MASQ_F_HASHED) {
- printk("ip_masq_hash(): request for already hashed\n");
+ printk(KERN_INFO "ip_masq_hash(): request for already hashed\n");
return 0;
}
/*
@@ -159,7 +155,7 @@ static __inline__ int ip_masq_unhash(struct ip_masq *ms)
unsigned hash;
struct ip_masq ** ms_p;
if (!(ms->flags & IP_MASQ_F_HASHED)) {
- printk("ip_masq_unhash(): request for unhash flagged\n");
+ printk(KERN_INFO "ip_masq_unhash(): request for unhash flagged\n");
return 0;
}
/*
@@ -323,7 +319,7 @@ static void masq_expire(unsigned long data)
masq_proto_name(ms->protocol),
ntohl(ms->saddr),ntohs(ms->sport));
#endif
-
+
save_flags(flags);
cli();
@@ -353,14 +349,14 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
if (*free_ports_p == 0) {
if (++n_fails < 5)
- printk("ip_masq_new(proto=%s): no free ports.\n",
+ printk(KERN_ERR "ip_masq_new(proto=%s): no free ports.\n",
masq_proto_name(proto));
return NULL;
}
ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC);
if (ms == NULL) {
if (++n_fails < 5)
- printk("ip_masq_new(proto=%s): no memory available.\n",
+ printk(KERN_ERR "ip_masq_new(proto=%s): no memory available.\n",
masq_proto_name(proto));
return NULL;
}
@@ -378,49 +374,49 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
if (proto == IPPROTO_UDP)
ms->flags |= IP_MASQ_F_NO_DADDR;
-
+
/* get masq address from rif */
ms->maddr = dev->pa_addr;
for (ports_tried = 0; ports_tried < *free_ports_p; ports_tried++){
save_flags(flags);
cli();
-
+
/*
* Try the next available port number
*/
-
+
ms->mport = htons(masq_port++);
if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN;
-
+
restore_flags(flags);
-
+
/*
* lookup to find out if this port is used.
*/
-
+
mst = ip_masq_getbym(proto, ms->maddr, ms->mport);
if (mst == NULL) {
save_flags(flags);
cli();
-
+
if (*free_ports_p == 0) {
restore_flags(flags);
break;
}
(*free_ports_p)--;
ip_masq_hash(ms);
-
+
restore_flags(flags);
-
+
ip_masq_bind_app(ms);
n_fails = 0;
return ms;
}
}
-
+
if (++n_fails < 5)
- printk("ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n",
+ printk(KERN_ERR "ip_masq_new(proto=%s): could not get free masq entry (free=%d).\n",
masq_proto_name(ms->protocol), *free_ports_p);
kfree_s(ms, sizeof(*ms));
return NULL;
@@ -451,11 +447,11 @@ static void recalc_check(struct udphdr *uh, __u32 saddr,
if(uh->check==0)
uh->check=0xFFFF;
}
-
+
int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
{
struct sk_buff *skb=*skb_ptr;
- struct iphdr *iph = skb->h.iph;
+ struct iphdr *iph = skb->nh.iph;
__u16 *portptr;
struct ip_masq *ms;
int size;
@@ -489,7 +485,7 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
/*
* Nope, not found, create a new entry for it
*/
-
+
if (ms==NULL)
{
ms = ip_masq_new(dev, iph->protocol,
@@ -503,8 +499,8 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
/*
* Change the fragments origin
*/
-
- size = skb->len - ((unsigned char *)portptr - skb->h.raw);
+
+ size = skb->len - ((unsigned char *)portptr - skb->nh.raw);
/*
* Set iph addr and port from ip_masq obj.
*/
@@ -521,15 +517,15 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
* skb has possibly changed, update pointers.
*/
skb = *skb_ptr;
- iph = skb->h.iph;
+ iph = skb->nh.iph;
portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
- size = skb->len - ((unsigned char *)portptr-skb->h.raw);
+ size = skb->len - ((unsigned char *)portptr-skb->nh.raw);
}
/*
* Adjust packet accordingly to protocol
*/
-
+
if (iph->protocol==IPPROTO_UDP)
{
timeout = ip_masq_expire->udp_timeout;
@@ -566,8 +562,10 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
else timeout = ip_masq_expire->tcp_timeout;
skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0);
- tcp_v4_check(th, size, iph->saddr, iph->daddr,
- skb->csum);
+ th->check = 0;
+ th->check = tcp_v4_check(th, size, iph->saddr, iph->daddr,
+ csum_partial((char *)th, sizeof(*th),
+ skb->csum));
}
ip_masq_set_expire(ms, timeout);
ip_send_check(iph);
@@ -590,7 +588,7 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
{
struct sk_buff *skb = *skb_p;
- struct iphdr *iph = skb->h.iph;
+ struct iphdr *iph = skb->nh.iph;
struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
struct iphdr *ciph; /* The ip header contained within the ICMP */
__u16 *pptr; /* port numbers from TCP/UDP contained header */
@@ -603,7 +601,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
ntohl(iph->saddr), ntohl(iph->daddr));
#endif
- /*
+ /*
* Work through seeing if this is for us.
* These checks are supposed to be in an order that
* means easy things are checked first to speed up
@@ -623,8 +621,8 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP))
return 0;
- /*
- * Find the ports involved - this packet was
+ /*
+ * Find the ports involved - this packet was
* incoming so the ports are right way round
* (but reversed relative to outer IP header!)
*/
@@ -634,10 +632,10 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
return 0;
/* Ensure the checksum is correct */
- if (ip_compute_csum((unsigned char *) icmph, len))
+ if (ip_compute_csum((unsigned char *) icmph, len))
{
/* Failed checksum! */
- printk(KERN_INFO "MASQ: forward ICMP: failed checksum from %s!\n",
+ printk(KERN_INFO "MASQ: forward ICMP: failed checksum from %s!\n",
in_ntoa(iph->saddr));
return(-1);
}
@@ -658,11 +656,11 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
/* First change the source IP address, and recalc checksum */
iph->saddr = ms->maddr;
ip_send_check(iph);
-
+
/* Now change the *dest* address in the contained IP */
ciph->daddr = ms->maddr;
ip_send_check(ciph);
-
+
/* the TCP/UDP dest port - cannot redo check */
pptr[1] = ms->mport;
@@ -689,7 +687,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
{
struct sk_buff *skb = *skb_p;
- struct iphdr *iph = skb->h.iph;
+ struct iphdr *iph = skb->nh.iph;
struct icmphdr *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
struct iphdr *ciph; /* The ip header contained within the ICMP */
__u16 *pptr; /* port numbers from TCP/UDP contained header */
@@ -714,8 +712,8 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP))
return 0;
- /*
- * Find the ports involved - remember this packet was
+ /*
+ * Find the ports involved - remember this packet was
* *outgoing* so the ports are reversed (and addresses)
*/
pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
@@ -724,10 +722,10 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
return 0;
/* Ensure the checksum is correct */
- if (ip_compute_csum((unsigned char *) icmph, len))
+ if (ip_compute_csum((unsigned char *) icmph, len))
{
/* Failed checksum! */
- printk(KERN_INFO "MASQ: reverse ICMP: failed checksum from %s!\n",
+ printk(KERN_INFO "MASQ: reverse ICMP: failed checksum from %s!\n",
in_ntoa(iph->saddr));
return(-1);
}
@@ -748,11 +746,11 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
/* First change the dest IP address, and recalc checksum */
iph->daddr = ms->saddr;
ip_send_check(iph);
-
+
/* Now change the *source* address in the contained IP */
ciph->saddr = ms->saddr;
ip_send_check(ciph);
-
+
/* the TCP/UDP source port - cannot redo check */
pptr[0] = ms->sport;
@@ -782,7 +780,7 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
{
struct sk_buff *skb = *skb_p;
- struct iphdr *iph = skb->h.iph;
+ struct iphdr *iph = skb->nh.iph;
__u16 *portptr;
struct ip_masq *ms;
unsigned short len;
@@ -804,7 +802,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
/* No UDP checksum */
break;
- switch (skb->ip_summed)
+ switch (skb->ip_summed)
{
case CHECKSUM_NONE:
skb->csum = csum_partial((char *)portptr, len, 0);
@@ -812,7 +810,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
if (csum_tcpudp_magic(iph->saddr, iph->daddr, len,
iph->protocol, skb->csum))
{
- printk(KERN_INFO "MASQ: failed TCP/UDP checksum from %s!\n",
+ printk(KERN_INFO "MASQ: failed TCP/UDP checksum from %s!\n",
in_ntoa(iph->saddr));
return -1;
}
@@ -877,7 +875,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
*/
skb = *skb_p;
- iph = skb->h.iph;
+ iph = skb->nh.iph;
portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
len = ntohs(iph->tot_len) - (iph->ihl * 4);
}
@@ -899,12 +897,13 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
len - sizeof(struct tcphdr), 0);
th = (struct tcphdr *) portptr;
th->check = 0;
+ th->check = tcp_v4_check(th, len, iph->saddr,
+ iph->daddr,
+ csum_partial((char *)th,
+ sizeof(*th),
+ skb->csum));
- tcp_v4_check(th, len, iph->saddr, iph->daddr,
- skb->csum);
-
/* Check if TCP FIN or RST */
-
if (th->fin)
{
ms->flags |= IP_MASQ_F_SAW_FIN_IN;
@@ -913,7 +912,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
{
ms->flags |= IP_MASQ_F_SAW_RST;
}
-
+
/* Now set the timeouts */
if (ms->flags & IP_MASQ_F_SAW_RST)
{
@@ -950,18 +949,18 @@ static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
char temp[129];
int idx = 0;
int len=0;
-
- if (offset < 128)
+
+ if (offset < 128)
{
sprintf(temp,
"Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires (free=%d,%d)",
- ip_masq_free_ports[0], ip_masq_free_ports[1]);
+ ip_masq_free_ports[0], ip_masq_free_ports[1]);
len = sprintf(buffer, "%-127s\n", temp);
}
pos = 128;
save_flags(flags);
cli();
-
+
for(idx = 0; idx < IP_MASQ_TAB_SIZE; idx++)
for(ms = ip_masq_m_tab[idx]; ms ; ms = ms->m_link)
{
@@ -999,24 +998,23 @@ done:
return len;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_net_ipmsqhst = {
PROC_NET_IPMSQHST, 13, "ip_masquerade",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
ip_msqhst_procinfo
};
-#endif
+#endif
/*
* Initialize ip masquerading
*/
int ip_masq_init(void)
{
- register_symtab (&ip_masq_syms);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_ipmsqhst);
-#endif
+#endif
ip_masq_app_init();
return 0;
diff --git a/net/ipv4/ip_masq_app.c b/net/ipv4/ip_masq_app.c
index abe65987e..456888bc1 100644
--- a/net/ipv4/ip_masq_app.c
+++ b/net/ipv4/ip_masq_app.c
@@ -19,7 +19,7 @@
*
* FIXME:
* - ip_masq_skb_replace(): use same skb if space available.
- *
+ *
*/
#include <linux/config.h>
@@ -53,13 +53,9 @@ static __inline__ const char * masq_proto_name(unsigned proto)
#define IP_MASQ_APP_PROTO(type) ( (type>>16) & 0x00ff )
-static struct symbol_table ip_masq_app_syms = {
-#include <linux/symtab_begin.h>
- X(register_ip_masq_app),
- X(unregister_ip_masq_app),
- X(ip_masq_skb_replace),
-#include <linux/symtab_end.h>
-};
+EXPORT_SYMBOL(register_ip_masq_app);
+EXPORT_SYMBOL(unregister_ip_masq_app);
+EXPORT_SYMBOL(ip_masq_skb_replace);
/*
* will hold masq app. hashed list heads
@@ -77,19 +73,19 @@ int register_ip_masq_app(struct ip_masq_app *mapp, unsigned short proto, __u16 p
unsigned long flags;
unsigned hash;
if (!mapp) {
- printk("register_ip_masq_app(): NULL arg\n");
+ printk(KERN_ERR "register_ip_masq_app(): NULL arg\n");
return -EINVAL;
}
mapp->type = IP_MASQ_APP_TYPE(proto, port);
mapp->n_attach = 0;
hash = IP_MASQ_APP_HASH(proto, port);
-
+
save_flags(flags);
cli();
mapp->next = ip_masq_app_base[hash];
- ip_masq_app_base[hash] = mapp;
+ ip_masq_app_base[hash] = mapp;
restore_flags(flags);
-
+
return 0;
}
@@ -103,14 +99,14 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp)
unsigned hash;
unsigned long flags;
if (!mapp) {
- printk("unregister_ip_masq_app(): NULL arg\n");
+ printk(KERN_ERR "unregister_ip_masq_app(): NULL arg\n");
return -EINVAL;
}
/*
* only allow unregistration if it has no attachments
*/
if (mapp->n_attach) {
- printk("unregister_ip_masq_app(): has %d attachments. failed\n",
+ printk(KERN_ERR "unregister_ip_masq_app(): has %d attachments. failed\n",
mapp->n_attach);
return -EINVAL;
}
@@ -124,9 +120,9 @@ int unregister_ip_masq_app(struct ip_masq_app *mapp)
restore_flags(flags);
return 0;
}
-
+
restore_flags(flags);
- printk("unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
+ printk(KERN_ERR "unregister_ip_masq_app(proto=%s,port=%u): not hashed!\n",
masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)), IP_MASQ_APP_PORT(mapp->type));
return -EINVAL;
}
@@ -168,7 +164,7 @@ static __inline__ int ip_masq_app_bind_chg(struct ip_masq_app *mapp, int delta)
n_at = mapp->n_attach + delta;
if (n_at < 0) {
restore_flags(flags);
- printk("ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
+ printk(KERN_ERR "ip_masq_app: tried to set n_attach < 0 for (proto=%s,port==%d) ip_masq_app object.\n",
masq_proto_name(IP_MASQ_APP_PROTO(mapp->type)),
IP_MASQ_APP_PORT(mapp->type));
return -1;
@@ -191,12 +187,12 @@ struct ip_masq_app * ip_masq_bind_app(struct ip_masq *ms)
/*
* don't allow binding if already bound
*/
-
+
if (ms->app != NULL) {
- printk("ip_masq_bind_app() called for already bound object.\n");
+ printk(KERN_ERR "ip_masq_bind_app() called for already bound object.\n");
return ms->app;
}
-
+
ms->app = mapp;
if (mapp->masq_init_1) mapp->masq_init_1(mapp, ms);
ip_masq_app_bind_chg(mapp, +1);
@@ -227,9 +223,9 @@ int ip_masq_unbind_app(struct ip_masq *ms)
static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
{
__u32 seq;
-
+
seq = ntohl(th->seq);
-
+
/*
* Adjust seq with delta-offset for all packets after
* the most recent resized pkt seq and with previous_delta offset
@@ -249,7 +245,7 @@ static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcp
#endif
}
}
-
+
}
@@ -260,15 +256,15 @@ static __inline__ void masq_fix_seq(const struct ip_masq_seq *ms_seq, struct tcp
static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct tcphdr *th)
{
__u32 ack_seq;
-
+
ack_seq=ntohl(th->ack_seq);
-
+
/*
* Adjust ack_seq with delta-offset for
* the packets AFTER most recent resized pkt has caused a shift
* for packets before most recent resized pkt, use previous_delta
*/
-
+
if (ms_seq->delta || ms_seq->previous_delta) {
if(after(ack_seq,ms_seq->init_seq)) {
th->ack_seq = htonl(ack_seq-ms_seq->delta);
@@ -282,7 +278,7 @@ static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct
#endif
}
}
-
+
}
/*
@@ -293,7 +289,7 @@ static __inline__ void masq_fix_ack_seq(const struct ip_masq_seq *ms_seq, struct
static __inline__ void masq_seq_update(struct ip_masq *ms, struct ip_masq_seq *ms_seq, unsigned mflag, __u32 seq, int diff)
{
/* if (diff == 0) return; */
-
+
if ( !(ms->flags & mflag) || after(seq, ms_seq->init_seq))
{
ms_seq->previous_delta=ms_seq->delta;
@@ -316,50 +312,50 @@ int ip_masq_app_pkt_out(struct ip_masq *ms, struct sk_buff **skb_p, struct devic
struct tcphdr *th;
int diff;
__u32 seq;
-
+
/*
* check if application masquerading is bound to
* this ip_masq.
* assumes that once an ip_masq is bound,
* it will not be unbound during its life.
*/
-
+
if ( (mapp = ms->app) == NULL)
return 0;
-
- iph = (*skb_p)->h.iph;
+
+ iph = (*skb_p)->nh.iph;
th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-
+
/*
* Remember seq number in case this pkt gets resized
*/
-
+
seq = ntohl(th->seq);
-
+
/*
* Fix seq stuff if flagged as so.
*/
-
+
if (ms->protocol == IPPROTO_TCP) {
if (ms->flags & IP_MASQ_F_OUT_SEQ)
masq_fix_seq(&ms->out_seq, th);
if (ms->flags & IP_MASQ_F_IN_SEQ)
masq_fix_ack_seq(&ms->in_seq, th);
}
-
+
/*
- * Call private output hook function
+ * Call private output hook function
*/
-
+
if ( mapp->pkt_out == NULL )
return 0;
-
+
diff = mapp->pkt_out(mapp, ms, skb_p, dev);
-
+
/*
* Update ip_masq seq stuff if len has changed.
*/
-
+
if (diff != 0 && ms->protocol == IPPROTO_TCP)
masq_seq_update(ms, &ms->out_seq, IP_MASQ_F_OUT_SEQ, seq, diff);
@@ -379,50 +375,50 @@ int ip_masq_app_pkt_in(struct ip_masq *ms, struct sk_buff **skb_p, struct device
struct tcphdr *th;
int diff;
__u32 seq;
-
+
/*
* check if application masquerading is bound to
* this ip_masq.
* assumes that once an ip_masq is bound,
* it will not be unbound during its life.
*/
-
+
if ( (mapp = ms->app) == NULL)
return 0;
-
- iph = (*skb_p)->h.iph;
+
+ iph = (*skb_p)->nh.iph;
th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
-
+
/*
* Remember seq number in case this pkt gets resized
*/
-
+
seq = ntohl(th->seq);
-
+
/*
* Fix seq stuff if flagged as so.
*/
-
+
if (ms->protocol == IPPROTO_TCP) {
if (ms->flags & IP_MASQ_F_IN_SEQ)
masq_fix_seq(&ms->in_seq, th);
if (ms->flags & IP_MASQ_F_OUT_SEQ)
masq_fix_ack_seq(&ms->out_seq, th);
}
-
+
/*
- * Call private input hook function
+ * Call private input hook function
*/
-
+
if ( mapp->pkt_in == NULL )
return 0;
-
+
diff = mapp->pkt_in(mapp, ms, skb_p, dev);
/*
* Update ip_masq seq stuff if len has changed.
*/
-
+
if (diff != 0 && ms->protocol == IPPROTO_TCP)
masq_seq_update(ms, &ms->in_seq, IP_MASQ_F_IN_SEQ, seq, diff);
@@ -446,7 +442,7 @@ int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int length, in
for (idx=0 ; idx < IP_MASQ_APP_TAB_SIZE; idx++)
for (mapp = ip_masq_app_base[idx]; mapp ; mapp = mapp->next) {
- /*
+ /*
* If you change the length of this sprintf, then all
* the length calculations need fixing too!
* Line length = 40 (3 + 2 + 7 + 1 + 7 + 1 + 2 + 17)
@@ -468,19 +464,19 @@ done:
*start = buffer + begin;
len -= begin;
if (len > length)
- len = length;
+ len = length;
return len;
}
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_net_ip_masq_app = {
PROC_NET_IP_MASQ_APP, 11, "ip_masq_app",
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_net_inode_operations,
ip_masq_app_getinfo
};
-#endif
+#endif
/*
* Initialization routine
@@ -488,11 +484,9 @@ static struct proc_dir_entry proc_net_ip_masq_app = {
int ip_masq_app_init(void)
{
-
- register_symtab (&ip_masq_app_syms);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_ip_masq_app);
-#endif
+#endif
return 0;
}
@@ -531,15 +525,14 @@ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, i
*
* FIXME: move this to core/sbuff.c:skb_grow()
*/
-
+
n_skb = alloc_skb(MAX_HEADER + skb->len + diff, pri);
if (n_skb == NULL) {
- printk("skb_replace(): no room left (from %p)\n",
+ printk(KERN_ERR "skb_replace(): no room left (from %p)\n",
return_address());
return skb;
}
- n_skb->free = skb->free;
skb_reserve(n_skb, MAX_HEADER);
skb_put(n_skb, skb->len + diff);
@@ -549,29 +542,30 @@ static struct sk_buff * skb_replace(struct sk_buff *skb, int pri, char *o_buf, i
* like skb->protocol (PPP driver wants it).
*/
offset = n_skb->data - skb->data;
+ n_skb->nh.raw = skb->nh.raw + offset;
n_skb->h.raw = skb->h.raw + offset;
n_skb->when = skb->when;
n_skb->dev = skb->dev;
n_skb->mac.raw = skb->mac.raw + offset;
- n_skb->ip_hdr = (struct iphdr *)(((char *)skb->ip_hdr)+offset);
n_skb->pkt_type = skb->pkt_type;
n_skb->protocol = skb->protocol;
n_skb->ip_summed = skb->ip_summed;
+ n_skb->dst = dst_clone(skb->dst);
/*
* Copy pkt in new buffer
*/
-
+
memcpy(n_skb->data, skb->data, o_offset);
memcpy(n_skb->data + o_offset, n_buf, n_len);
memcpy(n_skb->data + o_offset + n_len, o_buf + o_len,
skb->len - (o_offset + o_len) );
-
+
/*
* Problem, how to replace the new skb with old one,
* preferably inplace
*/
-
+
kfree_skb(skb, FREE_WRITE);
}
return n_skb;
@@ -590,7 +584,7 @@ struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf,
diff = n_len - o_len;
n_skb = skb_replace(skb, pri, o_buf, o_len, n_buf, n_len);
skb_len = skb->len;
-
+
if (diff)
{
struct iphdr *iph;
@@ -600,7 +594,7 @@ struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf,
/*
* update ip header
*/
- iph = n_skb->h.iph;
+ iph = n_skb->nh.iph;
iph->check = 0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
iph->tot_len = htons(skb_len + diff);
diff --git a/net/ipv4/ip_masq_ftp.c b/net/ipv4/ip_masq_ftp.c
index 75fbb01a7..cc2481746 100644
--- a/net/ipv4/ip_masq_ftp.c
+++ b/net/ipv4/ip_masq_ftp.c
@@ -5,21 +5,22 @@
* Version: @(#)ip_masq_ftp.c 0.01 02/05/96
*
* Author: Wouter Gadeyne
- *
+ *
*
* Fixes:
* Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
* Juan Jose Ciarlante : Code moved and adapted from ip_fw.c
- *
+ *
*
*
* This program 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 of the License, or (at your option) any later version.
- *
+ *
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <asm/system.h>
#include <linux/types.h>
@@ -63,7 +64,7 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
int diff;
skb = *skb_p;
- iph = skb->h.iph;
+ iph = skb->nh.iph;
th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
data = (char *)&th[1];
@@ -100,14 +101,14 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
port = (p5<<8) | p6;
#if DEBUG_CONFIG_IP_MASQ_FTP
printk("PORT %X:%X detected\n",from,port);
-#endif
+#endif
/*
* Now update or create an masquerade entry for it
*/
#if DEBUG_CONFIG_IP_MASQ_FTP
printk("protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0);
-#endif
+#endif
n_ms = ip_masq_out_get_2(iph->protocol,
htonl(from), htons(port),
iph->daddr, 0);
@@ -120,7 +121,7 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
htonl(from), htons(port),
iph->daddr, 0,
IP_MASQ_F_NO_DPORT);
-
+
if (n_ms==NULL)
return 0;
}
@@ -142,18 +143,18 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
buf_len = strlen(buf);
#if DEBUG_CONFIG_IP_MASQ_FTP
printk("new PORT %X:%X\n",from,port);
-#endif
+#endif
/*
* Calculate required delta-offset to keep TCP happy
*/
-
+
diff = buf_len - (data-p);
-
+
/*
* No shift.
*/
-
+
if (diff==0)
{
/*
@@ -201,19 +202,19 @@ int ip_masq_ftp_done(void)
}
#ifdef MODULE
+EXPORT_NO_SYMBOLS;
int init_module(void)
{
if (ip_masq_ftp_init() != 0)
return -EIO;
- register_symtab(0);
return 0;
}
void cleanup_module(void)
{
if (ip_masq_ftp_done() != 0)
- printk("ip_masq_ftp: can't remove module");
+ printk(KERN_INFO "ip_masq_ftp: can't remove module");
}
#endif /* MODULE */
diff --git a/net/ipv4/ip_masq_irc.c b/net/ipv4/ip_masq_irc.c
index 4bb93e5d1..e0b94f0d6 100644
--- a/net/ipv4/ip_masq_irc.c
+++ b/net/ipv4/ip_masq_irc.c
@@ -5,7 +5,7 @@
* Version: @(#)ip_masq_irc.c 0.01 03/20/96
*
* Author: Juan Jose Ciarlante
- *
+ *
*
* Fixes:
* - set NO_DADDR flag in ip_masq_new().
@@ -17,9 +17,10 @@
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
- *
+ *
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -65,7 +66,7 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
char *dcc_p, *addr_beg_p, *addr_end_p;
skb = *skb_p;
- iph = skb->h.iph;
+ iph = skb->nh.iph;
th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
data = (char *)&th[1];
@@ -77,29 +78,29 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
* AAAAAAAAA: bound addr (1.0.0.0==16777216, min 8 digits)
* P: bound port (min 1 d )
* F: filename (min 1 d )
- * S: size (min 1 d )
+ * S: size (min 1 d )
* 0x01, \n: terminators
*/
data_limit = skb->h.raw + skb->len;
-
+
while (data < (data_limit - 25) )
{
if (memcmp(data,"DCC ",4)) {
data ++;
continue;
}
-
+
dcc_p = data;
data += 4; /* point to DCC cmd */
-
+
if (memcmp(data, "CHAT ", 5) == 0 ||
memcmp(data, "SEND ", 5) == 0)
{
/*
* extra arg (file_size) req. for "SEND"
*/
-
+
if (*data == 'S') xtra_args++;
data += 5;
}
@@ -109,22 +110,22 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
/*
* skip next string.
*/
-
+
while( *data++ != ' ')
-
+
/*
* must still parse, at least, "AAAAAAAA P\x01\n",
* 12 bytes left.
*/
if (data > (data_limit-12)) return 0;
-
+
addr_beg_p = data;
-
+
/*
* client bound address in dec base
*/
-
+
s_addr = simple_strtoul(data,&data,10);
if (*data++ !=' ')
continue;
@@ -132,14 +133,14 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
/*
* client bound port in dec base
*/
-
+
s_port = simple_strtoul(data,&data,10);
addr_end_p = data;
-
+
/*
* should check args consistency?
*/
-
+
while(xtra_args) {
if (*data != ' ')
break;
@@ -147,24 +148,24 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
simple_strtoul(data,&data,10);
xtra_args--;
}
-
+
if (xtra_args != 0) continue;
-
+
/*
* terminators.
*/
-
+
if (data[0] != 0x01)
continue;
if (data[1]!='\r' && data[1]!='\n')
continue;
-
+
/*
* Now create an masquerade entry for it
* must set NO_DPORT and NO_DADDR because
* connection is requested by another client.
*/
-
+
n_ms = ip_masq_new(dev, IPPROTO_TCP,
htonl(s_addr),htons(s_port),
0, 0,
@@ -174,29 +175,29 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
return 0;
ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout);
-
+
/*
* Replace the old "address port" with the new one
*/
-
+
buf_len = sprintf(buf,"%lu %u",
ntohl(n_ms->maddr),ntohs(n_ms->mport));
-
+
/*
* Calculate required delta-offset to keep TCP happy
*/
-
+
diff = buf_len - (addr_end_p-addr_beg_p);
#if DEBUG_CONFIG_IP_MASQ_IRC
*addr_beg_p = '\0';
printk("masq_irc_out(): '%s' %X:%X detected (diff=%d)\n", dcc_p, s_addr,s_port, diff);
-#endif
+#endif
/*
* No shift.
*/
-
- if (diff==0)
+
+ if (diff==0)
{
/*
* simple case, just copy.
@@ -219,7 +220,7 @@ masq_irc_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
* You need 1 object per port in case you need
* to offer also other used irc ports (6665,6666,etc),
* they will share methods but they need own space for
- * data.
+ * data.
*/
struct ip_masq_app ip_masq_irc = {
@@ -252,19 +253,19 @@ int ip_masq_irc_done(void)
}
#ifdef MODULE
+EXPORT_NO_SYMBOLS;
int init_module(void)
{
if (ip_masq_irc_init() != 0)
return -EIO;
- register_symtab(NULL);
return 0;
}
void cleanup_module(void)
{
if (ip_masq_irc_done() != 0)
- printk("ip_masq_irc: can't remove module");
+ printk(KERN_INFO "ip_masq_irc: can't remove module");
}
#endif /* MODULE */
diff --git a/net/ipv4/ip_masq_quake.c b/net/ipv4/ip_masq_quake.c
new file mode 100644
index 000000000..3614f0cf5
--- /dev/null
+++ b/net/ipv4/ip_masq_quake.c
@@ -0,0 +1,316 @@
+/*
+ * IP_MASQ_QUAKE quake masquerading module
+ *
+ *
+ * Version: @(#)ip_masq_quake.c 0.02 22/02/97
+ *
+ * Author: Harald Hoyer mailto:HarryH@Royal.Net
+ *
+ *
+ * Fixes:
+ * Harald Hoyer : Unofficial Quake Specs found at
+ * http://www.gamers.org/dEngine/quake/spec/
+ * Harald Hoyer : Check for QUAKE-STRING
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+
+#define DEBUG_CONFIG_IP_MASQ_QUAKE 0
+
+typedef struct
+{
+ __u16 type; // (Little Endian) Type of message.
+ __u16 length; // (Little Endian) Length of message, header included.
+ char message[0]; // The contents of the message.
+} QUAKEHEADER;
+
+struct quake_priv_data {
+ /* Have we seen a client connect message */
+ char cl_connect;
+};
+
+static int
+masq_quake_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_INC_USE_COUNT;
+ if ((ms->app_data = kmalloc(sizeof(struct quake_priv_data),
+ GFP_ATOMIC)) == NULL)
+ printk(KERN_INFO "Quake: No memory for application data\n");
+ else
+ {
+ struct quake_priv_data *priv =
+ (struct quake_priv_data *)ms->app_data;
+ priv->cl_connect = 0;
+ }
+ return 0;
+}
+
+static int
+masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+ MOD_DEC_USE_COUNT;
+ if (ms->app_data)
+ kfree_s(ms->app_data, sizeof(struct quake_priv_data));
+ return 0;
+}
+
+int
+masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ QUAKEHEADER *qh;
+ __u16 udp_port;
+ char *data;
+ unsigned char code;
+ struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+ if(priv->cl_connect == -1)
+ return 0;
+
+ skb = *skb_p;
+
+ iph = skb->nh.iph;
+ uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /* Check for lenght */
+ if(ntohs(uh->len) < 5)
+ return 0;
+
+ qh = (QUAKEHEADER *)&uh[1];
+
+ if(qh->type != 0x0080)
+ return 0;
+
+
+ code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: code = %d \n", (int)code);
+#endif
+
+ switch(code) {
+ case 0x01:
+ /* Connection Request */
+
+ if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: length < 0xc \n");
+#endif
+ return 0;
+ }
+
+ data = &qh->message[1];
+
+ /* Check for stomping string */
+ if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp failed \n");
+#endif
+ return 0;
+ }
+ else {
+ priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp ok \n");
+#endif
+ }
+ break;
+
+ case 0x81:
+ /* Accept Connection */
+ if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+ return 0;
+ data = &qh->message[1];
+
+ memcpy(&udp_port, data, 2);
+
+ ms->dport = htons(udp_port);
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_in: in_rewrote UDP port %d \n", udp_port);
+#endif
+ priv->cl_connect = -1;
+
+ break;
+ }
+
+ return 0;
+}
+
+int
+masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+ struct sk_buff *skb;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ QUAKEHEADER *qh;
+ __u16 udp_port;
+ char *data;
+ unsigned char code;
+ struct ip_masq *n_ms;
+ struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+ if(priv->cl_connect == -1)
+ return 0;
+
+ skb = *skb_p;
+
+ iph = skb->nh.iph;
+ uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+ /* Check for lenght */
+ if(ntohs(uh->len) < 5)
+ return 0;
+
+ qh = (QUAKEHEADER *)&uh[1];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: qh->type = %d \n", (int)qh->type);
+#endif
+
+ if(qh->type != 0x0080)
+ return 0;
+
+ code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: code = %d \n", (int)code);
+#endif
+
+ switch(code) {
+ case 0x01:
+ /* Connection Request */
+
+ if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: length < 0xc \n");
+#endif
+ return 0;
+ }
+
+ data = &qh->message[1];
+
+ /* Check for stomping string */
+ if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp failed \n");
+#endif
+ return 0;
+ }
+ else {
+ priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: memcmp ok \n");
+#endif
+ }
+ break;
+
+ case 0x81:
+ /* Accept Connection */
+ if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+ return 0;
+
+ data = &qh->message[1];
+
+ memcpy(&udp_port, data, 2);
+
+ n_ms = ip_masq_new(dev, IPPROTO_UDP,
+ ms->saddr, htons(udp_port),
+ ms->daddr, ms->dport,
+ 0);
+
+ if (n_ms==NULL)
+ return 0;
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+ printk("Quake_out: out_rewrote UDP port %d -> %d\n",
+ udp_port, ntohs(n_ms->mport));
+#endif
+ udp_port = ntohs(n_ms->mport);
+ memcpy(data, &udp_port, 2);
+
+ break;
+ }
+
+ return 0;
+}
+
+struct ip_masq_app ip_masq_quake = {
+ NULL, /* next */
+ "Quake_26", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_quake_init_1, /* ip_masq_init_1 */
+ masq_quake_done_1, /* ip_masq_done_1 */
+ masq_quake_out, /* pkt_out */
+ masq_quake_in /* pkt_in */
+};
+struct ip_masq_app ip_masq_quakenew = {
+ NULL, /* next */
+ "Quake_27", /* name */
+ 0, /* type */
+ 0, /* n_attach */
+ masq_quake_init_1, /* ip_masq_init_1 */
+ masq_quake_done_1, /* ip_masq_done_1 */
+ masq_quake_out, /* pkt_out */
+ masq_quake_in /* pkt_in */
+};
+
+/*
+ * ip_masq_quake initialization
+ */
+
+int ip_masq_quake_init(void)
+{
+ return (register_ip_masq_app(&ip_masq_quake, IPPROTO_UDP, 26000) +
+ register_ip_masq_app(&ip_masq_quakenew, IPPROTO_UDP, 27000));
+}
+
+/*
+ * ip_masq_quake fin.
+ */
+
+int ip_masq_quake_done(void)
+{
+ return (unregister_ip_masq_app(&ip_masq_quake) +
+ unregister_ip_masq_app(&ip_masq_quakenew));
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ if (ip_masq_quake_init() != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ if (ip_masq_quake_done() != 0)
+ printk("ip_masq_quake: can't remove module");
+}
+
+#endif /* MODULE */
+
+
diff --git a/net/ipv4/ip_masq_raudio.c b/net/ipv4/ip_masq_raudio.c
index 9fe4e8f15..85bba590e 100644
--- a/net/ipv4/ip_masq_raudio.c
+++ b/net/ipv4/ip_masq_raudio.c
@@ -2,7 +2,7 @@
* IP_MASQ_RAUDIO - Real Audio masquerading module
*
*
- * Version: @(#)$Id: ip_masq_raudio.c,v 1.3 1996/05/20 13:24:26 nigel Exp $
+ * Version: @(#)$Id: ip_masq_raudio.c,v 1.5 1997/04/03 08:52:02 davem Exp $
*
* Author: Nigel Metheringham
* [strongly based on ftp module by Juan Jose Ciarlante & Wouter Gadeyne]
@@ -31,12 +31,13 @@
*
* At present the "first packet" is defined as a packet starting with
* the protocol ID string - "PNA".
- * When the link is up there appears to be enough control data
+ * When the link is up there appears to be enough control data
* crossing the control link to keep it open even if a long audio
* piece is playing.
- *
+ *
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <asm/system.h>
#include <linux/types.h>
@@ -64,11 +65,11 @@ masq_raudio_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
{
MOD_INC_USE_COUNT;
if ((ms->app_data = kmalloc(sizeof(struct raudio_priv_data),
- GFP_ATOMIC)) == NULL)
+ GFP_ATOMIC)) == NULL)
printk(KERN_INFO "RealAudio: No memory for application data\n");
- else
+ else
{
- struct raudio_priv_data *priv =
+ struct raudio_priv_data *priv =
(struct raudio_priv_data *)ms->app_data;
priv->seen_start = 0;
priv->data_conn = NULL;
@@ -94,7 +95,7 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
char *p, *data, *data_limit;
struct ip_masq *n_ms;
unsigned short version, msg_id, msg_len, udp_port;
- struct raudio_priv_data *priv =
+ struct raudio_priv_data *priv =
(struct raudio_priv_data *)ms->app_data;
/* Everything running correctly already */
@@ -102,7 +103,7 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
return 0;
skb = *skb_p;
- iph = skb->h.iph;
+ iph = skb->nh.iph;
th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
data = (char *)&th[1];
@@ -156,7 +157,7 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
ms->saddr, udp_port,
ms->daddr, 0,
IP_MASQ_F_NO_DPORT);
-
+
if (n_ms==NULL)
return 0;
@@ -171,7 +172,7 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
if (priv)
priv->data_conn = n_ms;
- /*
+ /*
* There is nothing else useful we can do
* Maybe a development could do more, but for now
* we exit gracefully!
@@ -214,19 +215,19 @@ int ip_masq_raudio_done(void)
}
#ifdef MODULE
+EXPORT_NO_SYMBOLS;
int init_module(void)
{
if (ip_masq_raudio_init() != 0)
return -EIO;
- register_symtab(0);
return 0;
}
void cleanup_module(void)
{
if (ip_masq_raudio_done() != 0)
- printk("ip_masq_raudio: can't remove module");
+ printk(KERN_INFO "ip_masq_raudio: can't remove module");
}
#endif /* MODULE */
diff --git a/net/ipv4/ip_nat_dumb.c b/net/ipv4/ip_nat_dumb.c
new file mode 100644
index 000000000..1d510af42
--- /dev/null
+++ b/net/ipv4/ip_nat_dumb.c
@@ -0,0 +1,77 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Dumb Network Address Translation.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ *
+ *
+ * NOTE: It is just working model of real NAT.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+
+
+int
+ip_do_nat(struct sk_buff *skb)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct iphdr *iph = skb->nh.iph;
+ u32 odaddr = iph->daddr;
+ u32 osaddr = iph->saddr;
+ u16 check;
+ u16 *cksum = NULL;
+
+ IPCB(skb)->flags |= IPSKB_TRANSLATED;
+
+ /* Rewrite IP header */
+ iph->daddr = rt->rt_dst_map;
+ iph->saddr = rt->rt_src_map;
+ iph->check = 0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+ /* If it is the first fragment, rewrite protocol headers */
+
+ if (!(iph->frag_off & htons(IP_OFFSET))) {
+ /* Only plain TCP/UDP headers rewriting is implemented :-( */
+ if (iph->protocol == IPPROTO_TCP)
+ cksum = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check;
+ else if (iph->protocol == IPPROTO_UDP)
+ cksum = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check;
+ if (cksum && (check = *cksum) != 0) {
+ check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
+ check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+ if (!check)
+ check = 0xFFFF;
+ *cksum = check;
+ }
+ }
+ return 0;
+}
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 517c0e219..2c7974506 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -10,6 +10,7 @@
*/
#include <linux/types.h>
+#include <asm/uaccess.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/icmp.h>
@@ -17,6 +18,7 @@
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
+#include <linux/net_alias.h>
/*
* Write options to IP header, record destination address to
@@ -24,30 +26,30 @@
* (we should already know it, so that this function is allowed be
* called only after routing decision) and timestamp,
* if we originate this datagram.
+ *
+ * daddr is real destination address, next hop is recorded in IP header.
+ * saddr is address of outgoing interface.
*/
-void ip_options_build(struct sk_buff * skb, struct options * opt,
- __u32 daddr, __u32 saddr,
- int is_frag)
+void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
+ u32 daddr, u32 saddr, int is_frag)
{
- unsigned char * iph = (unsigned char*)skb->ip_hdr;
+ unsigned char * iph = skb->nh.raw;
- memcpy(skb->proto_priv, opt, sizeof(struct options));
+ memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
- opt = (struct options*)skb->proto_priv;
+ opt = &(IPCB(skb)->opt);
opt->is_data = 0;
if (opt->srr)
memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
- if (!is_frag)
- {
+ if (!is_frag) {
if (opt->rr_needaddr)
memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
if (opt->ts_needaddr)
memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
- if (opt->ts_needtime)
- {
+ if (opt->ts_needtime) {
struct timeval tv;
__u32 midtime;
do_gettimeofday(&tv);
@@ -56,88 +58,96 @@ void ip_options_build(struct sk_buff * skb, struct options * opt,
}
return;
}
- if (opt->rr)
- {
+ if (opt->rr) {
memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
opt->rr = 0;
opt->rr_needaddr = 0;
}
- if (opt->ts)
- {
+ if (opt->ts) {
memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
opt->ts = 0;
opt->ts_needaddr = opt->ts_needtime = 0;
}
}
-int ip_options_echo(struct options * dopt, struct options * sopt,
- __u32 daddr, __u32 saddr,
- struct sk_buff * skb)
+/*
+ * Provided (sopt, skb) points to received options,
+ * build in dopt compiled option set appropriate for answering.
+ * i.e. invert SRR option, copy anothers,
+ * and grab room in RR/TS options.
+ *
+ * NOTE: dopt cannot point to skb.
+ */
+
+int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb)
{
+ struct ip_options *sopt;
unsigned char *sptr, *dptr;
int soffset, doffset;
int optlen;
+ u32 daddr;
- memset(dopt, 0, sizeof(struct options));
+#if 111
+ if (skb == NULL) {
+ printk(KERN_DEBUG "no skb in ip_options_echo\n");
+ return -EINVAL;
+ }
+#endif
+ memset(dopt, 0, sizeof(struct ip_options));
dopt->is_data = 1;
- if (!sopt)
- sopt = (struct options*)skb->proto_priv;
+ sopt = &(IPCB(skb)->opt);
- if (sopt->optlen == 0)
- {
+ if (sopt->optlen == 0) {
dopt->optlen = 0;
return 0;
}
- sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
- (unsigned char *)skb->ip_hdr);
+ sptr = skb->nh.raw;
dptr = dopt->__data;
- if (sopt->rr)
- {
+ if (skb->dst)
+ daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
+ else
+ daddr = skb->nh.iph->daddr;
+
+ if (sopt->rr) {
optlen = sptr[sopt->rr+1];
soffset = sptr[sopt->rr+2];
dopt->rr = dopt->optlen + sizeof(struct iphdr);
memcpy(dptr, sptr+sopt->rr, optlen);
if (sopt->rr_needaddr && soffset <= optlen) {
if (soffset + 3 > optlen)
- return -EINVAL;
+ return -EINVAL;
dptr[2] = soffset + 4;
dopt->rr_needaddr = 1;
}
- dptr += optlen;
+ dptr += optlen;
dopt->optlen += optlen;
}
- if (sopt->ts)
- {
+ if (sopt->ts) {
optlen = sptr[sopt->ts+1];
soffset = sptr[sopt->ts+2];
dopt->ts = dopt->optlen + sizeof(struct iphdr);
memcpy(dptr, sptr+sopt->ts, optlen);
- if (soffset <= optlen)
- {
- if (sopt->ts_needaddr)
- {
+ if (soffset <= optlen) {
+ if (sopt->ts_needaddr) {
if (soffset + 3 > optlen)
return -EINVAL;
dopt->ts_needaddr = 1;
soffset += 4;
}
- if (sopt->ts_needtime)
- {
+ if (sopt->ts_needtime) {
if (soffset + 3 > optlen)
return -EINVAL;
dopt->ts_needtime = 1;
soffset += 4;
}
- if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC)
- {
+ if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) {
__u32 addr;
memcpy(&addr, sptr+soffset-9, 4);
- if (ip_chk_addr(addr) == 0)
- {
+ if (__ip_chk_addr(addr) == 0) {
dopt->ts_needtime = 0;
dopt->ts_needaddr = 0;
soffset -= 8;
@@ -148,10 +158,9 @@ int ip_options_echo(struct options * dopt, struct options * sopt,
dptr += optlen;
dopt->optlen += optlen;
}
- if (sopt->srr)
- {
+ if (sopt->srr) {
unsigned char * start = sptr+sopt->srr;
- __u32 faddr;
+ u32 faddr;
optlen = start[1];
soffset = start[2];
@@ -159,19 +168,17 @@ int ip_options_echo(struct options * dopt, struct options * sopt,
if (soffset > optlen)
soffset = optlen + 1;
soffset -= 4;
- if (soffset > 3)
- {
+ if (soffset > 3) {
memcpy(&faddr, &start[soffset-1], 4);
for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
memcpy(&dptr[doffset-1], &start[soffset-1], 4);
/*
* RFC1812 requires to fix illegal source routes.
*/
- if (memcmp(&saddr, &start[soffset+3], 4) == 0)
+ if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
doffset -= 4;
}
- if (doffset > 3)
- {
+ if (doffset > 3) {
memcpy(&start[doffset-1], &daddr, 4);
dopt->faddr = faddr;
dptr[0] = start[0];
@@ -183,28 +190,31 @@ int ip_options_echo(struct options * dopt, struct options * sopt,
dopt->is_strictroute = sopt->is_strictroute;
}
}
- while (dopt->optlen & 3)
- {
+ while (dopt->optlen & 3) {
*dptr++ = IPOPT_END;
dopt->optlen++;
}
return 0;
}
+/*
+ * Options "fragmenting", just fill options not
+ * allowed in fragments with NOOPs.
+ * Simple and stupid 8), but the most efficient way.
+ */
+
void ip_options_fragment(struct sk_buff * skb)
{
- unsigned char * optptr = (unsigned char*)skb->ip_hdr;
- struct options * opt = (struct options*)skb->proto_priv;
+ unsigned char * optptr = skb->nh.raw;
+ struct ip_options * opt = &(IPCB(skb)->opt);
int l = opt->optlen;
int optlen;
- while (l > 0)
- {
- switch (*optptr)
- {
- case IPOPT_END:
+ while (l > 0) {
+ switch (*optptr) {
+ case IPOPT_END:
return;
- case IPOPT_NOOP:
+ case IPOPT_NOOP:
l--;
optptr++;
continue;
@@ -212,7 +222,7 @@ void ip_options_fragment(struct sk_buff * skb)
optlen = optptr[1];
if (optlen<2 || optlen>l)
return;
- if (!(*optptr & 0x80))
+ if (!IPOPT_COPIED(*optptr))
memset(optptr, IPOPT_NOOP, optlen);
l -= optlen;
optptr += optlen;
@@ -231,7 +241,7 @@ void ip_options_fragment(struct sk_buff * skb)
* If opt == NULL, then skb->data should point to IP header.
*/
-int ip_options_compile(struct options * opt, struct sk_buff * skb)
+int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
{
int l;
unsigned char * iph;
@@ -239,30 +249,23 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
int optlen;
unsigned char * pp_ptr = NULL;
- if (!opt)
- {
- opt = (struct options*)skb->proto_priv;
- memset(opt, 0, sizeof(struct options));
- iph = (unsigned char*)skb->ip_hdr;
+ if (!opt) {
+ opt = &(IPCB(skb)->opt);
+ memset(opt, 0, sizeof(struct ip_options));
+ iph = skb->nh.raw;
opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
optptr = iph + sizeof(struct iphdr);
opt->is_data = 0;
- }
- else
- {
- optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
+ } else {
+ optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
iph = optptr - sizeof(struct iphdr);
}
- for (l = opt->optlen; l > 0; )
- {
- switch (*optptr)
- {
+ for (l = opt->optlen; l > 0; ) {
+ switch (*optptr) {
case IPOPT_END:
- for (optptr++, l--; l>0; l--)
- {
- if (*optptr != IPOPT_END)
- {
+ for (optptr++, l--; l>0; l--) {
+ if (*optptr != IPOPT_END) {
*optptr = IPOPT_END;
opt->is_changed = 1;
}
@@ -274,35 +277,28 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
continue;
}
optlen = optptr[1];
- if (optlen<2 || optlen>l)
- {
+ if (optlen<2 || optlen>l) {
pp_ptr = optptr;
goto error;
}
- switch (*optptr)
- {
+ switch (*optptr) {
case IPOPT_SSRR:
case IPOPT_LSRR:
- if (optlen < 3)
- {
+ if (optlen < 3) {
pp_ptr = optptr + 1;
goto error;
}
- if (optptr[2] < 4)
- {
+ if (optptr[2] < 4) {
pp_ptr = optptr + 2;
goto error;
}
/* NB: cf RFC-1812 5.2.4.1 */
- if (opt->srr)
- {
+ if (opt->srr) {
pp_ptr = optptr;
goto error;
}
- if (!skb)
- {
- if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3))
- {
+ if (!skb) {
+ if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
pp_ptr = optptr + 1;
goto error;
}
@@ -314,30 +310,24 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
opt->srr = optptr - iph;
break;
case IPOPT_RR:
- if (opt->rr)
- {
+ if (opt->rr) {
pp_ptr = optptr;
goto error;
}
- if (optlen < 3)
- {
+ if (optlen < 3) {
pp_ptr = optptr + 1;
goto error;
}
- if (optptr[2] < 4)
- {
+ if (optptr[2] < 4) {
pp_ptr = optptr + 2;
goto error;
}
- if (optptr[2] <= optlen)
- {
- if (optptr[2]+3 > optlen)
- {
+ if (optptr[2] <= optlen) {
+ if (optptr[2]+3 > optlen) {
pp_ptr = optptr + 2;
goto error;
}
- if (skb)
- {
+ if (skb) {
memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
opt->is_changed = 1;
}
@@ -347,32 +337,26 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
opt->rr = optptr - iph;
break;
case IPOPT_TIMESTAMP:
- if (opt->ts)
- {
+ if (opt->ts) {
pp_ptr = optptr;
goto error;
}
- if (optlen < 4)
- {
+ if (optlen < 4) {
pp_ptr = optptr + 1;
goto error;
}
- if (optptr[2] < 5)
- {
+ if (optptr[2] < 5) {
pp_ptr = optptr + 2;
goto error;
}
- if (optptr[2] <= optlen)
- {
+ if (optptr[2] <= optlen) {
struct timestamp * ts = (struct timestamp*)(optptr+1);
__u32 * timeptr = NULL;
- if (ts->ptr+3 > ts->len)
- {
+ if (ts->ptr+3 > ts->len) {
pp_ptr = optptr + 2;
goto error;
}
- switch (ts->flags)
- {
+ switch (ts->flags) {
case IPOPT_TS_TSONLY:
opt->ts = optptr - iph;
if (skb)
@@ -381,14 +365,12 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
ts->ptr += 4;
break;
case IPOPT_TS_TSANDADDR:
- if (ts->ptr+7 > ts->len)
- {
+ if (ts->ptr+7 > ts->len) {
pp_ptr = optptr + 2;
goto error;
}
opt->ts = optptr - iph;
- if (skb)
- {
+ if (skb) {
memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
timeptr = (__u32*)&optptr[ts->ptr+3];
}
@@ -397,16 +379,15 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
ts->ptr += 8;
break;
case IPOPT_TS_PRESPEC:
- if (ts->ptr+7 > ts->len)
- {
+ if (ts->ptr+7 > ts->len) {
pp_ptr = optptr + 2;
goto error;
}
opt->ts = optptr - iph;
{
- __u32 addr;
+ u32 addr;
memcpy(&addr, &optptr[ts->ptr-1], 4);
- if (ip_chk_addr(addr) == 0)
+ if (__ip_chk_addr(addr) == 0)
break;
if (skb)
timeptr = (__u32*)&optptr[ts->ptr+3];
@@ -419,8 +400,7 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
pp_ptr = optptr + 3;
goto error;
}
- if (timeptr)
- {
+ if (timeptr) {
struct timeval tv;
__u32 midtime;
do_gettimeofday(&tv);
@@ -428,28 +408,31 @@ int ip_options_compile(struct options * opt, struct sk_buff * skb)
memcpy(timeptr, &midtime, sizeof(__u32));
opt->is_changed = 1;
}
- }
- else
- {
+ } else {
struct timestamp * ts = (struct timestamp*)(optptr+1);
- if (ts->overflow == 15)
- {
+ if (ts->overflow == 15) {
pp_ptr = optptr + 3;
goto error;
}
opt->ts = optptr - iph;
- if (skb)
- {
+ if (skb) {
ts->overflow++;
opt->is_changed = 1;
}
}
break;
+ case IPOPT_RA:
+ if (optlen < 4) {
+ pp_ptr = optptr + 1;
+ goto error;
+ }
+ if (optptr[2] == 0 && optptr[3] == 0)
+ opt->router_alert = optptr - iph;
+ break;
case IPOPT_SEC:
case IPOPT_SID:
default:
- if (!skb)
- {
+ if (!skb) {
pp_ptr = optptr;
goto error;
}
@@ -464,11 +447,168 @@ eol:
return 0;
error:
- if (skb)
- {
- icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
+ if (skb) {
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph);
kfree_skb(skb, FREE_READ);
}
return -EINVAL;
}
+
+/*
+ * Undo all the changes done by ip_options_compile().
+ */
+
+void ip_options_undo(struct ip_options * opt)
+{
+ if (opt->srr) {
+ unsigned char * optptr = opt->__data+opt->srr-sizeof(struct iphdr);
+ memmove(optptr+7, optptr+3, optptr[1]-7);
+ memcpy(optptr+3, &opt->faddr, 4);
+ }
+ if (opt->rr_needaddr) {
+ unsigned char * optptr = opt->__data+opt->rr-sizeof(struct iphdr);
+ memset(&optptr[optptr[2]-1], 0, 4);
+ optptr[2] -= 4;
+ }
+ if (opt->ts) {
+ unsigned char * optptr = opt->__data+opt->ts-sizeof(struct iphdr);
+ if (opt->ts_needtime) {
+ memset(&optptr[optptr[2]-1], 0, 4);
+ optptr[2] -= 4;
+ }
+ if (opt->ts_needaddr) {
+ memset(&optptr[optptr[2]-1], 0, 4);
+ optptr[2] -= 4;
+ }
+ }
+}
+
+int ip_options_get(struct ip_options **optp, unsigned char *data, int optlen, int user)
+{
+ struct ip_options *opt;
+
+ opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL);
+ if (!opt)
+ return -ENOMEM;
+ memset(opt, 0, sizeof(struct ip_options));
+ if (optlen) {
+ if (user) {
+ if (copy_from_user(opt->__data, data, optlen))
+ return -EFAULT;
+ } else
+ memcpy(opt->__data, data, optlen);
+ }
+ while (optlen & 3)
+ opt->__data[optlen++] = IPOPT_END;
+ opt->optlen = optlen;
+ opt->is_data = 1;
+ opt->is_setbyuser = 1;
+ if (optlen && ip_options_compile(opt, NULL)) {
+ kfree_s(opt, sizeof(struct options) + optlen);
+ return -EINVAL;
+ }
+ *optp = opt;
+ return 0;
+}
+
+void ip_forward_options(struct sk_buff *skb)
+{
+ struct ip_options * opt = &(IPCB(skb)->opt);
+ unsigned char * optptr;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ unsigned char *raw = skb->nh.raw;
+
+ if (opt->rr_needaddr) {
+ optptr = (unsigned char *)raw + opt->rr;
+ memcpy(&optptr[optptr[2]-5], &rt->u.dst.dev->pa_addr, 4);
+ opt->is_changed = 1;
+ }
+ if (opt->srr_is_hit) {
+ int srrptr, srrspace;
+
+ optptr = raw + opt->srr;
+
+ for ( srrptr=optptr[2], srrspace = optptr[1];
+ srrptr <= srrspace;
+ srrptr += 4
+ ) {
+ if (srrptr + 3 > srrspace)
+ break;
+ if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
+ break;
+ }
+ if (srrptr + 3 <= srrspace) {
+ opt->is_changed = 1;
+ memcpy(&optptr[srrptr-1], &rt->u.dst.dev->pa_addr, 4);
+ skb->nh.iph->daddr = rt->rt_dst;
+ optptr[2] = srrptr+4;
+ } else
+ printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
+ if (opt->ts_needaddr) {
+ optptr = raw + opt->ts;
+ memcpy(&optptr[optptr[2]-9], &rt->u.dst.dev->pa_addr, 4);
+ opt->is_changed = 1;
+ }
+ if (opt->is_changed) {
+ opt->is_changed = 0;
+ ip_send_check(skb->nh.iph);
+ }
+ }
+}
+
+int ip_options_rcv_srr(struct sk_buff *skb)
+{
+ struct ip_options *opt = &(IPCB(skb)->opt);
+ int srrspace, srrptr;
+ u32 nexthop;
+ struct iphdr *iph = skb->nh.iph;
+ unsigned char * optptr = skb->nh.raw + opt->srr;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct rtable *rt2;
+ int err;
+
+ if (!opt->srr)
+ return 0;
+
+ if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)
+ || skb->pkt_type != PACKET_HOST)
+ return -EINVAL;
+
+ if (!(rt->rt_flags & RTF_LOCAL)) {
+ if (!opt->is_strictroute)
+ return 0;
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, 16);
+ return -EINVAL;
+ }
+
+ for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
+ if (srrptr + 3 > srrspace) {
+ icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2);
+ return -EINVAL;
+ }
+ memcpy(&nexthop, &optptr[srrptr-1], 4);
+
+ rt = (struct rtable*)skb->dst;
+ skb->dst = NULL;
+ err = ip_route_input(skb, nexthop, iph->saddr, iph->tos,
+ net_alias_main_dev(skb->dev));
+ rt2 = (struct rtable*)skb->dst;
+ if (err || rt2->rt_flags&(RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)) {
+ ip_rt_put(rt2);
+ skb->dst = &rt->u.dst;
+ return -EINVAL;
+ }
+ ip_rt_put(rt);
+ if (!(rt2->rt_flags&RTF_LOCAL))
+ break;
+ /* Superfast 8) loopback forward */
+ memcpy(&iph->daddr, &optptr[srrptr-1], 4);
+ opt->is_changed = 1;
+ }
+ if (srrptr <= srrspace) {
+ opt->srr_is_hit = 1;
+ opt->is_changed = 1;
+ }
+ return 0;
+}
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index b8c7891e8..41e60de61 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -26,6 +26,7 @@
* Alexander Demenshin: Missing sk/skb free in ip_queue_xmit
* (in case if packet not accepted by
* output firewall rules)
+ * Alexey Kuznetsov: use new route cache
*/
#include <asm/uaccess.h>
@@ -64,226 +65,136 @@
#include <linux/firewall.h>
#include <linux/mroute.h>
#include <net/netlink.h>
+#include <linux/ipsec.h>
-/*
- * Loop a packet back to the sender.
- */
-
-static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
+static void __inline__ ip_ll_header_reserve(struct sk_buff *skb)
{
- struct device *dev=&loopback_dev;
- int len=ntohs(skb->ip_hdr->tot_len);
- struct sk_buff *newskb=dev_alloc_skb(len+dev->hard_header_len+15);
-
- if(newskb==NULL)
- return;
-
- newskb->link3=NULL;
- newskb->sk=NULL;
- newskb->dev=dev;
- newskb->saddr=skb->saddr;
- newskb->daddr=skb->daddr;
- newskb->raddr=skb->raddr;
- newskb->free=1;
- newskb->lock=0;
- newskb->users=0;
- newskb->pkt_type=skb->pkt_type;
-
- /*
- * Put a MAC header on the packet
- */
- ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
- /*
- * Add the rest of the data space.
- */
- newskb->ip_hdr=(struct iphdr *)skb_put(newskb, len);
- memcpy(newskb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
-
- /*
- * Copy the data
- */
- memcpy(newskb->ip_hdr,skb->ip_hdr,len);
-
- /* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */
-
- /*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/
- ip_queue_xmit(NULL, dev, newskb, 2);
+ struct rtable *rt = (struct rtable*)skb->dst;
+ skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15);
+ ip_ll_header(skb);
}
+int ip_id_count = 0;
-/*
- * Take an skb, and fill in the MAC header.
- */
-
-int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+int ip_build_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr,
+ struct ip_options *opt)
{
- int mac = 0;
+ struct rtable *rt;
+ u32 final_daddr = daddr;
+ struct iphdr *iph;
+ int err;
+
+ if (opt && opt->srr)
+ daddr = opt->faddr;
- skb->dev = dev;
- skb->arp = 1;
- skb->protocol = htons(ETH_P_IP);
- if (dev->hard_header)
+ err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) |
+ (sk->localroute||0), NULL);
+ if (err)
{
- /*
- * Build a hardware header. Source address is our mac, destination unknown
- * (rebuild header will sort this out)
- */
- skb_reserve(skb,(dev->hard_header_len+15)&~15); /* 16 byte aligned IP headers are good */
- if (rt && dev == rt->rt_dev && rt->rt_hh)
- {
- memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
- if (rt->rt_hh->hh_uptodate)
- return dev->hard_header_len;
-#if RT_CACHE_DEBUG >= 2
- printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
-#endif
- skb->arp = 0;
- skb->raddr = daddr;
- return dev->hard_header_len;
- }
- mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
- if (mac < 0)
- {
- mac = -mac;
- skb->arp = 0;
- skb->raddr = daddr; /* next routing address */
- }
+ ip_statistics.IpOutNoRoutes++;
+ return err;
}
- return mac;
-}
-static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
-{
- int mac = 0;
+ if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+ ip_rt_put(rt);
+ ip_statistics.IpOutNoRoutes++;
+ return -ENETUNREACH;
+ }
+
+ skb->dst = dst_clone(&rt->u.dst);
+
+ skb->dev = rt->u.dst.dev;
+ skb->arp = 0;
+
+ ip_ll_header_reserve(skb);
+
+ /*
+ * Now build the IP header.
+ */
+
+ /*
+ * Build the IP addresses
+ */
+
+ if (opt)
+ iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
+ else
+ iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
- skb->dev = dev;
- skb->arp = 1;
- skb->protocol = htons(ETH_P_IP);
- skb_reserve(skb,MAX_HEADER);
- if (dev->hard_header)
+ iph->version = 4;
+ iph->ihl = 5;
+ iph->tos = sk->ip_tos;
+ iph->frag_off = 0;
+ if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+ (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+ rt->rt_flags&RTF_NOPMTUDISC))
+ iph->frag_off |= htons(IP_DF);
+ iph->ttl = sk->ip_ttl;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = sk->protocol;
+ skb->nh.iph = iph;
+ skb->h.raw = (unsigned char*)(iph+1);
+
+ if (opt && opt->optlen)
{
- if (rt && dev == rt->rt_dev && rt->rt_hh)
- {
- memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
- if (rt->rt_hh->hh_uptodate)
- return dev->hard_header_len;
-#if RT_CACHE_DEBUG >= 2
- printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
-#endif
- skb->arp = 0;
- skb->raddr = daddr;
- return dev->hard_header_len;
- }
- mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
- if (mac < 0)
- {
- mac = -mac;
- skb->arp = 0;
- skb->raddr = daddr; /* next routing address */
- }
+ iph->ihl += opt->optlen>>2;
+ skb->h.raw += opt->optlen;
+ ip_options_build(skb, opt, final_daddr,
+ rt->u.dst.dev->pa_addr, 0);
}
- return mac;
+
+ ip_rt_put(rt);
+ return 0;
}
-int ip_id_count = 0;
-
/*
* This routine builds the appropriate hardware/IP headers for
- * the routine. It assumes that if *dev != NULL then the
- * protocol knows what it's doing, otherwise it uses the
- * routing/ARP tables to select a device struct.
+ * the routine.
*/
-int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
- struct device **dev, int type, struct options *opt,
- int len, int tos, int ttl, struct rtable ** rp)
+int ip_build_header(struct sk_buff *skb, struct sock *sk)
{
struct rtable *rt;
- __u32 raddr;
- int tmp;
+ struct ip_options *opt = sk->opt;
+ u32 daddr = sk->daddr;
+ u32 final_daddr = daddr;
struct iphdr *iph;
- __u32 final_daddr = daddr;
-
+ int err;
if (opt && opt->srr)
daddr = opt->faddr;
- /*
- * See if we need to look up the device.
- */
-
-#ifdef CONFIG_IP_MULTICAST
- if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
- *dev=dev_get(skb->sk->ip_mc_name);
-#endif
- if (rp)
- {
- rt = ip_check_route(rp, daddr, skb->localroute);
- /*
- * If rp != NULL rt_put following below should not
- * release route, so that...
- */
- if (rt)
- atomic_inc(&rt->rt_refcnt);
- }
- else
- rt = ip_rt_route(daddr, skb->localroute);
-
+ rt = (struct rtable*)sk->dst_cache;
- if (*dev == NULL)
- {
- if (rt == NULL)
- {
- ip_statistics.IpOutNoRoutes++;
- return(-ENETUNREACH);
- }
-
- *dev = rt->rt_dev;
+ if (!rt || rt->u.dst.obsolete) {
+ ip_rt_put(rt);
+ err = ip_route_output(&rt, daddr, sk->saddr, RT_TOS(sk->ip_tos) |
+ (sk->localroute||0), NULL);
+ if (err)
+ return err;
+ sk->dst_cache = &rt->u.dst;
}
- if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr)
- saddr = rt ? rt->rt_src : (*dev)->pa_addr;
-
- raddr = rt ? rt->rt_gateway : daddr;
-
- if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY))
- {
+ if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+ sk->dst_cache = NULL;
ip_rt_put(rt);
ip_statistics.IpOutNoRoutes++;
return -ENETUNREACH;
}
- /*
- * Now build the MAC header.
- */
-
- if (type==IPPROTO_TCP)
- tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr);
- else
- tmp = ip_send(rt, skb, raddr, len, *dev, saddr);
-
- ip_rt_put(rt);
+ skb->dst = dst_clone(sk->dst_cache);
- /*
- * Book keeping
- */
-
- skb->dev = *dev;
- skb->saddr = saddr;
+ skb->dev = rt->u.dst.dev;
+ skb->arp = 0;
+ skb_reserve(skb, MAX_HEADER);
+ skb->mac.raw = skb->data;
/*
* Now build the IP header.
*/
/*
- * If we are using IPPROTO_RAW, then we don't need an IP header, since
- * one is being supplied to us by the user
- */
-
- if(type == IPPROTO_RAW)
- return (tmp);
-
- /*
* Build the IP addresses
*/
@@ -294,21 +205,118 @@ int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
iph->version = 4;
iph->ihl = 5;
- iph->tos = tos;
+ iph->tos = sk->ip_tos;
iph->frag_off = 0;
- iph->ttl = ttl;
- iph->daddr = daddr;
- iph->saddr = saddr;
- iph->protocol = type;
- skb->ip_hdr = iph;
+ if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+ (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+ rt->rt_flags&RTF_NOPMTUDISC))
+ iph->frag_off |= htons(IP_DF);
+ iph->ttl = sk->ip_ttl;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = sk->protocol;
+ skb->nh.iph = iph;
+ skb->h.raw = (unsigned char*)(iph+1);
if (!opt || !opt->optlen)
- return sizeof(struct iphdr) + tmp;
+ return 0;
iph->ihl += opt->optlen>>2;
- ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
- return iph->ihl*4 + tmp;
+ skb->h.raw += opt->optlen;
+ ip_options_build(skb, opt, final_daddr, rt->u.dst.dev->pa_addr, 0);
+
+ return 0;
}
+int ip_mc_output(struct sk_buff *skb)
+{
+ struct sock *sk = skb->sk;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = rt->u.dst.dev;
+
+ /*
+ * If the indicated interface is up and running, send the packet.
+ */
+
+ ip_statistics.IpOutRequests++;
+#ifdef CONFIG_IP_ACCT
+ ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+#endif
+
+ if (rt->rt_flags & RTCF_NAT)
+ ip_do_nat(skb);
+
+ /*
+ * Multicasts are looped back for other local users
+ */
+
+ if (rt->rt_flags&RTF_MULTICAST && !(dev->flags&IFF_LOOPBACK)) {
+ if (sk==NULL || sk->ip_mc_loop)
+ dev_loopback_xmit(skb);
+
+ /* Multicasts with ttl 0 must not go beyond the host */
+
+ if (skb->nh.iph->ttl == 0) {
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+ }
+ }
+
+ if ((rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST)) == (RTF_LOCAL|RTF_BROADCAST) &&
+ !(dev->flags&IFF_LOOPBACK))
+ dev_loopback_xmit(skb);
+
+ if (dev->flags & IFF_UP) {
+ dev_queue_xmit(skb);
+ return 0;
+ }
+ ip_statistics.IpOutDiscards++;
+
+ kfree_skb(skb, FREE_WRITE);
+ return -ENETDOWN;
+}
+
+int ip_output(struct sk_buff *skb)
+{
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = rt->u.dst.dev;
+
+ /*
+ * If the indicated interface is up and running, send the packet.
+ */
+
+ ip_statistics.IpOutRequests++;
+
+#ifdef CONFIG_IP_ACCT
+ ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+#endif
+
+ if (rt->rt_flags&RTCF_NAT)
+ ip_do_nat(skb);
+
+ if (dev->flags & IFF_UP) {
+ dev_queue_xmit(skb);
+ return 0;
+ }
+ ip_statistics.IpOutDiscards++;
+
+ kfree_skb(skb, FREE_WRITE);
+ return -ENETDOWN;
+}
+
+#ifdef CONFIG_IP_ACCT
+int ip_acct_output(struct sk_buff *skb)
+{
+ /*
+ * Count mapping we shortcut
+ */
+
+ ip_fw_chk(skb->nh.iph, skb->dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT);
+
+ dev_queue_xmit(skb);
+
+ return 0;
+}
+#endif
/*
* Generate a checksum for an outgoing IP datagram.
@@ -331,54 +339,64 @@ void ip_send_check(struct iphdr *iph)
* and compute the checksum
*/
-void ip_queue_xmit(struct sock *sk, struct device *dev,
- struct sk_buff *skb, int free)
+void ip_queue_xmit(struct sk_buff *skb)
{
+ struct sock *sk = skb->sk;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct device *dev = rt->u.dst.dev;
unsigned int tot_len;
- struct iphdr *iph;
-
- IS_SKB(skb);
+ struct iphdr *iph = skb->nh.iph;
/*
- * Do some book-keeping in the packet for later
+ * Discard the surplus MAC header
*/
+
+ skb_pull(skb, skb->nh.raw - skb->data);
+ tot_len = skb->len;
- skb->sk = sk;
- skb->dev = dev;
- skb->when = jiffies;
+ iph->tot_len = htons(tot_len);
+ iph->id = htons(ip_id_count++);
+ if (call_out_firewall(PF_INET, dev, iph, NULL,&skb) < FW_ACCEPT) {
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+#ifdef CONFIG_NET_SECURITY
/*
- * Find the IP header and set the length. This is bad
- * but once we get the skb data handling code in the
- * hardware will push its header sensibly and we will
- * set skb->ip_hdr to avoid this mess and the fixed
- * header length problem
+ * Add an IP checksum (must do this before SECurity because
+ * of possible tunneling)
*/
- iph = skb->ip_hdr;
- tot_len = skb->len - (((unsigned char *)iph) - skb->data);
- iph->tot_len = htons(tot_len);
+ ip_send_check(iph);
- switch (free) {
- /* No reassigning numbers to fragments... */
- case 2:
- free = 1;
- break;
- default:
- free = 1;
- iph->id = htons(ip_id_count++);
+ if (call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 4, &skb)<FW_ACCEPT)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return;
}
+
+ iph = skb->nh.iph;
+ /* don't update tot_len, as the dev->mtu is already decreased */
+#endif
- skb->free = free;
+ if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
+ struct sk_buff *skb2;
+ /* ANK: It is almost impossible, but
+ * if you loaded module device with hh_len > MAX_HEADER,
+ * and if a route changed to this device,
+ * and if (uh...) TCP had segments queued on this route...
+ */
+ skb2 = skb_realloc_headroom(skb, (dev->hard_header_len+15)&~15);
+ kfree_skb(skb, FREE_WRITE);
+ if (skb2 == NULL)
+ return;
+ skb = skb2;
+ iph = skb->nh.iph;
+ }
- /* Sanity check */
- if (dev == NULL)
- goto no_device;
+ ip_ll_header(skb);
-#ifdef CONFIG_FIREWALL
- if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
- goto out;
-#endif
/*
* Do we need to fragment. Again this is inefficient.
@@ -386,10 +404,8 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
* bits of it.
*/
- if (tot_len > dev->mtu)
- {
+ if (tot_len > rt->u.dst.pmtu)
goto fragment;
- }
/*
* Add an IP checksum
@@ -397,101 +413,27 @@ void ip_queue_xmit(struct sock *sk, struct device *dev,
ip_send_check(iph);
- /*
- * More debugging. You cannot queue a packet already on a list
- * Spot this and moan loudly.
- */
- if (skb->next != NULL)
- {
- NETDEBUG(printk("ip_queue_xmit: next != NULL\n"));
- skb_unlink(skb);
- }
-
- /*
- * If the indicated interface is up and running, send the packet.
- */
-
- ip_statistics.IpOutRequests++;
-#ifdef CONFIG_IP_ACCT
- ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif
-
-#ifdef CONFIG_IP_MULTICAST
-
- /*
- * Multicasts are looped back for other local users
- */
-
- if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK))
- {
- if(sk==NULL || sk->ip_mc_loop)
- {
- if(iph->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
- {
- ip_loopback(dev,skb);
- }
- else
- {
- struct ip_mc_list *imc=dev->ip_mc_list;
- while(imc!=NULL)
- {
- if(imc->multiaddr==iph->daddr)
- {
- ip_loopback(dev,skb);
- break;
- }
- imc=imc->next;
- }
- }
- }
- /* Multicasts with ttl 0 must not go beyond the host */
-
- if (iph->ttl==0)
- goto out;
- }
-#endif
- if ((dev->flags & IFF_BROADCAST) && !(dev->flags & IFF_LOOPBACK)
- && (iph->daddr==dev->pa_brdaddr || iph->daddr==0xFFFFFFFF))
- ip_loopback(dev,skb);
-
- if (dev->flags & IFF_UP)
- {
- /*
- * If we have an owner use its priority setting,
- * otherwise use NORMAL
- */
- int priority = SOPRI_NORMAL;
- if (sk)
- priority = sk->priority;
-
- dev_queue_xmit(skb, dev, priority);
- return;
- }
- if(sk)
- sk->err = ENETDOWN;
- ip_statistics.IpOutDiscards++;
-out:
- if (free)
- kfree_skb(skb, FREE_WRITE);
+ if (sk)
+ skb->priority = sk->priority;
+ skb->dst->output(skb);
return;
-no_device:
- NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
- goto out;
-
fragment:
if ((iph->frag_off & htons(IP_DF)))
{
printk(KERN_DEBUG "sending pkt_too_big to self\n");
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
- htonl(dev->mtu), dev);
- goto out;
+ htonl(dev->mtu));
+
+ kfree_skb(skb, FREE_WRITE);
+ return;
}
- ip_fragment(sk,skb,dev,0);
- goto out;
+
+ ip_fragment(skb, 1, skb->dst->output);
}
+
/*
* Build and send a packet, with as little as one copy
*
@@ -514,166 +456,96 @@ fragment:
int ip_build_xmit(struct sock *sk,
int getfrag (const void *,
- __u32,
char *,
unsigned int,
unsigned int),
- const void *frag,
- unsigned short int length,
- __u32 daddr,
- __u32 user_saddr,
- struct options * opt,
- int flags,
- int type,
- int noblock)
+ const void *frag,
+ unsigned short length,
+ struct ipcm_cookie *ipc,
+ struct rtable *rt,
+ int flags)
{
- struct rtable *rt;
unsigned int fraglen, maxfraglen, fragheaderlen;
+ int err;
int offset, mf;
- __u32 saddr;
unsigned short id;
struct iphdr *iph;
- __u32 raddr;
- struct device *dev = NULL;
- struct hh_cache * hh=NULL;
+ int hh_len = rt->u.dst.dev->hard_header_len;
int nfrags=0;
- __u32 true_daddr = daddr;
- int err;
-
- if (opt && opt->srr && !sk->ip_hdrincl)
- daddr = opt->faddr;
-
- ip_statistics.IpOutRequests++;
-
-#ifdef CONFIG_IP_MULTICAST
- if(MULTICAST(daddr) && *sk->ip_mc_name)
- {
- dev=dev_get(sk->ip_mc_name);
- if(!dev)
- return -ENODEV;
- rt=NULL;
- if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
- saddr = sk->saddr;
- else
- saddr = dev->pa_addr;
- }
- else
- {
+ struct ip_options *opt = ipc->opt;
+ struct device *dev = rt->u.dst.dev;
+ int df = htons(IP_DF);
+#ifdef CONFIG_NET_SECURITY
+ int fw_res;
#endif
- rt = ip_check_route(&sk->ip_route_cache, daddr,
- sk->localroute || (flags&MSG_DONTROUTE) ||
- (opt && opt->is_strictroute));
- if (rt == NULL)
- {
- ip_statistics.IpOutNoRoutes++;
- return(-ENETUNREACH);
- }
- saddr = rt->rt_src;
- hh = rt->rt_hh;
-
- if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
- saddr = sk->saddr;
-
- dev=rt->rt_dev;
-#ifdef CONFIG_IP_MULTICAST
- }
- if (rt && !dev)
- dev = rt->rt_dev;
-#endif
- if (user_saddr)
- saddr = user_saddr;
+ if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+ (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+ rt->rt_flags&RTF_NOPMTUDISC))
+ df = 0;
- raddr = rt ? rt->rt_gateway : daddr;
- /*
- * Now compute the buffer space we require
- */
/*
- * Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by
+ * Try the simple case first. This leaves fragmented frames, and by
* choice RAW frames within 20 bytes of maximum size(rare) to the long path
*/
- if (!sk->ip_hdrincl) {
+ if (!sk->ip_hdrincl)
length += sizeof(struct iphdr);
- if(opt) length += opt->optlen;
- }
- if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
- {
+ if (length <= rt->u.dst.pmtu && opt == NULL) {
int error;
- struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, noblock, &error);
- if(skb==NULL)
- {
+ struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+hh_len,
+ 0, flags&MSG_DONTWAIT, &error);
+ if(skb==NULL) {
ip_statistics.IpOutDiscards++;
return error;
}
- skb->dev=dev;
- skb->protocol = htons(ETH_P_IP);
- skb->free=1;
+
skb->when=jiffies;
- skb->sk=sk;
- skb->arp=0;
- skb->saddr=saddr;
- skb->raddr = raddr;
- skb_reserve(skb,(dev->hard_header_len+15)&~15);
- if (hh)
- {
- skb->arp=1;
- memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
- if (!hh->hh_uptodate)
- {
- skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
- printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
-#endif
- }
- }
- else if(dev->hard_header)
- {
- if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0)
- skb->arp=1;
- }
- else
- skb->arp=1;
- skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length);
+ skb->priority = sk->priority;
+ skb->dst = dst_clone(&rt->u.dst);
+
+ ip_ll_header_reserve(skb);
+
+ skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
+
dev_lock_list();
- if(!sk->ip_hdrincl)
- {
+
+ if(!sk->ip_hdrincl) {
iph->version=4;
iph->ihl=5;
iph->tos=sk->ip_tos;
iph->tot_len = htons(length);
iph->id=htons(ip_id_count++);
- iph->frag_off = 0;
- iph->ttl=sk->ip_ttl;
- iph->protocol=type;
- iph->saddr=saddr;
- iph->daddr=daddr;
- if (opt)
- {
- iph->ihl += opt->optlen>>2;
- ip_options_build(skb, opt,
- true_daddr, dev->pa_addr, 0);
- }
+ iph->frag_off = df;
+ iph->ttl=sk->ip_mc_ttl;
+ if (!(rt->rt_flags&RTF_MULTICAST))
+ iph->ttl=sk->ip_ttl;
+ iph->protocol=sk->protocol;
+ iph->saddr=rt->rt_src;
+ iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
- err = getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
+ err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
}
else
- err = getfrag(frag, saddr, (void *)iph, 0, length);
-
+ err = getfrag(frag, (void *)iph, 0, length);
dev_unlock_list();
-
+
if (err)
- {
err = -EFAULT;
- }
-#ifdef CONFIG_FIREWALL
- if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL)< FW_ACCEPT)
+ if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
+ err = -EPERM;
+#ifdef CONFIG_NET_SECURITY
+ if ((fw_res=call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 5, &skb))<FW_ACCEPT)
{
- err = -EPERM;
+ kfree_skb(skb, FREE_WRITE);
+ if (fw_res != FW_QUEUE)
+ return -EPERM;
+ else
+ return 0;
}
#endif
@@ -683,39 +555,26 @@ int ip_build_xmit(struct sock *sk,
return err;
}
-#ifdef CONFIG_IP_ACCT
- ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif
- if(dev->flags&IFF_UP)
- dev_queue_xmit(skb,dev,sk->priority);
- else
- {
- ip_statistics.IpOutDiscards++;
- kfree_skb(skb, FREE_WRITE);
- }
- return 0;
+ return rt->u.dst.output(skb);
}
+
if (!sk->ip_hdrincl)
length -= sizeof(struct iphdr);
-
- if(opt)
- {
- length -= opt->optlen;
- fragheaderlen = dev->hard_header_len + sizeof(struct iphdr) + opt->optlen;
- maxfraglen = ((dev->mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
- }
- else
- {
- fragheaderlen = dev->hard_header_len;
+
+ if (opt) {
+ fragheaderlen = hh_len + sizeof(struct iphdr) + opt->optlen;
+ maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
+ } else {
+ fragheaderlen = hh_len;
if(!sk->ip_hdrincl)
- fragheaderlen += 20;
+ fragheaderlen += sizeof(struct iphdr);
/*
- * Fragheaderlen is the size of 'overhead' on each buffer.
- * Now work out the size of the frames to send.
+ * Fragheaderlen is the size of 'overhead' on each buffer. Now work
+ * out the size of the frames to send.
*/
- maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen;
+ maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
}
/*
@@ -730,8 +589,7 @@ int ip_build_xmit(struct sock *sk,
fraglen = length - offset + fragheaderlen;
- if(length-offset==0)
- {
+ if (length-offset==0) {
fraglen = maxfraglen;
offset -= maxfraglen-fragheaderlen;
}
@@ -747,7 +605,7 @@ int ip_build_xmit(struct sock *sk,
* Can't fragment raw packets
*/
- if (sk->ip_hdrincl && offset > 0)
+ if (offset > 0 && df)
return(-EMSGSIZE);
/*
@@ -766,8 +624,7 @@ int ip_build_xmit(struct sock *sk,
* Being outputting the bytes.
*/
- do
- {
+ do {
struct sk_buff * skb;
int error;
char *data;
@@ -776,12 +633,11 @@ int ip_build_xmit(struct sock *sk,
* Get the memory we require with some space left for alignment.
*/
- skb = sock_alloc_send_skb(sk, fraglen+15, 0, noblock, &error);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, fraglen+15, 0, flags&MSG_DONTWAIT, &error);
+ if (skb == NULL) {
ip_statistics.IpOutDiscards++;
if(nfrags>1)
- ip_statistics.IpFragCreates++;
+ ip_statistics.IpFragCreates++;
dev_unlock_list();
return(error);
}
@@ -790,81 +646,44 @@ int ip_build_xmit(struct sock *sk,
* Fill in the control structures
*/
- skb->dev = dev;
- skb->protocol = htons(ETH_P_IP);
skb->when = jiffies;
- skb->free = 1; /* dubious, this one */
- skb->sk = sk;
- skb->arp = 0;
- skb->saddr = saddr;
- skb->daddr = daddr;
- skb->raddr = raddr;
- skb_reserve(skb,(dev->hard_header_len+15)&~15);
- data = skb_put(skb, fraglen-dev->hard_header_len);
+ skb->priority = sk->priority;
+ skb->dst = dst_clone(&rt->u.dst);
+
+ ip_ll_header_reserve(skb);
- /*
- * Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok)
- * no ARP lookup (arp cache ok) and output. The cache checks are still too slow but
- * this can be fixed later. For gateway routes we ought to have a rt->.. header cache
- * pointer to speed header cache builds for identical targets.
- */
-
- if (hh)
- {
- skb->arp=1;
- memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
- if (!hh->hh_uptodate)
- {
- skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
- printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
-#endif
- }
- }
- else if (dev->hard_header)
- {
- if(dev->hard_header(skb, dev, ETH_P_IP,
- NULL, NULL, 0)>0)
- skb->arp=1;
- }
- else
- skb->arp = 1;
-
/*
* Find where to start putting bytes.
*/
- skb->ip_hdr = iph = (struct iphdr *)data;
+ data = skb_put(skb, fraglen-hh_len);
+ skb->nh.iph = iph = (struct iphdr *)data;
/*
* Only write IP header onto non-raw packets
*/
- if(!sk->ip_hdrincl)
- {
-
+ if(!sk->ip_hdrincl) {
iph->version = 4;
- iph->ihl = 5; /* ugh */
+ iph->ihl = 5;
if (opt) {
iph->ihl += opt->optlen>>2;
ip_options_build(skb, opt,
- true_daddr, dev->pa_addr, offset);
+ ipc->addr, dev->pa_addr, offset);
}
iph->tos = sk->ip_tos;
iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
iph->id = id;
iph->frag_off = htons(offset>>3);
- iph->frag_off |= mf;
-#ifdef CONFIG_IP_MULTICAST
- if (MULTICAST(daddr))
+ iph->frag_off |= mf|df;
+ if (rt->rt_flags&RTF_MULTICAST)
iph->ttl = sk->ip_mc_ttl;
else
-#endif
iph->ttl = sk->ip_ttl;
- iph->protocol = type;
+ iph->protocol = sk->protocol;
iph->check = 0;
- iph->saddr = saddr;
- iph->daddr = daddr;
+ iph->saddr = rt->rt_src;
+ iph->daddr = rt->rt_dst;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
data += iph->ihl*4;
@@ -879,127 +698,286 @@ int ip_build_xmit(struct sock *sk,
* User data callback
*/
- err = getfrag(frag, saddr, data, offset, fraglen-fragheaderlen);
+ err = getfrag(frag, data, offset, fraglen-fragheaderlen);
if (err)
- {
err = -EFAULT;
- }
/*
* Account for the fragment.
*/
-#ifdef CONFIG_FIREWALL
- if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
+ if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL, &skb) < FW_ACCEPT)
+ err = -EPERM;
+#ifdef CONFIG_NET_SECURITY
+ if ((fw_res=call_out_firewall(PF_SECURITY, NULL, NULL, (void *) 6, &skb))<FW_ACCEPT)
{
- err = -EPERM;
+ if (fw_res != FW_QUEUE)
+ err= -EPERM;
}
-#endif
+#endif
if (err)
- {
+ {
kfree_skb(skb, FREE_WRITE);
dev_unlock_list();
return err;
- }
-
-#ifdef CONFIG_IP_ACCT
- if(!offset)
- ip_fw_chk(iph, dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT);
-#endif
+ }
offset -= (maxfraglen-fragheaderlen);
fraglen = maxfraglen;
-#ifdef CONFIG_IP_MULTICAST
+ nfrags++;
+
+ if (rt->u.dst.output(skb)) {
+ if (nfrags>1)
+ ip_statistics.IpFragCreates += nfrags;
+ dev_unlock_list();
+ return -ENETDOWN;
+ }
+ } while (offset >= 0);
+
+ if (nfrags>1)
+ ip_statistics.IpFragCreates += nfrags;
+
+ dev_unlock_list();
+ return 0;
+}
+
+/*
+ * This IP datagram is too large to be sent in one piece. Break it up into
+ * smaller pieces (each of size equal to the MAC header plus IP header plus
+ * a block of the data of the original IP data part) that will yet fit in a
+ * single device frame, and queue such a frame for sending.
+ *
+ * Assumption: packet was ready for transmission, link layer header
+ * is already in.
+ *
+ * Yes this is inefficient, feel free to submit a quicker one.
+ */
+
+void ip_fragment(struct sk_buff *skb, int local, int (*output)(struct sk_buff*))
+{
+ struct iphdr *iph;
+ unsigned char *raw;
+ unsigned char *ptr;
+ struct device *dev;
+ struct sk_buff *skb2;
+ int left, mtu, hlen, len;
+ int offset;
+ int not_last_frag;
+ u16 dont_fragment;
+ struct rtable *rt = (struct rtable*)skb->dst;
+
+ dev = skb->dev;
+
+ /*
+ * Point into the IP datagram header.
+ */
+
+ raw = skb->data;
+ iph = skb->nh.iph;
+
+ /*
+ * Setup starting values.
+ */
+
+ hlen = iph->ihl * 4;
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ hlen += skb->nh.raw - raw;
+ if (local)
+ mtu = rt->u.dst.pmtu - hlen; /* Size of data space */
+ else
+ mtu = dev->mtu - hlen;
+ ptr = raw + hlen; /* Where to start from */
+
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ */
+
+ if (mtu<8) {
+ ip_statistics.IpFragFails++;
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+ /*
+ * Fragment the datagram.
+ */
+
+ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+ not_last_frag = iph->frag_off & htons(IP_MF);
+
+ /*
+ * Nice moment: if DF is set and we are here,
+ * it means that packet should be fragmented and
+ * DF is set on fragments. If it works,
+ * path MTU discovery can be done by ONE segment(!). --ANK
+ */
+ dont_fragment = iph->frag_off & htons(IP_DF);
+
+ /*
+ * Keep copying data until we run out.
+ */
+
+ while(left > 0) {
+ len = left;
+ /* IF: it doesn't fit, use 'mtu' - the data space left */
+ if (len > mtu)
+ len = mtu;
+ /* IF: we are not sending upto and including the packet end
+ then align the next start on an eight byte boundary */
+ if (len < left) {
+ len/=8;
+ len*=8;
+ }
/*
- * Multicasts are looped back for other local users
+ * Allocate buffer.
*/
-
- if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK))
- {
- /*
- * Loop back any frames. The check for IGMP_ALL_HOSTS is because
- * you are always magically a member of this group.
- *
- * Always loop back all host messages when running as a multicast router.
- */
-
- if(sk==NULL || sk->ip_mc_loop)
- {
- if(daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
- ip_loopback(dev,skb);
- else
- {
- struct ip_mc_list *imc=dev->ip_mc_list;
- while(imc!=NULL)
- {
- if(imc->multiaddr==daddr)
- {
- ip_loopback(dev,skb);
- break;
- }
- imc=imc->next;
- }
- }
- }
- /*
- * Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the
- * extra clone.
- */
-
- if(skb->ip_hdr->ttl==0)
- {
- kfree_skb(skb, FREE_WRITE);
- nfrags++;
- continue;
- }
+ if ((skb2 = alloc_skb(len+hlen+15,GFP_ATOMIC)) == NULL) {
+ NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+ ip_statistics.IpFragFails++;
+ kfree_skb(skb, FREE_WRITE);
+ return;
}
-#endif
- nfrags++;
-
/*
- * BSD loops broadcasts
+ * Set up data on packet
*/
-
- if((dev->flags&IFF_BROADCAST) && (daddr==0xFFFFFFFF || daddr==dev->pa_brdaddr) && !(dev->flags&IFF_LOOPBACK))
- ip_loopback(dev,skb);
+
+ skb2->arp = skb->arp;
+ skb2->dev = skb->dev;
+ skb2->when = skb->when;
+ skb2->pkt_type = skb->pkt_type;
+ skb2->priority = skb->priority;
+ skb_put(skb2, len + hlen);
+ skb2->mac.raw = (char *) skb2->data;
+ skb2->nh.raw = skb2->mac.raw + dev->hard_header_len;
+ skb2->h.raw = skb2->mac.raw + hlen;
/*
- * Now queue the bytes into the device.
+ * Charge the memory for the fragment to any owner
+ * it might possess
*/
-
- if (dev->flags & IFF_UP)
- {
- dev_queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- /*
- * Whoops...
- */
-
- ip_statistics.IpOutDiscards++;
- if(nfrags>1)
- ip_statistics.IpFragCreates+=nfrags;
- kfree_skb(skb, FREE_WRITE);
- dev_unlock_list();
- /*
- * BSD behaviour.
- */
- if(sk!=NULL)
- sk->err=ENETDOWN;
- return(0); /* lose rest of fragments */
- }
- }
- while (offset >= 0);
- if(nfrags>1)
- ip_statistics.IpFragCreates+=nfrags;
- dev_unlock_list();
- return(0);
+
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ skb2->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
+ memcpy(skb2->mac.raw, raw, hlen);
+
+ /*
+ * Copy a block of the IP datagram.
+ */
+ memcpy(skb2->h.raw, ptr, len);
+ left -= len;
+
+ /*
+ * Fill in the new header fields.
+ */
+ iph = skb2->nh.iph;
+ iph->frag_off = htons((offset >> 3))|dont_fragment;
+
+ /* ANK: dirty, but effective trick. Upgrade options only if
+ * the segment to be fragmented was THE FIRST (otherwise,
+ * options are already fixed) and make it ONCE
+ * on the initial skb, so that all the following fragments
+ * will inherit fixed options.
+ */
+ if (offset == 0)
+ ip_options_fragment(skb2);
+
+ /*
+ * Added AC : If we are fragmenting a fragment that's not the
+ * last fragment then keep MF on each bit
+ */
+ if (left > 0 || not_last_frag)
+ iph->frag_off |= htons(IP_MF);
+ ptr += len;
+ offset += len;
+
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ ip_statistics.IpFragCreates++;
+
+ iph->tot_len = htons(len + hlen - dev->hard_header_len);
+
+ ip_send_check(iph);
+
+ output(skb2);
+ }
+ kfree_skb(skb, FREE_WRITE);
+ ip_statistics.IpFragOKs++;
+}
+
+struct sk_buff * ip_reply(struct sk_buff *skb, int payload)
+{
+ struct {
+ struct ip_options opt;
+ char data[40];
+ } replyopts;
+
+ struct rtable *rt = (struct rtable*)skb->dst;
+ struct sk_buff *reply;
+ int iphlen;
+ struct iphdr *iph;
+
+ struct ipcm_cookie ipc;
+ u32 daddr;
+
+ if (ip_options_echo(&replyopts.opt, skb))
+ return NULL;
+
+ daddr = ipc.addr = rt->rt_src;
+ ipc.opt = &replyopts.opt;
+ if (ipc.opt->srr)
+ daddr = replyopts.opt.faddr;
+
+ if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), NULL))
+ return NULL;
+
+ iphlen = sizeof(struct iphdr) + replyopts.opt.optlen;
+ reply = alloc_skb(rt->u.dst.dev->hard_header_len+15+iphlen+payload, GFP_ATOMIC);
+ if (reply == NULL) {
+ ip_rt_put(rt);
+ return NULL;
+ }
+
+ reply->priority = skb->priority;
+ reply->dst = &rt->u.dst;
+
+ ip_ll_header_reserve(reply);
+
+ /*
+ * Now build the IP header.
+ */
+
+ /*
+ * Build the IP addresses
+ */
+
+ reply->nh.iph = iph = (struct iphdr *)skb_put(reply, iphlen);
+
+ iph->version = 4;
+ iph->ihl = iphlen>>2;
+ iph->tos = skb->nh.iph->tos;
+ iph->frag_off = 0;
+ iph->ttl = MAXTTL;
+ iph->daddr = rt->rt_dst;
+ iph->saddr = rt->rt_src;
+ iph->protocol = skb->nh.iph->protocol;
+
+ ip_options_build(reply, &replyopts.opt, daddr, rt->u.dst.dev->pa_addr, 0);
+
+ return reply;
}
-
/*
* IP protocol layer initialiser
@@ -1007,80 +985,44 @@ int ip_build_xmit(struct sock *sk,
static struct packet_type ip_packet_type =
{
- 0, /* MUTTER ntohs(ETH_P_IP),*/
+ __constant_htons(ETH_P_IP),
NULL, /* All devices */
ip_rcv,
NULL,
NULL,
};
-#ifdef CONFIG_RTNETLINK
-
-/*
- * Netlink hooks for IP
- */
-
-void ip_netlink_msg(unsigned long msg, __u32 daddr, __u32 gw, __u32 mask, short flags, short metric, char *name)
-{
- struct sk_buff *skb=alloc_skb(sizeof(struct netlink_rtinfo), GFP_ATOMIC);
- struct netlink_rtinfo *nrt;
- struct sockaddr_in *s;
- if(skb==NULL)
- return;
- skb->free=1;
- nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo));
- nrt->rtmsg_type=msg;
- s=(struct sockaddr_in *)&nrt->rtmsg_dst;
- s->sin_family=AF_INET;
- s->sin_addr.s_addr=daddr;
- s=(struct sockaddr_in *)&nrt->rtmsg_gateway;
- s->sin_family=AF_INET;
- s->sin_addr.s_addr=gw;
- s=(struct sockaddr_in *)&nrt->rtmsg_genmask;
- s->sin_family=AF_INET;
- s->sin_addr.s_addr=mask;
- nrt->rtmsg_flags=flags;
- nrt->rtmsg_metric=metric;
- strcpy(nrt->rtmsg_device,name);
- if (netlink_post(NETLINK_ROUTE, skb))
- kfree_skb(skb, FREE_WRITE);
-}
-
-#endif
/*
* Device notifier
*/
-static int ip_rt_event(struct notifier_block *this, unsigned long event, void *ptr)
+static int ip_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
struct device *dev=ptr;
- if(event==NETDEV_DOWN)
- {
- ip_netlink_msg(RTMSG_DELDEVICE, 0,0,0,0,0,dev->name);
- ip_rt_flush(dev);
- }
-/*
- * Join the initial group if multicast.
- */
- if(event==NETDEV_UP)
+
+ if (dev->family != AF_INET)
+ return NOTIFY_DONE;
+
+ if(event==NETDEV_UP)
{
-#ifdef CONFIG_IP_MULTICAST
+ /*
+ * Join the initial group if multicast.
+ */
ip_mc_allhost(dev);
-#endif
- ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name);
- ip_rt_update(NETDEV_UP, dev);
}
- return NOTIFY_DONE;
+ if(event==NETDEV_DOWN)
+ ip_mc_drop_device(dev);
+
+ return ip_rt_event(event, dev);
}
-struct notifier_block ip_rt_notifier={
- ip_rt_event,
+struct notifier_block ip_netdev_notifier={
+ ip_netdev_event,
NULL,
0
};
-#ifdef CONFIG_IP_MULTICAST
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_net_igmp = {
PROC_NET_IGMP, 4, "igmp",
@@ -1089,7 +1031,6 @@ static struct proc_dir_entry proc_net_igmp = {
ip_mc_procinfo
};
#endif
-#endif
/*
* IP registers the packet type and then calls the subprotocol initialisers
@@ -1097,21 +1038,15 @@ static struct proc_dir_entry proc_net_igmp = {
void ip_init(void)
{
- ip_packet_type.type=htons(ETH_P_IP);
dev_add_pack(&ip_packet_type);
- /* So we flush routes when a device is downed */
- register_netdevice_notifier(&ip_rt_notifier);
+ ip_rt_init();
-/* ip_raw_init();
- ip_packet_init();
- ip_tcp_init();
- ip_udp_init();*/
+ /* So we flush routes and multicast lists when a device is downed */
+ register_netdevice_notifier(&ip_netdev_notifier);
-#ifdef CONFIG_IP_MULTICAST
#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_igmp);
#endif
-#endif
}
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 7337fc08c..1689159ed 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -36,102 +36,155 @@
#include <asm/uaccess.h>
-#ifdef CONFIG_IP_MULTICAST
-
/*
- * Write an multicast group list table for the IGMP daemon to
- * read.
+ * SOL_IP control messages.
*/
-
-int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+
+static void ip_cmsg_recv_rxinfo(struct msghdr *msg, struct sk_buff *skb)
{
- off_t pos=0, begin=0;
- struct ip_mc_list *im;
- unsigned long flags;
- int len=0;
- struct device *dev;
-
- len=sprintf(buffer,"Device : Count\tGroup Users Timer\n");
- save_flags(flags);
- cli();
-
- for(dev = dev_base; dev; dev = dev->next)
- {
- if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST))
- {
- len+=sprintf(buffer+len,"%-10s: %5d\n",
- dev->name, dev->mc_count);
- for(im = dev->ip_mc_list; im; im = im->next)
- {
- len+=sprintf(buffer+len,
- "\t\t\t%08lX %5d %d:%08lX\n",
- im->multiaddr, im->users,
- im->tm_running, im->timer.expires-jiffies);
- pos=begin+len;
- if(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- }
- }
+ struct in_pktinfo info;
+ struct rtable *rt = (struct rtable *)skb->dst;
+
+ info.ipi_ifindex = skb->dev->ifindex;
+ info.ipi_addr.s_addr = skb->nh.iph->daddr;
+ info.ipi_spec_dst.s_addr = rt->rt_spec_dst;
+
+ put_cmsg(msg, SOL_IP, IP_RXINFO, sizeof(info), &info);
+}
+
+static void ip_cmsg_recv_localaddr(struct msghdr *msg, struct sk_buff *skb, int local)
+{
+ struct in_addr addr;
+
+ addr.s_addr = skb->nh.iph->daddr;
+
+ if (local) {
+ struct rtable *rt = (struct rtable *)skb->dst;
+ addr.s_addr = rt->rt_spec_dst;
}
- restore_flags(flags);
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
+ put_cmsg(msg, SOL_IP, local ? IP_LOCALADDR : IP_RECVDSTADDR,
+ sizeof(addr), &addr);
}
+static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
+{
+ if (IPCB(skb)->opt.optlen == 0)
+ return;
+
+ put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
+}
-/*
- * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
- * an IP socket.
- *
- * We implement IP_TOS (type of service), IP_TTL (time to live).
- */
-static struct device *ip_mc_find_devfor(unsigned long addr)
+void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
{
- struct device *dev;
- for(dev = dev_base; dev; dev = dev->next)
- {
- if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
- (dev->pa_addr==addr))
- return dev;
+ unsigned char optbuf[sizeof(struct ip_options) + 40];
+ struct ip_options * opt = (struct ip_options*)optbuf;
+
+ if (IPCB(skb)->opt.optlen == 0)
+ return;
+
+ if (ip_options_echo(opt, skb)) {
+ msg->msg_flags |= MSG_CTRUNC;
+ return;
}
+ ip_options_undo(opt);
- return NULL;
+ put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
}
-#endif
+
+void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
+{
+ unsigned flags = skb->sk->ip_cmsg_flags;
+
+ /* Ordered by supposed usage frequency */
+ if (flags & 1)
+ ip_cmsg_recv_rxinfo(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+ if (flags & 1)
+ ip_cmsg_recv_localaddr(msg, skb, 1);
+ if ((flags>>=1) == 0)
+ return;
+ if (flags & 1)
+ ip_cmsg_recv_opts(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+ if (flags & 1)
+ ip_cmsg_recv_retopts(msg, skb);
+ if ((flags>>=1) == 0)
+ return;
+ if (flags & 1)
+ ip_cmsg_recv_localaddr(msg, skb, 0);
+}
+
+int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc, struct device **devp)
+{
+ int err;
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_IP)
+ continue;
+ switch (cmsg->cmsg_type)
+ {
+ case IP_LOCALADDR:
+ if (cmsg->cmsg_len < sizeof(struct in_addr)+sizeof(*cmsg))
+ return -EINVAL;
+ memcpy(&ipc->addr, cmsg->cmsg_data, 4);
+ break;
+ case IP_RETOPTS:
+ err = cmsg->cmsg_len - sizeof(*cmsg);
+ err = ip_options_get(&ipc->opt, cmsg->cmsg_data,
+ err < 40 ? err : 40, 0);
+ if (err)
+ return err;
+ break;
+ case IP_TXINFO:
+ {
+ struct in_pktinfo *info;
+ if (cmsg->cmsg_len < sizeof(*info)+sizeof(*cmsg))
+ return -EINVAL;
+ info = (struct in_pktinfo*)cmsg->cmsg_data;
+ if (info->ipi_ifindex && !devp)
+ return -EINVAL;
+ if ((*devp = dev_get_by_index(info->ipi_ifindex)) == NULL)
+ return -ENODEV;
+ ipc->addr = info->ipi_spec_dst.s_addr;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
+ * an IP socket.
+ *
+ * We implement IP_TOS (type of service), IP_TTL (time to live).
+ */
int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
- int val,err;
- unsigned char ucval;
+ int val=0,err;
+ unsigned char ucval = 0;
#if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
struct ip_fw tmp_fw;
#endif
- if (optval == NULL)
- {
- val=0;
- ucval=0;
- }
- else
- {
- err = get_user(val, (int *) optval);
- if (!err)
- err = get_user(ucval, (unsigned char *) optval);
- if (err)
- return err;
+
+ if(optlen>=sizeof(int)) {
+ if(get_user(val, (int *) optval))
+ return -EFAULT;
+ } else if(optlen>=sizeof(char)) {
+ if(get_user(ucval, (unsigned char *) optval))
+ return -EFAULT;
}
if(level!=SOL_IP)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
#ifdef CONFIG_IP_MROUTE
if(optname>=MRT_BASE && optname <=MRT_BASE+10)
{
@@ -142,110 +195,149 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
switch(optname)
{
case IP_OPTIONS:
- {
- struct options * opt = NULL;
- struct options * old_opt;
- if (optlen > 40 || optlen < 0)
- return -EINVAL;
- opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL);
- if (!opt)
- return -ENOMEM;
- memset(opt, 0, sizeof(struct options));
- if (optlen)
- {
- err = copy_from_user(opt->__data, optval, optlen);
- if (err)
- {
- kfree_s(opt, sizeof(struct options) + ((optlen+3)&~3));
- return -EFAULT;
- }
- }
-
- while (optlen & 3)
- opt->__data[optlen++] = IPOPT_END;
- opt->optlen = optlen;
- opt->is_data = 1;
- opt->is_setbyuser = 1;
- if (optlen && ip_options_compile(opt, NULL))
- {
- kfree_s(opt, sizeof(struct options) + optlen);
- return -EINVAL;
- }
- /*
- * ANK: I'm afraid that receive handler may change
- * options from under us.
- */
- cli();
- old_opt = sk->opt;
- sk->opt = opt;
- sti();
- if (old_opt)
- kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
- return 0;
- }
- case IP_TOS: /* This sets both TOS and Precedence */
- if (val & ~0xfe) /* Reject setting of unused bits */
+ {
+ struct ip_options * opt = NULL;
+ struct ip_options * old_opt;
+ if (optlen > 40 || optlen < 0)
+ return -EINVAL;
+ err = ip_options_get(&opt, optval, optlen, 1);
+ if (err)
+ return err;
+ /*
+ * ANK: I'm afraid that receive handler may change
+ * options from under us.
+ */
+ cli();
+ old_opt = sk->opt;
+ sk->opt = opt;
+ sti();
+ if (old_opt)
+ kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
+ return 0;
+ }
+ case IP_RXINFO:
+ if (optlen<4)
+ return -EINVAL;
+ if (val)
+ sk->ip_cmsg_flags |= 1;
+ else
+ sk->ip_cmsg_flags &= ~1;
+ return 0;
+ case IP_LOCALADDR:
+ if (optlen<4)
+ return -EINVAL;
+ if (val)
+ sk->ip_cmsg_flags |= 2;
+ else
+ sk->ip_cmsg_flags &= ~2;
+ return 0;
+ case IP_RECVOPTS:
+ if (optlen<4)
+ return -EINVAL;
+ if (val)
+ sk->ip_cmsg_flags |= 4;
+ else
+ sk->ip_cmsg_flags &= ~4;
+ return 0;
+ case IP_RETOPTS:
+ if (optlen<4)
+ return -EINVAL;
+ if (val)
+ sk->ip_cmsg_flags |= 8;
+ else
+ sk->ip_cmsg_flags &= ~8;
+ return 0;
+ case IP_RECVDSTADDR:
+ if (optlen<4)
+ return -EINVAL;
+ if (val)
+ sk->ip_cmsg_flags |= 0x10;
+ else
+ sk->ip_cmsg_flags &= ~0x10;
+ return 0;
+ case IP_TOS: /* This sets both TOS and Precedence */
+ /* Reject setting of unused bits */
+ if (optlen<4)
+ return -EINVAL;
+ if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
return -EINVAL;
- if ((val>>5) > 4 && !suser()) /* Only root can set Prec>4 */
+ if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
return -EPERM;
sk->ip_tos=val;
- switch (val & 0x1E) {
- case IPTOS_LOWDELAY:
- sk->priority=SOPRI_INTERACTIVE;
- break;
- case IPTOS_THROUGHPUT:
- case IPTOS_MINCOST:
- sk->priority=SOPRI_BACKGROUND;
- break;
- default:
- sk->priority=SOPRI_NORMAL;
- break;
- }
+ sk->priority = rt_tos2priority(val);
return 0;
case IP_TTL:
+ if (optlen<4)
+ return -EINVAL;
if(val<1||val>255)
return -EINVAL;
sk->ip_ttl=val;
return 0;
case IP_HDRINCL:
+ if (optlen<4)
+ return -EINVAL;
if(sk->type!=SOCK_RAW)
return -ENOPROTOOPT;
sk->ip_hdrincl=val?1:0;
return 0;
-#ifdef CONFIG_IP_MULTICAST
+ case IP_PMTUDISC:
+ if (optlen<4)
+ return -EINVAL;
+ if (val<0 || val>2)
+ return -EINVAL;
+ sk->ip_pmtudisc = val;
+ return 0;
+ case IP_RECVERR:
+ if (optlen<4)
+ return -EINVAL;
+ if (sk->type==SOCK_STREAM)
+ return -ENOPROTOOPT;
+ lock_sock(sk);
+ if (sk->ip_recverr && !val) {
+ struct sk_buff *skb;
+ /* Drain queued errors */
+ while((skb=skb_dequeue(&sk->error_queue))!=NULL)
+ kfree_skb(skb, FREE_READ);
+ }
+ sk->ip_recverr = val?1:0;
+ release_sock(sk);
+ return 0;
case IP_MULTICAST_TTL:
- {
+ if (optlen<1)
+ return -EINVAL;
sk->ip_mc_ttl=(int)ucval;
return 0;
- }
case IP_MULTICAST_LOOP:
- {
+ if (optlen<1)
+ return -EINVAL;
if(ucval!=0 && ucval!=1)
return -EINVAL;
sk->ip_mc_loop=(int)ucval;
return 0;
- }
case IP_MULTICAST_IF:
{
struct in_addr addr;
- struct device *dev=NULL;
+ struct device *dev = NULL;
/*
* Check the arguments are allowable
*/
+
+ if(optlen<sizeof(addr))
+ return -EINVAL;
- err = copy_from_user(&addr,optval,sizeof(addr));
- if (err)
+ if(copy_from_user(&addr,optval,sizeof(addr)))
return -EFAULT;
+
/*
* What address has been requested
*/
- if(addr.s_addr==INADDR_ANY) /* Default */
+ if (addr.s_addr==INADDR_ANY) /* Default */
{
- sk->ip_mc_name[0]=0;
+ sk->ip_mc_index = 0;
return 0;
}
@@ -253,7 +345,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
* Find the device
*/
- dev=ip_mc_find_devfor(addr.s_addr);
+ dev=ip_dev_find(addr.s_addr, NULL);
/*
* Did we find one
@@ -261,11 +353,12 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
if(dev)
{
- strcpy(sk->ip_mc_name,dev->name);
+ sk->ip_mc_index = dev->ifindex;
return 0;
}
return -EADDRNOTAVAIL;
}
+
case IP_ADD_MEMBERSHIP:
{
@@ -280,35 +373,24 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
/*
* Check the arguments.
*/
-
- err = copy_from_user(&mreq,optval,sizeof(mreq));
- if (err)
+
+ if(optlen<sizeof(mreq))
+ return -EINVAL;
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
return -EFAULT;
/*
* Get device for use later
*/
- if(mreq.imr_interface.s_addr==INADDR_ANY)
- {
- /*
- * Not set so scan.
- */
- if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
- {
- dev=rt->rt_dev;
- atomic_dec(&rt->rt_use);
- ip_rt_put(rt);
- }
- }
- else
- {
- /*
- * Find a suitable device.
- */
-
- dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
- }
+ if (mreq.imr_interface.s_addr==INADDR_ANY) {
+ err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+ if (err)
+ return err;
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ } else
+ dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
/*
* No device, no cookies.
@@ -334,28 +416,23 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
* Check the arguments
*/
- err = copy_from_user(&mreq,optval,sizeof(mreq));
- if (err)
+ if(optlen<sizeof(mreq))
+ return -EINVAL;
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
return -EFAULT;
/*
* Get device for use later
*/
- if(mreq.imr_interface.s_addr==INADDR_ANY)
- {
- if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
- {
- dev=rt->rt_dev;
- atomic_dec(&rt->rt_use);
- ip_rt_put(rt);
- }
- }
- else
- {
-
- dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
- }
+ if (mreq.imr_interface.s_addr==INADDR_ANY) {
+ err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+ if (err)
+ return err;
+ dev = rt->u.dst.dev;
+ ip_rt_put(rt);
+ } else
+ dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
/*
* Did we find a suitable device.
@@ -370,7 +447,69 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
}
-#endif
+
+ case IP_MULTICAST_IFN:
+ {
+ struct ip_mreqn mreq;
+ struct device *dev = NULL;
+
+ if(optlen<sizeof(mreq))
+ return -EINVAL;
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
+ return -EFAULT;
+
+ if (!mreq.imr_ifindex) {
+ if (!mreq.imr_address.s_addr) {
+ sk->ip_mc_index = 0;
+ sk->ip_mc_addr = 0;
+ return 0;
+ }
+ dev = ip_dev_find(mreq.imr_address.s_addr, NULL);
+ } else
+ dev = dev_get_by_index(mreq.imr_ifindex);
+
+ if (!dev)
+ return -ENODEV;
+
+ sk->ip_mc_index = mreq.imr_ifindex;
+ sk->ip_mc_addr = mreq.imr_address.s_addr;
+ return 0;
+ }
+ case IP_ADD_MEMBERSHIPN:
+ {
+ struct ip_mreqn mreq;
+ struct device *dev = NULL;
+
+ if(optlen<sizeof(mreq))
+ return -EINVAL;
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
+ return -EFAULT;
+ dev = dev_get_by_index(mreq.imr_ifindex);
+ if (!dev)
+ return -ENODEV;
+ return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
+ }
+
+ case IP_DROP_MEMBERSHIPN:
+ {
+ struct ip_mreqn mreq;
+ struct device *dev=NULL;
+
+ /*
+ * Check the arguments
+ */
+
+ if(optlen<sizeof(mreq))
+ return -EINVAL;
+ if(copy_from_user(&mreq,optval,sizeof(mreq)))
+ return -EFAULT;
+
+ dev=dev_get_by_index(mreq.imr_ifindex);
+ if(!dev)
+ return -ENODEV;
+
+ return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
+ }
#ifdef CONFIG_IP_FIREWALL
case IP_FW_INSERT_IN:
case IP_FW_INSERT_OUT:
@@ -395,12 +534,11 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
case IP_FW_POLICY_FWD:
case IP_FW_MASQ_TIMEOUTS:
if(!suser())
- return -EPERM;
+ return -EACCES;
if(optlen>sizeof(tmp_fw) || optlen<1)
return -EINVAL;
- err = copy_from_user(&tmp_fw,optval,optlen);
- if (err)
- return -EFAULT;
+ if(copy_from_user(&tmp_fw,optval,optlen))
+ return -EFAULT;
err=ip_fw_ctl(optname, &tmp_fw,optlen);
return -err; /* -0 is 0 after all */
@@ -412,16 +550,14 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
case IP_ACCT_FLUSH:
case IP_ACCT_ZERO:
if(!suser())
- return -EPERM;
+ return -EACCES;
if(optlen>sizeof(tmp_fw) || optlen<1)
return -EINVAL;
- err = copy_from_user(&tmp_fw, optval,optlen);
- if (err)
+ if(copy_from_user(&tmp_fw, optval,optlen))
return -EFAULT;
err=ip_acct_ctl(optname, &tmp_fw,optlen);
return -err; /* -0 is 0 after all */
#endif
- /* IP_OPTIONS and friends go here eventually */
default:
return(-ENOPROTOOPT);
}
@@ -435,9 +571,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
{
int val,err;
-#ifdef CONFIG_IP_MULTICAST
int len;
-#endif
if(level!=SOL_IP)
return -EOPNOTSUPP;
@@ -449,59 +583,46 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
}
#endif
+ if(get_user(len,optlen))
+ return -EFAULT;
+
switch(optname)
{
case IP_OPTIONS:
{
- unsigned char optbuf[sizeof(struct options)+40];
- struct options * opt = (struct options*)optbuf;
-
+ unsigned char optbuf[sizeof(struct ip_options)+40];
+ struct ip_options * opt = (struct ip_options*)optbuf;
cli();
opt->optlen = 0;
if (sk->opt)
- memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen);
+ memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
sti();
if (opt->optlen == 0)
- {
return put_user(0, optlen);
- }
-/*
- * Now we should undo all the changes done by ip_options_compile().
- */
- if (opt->srr)
- {
- unsigned char * optptr = opt->__data+opt->srr-sizeof(struct iphdr);
- memmove(optptr+7, optptr+3, optptr[1]-7);
- memcpy(optptr+3, &opt->faddr, 4);
- }
- if (opt->rr_needaddr)
- {
- unsigned char * optptr = opt->__data+opt->rr-sizeof(struct iphdr);
- memset(&optptr[optptr[2]-1], 0, 4);
- optptr[2] -= 4;
- }
- if (opt->ts)
- {
- unsigned char * optptr = opt->__data+opt->ts-sizeof(struct iphdr);
- if (opt->ts_needtime)
- {
- memset(&optptr[optptr[2]-1], 0, 4);
- optptr[2] -= 4;
- }
- if (opt->ts_needaddr)
- {
- memset(&optptr[optptr[2]-1], 0, 4);
- optptr[2] -= 4;
- }
- }
- err = put_user(opt->optlen, optlen);
- if (!err)
- {
- if(copy_to_user(optval, opt->__data, opt->optlen))
- err = -EFAULT;
- }
- return err;
+
+ ip_options_undo(opt);
+
+ len=min(len, opt->optlen);
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval, opt->__data, len))
+ return -EFAULT;
+ return 0;
}
+ case IP_RXINFO:
+ val = (sk->ip_cmsg_flags & 1) != 0;
+ return 0;
+ case IP_LOCALADDR:
+ val = (sk->ip_cmsg_flags & 2) != 0;
+ return 0;
+ case IP_RECVOPTS:
+ val = (sk->ip_cmsg_flags & 4) != 0;
+ return 0;
+ case IP_RETOPTS:
+ val = (sk->ip_cmsg_flags & 8) != 0;
+ return 0;
+ case IP_RECVDSTADDR:
+ val = (sk->ip_cmsg_flags & 0x10) != 0;
return 0;
case IP_TOS:
val=sk->ip_tos;
@@ -512,29 +633,60 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
case IP_HDRINCL:
val=sk->ip_hdrincl;
break;
-#ifdef CONFIG_IP_MULTICAST
+ case IP_PMTUDISC:
+ val=sk->ip_pmtudisc;
+ return 0;
+ case IP_RECVERR:
+ val=sk->ip_recverr;
+ return 0;
case IP_MULTICAST_TTL:
val=sk->ip_mc_ttl;
break;
case IP_MULTICAST_LOOP:
val=sk->ip_mc_loop;
break;
+ case IP_MULTICAST_IFN:
+ {
+ struct ip_mreqn mreq;
+ len = min(len,sizeof(struct ip_mreqn));
+ if(put_user(len, optlen))
+ return -EFAULT;
+ mreq.imr_ifindex = sk->ip_mc_index;
+ mreq.imr_address.s_addr = sk->ip_mc_addr;
+ mreq.imr_multiaddr.s_addr = 0;
+ if(copy_to_user((void *)optval, &mreq, len))
+ return -EFAULT;
+ return 0;
+ }
case IP_MULTICAST_IF:
- len=strlen(sk->ip_mc_name);
+ {
+ struct device *dev = dev_get_by_index(sk->ip_mc_index);
+ if (dev == NULL)
+ {
+ len = 0;
+ return put_user(len, optlen);
+ }
+ dev_lock_list();
+ len = min(len,strlen(dev->name));
err = put_user(len, optlen);
if (!err)
{
- err = copy_to_user((void *)optval,sk->ip_mc_name, len);
- if (err)
- err = -EFAULT;
+ err = copy_to_user((void *)optval,dev->name, len);
+ if(err)
+ err=-EFAULT;
}
+ dev_unlock_list();
return err;
-#endif
+ }
default:
return(-ENOPROTOOPT);
}
- err = put_user(sizeof(int), optlen);
- if (err)
- return err;
- return put_user(val,(int *) optval);
+
+ len=min(sizeof(int),len);
+
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval,&val,len))
+ return -EFAULT;
+ return 0;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 372417cb1..75346d6dc 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -20,7 +20,7 @@
*/
#include <linux/module.h>
-
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -29,7 +29,8 @@
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
-#include <linux/firewall.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
#include <net/datalink.h>
#include <net/sock.h>
@@ -38,21 +39,26 @@
#include <net/protocol.h>
#include <net/ipip.h>
+void ipip_err(struct sk_buff *skb, unsigned char *dp)
+{
+ /* NI */
+ return;
+}
+
/*
* The IPIP protocol driver.
*
* On entry here
* skb->data is the original IP header
- * skb->ip_hdr points to the initial IP header.
- * skb->h.raw points at the new header.
+ * skb->nh points to the initial IP header.
+ * skb->h points at the new header.
*/
-int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- __u32 daddr, unsigned short len, __u32 saddr,
- int redo, struct inet_protocol *protocol)
+int ipip_rcv(struct sk_buff *skb, unsigned short len)
{
- /* Don't unlink in the middle of a turnaround */
- MOD_INC_USE_COUNT;
+ struct device *dev;
+ struct iphdr *iph;
+
#ifdef TUNNEL_DEBUG
printk("ipip_rcv: got a packet!\n");
#endif
@@ -60,15 +66,15 @@ int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
* Discard the original IP header
*/
- skb_pull(skb, ((struct iphdr *)skb->data)->ihl<<2);
+ skb_pull(skb, skb->h.raw - skb->nh.raw);
/*
* Adjust pointers
*/
- skb->h.iph=(struct iphdr *)skb->data;
- skb->ip_hdr=(struct iphdr *)skb->data;
- memset(skb->proto_priv, 0, sizeof(struct options));
+ iph = skb->nh.iph;
+ skb->nh.iph = skb->h.ipiph;
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
/*
* If you want to add LZ compressed IP or things like that here,
@@ -77,8 +83,35 @@ int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
skb->protocol = htons(ETH_P_IP);
skb->ip_summed = 0;
+ skb->pkt_type = PACKET_HOST;
+
+ /*
+ * Is it draconic? I do not think so. --ANK
+ */
+ dev = ip_dev_find_tunnel(iph->daddr, iph->saddr);
+ if (dev == NULL) {
+#ifdef CONFIG_IP_MROUTE
+ int vif;
+
+ if (!MULTICAST(skb->nh.iph->daddr) ||
+ !ipv4_config.multicast_route ||
+ LOCAL_MCAST(skb->nh.iph->daddr) ||
+ (vif=ip_mr_find_tunnel(iph->daddr, iph->saddr)) < 0)
+ {
+#endif
+ kfree_skb(skb, FREE_READ);
+ return -EINVAL;
+#ifdef CONFIG_IP_MROUTE
+ }
+ IPCB(skb)->flags |= IPSKB_TUNNELED;
+ IPCB(skb)->vif = vif;
+ dev = skb->dev;
+#endif
+ }
+ skb->dev = dev;
+ dst_release(skb->dst);
+ skb->dst = NULL;
netif_rx(skb);
- MOD_DEC_USE_COUNT;
return(0);
}
@@ -86,10 +119,7 @@ int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
static struct inet_protocol ipip_protocol = {
ipip_rcv, /* IPIP handler */
-#if 0
- NULL, /* Will be UDP fraglist handler */
-#endif
- NULL, /* TUNNEL error control */
+ ipip_err, /* TUNNEL error control */
0, /* next */
IPPROTO_IPIP, /* protocol ID */
0, /* copy */
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 79736745c..f76c5b52d 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -12,11 +12,14 @@
*
* Fixes:
* Michael Chastain : Incorrect size of copying.
- * Alan Cox : Added the cache manager code.
+ * Alan Cox : Added the cache manager code
* Alan Cox : Fixed the clone/copy bug and device race.
* Malcolm Beattie : Buffer handling fixes.
* Alexey Kuznetsov : Double buffer free and other fixes.
* SVR Anand : Fixed several multicast bugs and problems.
+ * Alexey Kuznetsov : Status, optimisations and more.
+ * Brad Parker : Better behaviour on mrouted upcall
+ * overflow.
*
* Status:
* Cache manager under test. Forwarding in vague test mode
@@ -59,39 +62,66 @@
static struct vif_device vif_table[MAXVIFS]; /* Devices */
static unsigned long vifc_map; /* Active device map */
+static int maxvif;
int mroute_do_pim = 0; /* Set in PIM assert */
static struct mfc_cache *mfc_cache_array[MFC_LINES]; /* Forwarding cache */
-static struct mfc_cache *cache_resolve_queue; /* Unresolved cache */
int cache_resolve_queue_len = 0; /* Size of unresolved */
/*
* Delete a VIF entry
*/
-static void vif_delete(struct vif_device *v)
+static int vif_delete(int vifi)
{
- if(!(v->flags&VIFF_TUNNEL))
- {
- v->dev->flags&=~IFF_ALLMULTI;
- dev_mc_upload(v->dev);
+ struct vif_device *v;
+
+ if (vifi < 0 || vifi >= maxvif || !(vifc_map&(1<<vifi)))
+ return -EADDRNOTAVAIL;
+
+ v = &vif_table[vifi];
+
+ start_bh_atomic();
+
+ if (!(v->flags&VIFF_TUNNEL)) {
+ v->u.dev->flags &= ~IFF_ALLMULTI;
+ dev_mc_upload(v->u.dev);
+ ip_rt_multicast_event(v->u.dev);
+ v->u.dev = NULL;
+ } else {
+ ip_rt_put(v->u.rt);
+ v->u.rt = NULL;
}
- v->dev=NULL;
+
+ vifc_map&=~(1<<vifi);
+
+ end_bh_atomic();
+
+ if (vifi+1 == maxvif) {
+ int tmp;
+ for (tmp=vifi-1; tmp>=0; tmp--) {
+ if (vifc_map&(1<<tmp))
+ break;
+ }
+ maxvif = tmp+1;
+ }
+ return 0;
}
-/*
- * Find a vif
- */
-
-static int ipmr_vifi_find(struct device *dev)
+static void ipmr_set_bounds(struct mfc_cache *cache)
{
- struct vif_device *v=&vif_table[0];
- int ct;
- for(ct=0;ct<MAXVIFS;ct++,v++)
- {
- if(v->dev==dev)
- return ct;
+ int vifi;
+ for (vifi=0; vifi<maxvif; vifi++) {
+ if (vifc_map&(1<<vifi) && cache->mfc_ttls[vifi]) {
+ cache->mfc_minvif = vifi;
+ cache->mfc_maxvif = vifi+1;
+ vifi++;
+ break;
+ }
+ }
+ for ( ; vifi<maxvif; vifi++) {
+ if (vifc_map&(1<<vifi) && cache->mfc_ttls[vifi])
+ cache->mfc_maxvif = vifi+1;
}
- return -1;
}
/*
@@ -108,16 +138,11 @@ static void ipmr_cache_delete(struct mfc_cache *cache)
* Find the right cache line
*/
+ line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);
+ cp=&(mfc_cache_array[line]);
+
if(cache->mfc_flags&MFC_QUEUED)
- {
- cp=&cache_resolve_queue;
del_timer(&cache->mfc_timer);
- }
- else
- {
- line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);
- cp=&(mfc_cache_array[line]);
- }
/*
* Unlink the buffer
@@ -176,6 +201,7 @@ struct mfc_cache *ipmr_cache_find(__u32 origin, __u32 mcastgrp)
{
int line=MFC_HASH(mcastgrp,origin);
struct mfc_cache *cache;
+
cache=mfc_cache_array[line];
while(cache!=NULL)
{
@@ -183,13 +209,6 @@ struct mfc_cache *ipmr_cache_find(__u32 origin, __u32 mcastgrp)
return cache;
cache=cache->next;
}
- cache=cache_resolve_queue;
- while(cache!=NULL)
- {
- if(cache->mfc_origin==origin && cache->mfc_mcastgrp==mcastgrp)
- return cache;
- cache=cache->next;
- }
return NULL;
}
@@ -207,6 +226,9 @@ static struct mfc_cache *ipmr_cache_alloc(int priority)
init_timer(&c->mfc_timer);
c->mfc_timer.data=(long)c;
c->mfc_timer.function=ipmr_cache_timer;
+ c->mfc_last_assert=0;
+ c->mfc_minvif = MAXVIFS;
+ c->mfc_maxvif = 0;
return c;
}
@@ -216,37 +238,28 @@ static struct mfc_cache *ipmr_cache_alloc(int priority)
static void ipmr_cache_resolve(struct mfc_cache *cache)
{
- struct mfc_cache **p;
struct sk_buff *skb;
+
+ start_bh_atomic();
+
/*
* Kill the queue entry timer.
*/
+
del_timer(&cache->mfc_timer);
- cache->mfc_flags&=~MFC_QUEUED;
- /*
- * Remove from the resolve queue
- */
- p=&cache_resolve_queue;
- while((*p)!=NULL)
- {
- if((*p)==cache)
- {
- *p=cache->next;
- break;
- }
- p=&((*p)->next);
+
+ if (cache->mfc_flags&MFC_QUEUED) {
+ cache->mfc_flags&=~MFC_QUEUED;
+ cache_resolve_queue_len--;
}
- cache_resolve_queue_len--;
- sti();
- /*
- * Insert into the main cache
- */
- ipmr_cache_insert(cache);
+
+ end_bh_atomic();
+
/*
* Play the pending entries through our router
*/
while((skb=skb_dequeue(&cache->mfc_unresolved)))
- ipmr_forward(skb, skb->protocol);
+ ip_mr_input(skb);
}
/*
@@ -254,50 +267,62 @@ static void ipmr_cache_resolve(struct mfc_cache *cache)
* expects the following bizarre scheme..
*/
-static void ipmr_cache_report(struct sk_buff *pkt)
+static int ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
{
- struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
- int ihl=pkt->ip_hdr->ihl<<2;
+ struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
+ int ihl = pkt->nh.iph->ihl<<2;
struct igmphdr *igmp;
+ struct igmpmsg *msg;
+ int ret;
+
if(!skb)
- return;
+ return -ENOMEM;
- skb->free=1;
-
/*
* Copy the IP header
*/
- skb->ip_hdr=(struct iphdr *)skb_put(skb,ihl);
- skb->h.iph=skb->ip_hdr;
+ skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);
memcpy(skb->data,pkt->data,ihl);
- skb->ip_hdr->protocol = 0; /* Flag to the kernel this is a route add */
+ skb->nh.iph->protocol = 0; /* Flag to the kernel this is a route add */
+ msg = (struct igmpmsg*)skb->nh.iph;
+ if (assert)
+ msg->im_vif = vifi;
/*
* Add our header
*/
igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
- igmp->type = IGMPMSG_NOCACHE; /* non IGMP dummy message */
+ igmp->type =
+ msg->im_msgtype = assert ? IGMPMSG_WRONGVIF : IGMPMSG_NOCACHE;
igmp->code = 0;
- skb->ip_hdr->tot_len=htons(skb->len); /* Fix the length */
+ skb->nh.iph->tot_len=htons(skb->len); /* Fix the length */
+ skb->h.raw = skb->nh.raw;
/*
* Deliver to mrouted
*/
- if(sock_queue_rcv_skb(mroute_socket,skb)<0)
+ if((ret=sock_queue_rcv_skb(mroute_socket,skb))<0)
{
- skb->sk=NULL;
+ static unsigned long last_warn;
+ if(jiffies-last_warn>10*HZ)
+ {
+ last_warn=jiffies;
+ printk("mroute: pending queue full, dropping entries.\n");
+ }
kfree_skb(skb, FREE_READ);
+ return ret;
}
+
+ return ret;
}
-
-
+
/*
* Queue a packet for resolution
*/
-static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb, int is_frag)
+static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb)
{
if(cache==NULL)
{
@@ -313,14 +338,13 @@ static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct s
* Fill in the new cache entry
*/
cache->mfc_parent=vifi;
- cache->mfc_origin=skb->ip_hdr->saddr;
- cache->mfc_mcastgrp=skb->ip_hdr->daddr;
+ cache->mfc_origin=skb->nh.iph->saddr;
+ cache->mfc_mcastgrp=skb->nh.iph->daddr;
cache->mfc_flags=MFC_QUEUED;
/*
* Link to the unresolved list
*/
- cache->next=cache_resolve_queue;
- cache_resolve_queue=cache;
+ ipmr_cache_insert(cache);
cache_resolve_queue_len++;
/*
* Fire off the expiry timer
@@ -331,7 +355,12 @@ static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct s
* Reflect first query at mrouted.
*/
if(mroute_socket)
- ipmr_cache_report(skb);
+ {
+ /* If the report failed throw the cache entry
+ out - Brad Parker */
+ if(ipmr_cache_report(skb, vifi, 0)<0)
+ ipmr_cache_delete(cache);
+ }
}
/*
* See if we can append the packet
@@ -341,12 +370,7 @@ static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct s
kfree_skb(skb, FREE_WRITE);
return;
}
- /*
- * Add to our 'pending' list. Cache the is_frag data
- * in skb->protocol now it is spare.
- */
cache->mfc_queuelen++;
- skb->protocol=is_frag;
skb_queue_tail(&cache->mfc_unresolved,skb);
}
@@ -357,13 +381,14 @@ static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct s
int ipmr_mfc_modify(int action, struct mfcctl *mfc)
{
struct mfc_cache *cache;
+
if(!MULTICAST(mfc->mfcc_mcastgrp.s_addr))
return -EINVAL;
/*
* Find the cache line
*/
- cli();
+ start_bh_atomic();
cache=ipmr_cache_find(mfc->mfcc_origin.s_addr,mfc->mfcc_mcastgrp.s_addr);
@@ -375,20 +400,23 @@ int ipmr_mfc_modify(int action, struct mfcctl *mfc)
if(cache)
{
ipmr_cache_delete(cache);
- sti();
+ end_bh_atomic();
return 0;
}
- sti();
+ end_bh_atomic();
return -ENOENT;
}
if(cache)
{
+
/*
* Update the cache, see if it frees a pending queue
*/
cache->mfc_flags|=MFC_RESOLVED;
+ cache->mfc_parent=mfc->mfcc_parent;
memcpy(cache->mfc_ttls, mfc->mfcc_ttls,sizeof(cache->mfc_ttls));
+ ipmr_set_bounds(cache);
/*
* Check to see if we resolved a queued list. If so we
@@ -397,9 +425,10 @@ int ipmr_mfc_modify(int action, struct mfcctl *mfc)
if(cache->mfc_flags&MFC_QUEUED)
ipmr_cache_resolve(cache); /* Unhook & send the frames */
- sti();
+ end_bh_atomic();
return 0;
}
+
/*
* Unsolicited update - that's ok, add anyway.
*/
@@ -408,7 +437,7 @@ int ipmr_mfc_modify(int action, struct mfcctl *mfc)
cache=ipmr_cache_alloc(GFP_ATOMIC);
if(cache==NULL)
{
- sti();
+ end_bh_atomic();
return -ENOMEM;
}
cache->mfc_flags=MFC_RESOLVED;
@@ -416,8 +445,9 @@ int ipmr_mfc_modify(int action, struct mfcctl *mfc)
cache->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
cache->mfc_parent=mfc->mfcc_parent;
memcpy(cache->mfc_ttls, mfc->mfcc_ttls,sizeof(cache->mfc_ttls));
+ ipmr_set_bounds(cache);
ipmr_cache_insert(cache);
- sti();
+ end_bh_atomic();
return 0;
}
@@ -458,9 +488,11 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
if(mroute_socket)
return -EADDRINUSE;
mroute_socket=sk;
+ ipv4_config.multicast_route = 1;
/* Initialise state */
return 0;
case MRT_DONE:
+ ipv4_config.multicast_route = 0;
mroute_close(sk);
mroute_socket=NULL;
return 0;
@@ -481,7 +513,7 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
if(vifc_map&(1<<vif.vifc_vifi))
return -EADDRINUSE;
/* Find the interface */
- dev=ip_dev_find(vif.vifc_lcl_addr.s_addr);
+ dev=ip_dev_find(vif.vifc_lcl_addr.s_addr, NULL);
if(!dev)
return -EADDRNOTAVAIL;
/* Must be tunnelled or multicastable */
@@ -489,7 +521,6 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
{
if(vif.vifc_flags&VIFF_SRCRT)
return -EOPNOTSUPP;
- /* IPIP will do all the work */
}
else
{
@@ -499,6 +530,7 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
how to do this yet.. */
dev->flags|=IFF_ALLMULTI;
dev_mc_upload(dev);
+ ip_rt_multicast_event(dev);
}
else
{
@@ -515,36 +547,29 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
v->remote=vif.vifc_rmt_addr.s_addr;
v->flags=vif.vifc_flags;
v->threshold=vif.vifc_threshold;
- v->dev=dev;
+ v->u.dev=NULL;
+ if (!(vif.vifc_flags&VIFF_TUNNEL))
+ v->u.dev=dev;
v->bytes_in = 0;
v->bytes_out = 0;
v->pkt_in = 0;
v->pkt_out = 0;
vifc_map|=(1<<vif.vifc_vifi);
+ if (vif.vifc_vifi+1 > maxvif)
+ maxvif = vif.vifc_vifi+1;
sti();
return 0;
- }
- else
- /*
- * VIF deletion
- */
- {
- struct vif_device *v=&vif_table[vif.vifc_vifi];
- if(vifc_map&(1<<vif.vifc_vifi))
- {
- vif_delete(v);
- vifc_map&=~(1<<vif.vifc_vifi);
- return 0;
- }
- else
- return -EADDRNOTAVAIL;
- }
+ } else
+ return vif_delete(vif.vifc_vifi);
+
/*
* Manipulate the forwarding caches. These live
* in a sort of kernel/user symbiosis.
*/
case MRT_ADD_MFC:
case MRT_DEL_MFC:
+ if(optlen!=sizeof(mfc))
+ return -EINVAL;
err = copy_from_user(&mfc,optval, sizeof(mfc));
return err ? -EFAULT : ipmr_mfc_modify(optname, &mfc);
/*
@@ -553,9 +578,6 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
case MRT_ASSERT:
{
int v;
- if(optlen!=sizeof(int))
- return -EINVAL;
-
if(get_user(v,(int *)optval))
return -EFAULT;
mroute_do_pim=(v)?1:0;
@@ -566,7 +588,7 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
* set.
*/
default:
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
}
}
@@ -577,26 +599,26 @@ int ip_mroute_setsockopt(struct sock *sk,int optname,char *optval,int optlen)
int ip_mroute_getsockopt(struct sock *sk,int optname,char *optval,int *optlen)
{
int olr;
- int err;
+ int val;
if(sk!=mroute_socket)
return -EACCES;
if(optname!=MRT_VERSION && optname!=MRT_ASSERT)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
- err = get_user(olr, optlen);
- if (err)
- return err;
- if(olr!=sizeof(int))
- return -EINVAL;
- err = put_user(sizeof(int),optlen);
- if (err)
- return err;
+ if(get_user(olr, optlen))
+ return -EFAULT;
+
+ olr=min(olr,sizeof(int));
+ if(put_user(olr,optlen))
+ return -EFAULT;
if(optname==MRT_VERSION)
- err = put_user(0x0305,(int *)optval);
+ val=0x0305;
else
- err = put_user(mroute_do_pim,(int *)optval);
- return err;
+ val=mroute_do_pim;
+ if(copy_to_user(optval,&val,olr))
+ return -EFAULT;
+ return 0;
}
/*
@@ -609,6 +631,7 @@ int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg)
struct sioc_sg_req sr;
struct sioc_vif_req vr;
struct vif_device *vif;
+ struct mfc_cache *c;
switch(cmd)
{
@@ -616,7 +639,7 @@ int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg)
err = copy_from_user(&vr,(void *)arg,sizeof(vr));
if (err)
return -EFAULT;
- if(vr.vifi>=MAXVIFS)
+ if(vr.vifi>=maxvif)
return -EINVAL;
vif=&vif_table[vr.vifi];
if(vifc_map&(1<<vr.vifi))
@@ -629,15 +652,28 @@ int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg)
if (err)
err = -EFAULT;
return err;
+ return 0;
}
return -EADDRNOTAVAIL;
case SIOCGETSGCNT:
err = copy_from_user(&sr,(void *)arg,sizeof(sr));
- if (!err)
- err = copy_to_user((void *)arg,&sr,sizeof(sr));
if (err)
- err = -EFAULT;
- return err;
+ return -EFAULT;
+ for (c = mfc_cache_array[MFC_HASH(sr.grp.s_addr, sr.src.s_addr)];
+ c; c = c->next) {
+ if (sr.grp.s_addr == c->mfc_mcastgrp &&
+ sr.src.s_addr == c->mfc_origin) {
+ sr.pktcnt = c->mfc_pkt;
+ sr.bytecnt = c->mfc_bytes;
+ sr.wrong_if = c->mfc_wrong_if;
+ err = copy_to_user((void *)arg,&sr,sizeof(sr));
+ if (err)
+ err = -EFAULT;
+ return err;
+ return 0;
+ }
+ }
+ return -EADDRNOTAVAIL;
default:
return -EINVAL;
}
@@ -650,34 +686,24 @@ int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg)
void mroute_close(struct sock *sk)
{
int i;
- struct vif_device *v=&vif_table[0];
/*
* Shut down all active vif entries
*/
- for(i=0;i<MAXVIFS;i++)
- {
- if(vifc_map&(1<<i))
- {
- if(!(v->flags&VIFF_TUNNEL))
- {
- v->dev->flags&=~IFF_ALLMULTI;
- dev_mc_upload(v->dev);
- }
- }
- v++;
- }
- vifc_map=0;
+ for(i=0; i<maxvif; i++)
+ vif_delete(i);
+
/*
* Wipe the cache
*/
for(i=0;i<MFC_LINES;i++)
{
+ start_bh_atomic();
while(mfc_cache_array[i]!=NULL)
ipmr_cache_delete(mfc_cache_array[i]);
- }
- /* The timer will clear any 'pending' stuff */
+ end_bh_atomic();
+ }
}
static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
@@ -687,13 +713,10 @@ static int ipmr_device_event(struct notifier_block *this, unsigned long event, v
if(event!=NETDEV_DOWN)
return NOTIFY_DONE;
v=&vif_table[0];
- for(ct=0;ct<MAXVIFS;ct++)
+ for(ct=0;ct<maxvif;ct++)
{
- if((vifc_map&(1<<ct)) && v->dev==ptr)
- {
- vif_delete(v);
- vifc_map&=~(1<<ct);
- }
+ if(vifc_map&(1<<ct) && !(v->flags&VIFF_TUNNEL) && v->u.dev==ptr)
+ vif_delete(ct);
v++;
}
return NOTIFY_DONE;
@@ -707,104 +730,204 @@ static struct notifier_block ip_mr_notifier={
};
/*
+ * Encapsulate a packet by attaching a valid IPIP header to it.
+ * This avoids tunnel drivers and other mess and gives us the speed so
+ * important for multicast video.
+ */
+
+static void ip_encap(struct sk_buff *skb, u32 saddr, u32 daddr)
+{
+ struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+ iph->version = 4;
+ iph->tos = skb->nh.iph->tos;
+ iph->ttl = skb->nh.iph->ttl;
+ iph->frag_off = 0;
+ iph->daddr = daddr;
+ iph->saddr = saddr;
+ iph->protocol = IPPROTO_IPIP;
+ iph->ihl = 5;
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
+ ip_send_check(iph);
+
+ skb->h.ipiph = skb->nh.iph;
+ skb->nh.iph = iph;
+}
+
+/*
* Processing handlers for ipmr_forward
*/
-static void ipmr_queue_xmit(struct sk_buff *skb, struct vif_device *vif, struct device *in_dev, int frag)
+static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c,
+ int vifi, int last)
{
- int tunnel=0;
- __u32 raddr=skb->raddr;
- if(vif->flags&VIFF_TUNNEL)
- {
- tunnel=IPFWD_MULTITUNNEL;
- raddr=vif->remote;
+ struct iphdr *iph = skb->nh.iph;
+ struct vif_device *vif = &vif_table[vifi];
+ struct device *dev;
+ struct rtable *rt;
+ int encap = 0;
+ struct sk_buff *skb2;
+ int err;
+
+ if (vif->flags&VIFF_TUNNEL) {
+ rt = vif->u.rt;
+ if (!rt || rt->u.dst.obsolete) {
+ ip_rt_put(rt);
+ vif->u.rt = NULL;
+ err = ip_route_output(&rt, vif->remote, vif->local, RT_TOS(iph->tos), NULL);
+ if (err)
+ return;
+ vif->u.rt = rt;
+ }
+ dst_clone(&rt->u.dst);
+ encap = sizeof(struct iphdr);
+ } else {
+ dev = vif->u.dev;
+ if (dev == NULL)
+ return;
+ err = ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos), dev);
+ if (err)
+ return;
+ }
+
+ dev = rt->u.dst.dev;
+
+ if (skb->len+encap > dev->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+ ip_statistics.IpFragFails++;
+ ip_rt_put(rt);
+ return;
}
+
+ encap += dev->hard_header_len;
+
+ if (skb->len+encap > 65534) {
+ ip_rt_put(rt);
+ return;
+ }
+
+ if (skb_headroom(skb) < encap || (encap && !last))
+ skb2 = skb_realloc_headroom(skb, (encap + 15)&~15);
+ else
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+
+ if (skb2 == NULL) {
+ ip_rt_put(rt);
+ return;
+ }
+
vif->pkt_out++;
vif->bytes_out+=skb->len;
- skb->dev=vif->dev;
- skb->raddr=skb->h.iph->daddr;
- /*
- * If the vif went down as we were forwarding.. just throw the
- * frame.
- */
- if(vif->dev==NULL || ip_forward(skb, in_dev, frag|IPFWD_MULTICASTING|tunnel, raddr)==-1)
- kfree_skb(skb, FREE_WRITE);
+
+ dst_release(skb2->dst);
+ skb2->dst = &rt->u.dst;
+
+ iph = skb2->nh.iph;
+ ip_decrease_ttl(iph);
+
+ if (vif->flags & VIFF_TUNNEL)
+ ip_encap(skb2, vif->local, vif->remote);
+
+ ip_send(skb2);
}
/*
* Multicast packets for forwarding arrive here
*/
-void ipmr_forward(struct sk_buff *skb, int is_frag)
+int ip_mr_input(struct sk_buff *skb)
{
struct mfc_cache *cache;
- struct sk_buff *skb2;
int psend = -1;
- int vif=ipmr_vifi_find(skb->dev);
- if(vif==-1)
- {
- kfree_skb(skb, FREE_WRITE);
- return;
+ int vif, ct;
+ int local = 0;
+ int tunneled = IPCB(skb)->flags&IPSKB_TUNNELED;
+
+ cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr);
+
+ /*
+ * No usable cache entry
+ */
+
+ if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) {
+ ipmr_cache_unresolved(cache, ALL_VIFS, skb);
+ return -EAGAIN;
}
+ vif = cache->mfc_parent;
+ cache->mfc_pkt++;
+ cache->mfc_bytes += skb->len;
+
/*
- * Without the following addition, skb->h.iph points to something
- * different that is not the ip header.
+ * Wrong interface: drop packet and (maybe) send PIM assert.
*/
-
- skb->h.iph = skb->ip_hdr; /* Anand, ernet. */
+ if (vif >= maxvif || !(vifc_map&(1<<vif)) ||
+ (tunneled && IPCB(skb)->vif != vif) ||
+ (!tunneled && (vif_table[vif].flags&VIFF_TUNNEL ||
+ vif_table[vif].u.dev != skb->dev))) {
+ cache->mfc_wrong_if++;
+ if (vif < MAXVIFS && mroute_do_pim &&
+ !(vif_table[vif].flags&VIFF_TUNNEL) &&
+ skb->dev->flags&IFF_BROADCAST &&
+ jiffies - cache->mfc_last_assert > MFC_ASSERT_THRESH) {
+ cache->mfc_last_assert = jiffies;
+ /*
+ * It is wrong! Routing daemon can
+ * determine vif itself, but it cannot
+ * determine REAL device.
+ * BSD bug. Fix it later, PIM does not
+ * work in any case 8) _ANK_
+ */
+ ipmr_cache_report(skb, vif, 1);
+ }
+ kfree_skb(skb, FREE_WRITE);
+ return -EINVAL;
+ }
vif_table[vif].pkt_in++;
vif_table[vif].bytes_in+=skb->len;
-
- cache=ipmr_cache_find(skb->ip_hdr->saddr,skb->ip_hdr->daddr);
-
+
+ if (IPCB(skb)->opt.router_alert ||
+ ((struct rtable*)skb->dst)->rt_flags&RTF_LOCAL ||
+ skb->nh.iph->protocol == IPPROTO_IGMP)
+ local = 1;
+
/*
- * No usable cache entry
+ * Forward the frame
*/
-
- if(cache==NULL || (cache->mfc_flags&MFC_QUEUED))
- ipmr_cache_unresolved(cache,vif,skb, is_frag);
- else
- {
+ ct = cache->mfc_maxvif-1;
+ while (ct>=cache->mfc_minvif) {
/*
- * Forward the frame
+ * 0 means don't do it. Silly idea, 255 as don't do it would be cleaner!
*/
- int ct=0;
- while(ct<MAXVIFS)
- {
- /*
- * 0 means don't do it. Silly idea, 255 as don't do it would be cleaner!
- */
- if(skb->ip_hdr->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0)
- {
- if(psend!=-1)
- {
- /*
- * May get variant mac headers
- * so must copy -- boo hoo.
- */
- skb2=skb_copy(skb, GFP_ATOMIC);
- if(skb2)
- {
- skb2->free=1;
- ipmr_queue_xmit(skb2, &vif_table[psend], skb->dev, is_frag);
- }
- }
- psend=ct;
- }
- ct++;
- }
- if(psend==-1)
- kfree_skb(skb, FREE_WRITE);
- else
- {
- ipmr_queue_xmit(skb, &vif_table[psend], skb->dev, is_frag);
+ if (skb->nh.iph->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0) {
+ if (psend != -1)
+ ipmr_queue_xmit(skb, cache, psend, 0);
+ psend=ct;
}
- /*
- * Adjust the stats
- */
+ ct--;
+ }
+ if (psend != -1)
+ ipmr_queue_xmit(skb, cache, psend, 1);
+ if (!local) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ return ip_local_deliver(skb);
+}
+
+int ip_mr_find_tunnel(u32 local, u32 remote)
+{
+ int ct;
+ struct vif_device *vif;
+
+ for (ct=0; ct<maxvif; ct++) {
+ vif = &vif_table[ct];
+ if (vifc_map&(1<<ct) && vif->flags&VIFF_TUNNEL &&
+ vif->local == local && vif->remote == remote)
+ return ct;
}
+ return -1;
}
/*
@@ -824,15 +947,13 @@ int ipmr_vif_info(char *buffer, char **start, off_t offset, int length, int dumm
"Interface Bytes In Pkts In Bytes Out Pkts Out Flags Local Remote\n");
pos=len;
- for (ct=0;ct<MAXVIFS;ct++)
+ for (ct=0;ct<maxvif;ct++)
{
vif=&vif_table[ct];
if(!(vifc_map&(1<<ct)))
continue;
- if(vif->dev==NULL)
- continue;
- size = sprintf(buffer+len, "%-10s %8ld %7ld %8ld %7ld %05X %08lX %08lX\n",
- vif->dev->name,vif->bytes_in, vif->pkt_in, vif->bytes_out,vif->pkt_out,
+ size = sprintf(buffer+len, "%2d %-10s %8ld %7ld %8ld %7ld %05X %08lX %08lX\n",
+ ct, vif->flags&VIFF_TUNNEL ? "Tunnel" : vif->u.dev->name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out,
vif->flags, vif->local, vif->remote);
len+=size;
pos+=size;
@@ -862,40 +983,44 @@ int ipmr_mfc_info(char *buffer, char **start, off_t offset, int length, int dumm
int ct;
len += sprintf(buffer,
- "Group Origin SrcIface \n");
+ "Group Origin SrcIface Pkts Bytes Wrong VifTtls\n");
pos=len;
for (ct=0;ct<MFC_LINES;ct++)
{
- cli();
+ start_bh_atomic();
mfc=mfc_cache_array[ct];
while(mfc!=NULL)
{
char *name="none";
- char vifmap[MAXVIFS+1];
int n;
/*
* Device name
*/
- if(vifc_map&(1<<mfc->mfc_parent))
- name=vif_table[mfc->mfc_parent].dev->name;
- /*
- * Interface forwarding map
- */
- for(n=0;n<MAXVIFS;n++)
- if(vifc_map&(1<<n) && mfc->mfc_ttls[ct])
- vifmap[n]='X';
+ if(mfc->mfc_parent < maxvif && vifc_map&(1<<mfc->mfc_parent)) {
+ if (vif_table[mfc->mfc_parent].flags&VIFF_TUNNEL)
+ name="Tunnel";
else
- vifmap[n]='-';
- vifmap[n]=0;
+ name=vif_table[mfc->mfc_parent].u.dev->name;
+ }
/*
- * Now print it out
+ * Interface forwarding map
*/
- size = sprintf(buffer+len, "%08lX %08lX %-8s %s\n",
+ size = sprintf(buffer+len, "%08lX %08lX %-8s %8ld %8ld %8ld",
(unsigned long)mfc->mfc_mcastgrp,
(unsigned long)mfc->mfc_origin,
name,
- vifmap);
+ mfc->mfc_bytes,
+ mfc->mfc_pkt,
+ mfc->mfc_wrong_if);
+ for(n=0;n<maxvif;n++)
+ {
+ if(vifc_map&(1<<n))
+ size += sprintf(buffer+len+size, " %-3d", mfc->mfc_ttls[n]);
+ else
+ size += sprintf(buffer+len+size, " --- ");
+ }
+ size += sprintf(buffer+len+size, "\n");
len+=size;
pos+=size;
if(pos<offset)
@@ -910,7 +1035,7 @@ int ipmr_mfc_info(char *buffer, char **start, off_t offset, int length, int dumm
}
mfc=mfc->next;
}
- sti();
+ end_bh_atomic();
}
done:
*start=buffer+(offset-begin);
@@ -935,6 +1060,7 @@ static struct proc_dir_entry proc_net_ipmr_mfc = {
};
#endif
+
/*
* Setup for IP multicast routing
*/
diff --git a/net/ipv4/packet.c b/net/ipv4/packet.c
index 89dd6549e..b98b58f35 100644
--- a/net/ipv4/packet.c
+++ b/net/ipv4/packet.c
@@ -96,7 +96,6 @@ int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
if(sock_queue_rcv_skb(sk,skb)<0)
{
- skb->sk = NULL;
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -113,8 +112,7 @@ int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
* protocol layers and you must therefore supply it with a complete frame
*/
-static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
- int noblock, int flags)
+static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
struct sk_buff *skb;
struct device *dev;
@@ -126,7 +124,7 @@ static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
* Check the flags.
*/
- if (flags)
+ if (msg->msg_flags&~MSG_DONTWAIT)
return(-EINVAL);
/*
@@ -179,11 +177,11 @@ static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
* Fill it in
*/
- skb->sk = sk;
- skb->free = 1;
err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
skb->arp = 1; /* No ARP needs doing on this (complete) frame */
skb->protocol = proto;
+ skb->dev = dev;
+ skb->priority = sk->priority;
/*
* Now send it
@@ -207,7 +205,7 @@ static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
return err;
}
- dev_queue_xmit(skb, dev, sk->priority);
+ dev_queue_xmit(skb);
return(len);
}
@@ -250,6 +248,7 @@ static void packet_close(struct sock *sk, unsigned long timeout)
}
release_sock(sk);
+ sk->dead = 1;
destroy_sock(sk);
}
@@ -492,27 +491,33 @@ int packet_recvmsg(struct sock *sk, struct msghdr *msg, int len,
struct proto packet_prot =
{
- packet_close,
- NULL,
- NULL, /* accept */
- NULL,
- NULL,
- NULL,
- datagram_select,
- NULL, /* No ioctl */
- packet_init,
- NULL,
- NULL,
- NULL, /* No set/get socket options */
- NULL,
- packet_sendmsg, /* Sendmsg */
- packet_recvmsg, /* Recvmsg */
- packet_bind, /* Bind */
- NULL, /* Backlog_rcv */
- 128,
- 0,
- "PACKET",
- 0, 0
+ (struct sock *)&packet_prot, /* sklist_next */
+ (struct sock *)&packet_prot, /* sklist_prev */
+ packet_close, /* close */
+ NULL, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ NULL, /* ioctl */
+ packet_init, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ NULL, /* setsockopt */
+ NULL, /* getsockopt */
+ packet_sendmsg, /* Sendmsg */
+ packet_recvmsg, /* Recvmsg */
+ packet_bind, /* bind */
+ NULL, /* backlog_rcv */
+ NULL, /* hash */
+ NULL, /* unhash */
+ NULL, /* rehash */
+ NULL, /* good_socknum */
+ NULL, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "PACKET", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-
-
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 4494270cc..e3b41e2ad 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -57,22 +57,17 @@
static int
get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
{
- struct sock **s_array;
struct sock *sp;
struct tcp_opt *tp;
- int i;
- int timer_active;
- int timer_active1;
- int timer_active2;
+ int timer_active, timer_active1, timer_active2;
unsigned long timer_expires;
unsigned long dest, src;
unsigned short destp, srcp;
- int len=0;
+ int len=0, i = 0;
off_t pos=0;
off_t begin;
char tmpbuf[129];
- s_array = pro->sock_array;
if (offset < 128)
len += sprintf(buffer, "%-127s\n",
" sl local_address rem_address st tx_queue "
@@ -84,76 +79,67 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of
* a memory timer destroy. Instead of playing with timers we just
* concede defeat and cli().
*/
- for(i = 0; i < SOCK_ARRAY_SIZE; i++)
- {
- cli();
- sp = s_array[i];
-
- while(sp != NULL)
- {
- pos += 128;
- if (pos < offset)
- {
- sp = sp->next;
- continue;
- }
+ SOCKHASH_LOCK();
+ sp = pro->sklist_next;
+ while(sp != (struct sock *)pro) {
+ pos += 128;
+ if (pos < offset)
+ goto next;
- tp = &(sp->tp_pinfo.af_tcp);
+ tp = &(sp->tp_pinfo.af_tcp);
+ dest = sp->daddr;
+ src = sp->saddr;
+ destp = sp->dummy_th.dest;
+ srcp = sp->dummy_th.source;
- dest = sp->daddr;
- src = sp->saddr;
- destp = sp->dummy_th.dest;
- srcp = sp->dummy_th.source;
+ /* FIXME: The fact that retransmit_timer occurs as a field
+ * in two different parts of the socket structure is,
+ * to say the least, confusing. This code now uses the
+ * right retransmit_timer variable, but I'm not sure
+ * the rest of the timer stuff is still correct.
+ * In particular I'm not sure what the timeout value
+ * is suppose to reflect (as opposed to tm->when). -- erics
+ */
- /* Since we are Little Endian we need to swap the bytes :-( */
- destp = ntohs(destp);
- srcp = ntohs(srcp);
- timer_active1 = del_timer(&sp->retransmit_timer);
- timer_active2 = del_timer(&sp->timer);
- if (!timer_active1) sp->retransmit_timer.expires=0;
- if (!timer_active2) sp->timer.expires=0;
- timer_active=0;
- timer_expires=(unsigned)-1;
- if (timer_active1 &&
- sp->retransmit_timer.expires < timer_expires) {
- timer_active=timer_active1;
- timer_expires=sp->retransmit_timer.expires;
- }
- if (timer_active2 &&
- sp->timer.expires < timer_expires) {
- timer_active=timer_active2;
- timer_expires=sp->timer.expires;
- }
- sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
- " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
- i, src, srcp, dest, destp, sp->state,
- format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc,
- format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
- timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
- (sp->socket&&SOCK_INODE(sp->socket))?SOCK_INODE(sp->socket)->i_uid:0,
- timer_active?sp->timeout:0,
- sp->socket && SOCK_INODE(sp->socket) ?
- SOCK_INODE(sp->socket)->i_ino : 0);
-
- if (timer_active1) add_timer(&sp->retransmit_timer);
- if (timer_active2) add_timer(&sp->timer);
- len += sprintf(buffer+len, "%-127s\n", tmpbuf);
- /*
- * All sockets with (port mod SOCK_ARRAY_SIZE) = i
- * are kept in sock_array[i], so we must follow the
- * 'next' link to get them all.
- */
- if(len >= length)
- break;
- sp = sp->next;
+ /* Since we are Little Endian we need to swap the bytes :-( */
+ destp = ntohs(destp);
+ srcp = ntohs(srcp);
+ timer_active1 = del_timer(&tp->retransmit_timer);
+ timer_active2 = del_timer(&sp->timer);
+ if (!timer_active1) tp->retransmit_timer.expires=0;
+ if (!timer_active2) sp->timer.expires=0;
+ timer_active=0;
+ timer_expires=(unsigned)-1;
+ if (timer_active1 && tp->retransmit_timer.expires < timer_expires) {
+ timer_active=timer_active1;
+ timer_expires=tp->retransmit_timer.expires;
+ }
+ if (timer_active2 && sp->timer.expires < timer_expires) {
+ timer_active=timer_active2;
+ timer_expires=sp->timer.expires;
}
- sti(); /* We only turn interrupts back on for a moment,
- but because the interrupt queues anything built
- up before this will clear before we jump back
- and cli(), so it's not as bad as it looks */
- if(len>= length)
+ sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+ " %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+ i, src, srcp, dest, destp, sp->state,
+ format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc),
+ format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+ timer_active, timer_expires-jiffies,
+ tp->retransmits,
+ sp->socket ? sp->socket->inode->i_uid:0,
+ timer_active?sp->timeout:0,
+ sp->socket ? sp->socket->inode->i_ino:0);
+
+ if (timer_active1) add_timer(&tp->retransmit_timer);
+ if (timer_active2) add_timer(&sp->timer);
+ len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+ if(len >= length)
break;
+ next:
+ sp = sp->sklist_next;
+ i++;
}
+ SOCKHASH_UNLOCK();
+
begin = len - (pos - offset);
*start = buffer + begin;
len -= begin;
@@ -162,25 +148,21 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of
return len;
}
-
int tcp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
return get__netinfo(&tcp_prot, buffer,0, start, offset, length);
}
-
int udp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
return get__netinfo(&udp_prot, buffer,1, start, offset, length);
}
-
int raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
return get__netinfo(&raw_prot, buffer,1, start, offset, length);
}
-
/*
* Report socket allocation statistics [mea@utu.fi]
*/
@@ -192,7 +174,6 @@ int afinet_get_info(char *buffer, char **start, off_t offset, int length, int du
int len = socket_get_info(buffer,start,offset,length);
- len += sprintf(buffer+len,"SOCK_ARRAY_SIZE=%d\n",SOCK_ARRAY_SIZE);
len += sprintf(buffer+len,"TCP: inuse %d highest %d\n",
tcp_prot.inuse, tcp_prot.highestinuse);
len += sprintf(buffer+len,"UDP: inuse %d highest %d\n",
@@ -201,6 +182,11 @@ int afinet_get_info(char *buffer, char **start, off_t offset, int length, int du
raw_prot.inuse, raw_prot.highestinuse);
len += sprintf(buffer+len,"PAC: inuse %d highest %d\n",
packet_prot.inuse, packet_prot.highestinuse);
+ if (offset >= len)
+ {
+ *start = buffer;
+ return 0;
+ }
*start = buffer + offset;
len -= offset;
if (len > length)
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index bb9ff5fbb..827dc4f12 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -46,13 +46,12 @@
#include <linux/igmp.h>
-#ifdef CONFIG_IP_FORWARD
#ifdef CONFIG_NET_IPIP
static struct inet_protocol ipip_protocol =
{
ipip_rcv, /* IPIP handler */
- NULL, /* TUNNEL error control */
+ ipip_err, /* TUNNEL error control */
0, /* next */
IPPROTO_IPIP, /* protocol ID */
0, /* copy */
@@ -62,13 +61,12 @@ static struct inet_protocol ipip_protocol =
#endif
-#endif
static struct inet_protocol tcp_protocol =
{
tcp_v4_rcv, /* TCP handler */
tcp_v4_err, /* TCP error control */
-#if defined(CONFIG_NET_IPIP) && defined(CONFIG_IP_FORWARD)
+#ifdef CONFIG_NET_IPIP
&ipip_protocol,
#else
NULL, /* next */
@@ -103,9 +101,6 @@ static struct inet_protocol icmp_protocol =
"ICMP" /* name */
};
-#ifndef CONFIG_IP_MULTICAST
-struct inet_protocol *inet_protocol_base = &icmp_protocol;
-#else
static struct inet_protocol igmp_protocol =
{
igmp_rcv, /* IGMP handler */
@@ -118,7 +113,6 @@ static struct inet_protocol igmp_protocol =
};
struct inet_protocol *inet_protocol_base = &igmp_protocol;
-#endif
struct inet_protocol *inet_protos[MAX_INET_PROTOS] =
{
diff --git a/net/ipv4/rarp.c b/net/ipv4/rarp.c
index 28e2f4087..fb9e2a738 100644
--- a/net/ipv4/rarp.c
+++ b/net/ipv4/rarp.c
@@ -330,11 +330,14 @@ static int rarp_req_set(struct arpreq *req)
* Is it reachable directly ?
*/
- rt = ip_rt_route(ip, 0);
- if (rt == NULL)
- return -ENETUNREACH;
- dev = rt->rt_dev;
- ip_rt_put(rt);
+ err = ip_route_output(&rt, ip, 0, 1, NULL);
+ if (err)
+ return err;
+ if (rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)) {
+ ip_rt_put(rt);
+ return -EINVAL;
+ }
+ dev = rt->u.dst.dev;
/*
* Is there an existing entry for this address? Find out...
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 89e03aed6..db54b567a 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -30,6 +30,7 @@
* Alan Cox : Beginnings of mrouted support.
* Alan Cox : Added IP_HDRINCL option.
* Alan Cox : Skip broadcast check if BSDism set.
+ * David S. Miller : New socket lookup architecture.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -58,60 +59,121 @@
#include <net/sock.h>
#include <net/icmp.h>
#include <net/udp.h>
+#include <net/raw.h>
#include <net/checksum.h>
#ifdef CONFIG_IP_MROUTE
struct sock *mroute_socket=NULL;
#endif
+struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+static void raw_v4_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV4_HTABLE_SIZE - 1);
+ skp = &raw_v4_htable[num];
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV4_HTABLE_SIZE - 1);
+ skp = &raw_v4_htable[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (RAWV4_HTABLE_SIZE - 1);
+ skp = &raw_v4_htable[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = raw_v4_htable[num];
+ raw_v4_htable[num] = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+ unsigned long raddr, unsigned long laddr)
+{
+ struct sock *s = sk;
+
+ SOCKHASH_LOCK();
+ for(s = sk; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE)) &&
+ !(s->daddr && s->daddr != raddr) &&
+ !(s->rcv_saddr && s->rcv_saddr != laddr))
+ break; /* gotcha */
+ }
+ SOCKHASH_UNLOCK();
+ return s;
+}
/*
* Raw_err does not currently get called by the icmp module - FIXME:
*/
-void raw_err (int type, int code, unsigned char *header, __u32 daddr,
- __u32 saddr, struct inet_protocol *protocol)
+void raw_err (struct sock *sk, struct sk_buff *skb)
{
- struct sock *sk;
-
- if (protocol == NULL)
- return;
- sk = (struct sock *) protocol->data;
- if (sk == NULL)
- return;
-
- /* This is meaningless in raw sockets. */
- if (type == ICMP_SOURCE_QUENCH)
- {
- if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2;
- return;
- }
-
- if(type == ICMP_PARAMETERPROB)
- {
- sk->err = EPROTO;
- sk->error_report(sk);
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
+
+ if (sk->ip_recverr && !sk->sock_readers) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 && sock_queue_err_skb(sk, skb2))
+ kfree_skb(skb, FREE_READ);
}
- if(code<=NR_ICMP_UNREACH)
- {
- sk->err = icmp_err_convert[code & 0xff].errno;
- sk->error_report(sk);
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+ sk->err = EMSGSIZE;
+ sk->error_report(sk);
+ }
}
-
- return;
}
-static inline int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
+static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
/* Charge it to the socket. */
if (__sock_queue_rcv_skb(sk,skb)<0)
{
ip_statistics.IpInDiscards++;
- skb->sk=NULL;
kfree_skb(skb, FREE_READ);
- return 0;
+ return -1;
}
ip_statistics.IpInDelivers++;
@@ -124,28 +186,14 @@ static inline int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
* in ip.c
*/
-int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr)
+int raw_rcv(struct sock *sk, struct sk_buff *skb)
{
/* Now we need to copy this into memory. */
- skb->sk = sk;
- skb_trim(skb,ntohs(skb->ip_hdr->tot_len));
+ skb_trim(skb, ntohs(skb->nh.iph->tot_len));
- skb->h.raw = (unsigned char *) skb->ip_hdr;
- skb->dev = dev;
- skb->saddr = daddr;
- skb->daddr = saddr;
+ skb->h.raw = skb->nh.raw;
-#if 0
- /*
- * For no adequately explained reasons BSD likes to mess up the header of
- * the received frame.
- */
-
- if(sk->bsdism)
- skb->ip_hdr->tot_len=ntohs(skb->ip_hdr->tot_len-4*skb->ip_hdr->ihl);
-#endif
-
- if (sk->users) {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return 0;
}
@@ -153,6 +201,12 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, __u32 sadd
return 0;
}
+struct rawfakehdr
+{
+ const unsigned char *from;
+ u32 saddr;
+};
+
/*
* Send a RAW IP packet.
*/
@@ -161,30 +215,29 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, __u32 sadd
* Callback support is trivial for SOCK_RAW
*/
-static int raw_getfrag(const void *p, __u32 saddr, char *to,
- unsigned int offset, unsigned int fraglen)
+static int raw_getfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
- return copy_from_user(to, (const unsigned char *)p+offset, fraglen);
+ struct rawfakehdr *rfh = (struct rawfakehdr *) p;
+ return copy_from_user(to, rfh->from + offset, fraglen);
}
/*
* IPPROTO_RAW needs extra work.
*/
-static int raw_getrawfrag(const void *p, __u32 saddr, char *to, unsigned int offset, unsigned int fraglen)
+static int raw_getrawfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
{
- int err;
- err = copy_from_user(to, (const unsigned char *)p+offset, fraglen);
- if (err)
- return err;
- if(offset==0)
- {
- struct iphdr *iph=(struct iphdr *)to;
- if(!iph->saddr)
- iph->saddr=saddr;
+ struct rawfakehdr *rfh = (struct rawfakehdr *) p;
+
+ if (copy_from_user(to, rfh->from + offset, fraglen))
+ return -EFAULT;
+ if (offset==0) {
+ struct iphdr *iph = (struct iphdr *)to;
+ if (!iph->saddr)
+ iph->saddr = rfh->saddr;
iph->check=0;
iph->tot_len=htons(fraglen); /* This is right as you can't frag
- RAW packets */
+ RAW packets */
/*
* Deliberate breach of modularity to keep
* ip_build_xmit clean (well less messy).
@@ -193,87 +246,132 @@ static int raw_getrawfrag(const void *p, __u32 saddr, char *to, unsigned int off
iph->id = htons(ip_id_count++);
iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
}
- return 0;
+ return 0;
}
static int raw_sendto(struct sock *sk, const unsigned char *from,
- int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len)
+ int len, struct msghdr *msg)
{
+ struct device *dev = NULL;
+ struct ipcm_cookie ipc;
+ struct rawfakehdr rfh;
+ struct rtable *rt;
+ int free = 0;
+ u32 daddr;
+ u8 tos;
int err;
- struct sockaddr_in sin;
+
+ if (len>65535)
+ return -EMSGSIZE;
/*
- * Check the flags. Only MSG_DONTROUTE is permitted.
+ * Check the flags.
*/
- if (flags & MSG_OOB) /* Mirror BSD error message compatibility */
+ if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
return -EOPNOTSUPP;
- if (flags & ~MSG_DONTROUTE)
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
+
/*
* Get and verify the address.
*/
- if (usin)
- {
- if (addr_len < sizeof(sin))
- return(-EINVAL);
- memcpy(&sin, usin, sizeof(sin));
- if (sin.sin_family && sin.sin_family != AF_INET)
+ if (msg->msg_namelen) {
+ struct sockaddr_in *usin = (struct sockaddr_in*)msg->msg_name;
+ if (msg->msg_namelen < sizeof(*usin))
return(-EINVAL);
- /*
- * Protocol type is host ordered byte.
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (!complained++)
+ printk(KERN_INFO "%s forgot to set AF_INET in raw sendmsg. Fix it!\n", current->comm);
+ if (usin->sin_family)
+ return -EINVAL;
+ }
+ daddr = usin->sin_addr.s_addr;
+ /* ANK: I did not forget to get protocol from port field.
+ * I just do not know, who uses this weirdness.
+ * IP_HDRINCL is much more convenient.
*/
- sin.sin_port=ntohs(sin.sin_port);
- }
- else
- {
+ } else {
if (sk->state != TCP_ESTABLISHED)
return(-EINVAL);
- sin.sin_family = AF_INET;
- sin.sin_port = sk->num;
- sin.sin_addr.s_addr = sk->daddr;
+ daddr = sk->daddr;
}
- if (sin.sin_port == 0)
- sin.sin_port = sk->num;
-
- if (sin.sin_addr.s_addr == INADDR_ANY)
- sin.sin_addr.s_addr = ip_my_addr();
- /*
- * BSD raw sockets forget to check SO_BROADCAST ....
- */
-
- if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
- return -EACCES;
+ ipc.addr = sk->saddr;
+ ipc.opt = NULL;
- if(sk->ip_hdrincl)
- {
- if(len>65535)
- return -EMSGSIZE;
- err=ip_build_xmit(sk, raw_getrawfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
+ if (msg->msg_controllen) {
+ int tmp = ip_cmsg_send(msg, &ipc, &dev);
+ if (tmp)
+ return tmp;
+ if (ipc.opt && sk->ip_hdrincl) {
+ kfree(ipc.opt);
+ return -EINVAL;
+ }
+ if (ipc.opt)
+ free=1;
}
+
+ rfh.saddr = ipc.addr;
+ ipc.addr = daddr;
+
+ if (!ipc.opt)
+ ipc.opt = sk->opt;
+ if (ipc.opt && ipc.opt->srr) {
+ if (!daddr)
+ return -EINVAL;
+ daddr = ipc.opt->faddr;
+ }
+ tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE));
+
+ if (MULTICAST(daddr) && sk->ip_mc_index && dev==NULL)
+ err = ip_route_output_dev(&rt, daddr, rfh.saddr, tos, sk->ip_mc_index);
else
- {
- if(len>65535-sizeof(struct iphdr))
- return -EMSGSIZE;
- err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
+ err = ip_route_output(&rt, daddr, rfh.saddr, tos, dev);
+
+ if (err) {
+ if (free) kfree(ipc.opt);
+ return err;
+ }
+
+ if (rt->rt_flags&RTF_BROADCAST && !sk->broadcast) {
+ if (free) kfree(ipc.opt);
+ ip_rt_put(rt);
+ return -EACCES;
}
- return err<0?err:len;
+
+ rfh.from = from;
+ rfh.saddr = rt->rt_src;
+ if (!ipc.addr)
+ ipc.addr = rt->rt_dst;
+ if(sk->ip_hdrincl)
+ err=ip_build_xmit(sk, raw_getrawfrag, &rfh, len, &ipc, rt, msg->msg_flags);
+ else {
+ if (len>65535-sizeof(struct iphdr))
+ err = -EMSGSIZE;
+ else
+ err=ip_build_xmit(sk, raw_getfrag, &rfh, len, &ipc, rt, msg->msg_flags);
+ }
+
+ if (free)
+ kfree(ipc.opt);
+ ip_rt_put(rt);
+
+ return err<0 ? err : len;
}
/*
* Temporary
*/
-static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock,
- int flags)
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
- if(msg->msg_iovlen==1)
- return raw_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
- else
- {
+ if (msg->msg_iovlen==1)
+ return raw_sendto(sk, msg->msg_iov[0].iov_base,len, msg);
+ else {
/*
* For awkward cases we linearise the buffer first. In theory this is only frames
* whose iovec's don't split on 4 byte boundaries, and soon encrypted stuff (to keep
@@ -281,7 +379,6 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock
*/
unsigned char *buf;
- int fs;
int err;
if(len>65515)
return -EMSGSIZE;
@@ -291,9 +388,10 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock
err = memcpy_fromiovec(buf, msg->msg_iov, len);
if (!err)
{
+ unsigned short fs;
fs=get_fs();
set_fs(get_ds());
- err=raw_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
+ err=raw_sendto(sk,buf,len, msg);
set_fs(fs);
}
else
@@ -310,20 +408,40 @@ static void raw_close(struct sock *sk, unsigned long timeout)
#ifdef CONFIG_IP_MROUTE
if(sk==mroute_socket)
{
+ ipv4_config.multicast_route = 0;
mroute_close(sk);
mroute_socket=NULL;
}
#endif
+ sk->dead=1;
destroy_sock(sk);
}
-
-static int raw_init(struct sock *sk)
+/* This gets rid of all the nasties in af_inet. -DaveM */
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
- return(0);
+ struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+ int chk_addr_ret;
+
+ if((sk->state != TCP_CLOSE) || (addr_len < sizeof(struct sockaddr_in)))
+ return -EINVAL;
+ chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+ if(addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+ chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ /* Superuser may bind to any address to allow transparent proxying. */
+ if(!suser())
+#endif
+ return -EADDRNOTAVAIL;
+ }
+ sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+ if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+ sk->saddr = 0; /* Use device */
+ dst_release(sk->dst_cache);
+ sk->dst_cache = NULL;
+ return 0;
}
-
/*
* This should be easy, if there is something there
* we return it, otherwise we block.
@@ -343,59 +461,78 @@ int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
if (sk->shutdown & RCV_SHUTDOWN)
return(0);
- if (addr_len)
+ if (addr_len)
*addr_len=sizeof(*sin);
+ if (sk->ip_recverr && (skb = skb_dequeue(&sk->error_queue)) != NULL) {
+ err = sock_error(sk);
+ if (msg->msg_controllen == 0) {
+ skb_free_datagram(sk, skb);
+ return err;
+ }
+ put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
+ skb_free_datagram(sk, skb);
+ return 0;
+ }
+
skb=skb_recv_datagram(sk,flags,noblock,&err);
if(skb==NULL)
return err;
- copied=skb->len;
- if(copied>len)
+ copied = skb->len;
+ if (len < copied)
{
- copied=len;
- msg->msg_flags|=MSG_TRUNC;
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
sk->stamp=skb->stamp;
/* Copy the address. */
- if (sin)
- {
+ if (sin) {
sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = skb->daddr;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
}
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
skb_free_datagram(sk, skb);
return err ? err : (copied);
}
struct proto raw_prot = {
- raw_close,
- udp_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
-#ifdef CONFIG_IP_MROUTE
- ipmr_ioctl,
+ (struct sock *)&raw_prot, /* sklist_next */
+ (struct sock *)&raw_prot, /* sklist_prev */
+ raw_close, /* close */
+ udp_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+#ifdef CONFIG_IP_MROUTE
+ ipmr_ioctl, /* ioctl */
#else
- NULL,
-#endif
- raw_init,
- NULL,
- NULL,
- ip_setsockopt,
- ip_getsockopt,
- raw_sendmsg,
- raw_recvmsg,
- NULL, /* No special bind */
- raw_rcv_skb,
- 128,
- 0,
- "RAW",
- 0, 0,
- NULL
+ NULL, /* ioctl */
+#endif
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ ip_setsockopt, /* setsockopt */
+ ip_getsockopt, /* getsockopt */
+ raw_sendmsg, /* sendmsg */
+ raw_recvmsg, /* recvmsg */
+ raw_bind, /* bind */
+ raw_rcv_skb, /* backlog_rcv */
+ raw_v4_hash, /* hash */
+ raw_v4_unhash, /* unhash */
+ raw_v4_rehash, /* rehash */
+ NULL, /* good_socknum */
+ NULL, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "RAW", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index c9161b3c0..5ba6467d9 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -11,6 +11,7 @@
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Alan Cox, <gw4pts@gw4pts.ampr.org>
* Linus Torvalds, <Linus.Torvalds@helsinki.fi>
+ * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
*
* Fixes:
* Alan Cox : Verify area fixes.
@@ -42,6 +43,8 @@
* Bjorn Ekwall : Kerneld route support.
* Alan Cox : Multicast fixed (I hope)
* Pavel Krauz : Limited broadcast fixed
+ * Alexey Kuznetsov : End of old history. Splitted to fib.c and
+ * route.c and rewritten from scratch.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -65,1661 +68,1327 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/route.h>
+#include <net/arp.h>
#include <net/tcp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/icmp.h>
-#include <net/netlink.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
+#include <linux/net_alias.h>
-/*
- * Forwarding Information Base definitions.
- */
-
-struct fib_node
-{
- struct fib_node *fib_next;
- __u32 fib_dst;
- unsigned long fib_use;
- struct fib_info *fib_info;
- short fib_metric;
- unsigned char fib_tos;
-};
+/* Compile time configuretion flags */
-/*
- * This structure contains data shared by many of routes.
- */
+#define CONFIG_IP_LOCAL_RT_POLICY 1
-struct fib_info
-{
- struct fib_info *fib_next;
- struct fib_info *fib_prev;
- __u32 fib_gateway;
- struct device *fib_dev;
- int fib_refcnt;
- unsigned long fib_window;
- unsigned short fib_flags;
- unsigned short fib_mtu;
- unsigned short fib_irtt;
-};
-
-struct fib_zone
-{
- struct fib_zone *fz_next;
- struct fib_node **fz_hash_table;
- struct fib_node *fz_list;
- int fz_nent;
- int fz_logmask;
- __u32 fz_mask;
-};
-
-static struct fib_zone *fib_zones[33];
-static struct fib_zone *fib_zone_list;
-static struct fib_node *fib_loopback = NULL;
-static struct fib_info *fib_info_list;
+static void rt_run_flush(unsigned long);
+
+static struct timer_list rt_flush_timer =
+ { NULL, NULL, RT_FLUSH_DELAY, 0L, rt_run_flush };
/*
- * Backlogging.
+ * Interface to generic destination cache.
*/
-#define RT_BH_REDIRECT 0
-#define RT_BH_GARBAGE_COLLECT 1
-#define RT_BH_FREE 2
+static void ipv4_dst_destroy(struct dst_entry * dst);
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32);
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+ struct sk_buff *);
-struct rt_req
+
+struct dst_ops ipv4_dst_ops =
{
- struct rt_req * rtr_next;
- struct device *dev;
- __u32 dst;
- __u32 gw;
- unsigned char tos;
+ AF_INET,
+ ipv4_dst_check,
+ ipv4_dst_reroute,
+ ipv4_dst_destroy
};
-int ip_rt_lock;
-unsigned ip_rt_bh_mask;
-static struct rt_req *rt_backlog;
/*
* Route cache.
*/
-struct rtable *ip_rt_hash_table[RT_HASH_DIVISOR];
-static int rt_cache_size;
-static struct rtable *rt_free_queue;
-struct wait_queue *rt_wait;
+static atomic_t rt_cache_size = ATOMIC_INIT(0);
+static struct rtable *rt_hash_table[RT_HASH_DIVISOR];
-static void rt_kick_backlog(void);
-static void rt_cache_add(unsigned hash, struct rtable * rth);
-static void rt_cache_flush(void);
-static void rt_garbage_collect_1(void);
+static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth, u16 protocol);
-/*
- * Evaluate mask length.
- */
-
-static __inline__ int rt_logmask(__u32 mask)
+static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos)
{
- if (!(mask = ntohl(mask)))
- return 32;
- return ffz(~mask);
+ unsigned hash = ((daddr&0xF0F0F0F0)>>4)|((daddr&0x0F0F0F0F)<<4);
+ hash = hash^saddr^tos;
+ hash = hash^(hash>>16);
+ return (hash^(hash>>8)) & 0xFF;
}
-/*
- * Create mask from length.
- */
+#ifdef CONFIG_PROC_FS
-static __inline__ __u32 rt_mask(int logmask)
+static int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
{
- if (logmask >= 32)
- return 0;
- return htonl(~((1<<logmask)-1));
-}
-
-static __inline__ unsigned fz_hash_code(__u32 dst, int logmask)
-{
- return ip_rt_hash_code(ntohl(dst)>>logmask);
-}
+ int len=0;
+ off_t pos=0;
+ char temp[129];
+ struct rtable *r;
+ int i;
-/*
- * Free FIB node.
- */
+ pos = 128;
-static void fib_free_node(struct fib_node * f)
-{
- struct fib_info * fi = f->fib_info;
- if (!--fi->fib_refcnt)
- {
-#if RT_CACHE_DEBUG >= 2
- printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev->name);
-#endif
- if (fi->fib_next)
- fi->fib_next->fib_prev = fi->fib_prev;
- if (fi->fib_prev)
- fi->fib_prev->fib_next = fi->fib_next;
- if (fi == fib_info_list)
- fib_info_list = fi->fib_next;
- }
- kfree_s(f, sizeof(struct fib_node));
-}
+ if (offset<128) {
+ sprintf(buffer,"%-127s\n", "Iface\tDestination\tGateway \tFlags\t\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHHRef\tHHUptod\tSpecDst\tHash");
+ len = 128;
+ }
+
+
+ start_bh_atomic();
-/*
- * Find gateway route by address.
- */
+ for (i = 0; i<RT_HASH_DIVISOR; i++) {
+ for (r = rt_hash_table[i]; r; r = r->u.rt_next) {
+ /*
+ * Spin through entries until we are ready
+ */
+ pos += 128;
-static struct fib_node * fib_lookup_gateway(__u32 dst)
-{
- struct fib_zone * fz;
- struct fib_node * f;
-
- for (fz = fib_zone_list; fz; fz = fz->fz_next)
- {
- if (fz->fz_hash_table)
- f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
- else
- f = fz->fz_list;
-
- for ( ; f; f = f->fib_next)
- {
- if ((dst ^ f->fib_dst) & fz->fz_mask)
+ if (pos <= offset) {
+ len = 0;
continue;
- if (f->fib_info->fib_flags & RTF_GATEWAY)
- return NULL;
- return f;
+ }
+
+ sprintf(temp, "%s\t%08lX\t%08lX\t%8X\t%d\t%u\t%d\t%08lX\t%d\t%u\t%u\t%02X\t%d\t%1d\t%08X\t%02X",
+ r->u.dst.dev ? r->u.dst.dev->name : "*",
+ (unsigned long)r->rt_dst,
+ (unsigned long)r->rt_gateway,
+ r->rt_flags, atomic_read(&r->u.dst.refcnt),
+ atomic_read(&r->u.dst.use), 0,
+ (unsigned long)r->rt_src, (int)r->u.dst.pmtu,
+ r->u.dst.window,
+ (int)r->u.dst.rtt, r->key.tos,
+ r->u.dst.hh ? atomic_read(&r->u.dst.hh->hh_refcnt) : -1,
+ r->u.dst.hh ? r->u.dst.hh->hh_uptodate : 0,
+ r->rt_spec_dst,
+ i);
+ sprintf(buffer+len,"%-127s\n",temp);
+ len += 128;
+ if (pos >= offset+length)
+ goto done;
}
- }
- return NULL;
-}
-
-/*
- * Find local route by address.
- * FIXME: I use "longest match" principle. If destination
- * has some non-local route, I'll not search shorter matches.
- * It's possible, I'm wrong, but I wanted to prevent following
- * situation:
- * route add 193.233.7.128 netmask 255.255.255.192 gw xxxxxx
- * route add 193.233.7.0 netmask 255.255.255.0 eth1
- * (Two ethernets connected by serial line, one is small and other is large)
- * Host 193.233.7.129 is locally unreachable,
- * but old (<=1.3.37) code will send packets destined for it to eth1.
- *
- */
+ }
-static struct fib_node * fib_lookup_local(__u32 dst)
+done:
+ end_bh_atomic();
+
+ *start = buffer+len-(pos-offset);
+ len = pos-offset;
+ if (len>length)
+ len = length;
+ return len;
+}
+#endif
+
+static void __inline__ rt_free(struct rtable *rt)
{
- struct fib_zone * fz;
- struct fib_node * f;
-
- for (fz = fib_zone_list; fz; fz = fz->fz_next)
- {
- int longest_match_found = 0;
-
- if (fz->fz_hash_table)
- f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
- else
- f = fz->fz_list;
-
- for ( ; f; f = f->fib_next)
- {
- if ((dst ^ f->fib_dst) & fz->fz_mask)
- continue;
- if (!(f->fib_info->fib_flags & RTF_GATEWAY))
- return f;
- longest_match_found = 1;
- }
- if (longest_match_found)
- return NULL;
- }
- return NULL;
+ dst_free(&rt->u.dst);
}
-/*
- * Main lookup routine.
- * IMPORTANT NOTE: this algorithm has small difference from <=1.3.37 visible
- * by user. It doesn't route non-CIDR broadcasts by default.
- *
- * F.e.
- * ifconfig eth0 193.233.7.65 netmask 255.255.255.192 broadcast 193.233.7.255
- * is valid, but if you really are not able (not allowed, do not want) to
- * use CIDR compliant broadcast 193.233.7.127, you should add host route:
- * route add -host 193.233.7.255 eth0
- */
-static struct fib_node * fib_lookup(__u32 dst)
+void ip_rt_check_expire()
{
- struct fib_zone * fz;
- struct fib_node * f;
-
- for (fz = fib_zone_list; fz; fz = fz->fz_next)
- {
- if (fz->fz_hash_table)
- f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
- else
- f = fz->fz_list;
-
- for ( ; f; f = f->fib_next)
- {
- if ((dst ^ f->fib_dst) & fz->fz_mask)
- continue;
- return f;
- }
- }
- return NULL;
-}
+ int i;
+ static int rover;
+ struct rtable *rth, **rthp;
+ unsigned long now = jiffies;
-static __inline__ struct device * get_gw_dev(__u32 gw)
-{
- struct fib_node * f;
- f = fib_lookup_gateway(gw);
- if (f)
- return f->fib_info->fib_dev;
- return NULL;
-}
+ start_bh_atomic();
-/*
- * Check if a mask is acceptable.
- */
-
-static inline int bad_mask(__u32 mask, __u32 addr)
-{
- if (addr & (mask = ~mask))
- return 1;
- mask = ntohl(mask);
- if (mask & (mask+1))
- return 1;
- return 0;
-}
+ for (i=0; i<RT_HASH_DIVISOR/5; i++) {
+ rover = (rover + 1) & (RT_HASH_DIVISOR-1);
+ rthp = &rt_hash_table[rover];
+ while ((rth = *rthp) != NULL) {
+ struct rtable * rth_next = rth->u.rt_next;
-static int fib_del_list(struct fib_node **fp, __u32 dst,
- struct device * dev, __u32 gtw, short flags, short metric, __u32 mask)
-{
- struct fib_node *f;
- int found=0;
+ /*
+ * Cleanup aged off entries.
+ */
+
+ if (!atomic_read(&rth->u.dst.use) &&
+ (now - rth->u.dst.lastuse > RT_CACHE_TIMEOUT)) {
+ *rthp = rth_next;
+ atomic_dec(&rt_cache_size);
+#if RT_CACHE_DEBUG >= 2
+ printk("rt_check_expire clean %02x@%08x\n", rover, rth->rt_dst);
+#endif
+ rt_free(rth);
+ continue;
+ }
- while((f = *fp) != NULL)
- {
- struct fib_info * fi = f->fib_info;
+ if (!rth_next)
+ break;
- /*
- * Make sure the destination and netmask match.
- * metric, gateway and device are also checked
- * if they were specified.
- */
- if (f->fib_dst != dst ||
- (gtw && fi->fib_gateway != gtw) ||
- (metric >= 0 && f->fib_metric != metric) ||
- (dev && fi->fib_dev != dev) )
- {
- fp = &f->fib_next;
- continue;
+ /*
+ * Pseudo-LRU ordering.
+ * Really we should teach it to move
+ * rarely used but permanently living entries
+ * (f.e. rdisc, igmp etc.) to the end of list.
+ */
+
+ if ( rth_next->u.dst.lastuse - rth->u.dst.lastuse > RT_CACHE_BUBBLE_THRESHOLD ||
+ (rth->u.dst.lastuse - rth_next->u.dst.lastuse < 0 &&
+ atomic_read(&rth->u.dst.use) < atomic_read(&rth_next->u.dst.use))) {
+#if RT_CACHE_DEBUG >= 2
+ printk("rt_check_expire bubbled %02x@%08x<->%08x\n", rover, rth->rt_dst, rth_next->rt_dst);
+#endif
+ *rthp = rth_next;
+ rth->u.rt_next = rth_next->u.rt_next;
+ rth_next->u.rt_next = rth;
+ sti();
+ rthp = &rth_next->u.rt_next;
+ continue;
+ }
+ rthp = &rth->u.rt_next;
}
- cli();
- *fp = f->fib_next;
- if (fib_loopback == f)
- fib_loopback = NULL;
- sti();
- ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, flags, metric, fi->fib_dev->name);
- fib_free_node(f);
- found++;
}
- return found;
-}
-static __inline__ int fib_del_1(__u32 dst, __u32 mask,
- struct device * dev, __u32 gtw, short flags, short metric)
+ end_bh_atomic();
+}
+
+
+void rt_cache_flush(int how)
{
- struct fib_node **fp;
- struct fib_zone *fz;
- int found=0;
-
- if (!mask)
- {
- for (fz=fib_zone_list; fz; fz = fz->fz_next)
- {
- int tmp;
- if (fz->fz_hash_table)
- fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
- else
- fp = &fz->fz_list;
-
- tmp = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
- fz->fz_nent -= tmp;
- found += tmp;
- }
- }
- else
- {
- if ((fz = fib_zones[rt_logmask(mask)]) != NULL)
- {
- if (fz->fz_hash_table)
- fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
- else
- fp = &fz->fz_list;
-
- found = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
- fz->fz_nent -= found;
- }
+ start_bh_atomic();
+ if (rt_flush_timer.expires) {
+ if (jiffies - rt_flush_timer.expires > 0 ||
+ rt_flush_timer.expires - jiffies > RT_FLUSH_DELAY/2)
+ how = 1;
}
-
- if (found)
- {
- rt_cache_flush();
- return 0;
+ if (how) {
+ if (rt_flush_timer.expires)
+ del_timer(&rt_flush_timer);
+ rt_flush_timer.expires = 0;
+ end_bh_atomic();
+ rt_run_flush(0);
+ return;
+ }
+ if (rt_flush_timer.expires) {
+ end_bh_atomic();
+ return;
}
- return -ESRCH;
+ del_timer(&rt_flush_timer);
+ rt_flush_timer.expires = jiffies + RT_FLUSH_DELAY;
+ add_timer(&rt_flush_timer);
+ end_bh_atomic();
}
-
-
-static struct fib_info * fib_create_info(__u32 gw, struct device * dev,
- unsigned short flags, unsigned short mss,
- unsigned long window, unsigned short irtt)
+
+void rt_run_flush(unsigned long dummy)
{
- struct fib_info * fi;
+ int i;
+ struct rtable * rth, * next;
- if (!(flags & RTF_MSS))
- {
- mss = dev->mtu;
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
- /*
- * If MTU was not specified, use default.
- * If you want to increase MTU for some net (local subnet)
- * use "route add .... mss xxx".
- *
- * The MTU isn't currently always used and computed as it
- * should be as far as I can tell. [Still verifying this is right]
- */
- if ((flags & RTF_GATEWAY) && mss > 576)
- mss = 576;
-#endif
- }
- if (!(flags & RTF_WINDOW))
- window = 0;
- if (!(flags & RTF_IRTT))
- irtt = 0;
-
- for (fi=fib_info_list; fi; fi = fi->fib_next)
- {
- if (fi->fib_gateway != gw ||
- fi->fib_dev != dev ||
- fi->fib_flags != flags ||
- fi->fib_mtu != mss ||
- fi->fib_window != window ||
- fi->fib_irtt != irtt)
+ for (i=0; i<RT_HASH_DIVISOR; i++) {
+ int nr=0;
+
+ cli();
+ if (!(rth = rt_hash_table[i])) {
+ sti();
continue;
- fi->fib_refcnt++;
+ }
+
+ rt_hash_table[i] = NULL;
+ sti();
+
+ for (; rth; rth=next) {
+ next = rth->u.rt_next;
+ atomic_dec(&rt_cache_size);
+ nr++;
+ rth->u.rt_next = NULL;
+ rt_free(rth);
+ }
#if RT_CACHE_DEBUG >= 2
- printk("fib_create_info: fi %08x/%s is duplicate\n", fi->fib_gateway, fi->fib_dev->name);
+ if (nr > 0)
+ printk("rt_cache_flush: %d@%02x\n", nr, i);
#endif
- return fi;
}
- fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
- if (!fi)
- return NULL;
- memset(fi, 0, sizeof(struct fib_info));
- fi->fib_flags = flags;
- fi->fib_dev = dev;
- fi->fib_gateway = gw;
- fi->fib_mtu = mss;
- fi->fib_window = window;
- fi->fib_refcnt++;
- fi->fib_next = fib_info_list;
- fi->fib_prev = NULL;
- fi->fib_irtt = irtt;
- if (fib_info_list)
- fib_info_list->fib_prev = fi;
- fib_info_list = fi;
-#if RT_CACHE_DEBUG >= 2
- printk("fib_create_info: fi %08x/%s is created\n", fi->fib_gateway, fi->fib_dev->name);
-#endif
- return fi;
}
-
-static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask,
- __u32 gw, struct device *dev, unsigned short mss,
- unsigned long window, unsigned short irtt, short metric)
+static void rt_garbage_collect(void)
{
- struct fib_node *f, *f1;
- struct fib_node **fp;
- struct fib_node **dup_fp = NULL;
- struct fib_zone * fz;
- struct fib_info * fi;
- int logmask;
+ int i;
+ static unsigned expire = RT_CACHE_TIMEOUT>>1;
+ static unsigned long last_gc;
+ struct rtable *rth, **rthp;
+ unsigned long now;
+
+ start_bh_atomic();
+ now = jiffies;
/*
- * Allocate an entry and fill it in.
+ * Garbage collection is pretty expensive,
+ * do not make it too frequently.
*/
-
- f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
- if (f == NULL)
- return;
-
- memset(f, 0, sizeof(struct fib_node));
- f->fib_dst = dst;
- f->fib_metric = metric;
- f->fib_tos = 0;
-
- if ((fi = fib_create_info(gw, dev, flags, mss, window, irtt)) == NULL)
- {
- kfree_s(f, sizeof(struct fib_node));
+ if (now - last_gc < 1*HZ) {
+ expire >>= 1;
+ end_bh_atomic();
return;
}
- f->fib_info = fi;
-
- logmask = rt_logmask(mask);
- fz = fib_zones[logmask];
+ expire++;
- if (!fz)
- {
- int i;
- fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
- if (!fz)
- {
- fib_free_node(f);
- return;
- }
- memset(fz, 0, sizeof(struct fib_zone));
- fz->fz_logmask = logmask;
- fz->fz_mask = mask;
- for (i=logmask-1; i>=0; i--)
- if (fib_zones[i])
- break;
- cli();
- if (i<0)
- {
- fz->fz_next = fib_zone_list;
- fib_zone_list = fz;
- }
- else
- {
- fz->fz_next = fib_zones[i]->fz_next;
- fib_zones[i]->fz_next = fz;
+ for (i=0; i<RT_HASH_DIVISOR; i++) {
+ if (!rt_hash_table[i])
+ continue;
+ for (rthp=&rt_hash_table[i]; (rth=*rthp); rthp=&rth->u.rt_next) {
+ if (atomic_read(&rth->u.dst.use) ||
+ (now - rth->u.dst.lastuse > expire))
+ continue;
+ atomic_dec(&rt_cache_size);
+ *rthp = rth->u.rt_next;
+ rth->u.rt_next = NULL;
+ rt_free(rth);
+ break;
}
- fib_zones[logmask] = fz;
- sti();
}
- /*
- * If zone overgrows RTZ_HASHING_LIMIT, create hash table.
- */
+ last_gc = now;
+ if (atomic_read(&rt_cache_size) < RT_CACHE_MAX_SIZE)
+ expire = RT_CACHE_TIMEOUT>>1;
+ else
+ expire >>= 1;
+ end_bh_atomic();
+}
+
+static int rt_ll_bind(struct rtable *rt)
+{
+ struct neighbour *neigh;
+ struct hh_cache *hh = NULL;
+
+ if (rt->u.dst.dev && rt->u.dst.dev->hard_header_cache) {
+ neigh = rt->u.dst.neighbour;
+ if (!neigh)
+ neigh = arp_find_neighbour(&rt->u.dst, 1);
+
+ if (neigh) {
+ rt->u.dst.neighbour = neigh;
+ for (hh=neigh->hh; hh; hh = hh->hh_next)
+ if (hh->hh_type == ETH_P_IP)
+ break;
+ }
- if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32)
- {
- struct fib_node ** ht;
+ if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
+#if RT_CACHE_DEBUG >= 2
+ extern atomic_t hh_count;
+ atomic_inc(&hh_count);
+#endif
+ memset(hh, 0, sizeof(struct hh_cache));
+ hh->hh_type = ETH_P_IP;
+ atomic_set(&hh->hh_refcnt, 0);
+ hh->hh_next = NULL;
+ if (rt->u.dst.dev->hard_header_cache(&rt->u.dst, neigh, hh)) {
+ kfree(hh);
#if RT_CACHE_DEBUG >= 2
- printk("fib_add_1: hashing for zone %d started\n", logmask);
+ atomic_dec(&hh_count);
#endif
- ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL);
-
- if (ht)
- {
- memset(ht, 0, RTZ_HASH_DIVISOR*sizeof(struct fib_node*));
- cli();
- f1 = fz->fz_list;
- while (f1)
- {
- struct fib_node * next;
- unsigned hash = fz_hash_code(f1->fib_dst, logmask);
- next = f1->fib_next;
- f1->fib_next = ht[hash];
- ht[hash] = f1;
- f1 = next;
+ hh = NULL;
+ } else if (neigh) {
+ atomic_inc(&hh->hh_refcnt);
+ hh->hh_next = neigh->hh;
+ neigh->hh = hh;
}
- fz->fz_list = NULL;
- fz->fz_hash_table = ht;
- sti();
+ }
+ if (hh) {
+ atomic_inc(&hh->hh_refcnt);
+ rt->u.dst.hh = hh;
+ return hh->hh_uptodate;
}
}
+ return 0;
+}
- if (fz->fz_hash_table)
- fp = &fz->fz_hash_table[fz_hash_code(dst, logmask)];
- else
- fp = &fz->fz_list;
- /*
- * Scan list to find the first route with the same destination
- */
- while ((f1 = *fp) != NULL)
- {
- if (f1->fib_dst == dst)
- break;
- fp = &f1->fib_next;
- }
+static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol)
+{
+ struct rtable *rth, **rthp;
+ unsigned long now = jiffies;
- /*
- * Find route with the same destination and less (or equal) metric.
- */
- while ((f1 = *fp) != NULL && f1->fib_dst == dst)
- {
- if (f1->fib_metric >= metric)
- break;
- /*
- * Record route with the same destination and gateway,
- * but less metric. We'll delete it
- * after instantiation of new route.
- */
- if (f1->fib_info->fib_gateway == gw &&
- (gw || f1->fib_info->fib_dev == dev))
- dup_fp = fp;
- fp = &f1->fib_next;
- }
+ rt->u.dst.priority = rt_tos2priority(rt->key.tos);
- /*
- * Is it already present?
- */
+ start_bh_atomic();
- if (f1 && f1->fib_metric == metric && f1->fib_info == fi)
- {
- fib_free_node(f);
- return;
- }
-
- /*
- * Insert new entry to the list.
- */
+ rthp = &rt_hash_table[hash];
- cli();
- f->fib_next = f1;
- *fp = f;
- if (!fib_loopback && (fi->fib_dev->flags & IFF_LOOPBACK))
- fib_loopback = f;
- sti();
- fz->fz_nent++;
- ip_netlink_msg(RTMSG_NEWROUTE, dst, gw, mask, flags, metric, fi->fib_dev->name);
+ while ((rth = *rthp) != NULL) {
+ if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) {
+ /* Put it first */
+ *rthp = rth->u.rt_next;
+ rth->u.rt_next = rt_hash_table[hash];
+ rt_hash_table[hash] = rth;
- /*
- * Delete route with the same destination and gateway.
- * Note that we should have at most one such route.
- */
- if (dup_fp)
- fp = dup_fp;
- else
- fp = &f->fib_next;
-
- while ((f1 = *fp) != NULL && f1->fib_dst == dst)
- {
- if (f1->fib_info->fib_gateway == gw &&
- (gw || f1->fib_info->fib_dev == dev))
- {
- cli();
- *fp = f1->fib_next;
- if (fib_loopback == f1)
- fib_loopback = NULL;
- sti();
- ip_netlink_msg(RTMSG_DELROUTE, dst, gw, mask, flags, metric, f1->fib_info->fib_dev->name);
- fib_free_node(f1);
- fz->fz_nent--;
- break;
+ atomic_inc(&rth->u.dst.refcnt);
+ atomic_inc(&rth->u.dst.use);
+ rth->u.dst.lastuse = now;
+ end_bh_atomic();
+
+ ip_rt_put(rt);
+ rt_free(rt);
+ return rth;
}
- fp = &f1->fib_next;
+
+ rthp = &rth->u.rt_next;
}
- rt_cache_flush();
- return;
-}
-static int rt_flush_list(struct fib_node ** fp, struct device *dev)
-{
- int found = 0;
- struct fib_node *f;
+ if (atomic_read(&rt_cache_size) >= RT_CACHE_MAX_SIZE)
+ rt_garbage_collect();
- while ((f = *fp) != NULL) {
-/*
- * "Magic" device route is allowed to point to loopback,
- * discard it too.
- */
- if (f->fib_info->fib_dev != dev &&
- (f->fib_info->fib_dev != &loopback_dev || f->fib_dst != dev->pa_addr)) {
- fp = &f->fib_next;
- continue;
- }
- cli();
- *fp = f->fib_next;
- if (fib_loopback == f)
- fib_loopback = NULL;
- sti();
- fib_free_node(f);
- found++;
+ rt->u.rt_next = rt_hash_table[hash];
+#if RT_CACHE_DEBUG >= 2
+ if (rt->u.rt_next) {
+ struct rtable * trt;
+ printk("rt_cache @%02x: %08x", hash, rt->rt_dst);
+ for (trt=rt->u.rt_next; trt; trt=trt->u.rt_next)
+ printk(" . %08x", trt->rt_dst);
+ printk("\n");
}
- return found;
-}
+#endif
+ rt_hash_table[hash] = rt;
+ atomic_inc(&rt_cache_size);
-static __inline__ void fib_flush_1(struct device *dev)
-{
- struct fib_zone *fz;
- int found = 0;
+ if (protocol == ETH_P_IP)
+ rt_ll_bind(rt);
- for (fz = fib_zone_list; fz; fz = fz->fz_next)
- {
- if (fz->fz_hash_table)
- {
- int i;
- int tmp = 0;
- for (i=0; i<RTZ_HASH_DIVISOR; i++)
- tmp += rt_flush_list(&fz->fz_hash_table[i], dev);
- fz->fz_nent -= tmp;
- found += tmp;
- }
- else
- {
- int tmp;
- tmp = rt_flush_list(&fz->fz_list, dev);
- fz->fz_nent -= tmp;
- found += tmp;
- }
- }
-
- if (found)
- rt_cache_flush();
+ end_bh_atomic();
+ return rt;
}
-
-/*
- * Called from the PROCfs module. This outputs /proc/net/route.
- *
- * We preserve the old format but pad the buffers out. This means that
- * we can spin over the other entries as we read them. Remember the
- * gated BGP4 code could need to read 60,000+ routes on occasion (that's
- * about 7Mb of data). To do that ok we will need to also cache the
- * last route we got to (reads will generally be following on from
- * one another without gaps).
- */
-
-int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw,
+ u32 saddr, u8 tos, struct device *dev)
{
- struct fib_zone *fz;
- struct fib_node *f;
- int len=0;
- off_t pos=0;
- char temp[129];
int i;
-
- pos = 128;
+ int off_link = 0;
+ struct fib_info *fi;
+ struct rtable *rth, **rthp;
+ u32 skeys[2] = { saddr, 0, };
+ struct device *pdev = net_alias_main_dev(dev);
+
+ tos &= IPTOS_TOS_MASK;
+
+ if (new_gw == old_gw || !ipv4_config.accept_redirects
+ || MULTICAST(new_gw) || BADCLASS(new_gw) || ZERONET(new_gw))
+ goto reject_redirect;
+
+ if ((new_gw^dev->pa_addr)&dev->pa_mask)
+ off_link = 1;
+
+ if (!ipv4_config.rfc1620_redirects) {
+ if (off_link)
+ goto reject_redirect;
+ if (ipv4_config.secure_redirects && ip_fib_chk_default_gw(new_gw, dev))
+ goto reject_redirect;
+ }
- if (offset<128)
- {
- sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
- len = 128;
- }
-
- while (ip_rt_lock)
- sleep_on(&rt_wait);
- ip_rt_fast_lock();
+ fi = fib_lookup_info(new_gw, 0, 0, &loopback_dev, NULL);
+ if (fi == NULL || fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_NAT))
+ goto reject_redirect;
- for (fz=fib_zone_list; fz; fz = fz->fz_next)
- {
- int maxslot;
- struct fib_node ** fp;
+ for (i=0; i<2; i++) {
+ unsigned hash = rt_hash_code(daddr, skeys[i], tos);
- if (fz->fz_nent == 0)
- continue;
+ rthp=&rt_hash_table[hash];
- if (pos + 128*fz->fz_nent <= offset)
- {
- pos += 128*fz->fz_nent;
- len = 0;
- continue;
- }
+ while ( (rth = *rthp) != NULL) {
+ struct rtable *rt;
- if (fz->fz_hash_table)
- {
- maxslot = RTZ_HASH_DIVISOR;
- fp = fz->fz_hash_table;
- }
- else
- {
- maxslot = 1;
- fp = &fz->fz_list;
- }
-
- for (i=0; i < maxslot; i++, fp++)
- {
-
- for (f = *fp; f; f = f->fib_next)
- {
- struct fib_info * fi;
- /*
- * Spin through entries until we are ready
- */
- pos += 128;
-
- if (pos <= offset)
- {
- len=0;
- continue;
- }
-
- fi = f->fib_info;
- sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
- fi->fib_dev->name, (unsigned long)f->fib_dst, (unsigned long)fi->fib_gateway,
- fi->fib_flags, 0, f->fib_use, f->fib_metric,
- (unsigned long)fz->fz_mask, (int)fi->fib_mtu, fi->fib_window, (int)fi->fib_irtt);
- sprintf(buffer+len,"%-127s\n",temp);
-
- len += 128;
- if (pos >= offset+length)
- goto done;
+ if (rth->key.dst != daddr ||
+ rth->key.src != skeys[i] ||
+ rth->key.tos != tos ||
+ rth->key.dst_dev != NULL ||
+ rth->key.src_dev != NULL) {
+ rthp = &rth->u.rt_next;
+ continue;
}
- }
- }
-
-done:
- ip_rt_unlock();
- wake_up(&rt_wait);
-
- *start = buffer+len-(pos-offset);
- len = pos - offset;
- if (len>length)
- len = length;
- return len;
-}
-int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
- int len=0;
- off_t pos=0;
- char temp[129];
- struct rtable *r;
- int i;
+ if (rth->rt_dst != daddr ||
+ rth->rt_src != saddr ||
+ rth->rt_flags&RTF_REJECT ||
+ rth->rt_gateway != old_gw ||
+ rth->u.dst.dev != dev)
+ break;
- pos = 128;
+ rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (rt == NULL)
+ return;
- if (offset<128)
- {
- sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tHH\tARP");
- len = 128;
- }
-
-
- while (ip_rt_lock)
- sleep_on(&rt_wait);
- ip_rt_fast_lock();
-
- for (i = 0; i<RT_HASH_DIVISOR; i++)
- {
- for (r = ip_rt_hash_table[i]; r; r = r->rt_next)
- {
/*
- * Spin through entries until we are ready
+ * Copy all the information.
*/
- pos += 128;
+ atomic_set(&rt->u.dst.refcnt, 1);
+ rt->u.dst.dev = dev;
+ rt->u.dst.input = rth->u.dst.input;
+ rt->u.dst.output = rth->u.dst.output;
+ rt->u.dst.pmtu = dev->mtu;
+ rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+ rt->u.dst.window = 0;
+ atomic_set(&rt->u.dst.use, 1);
+ rt->u.dst.lastuse = jiffies;
+
+ rt->rt_flags = rth->rt_flags|RTF_DYNAMIC|RTF_MODIFIED;
+ rt->rt_flags &= ~RTF_GATEWAY;
+ if (new_gw != daddr)
+ rt->rt_flags |= RTF_GATEWAY;
+
+ rt->rt_src = rth->rt_src;
+ rt->rt_dst = rth->rt_dst;
+ rt->rt_src_dev = rth->rt_src_dev;
+ rt->rt_spec_dst = rth->rt_spec_dst;
+ rt->key = rth->key;
+
+ /* But gateway is different ... */
+ rt->rt_gateway = new_gw;
+
+ if (off_link) {
+ if (fi->fib_dev != dev &&
+ net_alias_main_dev(fi->fib_dev) == pdev)
+ rt->u.dst.dev = fi->fib_dev;
+ }
- if (pos <= offset)
- {
- len = 0;
- continue;
+ if (ipv4_config.rfc1620_redirects && !rt_ll_bind(rt)) {
+ ip_rt_put(rt);
+ rt_free(rt);
+ break;
}
-
- sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%d\t%1d",
- r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
- r->rt_flags, r->rt_refcnt, r->rt_use, 0,
- (unsigned long)r->rt_src, (int)r->rt_mtu, r->rt_window, (int)r->rt_irtt, r->rt_hh ? r->rt_hh->hh_refcnt : -1, r->rt_hh ? r->rt_hh->hh_uptodate : 0);
- sprintf(buffer+len,"%-127s\n",temp);
- len += 128;
- if (pos >= offset+length)
- goto done;
+
+ *rthp = rth->u.rt_next;
+ rt_free(rth);
+ rt = rt_intern_hash(hash, rt, ETH_P_IP);
+ ip_rt_put(rt);
+ break;
}
- }
+ }
+ return;
-done:
- ip_rt_unlock();
- wake_up(&rt_wait);
-
- *start = buffer+len-(pos-offset);
- len = pos-offset;
- if (len>length)
- len = length;
- return len;
+reject_redirect:
+ if (ipv4_config.log_martians)
+ printk(KERN_INFO "Redirect from %lX/%s to %lX ignored."
+ "Path = %lX -> %lX, tos %02x\n",
+ ntohl(old_gw), dev->name, ntohl(new_gw),
+ ntohl(saddr), ntohl(daddr), tos);
}
-static void rt_free(struct rtable * rt)
+void ip_rt_advice(struct rtable **rp, int advice)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
- if (!rt->rt_refcnt)
- {
- struct hh_cache * hh = rt->rt_hh;
- rt->rt_hh = NULL;
- restore_flags(flags);
- if (hh && atomic_dec_and_test(&hh->hh_refcnt))
- kfree_s(hh, sizeof(struct hh_cache));
- kfree_s(rt, sizeof(struct rt_table));
+ struct rtable *rt;
+
+ if (advice)
return;
- }
- rt->rt_next = rt_free_queue;
- rt->rt_flags &= ~RTF_UP;
- rt_free_queue = rt;
- ip_rt_bh_mask |= RT_BH_FREE;
-#if RT_CACHE_DEBUG >= 2
- printk("rt_free: %08x\n", rt->rt_dst);
+
+ start_bh_atomic();
+ if ((rt = *rp) != NULL && (rt->rt_flags&(RTF_DYNAMIC|RTF_MODIFIED))) {
+#if RT_CACHE_DEBUG >= 1
+ printk(KERN_DEBUG "ip_rt_advice: redirect to %08x/%02x dropped\n", rt->rt_dst, rt->key.tos);
#endif
- restore_flags(flags);
+ *rp = NULL;
+ ip_rt_put(rt);
+ rt_cache_flush(0);
+ }
+ end_bh_atomic();
+ return;
}
/*
- * RT "bottom half" handlers. Called with masked interrupts.
+ * Algorithm:
+ * 1. The first RT_REDIRECT_NUMBER redirects are sent
+ * with exponential backoff, then we stop sending them at all,
+ * assuming that the host ignores our redirects.
+ * 2. If we did not see a packets requiring redirects
+ * during RT_REDIRECT_SILENCE, we assume that the host
+ * forgot redirected route and start to send redirects again.
+ *
+ * This algorithm is much cheaper and more intelligent than dumb load limiting
+ * in icmp.c.
+ *
+ * NOTE. Do not forget to inhibit load limiting for redirects (redundant)
+ * and "frag. need" (breaks PMTU discovery) in icmp.c.
*/
-static __inline__ void rt_kick_free_queue(void)
+void ip_rt_send_redirect(struct sk_buff *skb)
{
- struct rtable *rt, **rtp;
+ struct rtable *rt = (struct rtable*)skb->dst;
- rtp = &rt_free_queue;
+ /* No redirected packets during RT_REDIRECT_SILENCE;
+ * reset the algorithm.
+ */
+ if (jiffies - rt->last_error > RT_REDIRECT_SILENCE)
+ rt->errors = 0;
- while ((rt = *rtp) != NULL)
- {
- if (!rt->rt_refcnt)
- {
- struct hh_cache * hh = rt->rt_hh;
-#if RT_CACHE_DEBUG >= 2
- __u32 daddr = rt->rt_dst;
-#endif
- *rtp = rt->rt_next;
- rt->rt_hh = NULL;
- sti();
- if (hh && atomic_dec_and_test(&hh->hh_refcnt))
- kfree_s(hh, sizeof(struct hh_cache));
- kfree_s(rt, sizeof(struct rt_table));
-#if RT_CACHE_DEBUG >= 2
- printk("rt_kick_free_queue: %08x is free\n", daddr);
-#endif
- cli();
- continue;
- }
- rtp = &rt->rt_next;
+ /* Too many ignored redirects; do not send anything
+ * set last_error to the last seen redirected packet.
+ */
+ if (rt->errors >= RT_REDIRECT_NUMBER) {
+ rt->last_error = jiffies;
+ return;
}
-}
-
-void ip_rt_run_bh()
-{
- unsigned long flags;
- save_flags(flags);
- cli();
- if (ip_rt_bh_mask && !ip_rt_lock)
- {
- if (ip_rt_bh_mask & RT_BH_REDIRECT)
- rt_kick_backlog();
-
- if (ip_rt_bh_mask & RT_BH_GARBAGE_COLLECT)
- {
- ip_rt_fast_lock();
- ip_rt_bh_mask &= ~RT_BH_GARBAGE_COLLECT;
- sti();
- rt_garbage_collect_1();
- cli();
- ip_rt_fast_unlock();
- }
- if (ip_rt_bh_mask & RT_BH_FREE)
- rt_kick_free_queue();
+ /* Check for load limit; set last_error to the latest sent
+ * redirect.
+ */
+ if (jiffies - rt->last_error > (RT_REDIRECT_LOAD<<rt->errors)) {
+ icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
+ rt->last_error = jiffies;
+ if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER)
+ printk(KERN_WARNING "host %08x/%s ignores redirects for %08x to %08x.\n",
+ rt->rt_src, rt->rt_src_dev->name, rt->rt_dst, rt->rt_gateway);
}
- restore_flags(flags);
}
-
-void ip_rt_check_expire()
+static int ip_error(struct sk_buff *skb)
{
- ip_rt_fast_lock();
- if (ip_rt_lock == 1)
- {
- int i;
- struct rtable *rth, **rthp;
- unsigned long flags;
- unsigned long now = jiffies;
-
- save_flags(flags);
- for (i=0; i<RT_HASH_DIVISOR; i++)
- {
- rthp = &ip_rt_hash_table[i];
-
- while ((rth = *rthp) != NULL)
- {
- struct rtable * rth_next = rth->rt_next;
-
- /*
- * Cleanup aged off entries.
- */
-
- cli();
- if (!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
- {
- *rthp = rth_next;
- sti();
- rt_cache_size--;
-#if RT_CACHE_DEBUG >= 2
- printk("rt_check_expire clean %02x@%08x\n", i, rth->rt_dst);
-#endif
- rt_free(rth);
- continue;
- }
- sti();
-
- if (!rth_next)
- break;
-
- /*
- * LRU ordering.
- */
+ struct rtable *rt = (struct rtable*)skb->dst;
+ int code;
- if (rth->rt_lastuse + RT_CACHE_BUBBLE_THRESHOLD < rth_next->rt_lastuse ||
- (rth->rt_lastuse < rth_next->rt_lastuse &&
- rth->rt_use < rth_next->rt_use))
- {
-#if RT_CACHE_DEBUG >= 2
- printk("rt_check_expire bubbled %02x@%08x<->%08x\n", i, rth->rt_dst, rth_next->rt_dst);
-#endif
- cli();
- *rthp = rth_next;
- rth->rt_next = rth_next->rt_next;
- rth_next->rt_next = rth;
- sti();
- rthp = &rth_next->rt_next;
- continue;
- }
- rthp = &rth->rt_next;
- }
- }
- restore_flags(flags);
- rt_kick_free_queue();
+ switch (rt->u.dst.error) {
+ case EINVAL:
+ default:
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ case ENETUNREACH:
+ code = ICMP_NET_UNREACH;
+ break;
+ case EACCES:
+ code = ICMP_PKT_FILTERED;
+ break;
}
- ip_rt_unlock();
-}
+ if (jiffies - rt->last_error > RT_ERROR_LOAD) {
+ icmp_send(skb, ICMP_DEST_UNREACH, code, 0);
+ rt->last_error = jiffies;
+ }
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
-static void rt_redirect_1(__u32 dst, __u32 gw, struct device *dev)
-{
- struct rtable *rt;
- unsigned long hash = ip_rt_hash_code(dst);
- if (gw == dev->pa_addr)
- return;
- if (dev != get_gw_dev(gw))
- return;
- rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
- if (rt == NULL)
- return;
- memset(rt, 0, sizeof(struct rtable));
- rt->rt_flags = RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY | RTF_UP;
- rt->rt_dst = dst;
- rt->rt_dev = dev;
- rt->rt_gateway = gw;
- rt->rt_src = dev->pa_addr;
- rt->rt_mtu = dev->mtu;
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
- if (dev->mtu > 576)
- rt->rt_mtu = 576;
-#endif
- rt->rt_lastuse = jiffies;
- rt->rt_refcnt = 1;
- rt_cache_add(hash, rt);
- ip_rt_put(rt);
- return;
+static __inline__ unsigned short guess_mtu(unsigned short old_mtu)
+{
+ if (old_mtu > 32000)
+ return 32000;
+ else if (old_mtu > 17914)
+ return 17914;
+ else if (old_mtu > 8166)
+ return 8166;
+ else if (old_mtu > 4352)
+ return 4352;
+ else if (old_mtu > 2002)
+ return 2002;
+ else if (old_mtu > 1492)
+ return 1492;
+ else if (old_mtu > 576)
+ return 576;
+ else if (old_mtu > 296)
+ return 296;
+ /*
+ * These two are not from the RFC but
+ * are needed for AMPRnet AX.25 paths.
+ */
+ else if (old_mtu > 216)
+ return 216;
+ else if (old_mtu > 128)
+ return 128;
+ return 68;
}
-static void rt_cache_flush(void)
+
+unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu)
{
int i;
- struct rtable * rth, * next;
-
- for (i=0; i<RT_HASH_DIVISOR; i++)
- {
- int nr=0;
+ unsigned short old_mtu = ntohs(iph->tot_len);
+ struct rtable *rth;
+ u32 skeys[2] = { iph->saddr, 0, };
+ u32 daddr = iph->daddr;
+ u8 tos = iph->tos & IPTOS_TOS_MASK;
+ unsigned short est_mtu = 0;
+
+ if (ipv4_config.no_pmtu_disc)
+ return 0;
- cli();
- if (!(rth = ip_rt_hash_table[i]))
- {
- sti();
- continue;
- }
+ for (i=0; i<2; i++) {
+ unsigned hash = rt_hash_code(daddr, skeys[i], tos);
- ip_rt_hash_table[i] = NULL;
- sti();
+ for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == skeys[i] &&
+ rth->rt_dst == daddr &&
+ rth->rt_src == iph->saddr &&
+ rth->key.tos == tos &&
+ !rth->key.src_dev &&
+ !(rth->rt_flags&RTF_NOPMTUDISC)) {
+ unsigned short mtu = new_mtu;
- for (; rth; rth=next)
- {
- next = rth->rt_next;
- rt_cache_size--;
- nr++;
- rth->rt_next = NULL;
- rt_free(rth);
- }
-#if RT_CACHE_DEBUG >= 2
- if (nr > 0)
- printk("rt_cache_flush: %d@%02x\n", nr, i);
-#endif
- }
-#if RT_CACHE_DEBUG >= 1
- if (rt_cache_size)
- {
- printk("rt_cache_flush: bug rt_cache_size=%d\n", rt_cache_size);
- rt_cache_size = 0;
- }
-#endif
-}
+ if (new_mtu < 68 || new_mtu >= old_mtu) {
-static void rt_garbage_collect_1(void)
-{
- int i;
- unsigned expire = RT_CACHE_TIMEOUT>>1;
- struct rtable * rth, **rthp;
- unsigned long now = jiffies;
+ /* BSD 4.2 compatibility hack :-( */
+ if (mtu == 0 && old_mtu >= rth->u.dst.pmtu &&
+ old_mtu >= 68 + (iph->ihl<<2))
+ old_mtu -= iph->ihl<<2;
- for (;;)
- {
- for (i=0; i<RT_HASH_DIVISOR; i++)
- {
- if (!ip_rt_hash_table[i])
- continue;
- for (rthp=&ip_rt_hash_table[i]; (rth=*rthp); rthp=&rth->rt_next)
- {
- if (rth->rt_lastuse + expire*(rth->rt_refcnt+1) > now)
- continue;
- rt_cache_size--;
- cli();
- *rthp=rth->rt_next;
- rth->rt_next = NULL;
- sti();
- rt_free(rth);
- break;
+ mtu = guess_mtu(old_mtu);
+ }
+ if (mtu < rth->u.dst.pmtu) {
+ rth->u.dst.pmtu = mtu;
+ est_mtu = mtu;
+ }
}
}
- if (rt_cache_size < RT_CACHE_SIZE_MAX)
- return;
- expire >>= 1;
}
+ return est_mtu;
}
-static __inline__ void rt_req_enqueue(struct rt_req **q, struct rt_req *rtr)
+
+static void ipv4_dst_destroy(struct dst_entry * dst)
{
- unsigned long flags;
- struct rt_req * tail;
-
- save_flags(flags);
- cli();
- tail = *q;
- if (!tail)
- rtr->rtr_next = rtr;
- else
- {
- rtr->rtr_next = tail->rtr_next;
- tail->rtr_next = rtr;
+ struct rtable * rt = (struct rtable*)dst;
+ struct hh_cache * hh = rt->u.dst.hh;
+ rt->u.dst.hh = NULL;
+ if (hh && atomic_dec_and_test(&hh->hh_refcnt)) {
+#if RT_CACHE_DEBUG >= 2
+ extern atomic_t hh_count;
+ atomic_dec(&hh_count);
+#endif
+ kfree(hh);
}
- *q = rtr;
- restore_flags(flags);
- return;
}
-/*
- * Caller should mask interrupts.
- */
-
-static __inline__ struct rt_req * rt_req_dequeue(struct rt_req **q)
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst, u32 cookie)
{
- struct rt_req * rtr;
-
- if (*q)
- {
- rtr = (*q)->rtr_next;
- (*q)->rtr_next = rtr->rtr_next;
- if (rtr->rtr_next == rtr)
- *q = NULL;
- rtr->rtr_next = NULL;
- return rtr;
- }
return NULL;
}
-/*
- Called with masked interrupts
- */
-
-static void rt_kick_backlog()
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst,
+ struct sk_buff *skb)
{
- if (!ip_rt_lock)
- {
- struct rt_req * rtr;
-
- ip_rt_fast_lock();
-
- while ((rtr = rt_req_dequeue(&rt_backlog)) != NULL)
- {
- sti();
- rt_redirect_1(rtr->dst, rtr->gw, rtr->dev);
- kfree_s(rtr, sizeof(struct rt_req));
- cli();
- }
-
- ip_rt_bh_mask &= ~RT_BH_REDIRECT;
-
- ip_rt_fast_unlock();
- }
+ return NULL;
}
-/*
- * rt_{del|add|flush} called only from USER process. Waiting is OK.
- */
-
-static int rt_del(__u32 dst, __u32 mask,
- struct device * dev, __u32 gtw, short rt_flags, short metric)
+int
+ip_check_mc(struct device *dev, u32 mc_addr)
{
- int retval;
-
- while (ip_rt_lock)
- sleep_on(&rt_wait);
- ip_rt_fast_lock();
- retval = fib_del_1(dst, mask, dev, gtw, rt_flags, metric);
- ip_rt_unlock();
- wake_up(&rt_wait);
- return retval;
-}
+ struct ip_mc_list *ip_mc;
-static void rt_add(short flags, __u32 dst, __u32 mask,
- __u32 gw, struct device *dev, unsigned short mss,
- unsigned long window, unsigned short irtt, short metric)
-{
- while (ip_rt_lock)
- sleep_on(&rt_wait);
- ip_rt_fast_lock();
- fib_add_1(flags, dst, mask, gw, dev, mss, window, irtt, metric);
- ip_rt_unlock();
- wake_up(&rt_wait);
+ if (mc_addr==htonl(INADDR_ALLHOSTS_GROUP))
+ return 1;
+
+ for (ip_mc=dev->ip_mc_list; ip_mc; ip_mc=ip_mc->next)
+ if (ip_mc->multiaddr == mc_addr)
+ return 1;
+ return 0;
}
-void ip_rt_flush(struct device *dev)
+static int ip_rt_bug(struct sk_buff *skb)
{
- while (ip_rt_lock)
- sleep_on(&rt_wait);
- ip_rt_fast_lock();
- fib_flush_1(dev);
- ip_rt_unlock();
- wake_up(&rt_wait);
+ kfree_skb(skb, FREE_WRITE);
+ printk(KERN_DEBUG "ip_rt_bug: %08x -> %08x, %s\n", skb->nh.iph->saddr,
+ skb->nh.iph->daddr, skb->dev ? skb->dev->name : "?");
+ return 0;
}
/*
- Called by ICMP module.
+ * This function is called ONLY FROM NET BH. No locking!
+ *
+ * NOTE. We drop all the packets that has local source
+ * addresses, because every properly looped back packet
+ * must have correct destination already attached by output routine.
+ *
+ * Such approach solves two big problems:
+ * 1. Not simplex devices (if they exist 8)) are handled properly.
+ * 2. IP spoofing attempts are filtered with 100% of guarantee.
*/
-void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev)
+int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr,
+ u8 tos, struct device *pdev)
{
- struct rt_req * rtr;
- struct rtable * rt;
+ struct device * dev = pdev;
+ struct fib_info *fi = NULL;
+ struct fib_info *src_fi = NULL;
+ unsigned flags = 0;
+ struct device *devout;
+ struct rtable * rth;
+ unsigned hash;
+ struct fib_result res;
+ u32 src_key = saddr;
+ u32 dst_key = daddr;
+ int err = -EINVAL;
+ int log = 0;
- rt = ip_rt_route(dst, 0);
- if (!rt)
- return;
+ hash = rt_hash_code(daddr, saddr^(unsigned long)pdev, tos);
- if (rt->rt_gateway != src ||
- rt->rt_dev != dev ||
- ((gw^dev->pa_addr)&dev->pa_mask) ||
- ip_chk_addr(gw))
- {
- ip_rt_put(rt);
- return;
- }
- ip_rt_put(rt);
+ /* Check for martians... */
- ip_rt_fast_lock();
- if (ip_rt_lock == 1)
- {
- rt_redirect_1(dst, gw, dev);
- ip_rt_unlock();
- return;
- }
+ if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr))
+ goto martian_source;
+ if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
+ goto mc_input;
- rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC);
- if (rtr)
- {
- rtr->dst = dst;
- rtr->gw = gw;
- rtr->dev = dev;
- rt_req_enqueue(&rt_backlog, rtr);
- ip_rt_bh_mask |= RT_BH_REDIRECT;
+ /* Accept zero addresses only to limited broadcast/multicasts;
+ * I even do not know to fix it or not.
+ */
+ if (ZERONET(saddr))
+ goto martian_source;
+ if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
+ goto martian_destination;
+
+ /*
+ * Device is not yet initialized, accept all addresses as ours.
+ */
+ if (ZERONET(dev->pa_addr))
+ goto promisc_ip;
+
+ /*
+ * Now we are able to route packet.
+ */
+ if ((err = fib_lookup(&res, daddr, saddr, tos, pdev, NULL)) < 0) {
+ if (!IS_ROUTER)
+ return -EINVAL;
+ goto no_route;
}
- ip_rt_unlock();
-}
+ fi = res.f->fib_info;
+ flags = fi->fib_flags;
+ devout = fi->fib_dev;
-static __inline__ void rt_garbage_collect(void)
-{
- if (ip_rt_lock == 1)
- {
- rt_garbage_collect_1();
- return;
+ if (flags&RTF_NAT) {
+ daddr = htonl((ntohl(daddr)&((1<<res.fm)-1)))|fi->fib_gateway;
+ fi = fib_lookup_info(daddr, saddr, tos, pdev, NULL);
+ if (!fi || fi->fib_flags&(RTF_NAT|RTF_LOCAL|RTF_MULTICAST|RTF_BROADCAST))
+ return -EINVAL;
+ devout = fi->fib_dev;
+ flags = fi->fib_flags|RTCF_NAT|RTF_NAT;
}
- ip_rt_bh_mask |= RT_BH_GARBAGE_COLLECT;
-}
-static void rt_cache_add(unsigned hash, struct rtable * rth)
-{
- unsigned long flags;
- struct rtable **rthp;
- __u32 daddr = rth->rt_dst;
- unsigned long now = jiffies;
+ switch (res.fr->cl_action) {
+ case RTP_NAT:
+ /* Packet is from translated source; remember it */
+ saddr = (saddr&~res.fr->cl_srcmask)|res.fr->cl_srcmap;
+ flags |= RTCF_NAT;
+ break;
+ case RTP_MASQUERADE:
+ /* Packet is from masqueraded source; remember it */
+ flags |= RTCF_MASQ;
+ break;
+ default:
+ }
+ log = res.fr->cl_flags&RTRF_LOG;
-#if RT_CACHE_DEBUG >= 2
- if (ip_rt_lock != 1)
- {
- printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock);
- return;
+ if (!(flags & RTF_LOCAL)) {
+ if (!IS_ROUTER || flags&RTF_NOFORWARD)
+ return -EINVAL;
+ } else {
+ fi = NULL;
+ devout = &loopback_dev;
+ if (flags&RTF_BROADCAST)
+ goto mc_input;
}
-#endif
- save_flags(flags);
+#ifndef CONFIG_IP_LOCAL_RT_POLICY
+ if (flags&RTF_LOCAL)
+ src_fi = fib_lookup_info(src_key, 0, tos, &loopback_dev, NULL);
+ else
+#endif
+ if (fib_lookup(&res, src_key, daddr, tos, net_alias_main_dev(devout), NULL) == 0) {
+ src_fi = res.f->fib_info;
+ /* Destination is on masqueraded network:
+ * if it is real incoming frame, ip_forward will drop it.
+ */
+ if (res.fr->cl_flags&RTRF_VALVE)
+ flags |= RTCF_VALVE;
+ }
- if (rth->rt_dev->header_cache_bind)
- {
- struct rtable * rtg = rth;
+ if (src_fi) {
+ if (src_fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT))
+ goto martian_source;
+
+ if (!(src_fi->fib_flags&RTF_GATEWAY))
+ flags |= RTCF_DIRECTSRC;
+
+ if (net_alias_main_dev(src_fi->fib_dev) == pdev)
+ skb->dev = dev = src_fi->fib_dev;
+ else {
+ /* Route to packet source goes via
+ different interface; rfc1812 proposes
+ to drop them.
+ It is dangerous on not-stub/transit networks
+ because of path asymmetry.
+ */
+ if (ipv4_config.rfc1812_filter >= 2)
+ goto martian_source;
- if (rth->rt_gateway != daddr)
- {
- ip_rt_fast_unlock();
- rtg = ip_rt_route(rth->rt_gateway, 0);
- ip_rt_fast_lock();
+ /* Weaker form of rfc1812 filtering.
+ If source is on directly connected network,
+ it can mean either local network configuration error
+ (the most probable case) or real IP spoofing attempt.
+ */
+ if (ipv4_config.rfc1812_filter >= 1 && !(flags&RTCF_DIRECTSRC))
+ goto martian_source;
}
+ } else if (ipv4_config.rfc1812_filter >= 1)
+ goto martian_source;
- if (rtg)
- {
- if (rtg == rth)
- rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst);
- else
- {
- if (rtg->rt_hh)
- atomic_inc(&rtg->rt_hh->hh_refcnt);
- rth->rt_hh = rtg->rt_hh;
- ip_rt_put(rtg);
+make_route:
+ if (skb->protocol != __constant_htons(ETH_P_IP)) {
+ /* ARP request. Do not make route for invalid destination or
+ * if it is redirected.
+ */
+ if (flags&(RTF_REJECT|RTF_BROADCAST|RTF_MULTICAST) ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ (devout == dev && !(flags&(RTF_LOCAL|RTCF_NAT))))
+ return -EINVAL;
+ }
+
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ rth->u.dst.output= ip_rt_bug;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = dst_key;
+ rth->rt_dst = dst_key;
+ rth->rt_dst_map = daddr;
+ rth->key.tos = tos;
+ rth->key.src = src_key;
+ rth->rt_src = src_key;
+ rth->rt_src_map = saddr;
+ rth->rt_src_dev = dev;
+ rth->key.src_dev= pdev;
+ rth->u.dst.dev = devout;
+ rth->key.dst_dev= NULL;
+ rth->rt_gateway = daddr;
+ rth->rt_spec_dst= daddr;
+
+ if (!(flags&RTF_REJECT)) {
+ if (flags&RTF_LOCAL)
+ rth->u.dst.input= ip_local_deliver;
+ if (!(flags&(RTF_NOFORWARD|RTF_BROADCAST))) {
+ if (flags&RTF_MULTICAST) {
+#ifdef CONFIG_IP_MROUTE
+ if (!LOCAL_MCAST(daddr) && ipv4_config.multicast_route) {
+ rth->u.dst.input = ip_mr_input;
+ rth->u.dst.output = ip_output;
+ }
+#endif
+ } else if (!(flags&RTF_LOCAL)) {
+ rth->u.dst.input = ip_forward;
+ rth->u.dst.output = ip_output;
}
}
+ } else if (IS_ROUTER && !(flags&(RTF_MULTICAST|RTF_BROADCAST))) {
+ rth->u.dst.input= ip_error;
+ rth->u.dst.error= -err;
}
- if (rt_cache_size >= RT_CACHE_SIZE_MAX)
- rt_garbage_collect();
-
- cli();
- rth->rt_next = ip_rt_hash_table[hash];
-#if RT_CACHE_DEBUG >= 2
- if (rth->rt_next)
- {
- struct rtable * trth;
- printk("rt_cache @%02x: %08x", hash, daddr);
- for (trth=rth->rt_next; trth; trth=trth->rt_next)
- printk(" . %08x", trth->rt_dst);
- printk("\n");
+ if ((flags&(RTF_BROADCAST|RTF_MULTICAST)) || !(flags&RTF_LOCAL))
+ rth->rt_spec_dst= dev->pa_addr;
+
+ if (fi) {
+ rth->u.dst.pmtu = fi->fib_mtu;
+ rth->u.dst.window=fi->fib_window;
+ rth->u.dst.rtt = fi->fib_irtt;
+ if (flags & RTF_GATEWAY)
+ rth->rt_gateway = fi->fib_gateway;
+ } else {
+ rth->u.dst.pmtu = devout->mtu;
+ rth->u.dst.window=0;
+ rth->u.dst.rtt = TCP_TIMEOUT_INIT;
}
-#endif
- ip_rt_hash_table[hash] = rth;
- rthp = &rth->rt_next;
- sti();
- rt_cache_size++;
- /*
- * Cleanup duplicate (and aged off) entries.
- */
+ if (!(flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTCF_NAT)) &&
+ flags&RTCF_DIRECTSRC &&
+ (devout == dev || (ipv4_config.rfc1620_redirects &&
+ net_alias_main_dev(devout) == pdev)))
+ flags |= RTCF_DOREDIRECT;
- while ((rth = *rthp) != NULL)
- {
+ rth->rt_flags = flags;
- cli();
- if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
- || rth->rt_dst == daddr)
- {
- *rthp = rth->rt_next;
- rt_cache_size--;
- sti();
-#if RT_CACHE_DEBUG >= 2
- printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst);
-#endif
- rt_free(rth);
- continue;
- }
- sti();
- rthp = &rth->rt_next;
- }
- restore_flags(flags);
-}
+ if (log)
+ printk(KERN_INFO "installing route %08lX -> %08lX\n", ntohl(rth->rt_src), ntohl(rth->rt_dst));
-/*
- RT should be already locked.
-
- We could improve this by keeping a chain of say 32 struct rtable's
- last freed for fast recycling.
-
- */
+ if (flags&(RTF_LOCAL|RTF_MULTICAST|RTF_BROADCAST|RTF_REJECT)) {
+ skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0);
+ return 0;
+ }
+ skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, __constant_ntohs(skb->protocol));
+ return 0;
-struct rtable * ip_rt_slow_route (__u32 daddr, int local)
-{
- unsigned hash = ip_rt_hash_code(daddr)^local;
- struct rtable * rth;
- struct fib_node * f;
- struct fib_info * fi;
- __u32 saddr;
+mc_input:
+ if (skb->protocol != __constant_htons(ETH_P_IP))
+ return -EINVAL;
-#if RT_CACHE_DEBUG >= 2
- printk("rt_cache miss @%08x\n", daddr);
-#endif
+ if (ZERONET(saddr)) {
+ if (!ipv4_config.bootp_agent)
+ goto martian_source;
+ flags |= RTF_NOFORWARD|RTF_LOCAL;
+ } else {
+ src_fi = fib_lookup_info(saddr, 0, tos, &loopback_dev, NULL);
+ if (!src_fi)
+ goto martian_source;
+
+ if (src_fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT))
+ goto martian_source;
+
+ if (!(src_fi->fib_flags&RTF_GATEWAY))
+ flags |= RTCF_DIRECTSRC;
+
+ if (!MULTICAST(daddr) || !ipv4_config.multicast_route ||
+ LOCAL_MCAST(daddr)) {
+ if (net_alias_main_dev(src_fi->fib_dev) == pdev) {
+ skb->dev = dev = src_fi->fib_dev;
+ } else {
+ /* Fascist not-unicast filtering 8) */
+ goto martian_source;
+ }
+ }
+ }
- rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC);
- if (!rth)
- {
- ip_rt_unlock();
- return NULL;
+ if (!MULTICAST(daddr)) {
+ flags |= RTF_LOCAL|RTF_BROADCAST|RTF_NOFORWARD;
+ devout = dev;
+ goto make_route;
}
- if (local)
- f = fib_lookup_local(daddr);
- else
- f = fib_lookup (daddr);
+ flags |= RTF_MULTICAST|RTF_LOCAL;
- if (f)
- {
- fi = f->fib_info;
- f->fib_use++;
- }
+ if (ip_check_mc(dev, daddr) == 0) {
+ flags &= ~RTF_LOCAL;
- if (!f || (fi->fib_flags & RTF_REJECT))
- {
-#ifdef CONFIG_KERNELD
- char wanted_route[20];
-#endif
-#if RT_CACHE_DEBUG >= 2
- printk("rt_route failed @%08x\n", daddr);
-#endif
- ip_rt_unlock();
- kfree_s(rth, sizeof(struct rtable));
-#ifdef CONFIG_KERNELD
- daddr=ntohl(daddr);
- sprintf(wanted_route, "%d.%d.%d.%d",
- (int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff,
- (int)(daddr >> 8) & 0xff, (int)daddr & 0xff);
- kerneld_route(wanted_route); /* Dynamic route request */
-#endif
- return NULL;
+ if (!ipv4_config.multicast_route || !(dev->flags&IFF_ALLMULTI))
+ goto no_route;
}
+ devout = dev;
+ goto make_route;
- saddr = fi->fib_dev->pa_addr;
+promisc_ip:
+ flags |= RTF_LOCAL|RTF_NOFORWARD;
+ if (MULTICAST(daddr))
+ flags |= RTF_MULTICAST;
+ else
+ flags |= RTF_BROADCAST;
+ devout = dev;
+ goto make_route;
- if (daddr == fi->fib_dev->pa_addr)
- {
- f->fib_use--;
- if ((f = fib_loopback) != NULL)
- {
- f->fib_use++;
- fi = f->fib_info;
- }
- }
-
- if (!f)
- {
- ip_rt_unlock();
- kfree_s(rth, sizeof(struct rtable));
- return NULL;
- }
+no_route:
+ flags |= RTF_REJECT;
+ devout = dev;
+ goto make_route;
- rth->rt_dst = daddr;
- rth->rt_src = saddr;
- rth->rt_lastuse = jiffies;
- rth->rt_refcnt = 1;
- rth->rt_use = 1;
- rth->rt_next = NULL;
- rth->rt_hh = NULL;
- rth->rt_gateway = fi->fib_gateway;
- rth->rt_dev = fi->fib_dev;
- rth->rt_mtu = fi->fib_mtu;
- rth->rt_window = fi->fib_window;
- rth->rt_irtt = fi->fib_irtt;
- rth->rt_tos = f->fib_tos;
- rth->rt_flags = fi->fib_flags | RTF_HOST;
- if (local)
- rth->rt_flags |= RTF_LOCAL;
-
- if (!(rth->rt_flags & RTF_GATEWAY))
- rth->rt_gateway = rth->rt_dst;
/*
- * Multicast or limited broadcast is never gatewayed.
+ * Do not cache martian addresses: they should be logged (RFC1812)
*/
- if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
- rth->rt_gateway = rth->rt_dst;
+martian_destination:
+ if (ipv4_config.log_martians)
+ printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name);
+ return -EINVAL;
- if (ip_rt_lock == 1)
- rt_cache_add(hash, rth);
- else
- {
- rt_free(rth);
-#if RT_CACHE_DEBUG >= 1
- printk(KERN_DEBUG "rt_cache: route to %08x was born dead\n", daddr);
-#endif
+martian_source:
+ if (ipv4_config.log_martians) {
+ /*
+ * RFC1812 recommenadtion, if source is martian,
+ * the only hint is MAC header.
+ */
+ printk(KERN_WARNING "martian source %08x for %08x, dev %s\n", saddr, daddr, dev->name);
+ if (dev->hard_header_len) {
+ int i;
+ unsigned char *p = skb->mac.raw;
+ printk(KERN_WARNING "ll header:");
+ for (i=0; i<dev->hard_header_len; i++, p++)
+ printk(" %02x", *p);
+ printk("\n");
+ }
}
-
- ip_rt_unlock();
- return rth;
-}
-
-void ip_rt_put(struct rtable * rt)
-{
- if (rt)
- atomic_dec(&rt->rt_refcnt);
+ return -EINVAL;
}
-struct rtable * ip_rt_route(__u32 daddr, int local)
+int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
+ u8 tos, struct device *dev)
{
struct rtable * rth;
+ unsigned hash;
- ip_rt_fast_lock();
+ if (skb->dst)
+ return 0;
- for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
- {
- if (rth->rt_dst == daddr)
- {
- rth->rt_lastuse = jiffies;
- atomic_inc(&rth->rt_use);
- atomic_inc(&rth->rt_refcnt);
- ip_rt_unlock();
- return rth;
+#if RT_CACHE_DEBUG >= 1
+ if (dev->flags & IFF_LOOPBACK) {
+ printk(KERN_DEBUG "ip_route_input: bug: packet is looped back\n");
+ return -EINVAL;
+ }
+ if (net_alias_main_dev(dev) != dev)
+ printk(KERN_DEBUG "ip_route_input: bug: packet is received on alias %s\n", dev->name);
+#endif
+
+ tos &= IPTOS_TOS_MASK;
+ hash = rt_hash_code(daddr, saddr^(unsigned long)dev, tos);
+ skb->dev = dev;
+
+ for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == saddr &&
+ rth->key.src_dev == dev &&
+ rth->key.dst_dev == NULL &&
+ rth->key.tos == tos) {
+ rth->u.dst.lastuse = jiffies;
+ atomic_inc(&rth->u.dst.use);
+ atomic_inc(&rth->u.dst.refcnt);
+ skb->dst = (struct dst_entry*)rth;
+ skb->dev = rth->rt_src_dev;
+ return 0;
}
}
- return ip_rt_slow_route (daddr, local);
+ return ip_route_input_slow(skb, daddr, saddr, tos, dev);
}
+
/*
- * Process a route add request from the user, or from a kernel
- * task.
+ * Major route resolver routine.
*/
-
-int ip_rt_new(struct rtentry *r)
+
+int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u8 tos,
+ struct device *dev_out)
{
- int err;
- char * devname;
- struct device * dev = NULL;
- unsigned long flags;
- __u32 daddr, mask, gw;
- short metric;
+ u32 src_key = saddr;
+ u32 dst_key = daddr;
+ u32 dst_map;
+ struct device *dst_dev_key = dev_out;
+ unsigned flags = 0;
+ struct fib_info *fi = NULL;
+ struct rtable *rth;
+#ifdef CONFIG_IP_LOCAL_RT_POLICY
+ struct fib_result res;
+#endif
+ unsigned hash;
- /*
- * If a device is specified find it.
- */
+ tos &= IPTOS_TOS_MASK|1;
- if ((devname = r->rt_dev) != NULL)
- {
- err = getname(devname, &devname);
- if (err)
- return err;
- dev = dev_get(devname);
- putname(devname);
- if (!dev)
- return -ENODEV;
+ if (saddr) {
+ if (MULTICAST(saddr) || BADCLASS(saddr) || ZERONET(saddr) ||
+ __ip_chk_addr(saddr) != IS_MYADDR)
+ return -EINVAL;
+ if (dev_out == NULL && (MULTICAST(daddr) || daddr == 0xFFFFFFFF))
+ dev_out = ip_dev_find(saddr, NULL);
}
-
- /*
- * If the device isn't INET, don't allow it
- */
-
- if (r->rt_dst.sa_family != AF_INET)
- return -EAFNOSUPPORT;
-
- /*
- * Make local copies of the important bits
- * We decrement the metric by one for BSD compatibility.
- */
-
- flags = r->rt_flags;
- daddr = (__u32) ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr;
- mask = (__u32) ((struct sockaddr_in *) &r->rt_genmask)->sin_addr.s_addr;
- gw = (__u32) ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr;
- metric = r->rt_metric > 0 ? r->rt_metric - 1 : 0;
-
- /*
- * BSD emulation: Permits route add someroute gw one-of-my-addresses
- * to indicate which iface. Not as clean as the nice Linux dev technique
- * but people keep using it... (and gated likes it ;))
- */
-
- if (!dev && (flags & RTF_GATEWAY))
- {
- struct device *dev2;
- for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next)
- {
- if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw)
- {
- flags &= ~RTF_GATEWAY;
- dev = dev2;
- break;
- }
+ if (!daddr)
+ daddr = saddr;
+
+ if (dev_out) {
+ if (!saddr) {
+ saddr = dev_out->pa_addr;
+ if (!daddr)
+ daddr = saddr;
}
+ dst_map = daddr;
+ if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
+ goto make_route;
}
- if (flags & RTF_HOST)
- mask = 0xffffffff;
- else if (mask && r->rt_genmask.sa_family != AF_INET)
- return -EAFNOSUPPORT;
+ if (!daddr)
+ daddr = htonl(INADDR_LOOPBACK);
+
+#ifdef CONFIG_IP_LOCAL_RT_POLICY
+ if (fib_lookup(&res, daddr, saddr, tos, &loopback_dev, dev_out))
+ return -ENETUNREACH;
+ fi = res.f->fib_info;
+ dst_map = daddr;
+
+ if (fi->fib_flags&RTF_NAT)
+ return -EINVAL;
- if (flags & RTF_GATEWAY)
- {
- if (r->rt_gateway.sa_family != AF_INET)
- return -EAFNOSUPPORT;
+ if (!saddr) {
+ saddr = fi->fib_dev->pa_addr;
/*
- * Don't try to add a gateway we can't reach..
- * Tunnel devices are exempt from this rule.
+ * "Stabilization" of route.
+ * This step is necessary, if locally originated packets
+ * are subjected to source routing, else we could get
+ * route flapping.
*/
-
- if (!dev)
- dev = get_gw_dev(gw);
- else if (dev != get_gw_dev(gw) && dev->type != ARPHRD_TUNNEL)
- return -EINVAL;
- if (!dev)
- return -ENETUNREACH;
- }
- else
- {
- gw = 0;
- if (!dev)
- dev = ip_dev_bynet(daddr, mask);
- if (!dev)
+ fi = fib_lookup_info(dst_map, saddr, tos, &loopback_dev, dev_out);
+ if (!fi)
return -ENETUNREACH;
- if (!mask)
- {
- if (((daddr ^ dev->pa_addr) & dev->pa_mask) == 0)
- mask = dev->pa_mask;
- }
}
+#else
+ fi = fib_lookup_info(daddr, 0, tos, &loopback_dev, dev_out);
+ if (!fi)
+ return -ENETUNREACH;
-#ifndef CONFIG_IP_CLASSLESS
- if (!mask)
- mask = ip_get_mask(daddr);
+ if (fi->fib_flags&RTF_NAT)
+ return -EINVAL;
+
+ dst_map = daddr;
+ if (!saddr)
+ saddr = fi->fib_dev->pa_addr;
#endif
-
- if (bad_mask(mask, daddr))
+
+ flags |= fi->fib_flags;
+ dev_out = fi->fib_dev;
+
+ if (RT_LOCALADDR(flags)) {
+ dev_out = &loopback_dev;
+ fi = NULL;
+ }
+
+ if (dst_dev_key && dev_out != dst_dev_key)
return -EINVAL;
- /*
- * Add the route
- */
+make_route:
+ if (LOOPBACK(saddr) && !(dev_out->flags&IFF_LOOPBACK)) {
+ printk(KERN_DEBUG "this guy talks to %08x from loopback\n", daddr);
+ return -EINVAL;
+ }
- rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
- return 0;
-}
+ if (daddr == 0xFFFFFFFF)
+ flags |= RTF_BROADCAST;
+ else if (MULTICAST(daddr))
+ flags |= RTF_MULTICAST;
+ else if (BADCLASS(daddr) || ZERONET(daddr))
+ return -EINVAL;
+
+ if (flags&RTF_BROADCAST && (dev_out->flags&IFF_LOOPBACK ||
+ !(dev_out->flags&IFF_BROADCAST)))
+ flags &= ~RTF_LOCAL;
+ else if (flags&RTF_MULTICAST) {
+ if (ip_check_mc(dev_out, daddr))
+ flags |= RTF_LOCAL;
+ }
+
+ rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+ if (!rth)
+ return -ENOBUFS;
+
+ atomic_set(&rth->u.dst.use, 1);
+ rth->key.dst = dst_key;
+ rth->key.tos = tos;
+ rth->key.src = src_key;
+ rth->key.src_dev= NULL;
+ rth->key.dst_dev= dst_dev_key;
+ rth->rt_dst = daddr;
+ rth->rt_dst_map = dst_map;
+ rth->rt_src = saddr;
+ rth->rt_src_map = saddr;
+ rth->rt_src_dev = dev_out;
+ rth->u.dst.dev = dev_out;
+ rth->rt_gateway = dst_map;
+ rth->rt_spec_dst= dev_out->pa_addr;
+ rth->u.dst.output=ip_output;
-/*
- * Remove a route, as requested by the user.
- */
+ if (flags&RTF_LOCAL) {
+ rth->u.dst.input = ip_local_deliver;
+ rth->rt_spec_dst = daddr;
+ }
+ if (flags&(RTF_BROADCAST|RTF_MULTICAST)) {
+ rth->rt_spec_dst = dev_out->pa_addr;
+ flags &= ~RTF_GATEWAY;
+ if (flags&RTF_LOCAL)
+ rth->u.dst.output = ip_mc_output;
+ if (flags&RTF_MULTICAST) {
+ if (dev_out->flags&IFF_ALLMULTI)
+ rth->u.dst.output = ip_mc_output;
+#ifdef CONFIG_IP_MROUTE
+ if (ipv4_config.multicast_route && !LOCAL_MCAST(daddr))
+ rth->u.dst.input = ip_mr_input;
+#endif
+ }
+ }
+
+ if (fi) {
+ if (flags&RTF_GATEWAY)
+ rth->rt_gateway = fi->fib_gateway;
+ rth->u.dst.pmtu = fi->fib_mtu;
+ rth->u.dst.window=fi->fib_window;
+ rth->u.dst.rtt = fi->fib_irtt;
+ } else {
+ rth->u.dst.pmtu = dev_out->mtu;
+ rth->u.dst.window=0;
+ rth->u.dst.rtt = TCP_TIMEOUT_INIT;
+ }
+ rth->rt_flags = flags;
+ hash = rt_hash_code(dst_key, dst_dev_key ? src_key^(dst_dev_key->ifindex<<5) : src_key, tos);
+ *rp = rt_intern_hash(hash, rth, ETH_P_IP);
+ return 0;
+}
-int ip_rt_kill(struct rtentry *r)
+int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, struct device *dev_out)
{
- struct sockaddr_in *trg;
- struct sockaddr_in *msk;
- struct sockaddr_in *gtw;
- char *devname;
- int err;
- struct device * dev = NULL;
-
- trg = (struct sockaddr_in *) &r->rt_dst;
- msk = (struct sockaddr_in *) &r->rt_genmask;
- gtw = (struct sockaddr_in *) &r->rt_gateway;
- if ((devname = r->rt_dev) != NULL)
- {
- err = getname(devname, &devname);
- if (err)
- return err;
- dev = dev_get(devname);
- putname(devname);
- if (!dev)
- return -ENODEV;
+ unsigned hash;
+ struct rtable *rth;
+
+ hash = rt_hash_code(daddr, dev_out ? saddr^(dev_out->ifindex<<5)
+ : saddr, tos);
+
+ start_bh_atomic();
+ for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == saddr &&
+ rth->key.src_dev == NULL &&
+ rth->key.dst_dev == dev_out &&
+ rth->key.tos == tos) {
+ rth->u.dst.lastuse = jiffies;
+ atomic_inc(&rth->u.dst.use);
+ atomic_inc(&rth->u.dst.refcnt);
+ end_bh_atomic();
+ *rp = rth;
+ return 0;
+ }
}
- /*
- * metric can become negative here if it wasn't filled in
- * but that's a fortunate accident; we really use that in rt_del.
- */
- err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, dev,
- (__u32)gtw->sin_addr.s_addr, r->rt_flags, r->rt_metric - 1);
- return err;
+ end_bh_atomic();
+
+ return ip_route_output_slow(rp, daddr, saddr, tos, dev_out);
}
-/*
- * Handle IP routing ioctl calls. These are used to manipulate the routing tables
- */
-
-int ip_rt_ioctl(unsigned int cmd, void *arg)
+int ip_route_output_dev(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, int ifindex)
{
- int err;
- struct rtentry rt;
-
- switch(cmd)
- {
- case SIOCADDRT: /* Add a route */
- case SIOCDELRT: /* Delete a route */
- if (!suser())
- return -EPERM;
- err = copy_from_user(&rt, arg, sizeof(struct rtentry));
- if (err)
- return -EFAULT;
- return (cmd == SIOCDELRT) ? ip_rt_kill(&rt) : ip_rt_new(&rt);
+ unsigned hash;
+ struct rtable *rth;
+ struct device *dev_out;
+
+ hash = rt_hash_code(daddr, saddr^(ifindex<<5), tos);
+
+ start_bh_atomic();
+ for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+ if (rth->key.dst == daddr &&
+ rth->key.src == saddr &&
+ rth->key.src_dev == NULL &&
+ rth->key.tos == tos &&
+ rth->key.dst_dev &&
+ rth->key.dst_dev->ifindex == ifindex) {
+ rth->u.dst.lastuse = jiffies;
+ atomic_inc(&rth->u.dst.use);
+ atomic_inc(&rth->u.dst.refcnt);
+ end_bh_atomic();
+ *rp = rth;
+ return 0;
+ }
}
+ end_bh_atomic();
- return -EINVAL;
+ dev_out = dev_get_by_index(ifindex);
+ if (!dev_out)
+ return -ENODEV;
+ return ip_route_output_slow(rp, daddr, saddr, tos, dev_out);
}
-void ip_rt_advice(struct rtable **rp, int advice)
+void ip_rt_multicast_event(struct device *dev)
{
- /* Thanks! */
- return;
+ rt_cache_flush(0);
}
-void ip_rt_update(int event, struct device *dev)
+void ip_rt_init()
{
-/*
- * This causes too much grief to do now.
- */
-#ifdef COMING_IN_2_1
- if (event == NETDEV_UP)
- rt_add(RTF_HOST|RTF_UP, dev->pa_addr, ~0, 0, dev, 0, 0, 0, 0);
- else if (event == NETDEV_DOWN)
- rt_del(dev->pa_addr, ~0, dev, 0, RTF_HOST|RTF_UP, 0);
-#endif
+ ip_fib_init();
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_RTCACHE, 8, "rt_cache",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt_cache_get_info
+ });
+#endif
}
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 5f6328d68..84ba6578b 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -1,4 +1,4 @@
-/* -*- linux-c -*-
+/*
* sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
*
* Begun April 1, 1996, Mike Shaver.
@@ -7,6 +7,10 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/route.h>
#include <net/tcp.h>
/*
@@ -32,12 +36,43 @@ extern int sysctl_arp_confirm_interval;
extern int sysctl_arp_confirm_timeout;
extern int sysctl_tcp_cong_avoidance;
+extern int sysctl_tcp_hoe_retransmits;
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_tsack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+
extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp);
+struct ipv4_config ipv4_config = { 1, 1, 1, 1, };
+
+#ifdef CONFIG_SYSCTL
+
+struct ipv4_config ipv4_def_router_config = { 0, 1, 1, 1, 1, 1, 1, };
+struct ipv4_config ipv4_def_host_config = { 1, 1, 1, 1, };
+
+int ipv4_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
+ void *buffer, size_t *lenp)
+{
+ int val = IS_ROUTER;
+ int ret;
+
+ ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+ if (write && IS_ROUTER != val) {
+ if (IS_ROUTER)
+ ipv4_config = ipv4_def_router_config;
+ else
+ ipv4_config = ipv4_def_host_config;
+ rt_cache_flush(0);
+ }
+ return ret;
+}
+
ctl_table ipv4_table[] = {
{NET_IPV4_ARP_RES_TIME, "arp_res_time",
- &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
+ &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_ARP_DEAD_RES_TIME, "arp_dead_res_time",
&sysctl_arp_dead_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_ARP_MAX_TRIES, "arp_max_tries",
@@ -52,15 +87,64 @@ ctl_table ipv4_table[] = {
{NET_IPV4_ARP_CONFIRM_TIMEOUT, "arp_confirm_timeout",
&sysctl_arp_confirm_timeout, sizeof(int), 0644, NULL,
&proc_dointvec},
-#if 0
- {TCP_PMTU_DISC, "tcp_pmtu_discovery",
- &ipv4_pmtu_discovery, sizeof(int), 644,
- NULL, &proc_dointvec, &sysctl_intvec_minmax,
- &boolean_min, &boolean_max},
-#endif
-
+ {NET_IPV4_TCP_HOE_RETRANSMITS, "tcp_hoe_retransmits",
+ &sysctl_tcp_hoe_retransmits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_SACK, "tcp_sack",
+ &sysctl_tcp_sack, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_TSACK, "tcp_tsack",
+ &sysctl_tcp_tsack, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
+ &sysctl_tcp_timestamps, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_TCP_WINDOW_SCALING, "tcp_window_scaling",
+ &sysctl_tcp_window_scaling, sizeof(int), 0644, NULL,
+ &proc_dointvec},
{NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid",
&sysctl_tcp_cong_avoidance, sizeof(int), 0644,
NULL, &tcp_sysctl_congavoid },
+ {NET_IPV4_FORWARDING, "ip_forwarding",
+ &ip_statistics.IpForwarding, sizeof(int), 0644, NULL,
+ &ipv4_sysctl_forwarding},
+ {NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
+ &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_RFC1812_FILTER, "ip_rfc1812_filter",
+ &ipv4_config.rfc1812_filter, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_LOG_MARTIANS, "ip_log_martians",
+ &ipv4_config.log_martians, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_SOURCE_ROUTE, "ip_source_route",
+ &ipv4_config.source_route, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ADDRMASK_AGENT, "ip_addrmask_agent",
+ &ipv4_config.addrmask_agent, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_BOOTP_AGENT, "ip_bootp_agent",
+ &ipv4_config.bootp_agent, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_BOOTP_RELAY, "ip_bootp_relay",
+ &ipv4_config.bootp_relay, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_FIB_MODEL, "ip_fib_model",
+ &ipv4_config.fib_model, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
+ &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_ACCEPT_REDIRECTS, "ip_accept_redirects",
+ &ipv4_config.accept_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_SECURE_REDIRECTS, "ip_secure_redirects",
+ &ipv4_config.secure_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+ {NET_IPV4_RFC1620_REDIRECTS, "ip_rfc1620_redirects",
+ &ipv4_config.rfc1620_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
{0}
};
+
+#endif /* CONFIG_SYSCTL */
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index ac6e2ea53..420db4777 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: @(#)tcp.c 1.0.16 05/25/93
+ * Version: $Id: tcp.c,v 1.61 1997/04/22 02:53:10 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -30,7 +30,7 @@
* socket was looked up backwards. Nobody
* tested any icmp error code obviously.
* Alan Cox : tcp_err() now handled properly. It
- * wakes people on errors. select
+ * wakes people on errors. poll
* behaves and the icmp error race
* has gone by moving it into sock.c
* Alan Cox : tcp_send_reset() fixed to work for
@@ -102,12 +102,12 @@
* Alan Cox : BSD accept semantics.
* Alan Cox : Reset on closedown bug.
* Peter De Schrijver : ENOTCONN check missing in tcp_sendto().
- * Michael Pall : Handle select() after URG properly in
+ * Michael Pall : Handle poll() after URG properly in
* all cases.
* Michael Pall : Undo the last fix in tcp_read_urg()
* (multi URG PUSH broke rlogin).
* Michael Pall : Fix the multi URG PUSH problem in
- * tcp_readable(), select() after URG
+ * tcp_readable(), poll() after URG
* works now.
* Michael Pall : recv(...,MSG_OOB) never blocks in the
* BSD api.
@@ -128,7 +128,7 @@
* Alan Cox : Reset tracing code.
* Alan Cox : Spurious resets on shutdown.
* Alan Cox : Giant 15 minute/60 second timer error
- * Alan Cox : Small whoops in selecting before an
+ * Alan Cox : Small whoops in polling before an
* accept.
* Alan Cox : Kept the state trace facility since
* it's handy for debugging.
@@ -162,7 +162,7 @@
* generates them.
* Alan Cox : Cache last socket.
* Alan Cox : Per route irtt.
- * Matt Day : Select() match BSD precisely on error
+ * Matt Day : poll()->select() match BSD precisely on error
* Alan Cox : New buffers
* Marc Tamsky : Various sk->prot->retransmits and
* sk->retransmits misupdating fixed.
@@ -196,6 +196,10 @@
* improvement.
* Stefan Magdalinski : adjusted tcp_readable() to fix FIONREAD
* Willy Konynenberg : Transparent proxying support.
+ * Keith Owens : Do proper meging with partial SKB's in
+ * tcp_do_sendmsg to avoid burstiness.
+ * Eric Schenk : Fix fast close down bug with
+ * shutdown() followed by close().
*
* To Fix:
* Fast path the code. Two things here - fix the window calculation
@@ -204,7 +208,7 @@
*
* Rewrite output state machine to use a single queue.
* Speed up input assembly algorithm.
- * RFC1323 - PAWS and window scaling.
+ * RFC1323 - PAWS and window scaling.[Required for IPv6]
* User settable/learned rtt/max window/mtu
*
* Change the fundamental structure to a single send queue maintained
@@ -419,6 +423,7 @@
#include <linux/types.h>
#include <linux/fcntl.h>
+#include <linux/poll.h>
#include <net/icmp.h>
#include <net/tcp.h>
@@ -428,7 +433,7 @@
unsigned long seq_offset;
struct tcp_mib tcp_statistics;
-
+kmem_cache_t *tcp_openreq_cachep;
/*
* Find someone to 'accept'. Must be called with
@@ -437,26 +442,16 @@ struct tcp_mib tcp_statistics;
static struct open_request *tcp_find_established(struct tcp_opt *tp)
{
- struct open_request *req;
+ struct open_request *req = tp->syn_wait_queue;
- req = tp->syn_wait_queue;
-
- if (!req)
- return NULL;
-
- do {
+ while(req) {
if (req->sk &&
(req->sk->state == TCP_ESTABLISHED ||
req->sk->state >= TCP_FIN_WAIT1))
- {
- return req;
- }
-
+ break;
req = req->dl_next;
-
- } while (req != tp->syn_wait_queue);
-
- return NULL;
+ }
+ return req;
}
/*
@@ -467,14 +462,26 @@ static struct open_request *tcp_find_established(struct tcp_opt *tp)
static void tcp_close_pending (struct sock *sk)
{
- struct sk_buff *skb;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ struct open_request *req = tp->syn_wait_queue;
- while ((skb = skb_dequeue(&sk->receive_queue)) != NULL)
- {
- tcp_close(skb->sk, 0);
- kfree_skb(skb, FREE_READ);
+ while(req) {
+ struct open_request *iter;
+
+ if (req->sk)
+ tcp_close(req->sk, 0);
+
+ iter = req;
+ req = req->dl_next;
+
+ (*iter->class->destructor)(iter);
+ tcp_dec_slow_timer(TCP_SLT_SYNACK);
+ sk->ack_backlog--;
+ tcp_openreq_free(iter);
}
- return;
+
+ tp->syn_wait_queue = NULL;
+ tp->syn_wait_last = &tp->syn_wait_queue;
}
/*
@@ -505,48 +512,40 @@ static int tcp_readable(struct sock *sk)
int sum;
unsigned long flags;
- if(sk && sk->debug)
- printk("tcp_readable: %p - ",sk);
+ SOCK_DEBUG(sk, "tcp_readable: %p - ",sk);
save_flags(flags);
cli();
- if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL)
- {
+ if (sk == NULL || (skb = skb_peek(&sk->receive_queue)) == NULL) {
restore_flags(flags);
- if(sk && sk->debug)
- printk("empty\n");
+ SOCK_DEBUG(sk, "empty\n");
return(0);
}
counted = sk->copied_seq; /* Where we are at the moment */
amount = 0;
- /*
- * Do until a push or until we are out of data.
- */
-
- do
- {
- /* Found a hole so stops here */
+ /* Do until a push or until we are out of data. */
+ do {
+ /* Found a hole so stops here. */
if (before(counted, skb->seq))
break;
- /*
- * Length - header but start from where we are up to
- * avoid overlaps
+
+ /* Length - header but start from where we are up to
+ * avoid overlaps.
*/
- sum = skb->len - (counted - skb->seq);
+ sum = skb->len - (counted - skb->seq);
if (skb->h.th->syn)
sum++;
- if (sum > 0)
- {
- /* Add it up, move on */
+ if (sum > 0) {
+ /* Add it up, move on. */
amount += sum;
if (skb->h.th->syn)
amount--;
counted += sum;
}
- /*
- * Don't count urg data ... but do it in the right place!
+
+ /* Don't count urg data ... but do it in the right place!
* Consider: "old_data (ptr is here) URG PUSH data"
* The old code would stop at the first push because
* it counted the urg (amount==1) and then does amount--
@@ -555,111 +554,89 @@ static int tcp_readable(struct sock *sk)
* though there was normal data available. If we subtract
* the urg data right here, we even get it to work for more
* than one URG PUSH skb without normal data.
- * This means that select() finally works now with urg data
+ * This means that poll() finally works now with urg data
* in the queue. Note that rlogin was never affected
- * because it doesn't use select(); it uses two processes
+ * because it doesn't use poll(); it uses two processes
* and a blocking read(). And the queue scan in tcp_read()
* was correct. Mike <pall@rz.uni-karlsruhe.de>
*/
- /* don't count urg data */
+ /* Don't count urg data. */
if (skb->h.th->urg)
amount--;
#if 0
if (amount && skb->h.th->psh) break;
#endif
skb = skb->next;
- }
- while(skb != (struct sk_buff *)&sk->receive_queue);
+ } while(skb != (struct sk_buff *)&sk->receive_queue);
restore_flags(flags);
- if(sk->debug)
- printk("got %lu bytes.\n",amount);
+ SOCK_DEBUG(sk, "got %lu bytes.\n",amount);
return(amount);
}
/*
- * LISTEN is a special case for select..
+ * LISTEN is a special case for poll..
*/
-static int tcp_listen_select(struct sock *sk, int sel_type, select_table *wait)
+static unsigned int tcp_listen_poll(struct sock *sk, poll_table *wait)
{
- if (sel_type == SEL_IN) {
- struct open_request *req;
+ struct open_request *req;
- lock_sock(sk);
- req = tcp_find_established(&sk->tp_pinfo.af_tcp);
- release_sock(sk);
- if (req)
- return 1;
- select_wait(sk->sleep,wait);
- return 0;
- }
+ lock_sock(sk);
+ req = tcp_find_established(&sk->tp_pinfo.af_tcp);
+ release_sock(sk);
+ if (req)
+ return POLLIN | POLLRDNORM;
return 0;
}
/*
* Wait for a TCP event.
*
- * Note that we don't need to lock the socket, as the upper select layers
+ * Note that we don't need to lock the socket, as the upper poll layers
* take care of normal races (between the test and the event) and we don't
* go look at any of the socket buffers directly.
*/
-int tcp_select(struct sock *sk, int sel_type, select_table *wait)
+unsigned int tcp_poll(struct socket *sock, poll_table *wait)
{
+ unsigned int mask;
+ struct sock *sk = sock->sk;
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ poll_wait(sk->sleep, wait);
if (sk->state == TCP_LISTEN)
- return tcp_listen_select(sk, sel_type, wait);
-
- switch(sel_type) {
- case SEL_IN:
- if (sk->err)
- return 1;
- if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- break;
+ return tcp_listen_poll(sk, wait);
+ mask = 0;
+ if (sk->err)
+ mask = POLLERR;
+ /* Connected? */
+ if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) {
if (sk->shutdown & RCV_SHUTDOWN)
- return 1;
-
- if (tp->rcv_nxt == sk->copied_seq)
- break;
+ mask |= POLLHUP;
- if (sk->urg_seq != sk->copied_seq ||
- tp->rcv_nxt != sk->copied_seq+1 ||
- sk->urginline || !sk->urg_data)
- return 1;
- break;
+ if ((tp->rcv_nxt != sk->copied_seq) &&
+ (sk->urg_seq != sk->copied_seq ||
+ tp->rcv_nxt != sk->copied_seq+1 ||
+ sk->urginline || !sk->urg_data))
+ mask |= POLLIN | POLLRDNORM;
- case SEL_OUT:
- if (sk->err)
- return 1;
- if (sk->shutdown & SEND_SHUTDOWN)
- return 0;
- if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- break;
- /*
- * This is now right thanks to a small fix
- * by Matt Dillon.
+ /* FIXME: this assumed sk->mtu is correctly maintained.
+ * I see no evidence this is the case. -- erics
*/
+ if (!(sk->shutdown & SEND_SHUTDOWN) &&
+ (sock_wspace(sk) >= sk->mtu+128+sk->prot->max_header))
+ mask |= POLLOUT | POLLWRNORM;
- if (sock_wspace(sk) < sk->mtu+128+sk->prot->max_header)
- break;
- return 1;
-
- case SEL_EX:
if (sk->urg_data)
- return 1;
- break;
+ mask |= POLLPRI;
}
- select_wait(sk->sleep, wait);
- return 0;
+ return mask;
}
int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
- switch(cmd)
- {
-
+ switch(cmd) {
case TIOCINQ:
#ifdef FIXME /* FIXME: */
case FIONREAD:
@@ -690,28 +667,39 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
}
default:
return(-EINVAL);
- }
+ };
}
/*
* This routine builds a generic TCP header.
+ * It also builds in the RFC1323 Timestamp.
+ * It can't (unfortunately) do SACK as well.
*/
-extern __inline int tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
+extern __inline void tcp_build_header(struct tcphdr *th, struct sock *sk, int push)
{
- struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
th->seq = htonl(sk->write_seq);
-
th->psh =(push == 0) ? 1 : 0;
-
- sk->bytes_rcv = 0;
- sk->ack_timed = 0;
th->ack_seq = htonl(tp->rcv_nxt);
th->window = htons(tcp_select_window(sk));
- return(sizeof(*th));
+ /* FIXME: could use the inline found in tcp_output.c as well.
+ * Probably that means we should move these up to an include file. --erics
+ */
+ if (tp->tstamp_ok) {
+ __u32 *ptr = (__u32 *)(th+1);
+ *ptr++ = ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+ | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+ /* FIXME: Not sure it's worth setting these here already, but I'm
+ * also not sure we replace them on all paths later. --erics
+ */
+ *ptr++ = jiffies;
+ *ptr++ = tp->ts_recent;
+ }
}
/*
@@ -722,16 +710,14 @@ static void wait_for_tcp_connect(struct sock * sk)
release_sock(sk);
cli();
if (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT && sk->err == 0)
- {
interruptible_sleep_on(sk->sleep);
- }
sti();
lock_sock(sk);
}
static inline int tcp_memory_free(struct sock *sk)
{
- return sk->wmem_alloc < sk->sndbuf;
+ return atomic_read(&sk->wmem_alloc) < sk->sndbuf;
}
/*
@@ -770,22 +756,15 @@ static int tcp_append_tail(struct sock *sk, struct sk_buff *skb, u8 *from,
int fault;
int copy;
- /*
- * Add more stuff to the end
- * of the skb
- */
-
+ /* Add more stuff to the end of the skb. */
copy = min(sk->mss - tcp_size, skb_tailroom(skb));
copy = min(copy, seglen);
-
+
tcp_size += copy;
-
+
fault = copy_from_user(skb->tail, from, copy);
-
if (fault)
- {
return -1;
- }
skb_put(skb, copy);
skb->csum = csum_partial(skb->tail - tcp_size, tcp_size, 0);
@@ -801,32 +780,24 @@ static int tcp_append_tail(struct sock *sk, struct sk_buff *skb, u8 *from,
* and starts the transmit system.
*/
-int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
- int len, int nonblock, int flags)
+int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov, int flags)
{
+ int err = 0;
int copied = 0;
struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
- /*
- * Wait for a connection to finish.
- */
- while (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT)
- {
-
- if (copied)
- return copied;
-
- if (sk->err)
+ /* Wait for a connection to finish. */
+ while (sk->state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT) {
+ if (sk->err)
return sock_error(sk);
-
- if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV)
- {
+
+ if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV) {
if (sk->keepopen)
send_sig(SIGPIPE, current, 0);
return -EPIPE;
}
- if (nonblock)
+ if (flags&MSG_DONTWAIT)
return -EAGAIN;
if (current->signal & ~current->blocked)
@@ -834,163 +805,126 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
wait_for_tcp_connect(sk);
}
-
-
- /*
- * Ok commence sending
- */
-
- while(--iovlen >= 0)
- {
+
+ /* Ok commence sending. */
+ while(--iovlen >= 0) {
int seglen=iov->iov_len;
unsigned char * from=iov->iov_base;
- u32 actual_win;
iov++;
- while(seglen > 0)
- {
+ while(seglen > 0) {
+ unsigned int actual_win;
int copy;
int tmp;
struct sk_buff *skb;
- /*
- * Stop on errors
- */
- if (sk->err)
- {
+ if (err)
+ return (err);
+
+ /* Stop on errors. */
+ if (sk->err) {
if (copied)
return copied;
return sock_error(sk);
}
- /*
- * Make sure that we are established.
- */
- if (sk->shutdown & SEND_SHUTDOWN)
- {
+ /* Make sure that we are established. */
+ if (sk->shutdown & SEND_SHUTDOWN) {
if (copied)
return copied;
send_sig(SIGPIPE,current,0);
return -EPIPE;
}
- /*
- *Now we need to check if we have a half built packet.
- */
+ /* Now we need to check if we have a half built packet. */
- /* if we have queued packets */
- if (tp->send_head && !(flags & MSG_OOB) )
- {
+ /* If we have queued packets.. */
+ if (tp->send_head && !(flags & MSG_OOB)) {
int tcp_size;
/* Tail */
-
+
skb = sk->write_queue.prev;
- tcp_size = skb->tail -
- (unsigned char *)(skb->h.th + 1);
-
- /*
- * This window_seq test is somewhat dangerous
+ tcp_size = skb->tail -
+ ((unsigned char *)(skb->h.th) + tp->tcp_header_len);
+
+ /* printk("extending buffer\n"); */
+ /* This window_seq test is somewhat dangerous
* If the remote does SWS avoidance we should
- * queue the best we can
- * if not we should in fact send multiple
- * packets...
+ * queue the best we can if not we should in
+ * fact send multiple packets...
* a method for detecting this would be most
* welcome
*/
-
if (skb->end > skb->tail &&
sk->mss - tcp_size > 0 &&
- skb->end_seq < tp->snd_una + tp->snd_wnd)
- {
+ tp->snd_nxt < skb->end_seq) {
int tcopy;
-
+
tcopy = tcp_append_tail(sk, skb, from,
tcp_size,
seglen);
if (tcopy == -1)
- {
return -EFAULT;
- }
-
+
from += tcopy;
copied += tcopy;
- len -= tcopy;
seglen -= tcopy;
-
- /*
- * FIXME: if we're nagling we
+
+ /* FIXME: if we're nagling we
* should send here.
*/
continue;
}
}
-
- /*
- * We also need to worry about the window.
- * If window < 1/2 the maximum window we've seen from this
- * host, don't use it. This is sender side
- * silly window prevention, as specified in RFC1122.
- * (Note that this is different than earlier versions of
- * SWS prevention, e.g. RFC813.). What we actually do is
- * use the whole MSS. Since the results in the right
- * edge of the packet being outside the window, it will
- * be queued for later rather than sent.
- */
-
+ /* We also need to worry about the window.
+ * If window < 1/2 the maximum window we've seen from this
+ * host, don't use it. This is sender side
+ * silly window prevention, as specified in RFC1122.
+ * (Note that this is different than earlier versions of
+ * SWS prevention, e.g. RFC813.). What we actually do is
+ * use the whole MSS. Since the results in the right
+ * edge of the packet being outside the window, it will
+ * be queued for later rather than sent.
+ */
copy = min(seglen, sk->mss);
-
actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
- if (copy > actual_win &&
- (((long) actual_win) >= (sk->max_window >> 1)))
- {
+ if (copy > actual_win &&
+ (((int) actual_win) >= (tp->max_window >> 1)) &&
+ actual_win)
copy = actual_win;
- }
- if (copy <= 0)
- {
+ if (copy <= 0) {
printk(KERN_DEBUG "sendmsg: copy < 0\n");
return -EIO;
}
- /*
- * If sk->packets_out > 0 segment will be nagled
- * else we kick it right away
+ /* If tp->packets_out > 0 segment will be nagled
+ * else we kick it right away.
*/
-
tmp = MAX_HEADER + sk->prot->max_header +
sizeof(struct sk_buff) + 15;
- if (copy < min(sk->mss, sk->max_window >> 1) &&
- !(flags & MSG_OOB) && sk->packets_out)
- {
- tmp += min(sk->mss, sk->max_window);
- }
+ if (copy < min(sk->mss, tp->max_window >> 1) &&
+ !(flags & MSG_OOB) && tp->packets_out)
+ tmp += min(sk->mss, tp->max_window);
else
- {
tmp += copy;
- }
skb = sock_wmalloc(sk, tmp, 0, GFP_KERNEL);
-
- /*
- * If we didn't get any memory, we need to sleep.
- */
-
- if (skb == NULL)
- {
+
+ /* If we didn't get any memory, we need to sleep. */
+ if (skb == NULL) {
sk->socket->flags |= SO_NOSPACE;
- if (nonblock)
- {
+ if (flags&MSG_DONTWAIT) {
if (copied)
return copied;
return -EAGAIN;
}
- if (current->signal & ~current->blocked)
- {
+ if (current->signal & ~current->blocked) {
if (copied)
return copied;
return -ERESTARTSYS;
@@ -1000,54 +934,37 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
continue;
}
- skb->sk = sk;
- skb->free = 0;
- skb->localroute = sk->localroute|(flags&MSG_DONTROUTE);
-
- /*
- * FIXME: we need to optimize this.
+ /* FIXME: we need to optimize this.
* Perhaps some hints here would be good.
*/
-
tmp = tp->af_specific->build_net_header(sk, skb);
-
- if (tmp < 0)
- {
- sock_wfree(sk, skb);
+ if (tmp < 0) {
+ kfree_skb(skb, FREE_WRITE);
if (copied)
return(copied);
return(tmp);
}
- skb->h.th =(struct tcphdr *)
- skb_put(skb,sizeof(struct tcphdr));
+ skb->h.th =(struct tcphdr *)
+ skb_put(skb,tp->tcp_header_len);
seglen -= copy;
- tmp = tcp_build_header(skb->h.th, sk, seglen || iovlen);
-
- if (tmp < 0)
- {
- sock_wfree(sk, skb);
- if (copied)
- return(copied);
- return(tmp);
- }
-
- if (flags & MSG_OOB)
- {
+ tcp_build_header(skb->h.th, sk, seglen || iovlen);
+ /* FIXME: still need to think about SACK options here. */
+
+ if (flags & MSG_OOB) {
skb->h.th->urg = 1;
skb->h.th->urg_ptr = ntohs(copy);
}
- skb->csum = csum_partial_copy_fromuser(from,
- skb_put(skb, copy), copy, 0);
-
+ skb->csum = csum_partial_copy_from_user(from,
+ skb_put(skb, copy), copy, 0, &err);
+
from += copy;
copied += copy;
- len -= copy;
- skb->free = 0;
+
sk->write_seq += copy;
-
+
tcp_send_skb(sk, skb);
release_sock(sk);
@@ -1057,12 +974,12 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
sk->err = 0;
+ if (err)
+ return (err);
+
return copied;
}
-
-
-
/*
* Send an ack if one is backlogged at this point. Ought to merge
* this with tcp_send_ack().
@@ -1071,18 +988,15 @@ int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
void tcp_read_wakeup(struct sock *sk)
{
- /*
- * If we're closed, don't send an ack, or we'll get a RST
+ /* If we're closed, don't send an ack, or we'll get a RST
* from the closed destination.
*/
-
if ((sk->state == TCP_CLOSE) || (sk->state == TCP_TIME_WAIT))
return;
tcp_send_ack(sk);
}
-
/*
* Handle reading urgent data. BSD has very simple semantics for
* this, no blocking and very strange errors 8)
@@ -1095,33 +1009,28 @@ static int tcp_recv_urg(struct sock * sk, int nonblock,
int err=0;
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- /*
- * No URG data to read
- */
+ /* No URG data to read. */
if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ)
return -EINVAL; /* Yes this is right ! */
if (sk->err)
return sock_error(sk);
- if (sk->state == TCP_CLOSE || sk->done)
- {
- if (!sk->done)
- {
+ if (sk->state == TCP_CLOSE || sk->done) {
+ if (!sk->done) {
sk->done = 1;
return 0;
}
return -ENOTCONN;
}
- if (sk->shutdown & RCV_SHUTDOWN)
- {
+ if (sk->shutdown & RCV_SHUTDOWN) {
sk->done = 1;
return 0;
}
+
lock_sock(sk);
- if (sk->urg_data & URG_VALID)
- {
+ if (sk->urg_data & URG_VALID) {
char c = sk->urg_data;
if (!(flags & MSG_PEEK))
sk->urg_data = URG_READ;
@@ -1132,23 +1041,20 @@ static int tcp_recv_urg(struct sock * sk, int nonblock,
msg->msg_flags|=MSG_TRUNC;
if(msg->msg_name)
- {
tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
msg->msg_name);
- }
+
if(addr_len)
- *addr_len= tp->af_specific->sockaddr_len;
- /*
- * Read urgent data
- */
+ *addr_len = tp->af_specific->sockaddr_len;
+
+ /* Read urgent data. */
msg->msg_flags|=MSG_OOB;
release_sock(sk);
return err ? -EFAULT : 1;
}
release_sock(sk);
- /*
- * Fixed the recv(..., MSG_OOB) behaviour. BSD docs and
+ /* Fixed the recv(..., MSG_OOB) behaviour. BSD docs and
* the available implementations agree in this case:
* this call should never block, independent of the
* blocking state of the socket.
@@ -1165,9 +1071,8 @@ static int tcp_recv_urg(struct sock * sk, int nonblock,
static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb)
{
- sk->ack_backlog++;
+ sk->tp_pinfo.af_tcp.delayed_acks++;
- skb->sk = sk;
__skb_unlink(skb, &sk->receive_queue);
kfree_skb(skb, FREE_READ);
}
@@ -1176,36 +1081,31 @@ static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb)
static void cleanup_rbuf(struct sock *sk)
{
struct sk_buff *skb;
- struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- /*
- * NOTE! The socket must be locked, so that we don't get
+ /* NOTE! The socket must be locked, so that we don't get
* a messed-up receive queue.
*/
-
while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
- if (!skb->used || skb->users)
+ if (!skb->used || atomic_read(&skb->users)>1)
break;
tcp_eat_skb(sk, skb);
}
-
- if(sk->debug)
- printk("sk->rspace = %lu\n", sock_rspace(sk));
-
- /*
- * We send a ACK if the sender is blocked
- * else let tcp_data deal with the acking policy.
- */
- if (sock_rspace(sk) > tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) &&
- (tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup) < sk->mss))
- {
- /* Send an ack right now. */
- sk->delayed_acks++;
- tcp_read_wakeup(sk);
- }
-
-}
+ SOCK_DEBUG(sk, "sk->rspace = %lu\n", sock_rspace(sk));
+
+ /* We send a ACK if the sender is blocked
+ * else let tcp_data deal with the acking policy.
+ */
+ if (sk->tp_pinfo.af_tcp.delayed_acks) {
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ __u32 rcv_wnd;
+
+ rcv_wnd = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
+
+ if ((rcv_wnd < sk->mss) && (sock_rspace(sk) > rcv_wnd))
+ tcp_read_wakeup(sk);
+ }
+}
/*
@@ -1227,47 +1127,34 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
if (sk->state == TCP_LISTEN)
return -ENOTCONN;
- /*
- * Urgent data needs to be handled specially.
- */
-
+ /* Urgent data needs to be handled specially. */
if (flags & MSG_OOB)
return tcp_recv_urg(sk, nonblock, msg, len, flags, addr_len);
- /*
- * Copying sequence to update. This is volatile to handle
+ /* Copying sequence to update. This is volatile to handle
* the multi-reader case neatly (memcpy_to/fromfs might be
* inline and thus not flush cached variables otherwise).
*/
-
peek_seq = sk->copied_seq;
seq = &sk->copied_seq;
if (flags & MSG_PEEK)
seq = &peek_seq;
- /*
- * Handle the POSIX bogosity MSG_WAITALL
- */
-
+ /* Handle the POSIX bogosity MSG_WAITALL. */
if (flags & MSG_WAITALL)
target=len;
add_wait_queue(sk->sleep, &wait);
lock_sock(sk);
- while (len > 0)
- {
+ while (len > 0) {
struct sk_buff * skb;
u32 offset;
- /*
- * Are we at urgent data? Stop if we have read anything.
- */
-
+ /* Are we at urgent data? Stop if we have read anything. */
if (copied && sk->urg_data && sk->urg_seq == *seq)
break;
- /*
- * We need to check signals first, to get correct SIGURG
+ /* We need to check signals first, to get correct SIGURG
* handling. FIXME: Need to check this doesnt impact 1003.1g
* and move it down to the bottom of the loop
*/
@@ -1275,26 +1162,24 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
if (copied)
break;
copied = -ERESTARTSYS;
+ if (nonblock)
+ copied = -EAGAIN;
break;
}
- /*
- * Next get a buffer.
- */
-
+ /* Next get a buffer. */
current->state = TASK_INTERRUPTIBLE;
skb = skb_peek(&sk->receive_queue);
- do
- {
+ do {
if (!skb)
break;
- /*
- * now that we have two receive queues this
- * shouldn't happen
+
+ /* Now that we have two receive queues this
+ * shouldn't happen.
*/
if (before(*seq, skb->seq)) {
- printk("recvmsg bug: copied %X seq %X\n",
+ printk(KERN_INFO "recvmsg bug: copied %X seq %X\n",
*seq, skb->seq);
break;
}
@@ -1308,22 +1193,18 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
if (!(flags & MSG_PEEK))
skb->used = 1;
skb = skb->next;
- }
- while (skb != (struct sk_buff *)&sk->receive_queue);
+ } while (skb != (struct sk_buff *)&sk->receive_queue);
if (copied >= target)
break;
- if (sk->err && !(flags&MSG_PEEK))
- {
+ if (sk->err && !(flags&MSG_PEEK)) {
copied = sock_error(sk);
break;
}
- if (sk->state == TCP_CLOSE)
- {
- if (!sk->done)
- {
+ if (sk->state == TCP_CLOSE) {
+ if (!sk->done) {
sk->done = 1;
break;
}
@@ -1331,14 +1212,12 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
break;
}
- if (sk->shutdown & RCV_SHUTDOWN)
- {
+ if (sk->shutdown & RCV_SHUTDOWN) {
sk->done = 1;
break;
}
- if (nonblock)
- {
+ if (nonblock) {
copied = -EAGAIN;
break;
}
@@ -1352,97 +1231,75 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
continue;
found_ok_skb:
- /*
- * Lock the buffer. We can be fairly relaxed as
+ /* Lock the buffer. We can be fairly relaxed as
* an interrupt will never steal a buffer we are
* using unless I've missed something serious in
* tcp_data.
*/
+ atomic_inc(&skb->users);
- skb->users++;
-
- /*
- * Ok so how much can we use ?
- */
-
+ /* Ok so how much can we use? */
used = skb->len - offset;
if (len < used)
used = len;
- /*
- * Do we have urgent data here?
- */
- if (sk->urg_data)
- {
+ /* Do we have urgent data here? */
+ if (sk->urg_data) {
u32 urg_offset = sk->urg_seq - *seq;
- if (urg_offset < used)
- {
- if (!urg_offset)
- {
- if (!sk->urginline)
- {
+ if (urg_offset < used) {
+ if (!urg_offset) {
+ if (!sk->urginline) {
++*seq;
offset++;
used--;
}
- }
- else
+ } else
used = urg_offset;
}
}
- /*
- * Copy it - We _MUST_ update *seq first so that we
+ /* Copy it - We _MUST_ update *seq first so that we
* don't ever double read when we have dual readers
*/
-
*seq += used;
- /*
- * This memcpy_toiovec can sleep. If it sleeps and we
+ /* This memcpy_toiovec can sleep. If it sleeps and we
* do a second read it relies on the skb->users to avoid
* a crash when cleanup_rbuf() gets called.
*/
-
err = memcpy_toiovec(msg->msg_iov, ((unsigned char *)skb->h.th) + skb->h.th->doff*4 + offset, used);
- if (err)
- {
- /*
- * exception. bailout!
- */
+ if (err) {
+ /* Exception. Bailout! */
*seq -= err;
- skb->users--;
- return -EFAULT;
+ atomic_dec(&skb->users);
+ copied = -EFAULT;
+ break;
}
copied += used;
len -= used;
- /*
- * We now will not sleep again until we are finished
+ /* We now will not sleep again until we are finished
* with skb. Sorry if you are doing the SMP port
* but you'll just have to fix it neatly ;)
*/
-
- skb->users--;
+ atomic_dec(&skb->users);
if (after(sk->copied_seq,sk->urg_seq))
sk->urg_data = 0;
if (used + offset < skb->len)
continue;
- /*
- * Process the FIN. We may also need to handle PSH
- * here and make it break out of MSG_WAITALL
+ /* Process the FIN. We may also need to handle PSH
+ * here and make it break out of MSG_WAITALL.
*/
-
if (skb->h.th->fin)
goto found_fin_ok;
if (flags & MSG_PEEK)
continue;
skb->used = 1;
- if (!skb->users)
+ if (atomic_read(&skb->users) == 1)
tcp_eat_skb(sk, skb);
continue;
@@ -1451,35 +1308,28 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg,
if (flags & MSG_PEEK)
break;
- /*
- * All is done
- */
-
+ /* All is done. */
skb->used = 1;
sk->shutdown |= RCV_SHUTDOWN;
break;
-
}
if(copied > 0 && msg->msg_name)
- {
tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
msg->msg_name);
- }
+
if(addr_len)
- *addr_len= tp->af_specific->sockaddr_len;
+ *addr_len = tp->af_specific->sockaddr_len;
remove_wait_queue(sk->sleep, &wait);
current->state = TASK_RUNNING;
- /* Clean up data we have read: This will do ACK frames */
+ /* Clean up data we have read: This will do ACK frames. */
cleanup_rbuf(sk);
release_sock(sk);
return copied;
}
-
-
/*
* State processing on a close. This implements the state shift for
* sending our FIN frame. Note that we only send a FIN for some
@@ -1491,8 +1341,7 @@ static int tcp_close_state(struct sock *sk, int dead)
{
int ns=TCP_CLOSE;
int send_fin=0;
- switch(sk->state)
- {
+ switch(sk->state) {
case TCP_SYN_SENT: /* No SYN back, no FIN needed */
break;
case TCP_SYN_RECV:
@@ -1508,16 +1357,16 @@ static int tcp_close_state(struct sock *sk, int dead)
case TCP_CLOSE:
case TCP_LISTEN:
break;
+ case TCP_LAST_ACK: /* Could have shutdown() then close() */
case TCP_CLOSE_WAIT: /* They have FIN'd us. We send our FIN and
wait only for the ACK */
ns=TCP_LAST_ACK;
send_fin=1;
- }
+ };
tcp_set_state(sk,ns);
- /*
- * This is a (useful) BSD violating of the RFC. There is a
+ /* This is a (useful) BSD violating of the RFC. There is a
* problem with TCP as specified in that the other end could
* keep a socket open forever with no application left this end.
* We use a 3 minute timeout (about the same as BSD) then kill
@@ -1525,8 +1374,7 @@ static int tcp_close_state(struct sock *sk, int dead)
* that we won't make the old 4*rto = almost no time - whoops
* reset mistake.
*/
- if(dead && ns==TCP_FIN_WAIT2)
- {
+ if(dead && ns==TCP_FIN_WAIT2) {
int timer_active=del_timer(&sk->timer);
if(timer_active)
add_timer(&sk->timer);
@@ -1544,50 +1392,29 @@ static int tcp_close_state(struct sock *sk, int dead)
void tcp_shutdown(struct sock *sk, int how)
{
- /*
- * We need to grab some memory, and put together a FIN,
+ /* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
* Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
*/
-
if (!(how & SEND_SHUTDOWN))
return;
- /*
- * If we've already sent a FIN, or it's a closed state
- */
-
- if (sk->state == TCP_FIN_WAIT1 ||
- sk->state == TCP_FIN_WAIT2 ||
- sk->state == TCP_CLOSING ||
- sk->state == TCP_LAST_ACK ||
- sk->state == TCP_TIME_WAIT ||
- sk->state == TCP_CLOSE ||
- sk->state == TCP_LISTEN
- )
- {
- return;
- }
- lock_sock(sk);
-
- /*
- * flag that the sender has shutdown
- */
+ /* If we've already sent a FIN, or it's a closed state, skip this. */
+ if (sk->state == TCP_ESTABLISHED ||
+ sk->state == TCP_SYN_SENT ||
+ sk->state == TCP_SYN_RECV ||
+ sk->state == TCP_CLOSE_WAIT) {
+ lock_sock(sk);
- sk->shutdown |= SEND_SHUTDOWN;
+ /* Flag that the sender has shutdown. */
+ sk->shutdown |= SEND_SHUTDOWN;
- /*
- * Clear out any half completed packets.
- */
+ /* Clear out any half completed packets. FIN if needed. */
+ if (tcp_close_state(sk,0))
+ tcp_send_fin(sk);
- /*
- * FIN if needed
- */
-
- if (tcp_close_state(sk,0))
- tcp_send_fin(sk);
-
- release_sock(sk);
+ release_sock(sk);
+ }
}
@@ -1602,7 +1429,7 @@ static inline int closing(struct sock * sk)
case TCP_CLOSING:
case TCP_LAST_ACK:
return 1;
- }
+ };
return 0;
}
@@ -1611,21 +1438,17 @@ void tcp_close(struct sock *sk, unsigned long timeout)
{
struct sk_buff *skb;
- /*
- * We need to grab some memory, and put together a FIN,
+ /* We need to grab some memory, and put together a FIN,
* and then put it into the queue to be sent.
*/
-
lock_sock(sk);
-
- tcp_cache_zap();
- if(sk->state == TCP_LISTEN)
- {
- /* Special case */
+ if(sk->state == TCP_LISTEN) {
+ /* Special case. */
tcp_set_state(sk, TCP_CLOSE);
tcp_close_pending(sk);
release_sock(sk);
sk->dead = 1;
+ sk->prot->unhash(sk);
return;
}
@@ -1635,54 +1458,37 @@ void tcp_close(struct sock *sk, unsigned long timeout)
if (!sk->dead)
sk->state_change(sk);
- /*
- * We need to flush the recv. buffs. We do this only on the
+ /* We need to flush the recv. buffs. We do this only on the
* descriptor close, not protocol-sourced closes, because the
* reader process may not have drained the data yet!
*/
-
while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
kfree_skb(skb, FREE_READ);
-
- /*
- * Timeout is not the same thing - however the code likes
- * to send both the same way (sigh).
+ /* Timeout is not the same thing - however the code likes
+ * to send both the same way (sigh).
*/
-
if (tcp_close_state(sk,1)==1)
- {
tcp_send_fin(sk);
- }
if (timeout) {
cli();
release_sock(sk);
current->timeout = timeout;
- while(closing(sk) && current->timeout)
- {
+ while(closing(sk) && current->timeout) {
interruptible_sleep_on(sk->sleep);
if (current->signal & ~current->blocked)
- {
break;
- }
}
current->timeout=0;
lock_sock(sk);
sti();
}
- /*
- * This will destroy it. The timers will take care of actually
- * free'ing up the memory.
- */
- tcp_cache_zap(); /* Kill the cache again. */
-
/* Now that the socket is dead, if we are in the FIN_WAIT2 state
* we may need to set up a timer.
*/
- if (sk->state==TCP_FIN_WAIT2)
- {
+ if (sk->state==TCP_FIN_WAIT2) {
int timer_active=del_timer(&sk->timer);
if(timer_active)
add_timer(&sk->timer);
@@ -1690,10 +1496,12 @@ void tcp_close(struct sock *sk, unsigned long timeout)
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);
}
- release_sock(sk);
sk->dead = 1;
-}
+ release_sock(sk);
+ if(sk->state == TCP_CLOSE)
+ sk->prot->unhash(sk);
+}
/*
* Wait for an incoming connection, avoid race
@@ -1720,7 +1528,6 @@ static struct open_request * wait_for_connect(struct sock * sk)
return req;
}
-
/*
* This will accept the next outstanding connection.
*
@@ -1734,11 +1541,9 @@ struct sock *tcp_accept(struct sock *sk, int flags)
struct sock *newsk = NULL;
int error;
- /*
- * We need to make sure that this socket is listening,
- * and that it has something pending.
- */
-
+ /* We need to make sure that this socket is listening,
+ * and that it has something pending.
+ */
error = EINVAL;
if (sk->state != TCP_LISTEN)
goto no_listen;
@@ -1750,7 +1555,7 @@ struct sock *tcp_accept(struct sock *sk, int flags)
got_new_connect:
tcp_synq_unlink(tp, req);
newsk = req->sk;
- kfree(req);
+ tcp_openreq_free(req);
sk->ack_backlog--;
error = 0;
out:
@@ -1770,7 +1575,6 @@ no_listen:
goto out;
}
-
/*
* Socket option code for TCP.
*/
@@ -1782,22 +1586,18 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval,
int val;
if (level != SOL_TCP)
- {
return tp->af_specific->setsockopt(sk, level, optname,
optval, optlen);
- }
-
- if (optval == NULL)
- return(-EINVAL);
+
+ if(optlen<sizeof(int))
+ return -EINVAL;
if (get_user(val, (int *)optval))
return -EFAULT;
- switch(optname)
- {
+ switch(optname) {
case TCP_MAXSEG:
-/*
- * values greater than interface MTU won't take effect. however at
+/* values greater than interface MTU won't take effect. however at
* the point when this call is done we typically don't yet know
* which interface is going to be used
*/
@@ -1810,23 +1610,26 @@ int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval,
return 0;
default:
return(-ENOPROTOOPT);
- }
+ };
}
int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval,
int *optlen)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- int val,err;
+ int val;
+ int len;
if(level != SOL_TCP)
- {
return tp->af_specific->getsockopt(sk, level, optname,
optval, optlen);
- }
+
+ if(get_user(len,optlen))
+ return -EFAULT;
+
+ len = min(len,sizeof(int));
- switch(optname)
- {
+ switch(optname) {
case TCP_MAXSEG:
val=sk->user_mss;
break;
@@ -1835,30 +1638,29 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval,
break;
default:
return(-ENOPROTOOPT);
- }
-
- err = put_user(sizeof(int),(int *) optlen);
- if (!err)
- err = put_user(val,(int *)optval);
+ };
- return err;
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval, &val,len))
+ return -EFAULT;
+ return 0;
}
void tcp_set_keepalive(struct sock *sk, int val)
{
if (!sk->keepopen && val)
- {
tcp_inc_slow_timer(TCP_SLT_KEEPALIVE);
- }
else if (sk->keepopen && !val)
- {
tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
- }
}
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -c -o tcp.o tcp.c"
- * c-file-style: "Linux"
- * End:
- */
+void tcp_init(void)
+{
+ tcp_openreq_cachep = kmem_cache_create("tcp_open_request",
+ sizeof(struct open_request),
+ sizeof(long)*8, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if(!tcp_openreq_cachep)
+ panic("tcp_init: Cannot alloc open_request cache.");
+}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 076568961..ab2b1ef82 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: @(#)tcp_input.c 1.0.16 05/25/93
+ * Version: $Id: tcp_input.c,v 1.50 1997/04/22 02:53:12 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -21,12 +21,6 @@
*/
/*
- * TODO
- * - A better sock cache
- *
- */
-
-/*
* Changes:
* Pedro Roque : Fast Retransmit/Recovery.
* Two receive queues.
@@ -42,14 +36,16 @@
* Eric Schenk : Yet another double ACK bug.
* Eric Schenk : Delayed ACK bug fixes.
* Eric Schenk : Floyd style fast retrans war avoidance.
+ * David S. Miller : Don't allow zero congestion window.
+ * Eric Schenk : Fix retransmitter so that it sends
+ * next packet on ack of previous packet.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <net/tcp.h>
-
-
+#include <linux/ipsec.h>
typedef void (*tcp_sys_cong_ctl_t)(struct sock *sk,
u32 seq, u32 ack,
@@ -61,6 +57,12 @@ static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
u32 seq_rtt);
int sysctl_tcp_cong_avoidance = 0;
+int sysctl_tcp_hoe_retransmits = 0;
+int sysctl_tcp_sack = 0;
+int sysctl_tcp_tsack = 0;
+int sysctl_tcp_timestamps = 0;
+int sysctl_tcp_window_scaling = 0;
+
static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
@@ -76,9 +78,7 @@ static void tcp_delack_estimator(struct tcp_opt *tp)
{
int m;
- /*
- * Delayed ACK time estimator.
- */
+ /* Delayed ACK time estimator. */
m = jiffies - tp->lrcvtime;
@@ -87,12 +87,10 @@ static void tcp_delack_estimator(struct tcp_opt *tp)
if (m < 0)
return;
- /*
- * if the mesured value is bigger than
+ /* if the mesured value is bigger than
* twice the round trip time ignore it.
*/
- if ((m << 2) <= tp->srtt)
- {
+ if ((m << 2) <= tp->srtt) {
m -= (tp->iat >> 3);
tp->iat += m;
@@ -106,18 +104,21 @@ static void tcp_delack_estimator(struct tcp_opt *tp)
if (tp->ato < HZ/50)
tp->ato = HZ/50;
- }
- else
+ } else
tp->ato = 0;
}
-/*
- * Called on frames that were known _not_ to have been
- * retransmitted [see Karn/Partridge Proceedings SIGCOMM 87].
- * The algorithm is from the SIGCOMM 88 piece by Van Jacobson.
+/* Called to compute a smoothed rtt estimate. The data fed to this
+ * routine either comes from timestamps, or from segments that were
+ * known _not_ to have been retransmitted [see Karn/Partridge
+ * Proceedings SIGCOMM 87]. The algorithm is from the SIGCOMM 88
+ * piece by Van Jacobson.
+ * NOTE: the next three routines used to be one big routine.
+ * To save cycles in the RFC 1323 implementation it was better to break
+ * it up into three procedures. -- erics
*/
-extern __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
+static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
{
long m;
/*
@@ -126,8 +127,7 @@ extern __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
* are scaled versions of rtt and mean deviation.
* This is designed to be as fast as possible
* m stands for "measurement".
- */
- /*
+ *
* On a 1990 paper the rto value is changed to:
* RTO = rtt + 4 * mdev
*/
@@ -144,55 +144,94 @@ extern __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
m -= (tp->mdev >> 2); /* similar update on mdev */
tp->mdev += m; /* mdev = 3/4 mdev + 1/4 new */
} else {
- /* no previous measure. */
+ /* no previous measure. */
tp->srtt = m<<3; /* take the measured time to be rtt */
tp->mdev = m<<2; /* make sure rto = 3*rtt */
}
+}
+/* Calculate rto without backoff. This is the second half of Van Jacobsons
+ * routine refered to above.
+ */
- /*
- * Now update timeout. Note that this removes any backoff.
- */
-
+static __inline__ void tcp_set_rto(struct tcp_opt *tp)
+{
tp->rto = (tp->srtt >> 3) + tp->mdev;
+ tp->rto += (tp->rto >> 2) + (tp->rto >> (tp->snd_cwnd-1));
+}
+
+
+/* Keep the rto between HZ/5 and 120*HZ. 120*HZ is the upper bound
+ * on packet lifetime in the internet. We need the HZ/5 lower
+ * bound to behave correctly against BSD stacks with a fixed
+ * delayed ack.
+ * FIXME: It's not entirely clear this lower bound is the best
+ * way to avoid the problem. Is it possible to drop the lower
+ * bound and still avoid trouble with BSD stacks? Perhaps
+ * some modification to the RTO calculation that takes delayed
+ * ack bais into account? This needs serious thought. -- erics
+ */
+static __inline__ void tcp_bound_rto(struct tcp_opt *tp)
+{
if (tp->rto > 120*HZ)
tp->rto = 120*HZ;
-
- /* Was 1*HZ - keep .2 as minimum cos of the BSD delayed acks */
if (tp->rto < HZ/5)
tp->rto = HZ/5;
+}
+
+/* WARNING: this must not be called if tp->saw_timestamp was false. */
- tp->backoff = 0;
+extern __inline__ void tcp_replace_ts_recent(struct tcp_opt *tp, __u32 end_seq)
+{
+ /* From draft-ietf-tcplw-high-performance: the correct
+ * test is last_ack_sent <= end_seq.
+ * (RFC1323 stated last_ack_sent < end_seq.)
+ */
+ if (!before(end_seq,tp->last_ack_sent)) {
+ tp->ts_recent = tp->rcv_tsval;
+ /* FIXME: need a corse timestamp. Days uptime
+ * would be good.
+ */
+ tp->ts_recent_stamp = jiffies;
+ }
+}
+
+extern __inline__ int tcp_paws_discard(struct tcp_opt *tp)
+{
+ /* FIXME: must check that ts_recent is not
+ * more than 24 days old here. Yuck.
+ */
+ return (tp->rcv_tsval-tp->ts_recent < 0);
+}
+
+
+static int __tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
+{
+ u32 end_window = tp->rcv_wup + tp->rcv_wnd;
+
+ if (tp->rcv_wnd) {
+ if (!before(seq, tp->rcv_nxt) && before(seq, end_window))
+ return 1;
+
+ if ((end_seq - seq) && after(end_seq, tp->rcv_nxt) &&
+ !after(end_seq, end_window))
+ return 1;
+ }
+
+ return 0;
}
-
/*
* This functions checks to see if the tcp header is actually acceptable.
*/
-extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 seg_nxt)
+extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 end_seq)
{
- u32 end_window = tp->rcv_wup + tp->rcv_wnd;
- u32 end_seq = seg_nxt;
-
- /*
- * When the window is open (most common case)
- * we want to accept segments if they have yet unseen data
- * or in the case of a dataless segment if seg.seq == rcv.nxt
- * this means:
- *
- * if (seq == end_seq)
- * end_seq >= rcv.nxt
- * else
- * end_seq > rcv.nxt
- */
-
- if (seq == end_seq)
- end_seq++;
+ if (seq == tp->rcv_nxt)
+ return (tp->rcv_wnd || (end_seq == seq));
- return ((before(seq, end_window) && after(end_seq, tp->rcv_nxt)) ||
- (seq == end_window && seq == end_seq));
+ return __tcp_sequence(tp, seq, end_seq);
}
/*
@@ -203,9 +242,8 @@ extern __inline__ int tcp_sequence(struct tcp_opt *tp, u32 seq, u32 seg_nxt)
static int tcp_reset(struct sock *sk, struct sk_buff *skb)
{
sk->zapped = 1;
- /*
- * We want the right error as BSD sees it (and indeed as we do).
- */
+
+ /* We want the right error as BSD sees it (and indeed as we do). */
switch (sk->state) {
case TCP_TIME_WAIT:
break;
@@ -217,7 +255,7 @@ static int tcp_reset(struct sock *sk, struct sk_buff *skb)
break;
default:
sk->err = ECONNRESET;
- }
+ };
#ifdef CONFIG_TCP_RFC1337
/*
* Time wait assassination protection [RFC1337]
@@ -227,8 +265,7 @@ static int tcp_reset(struct sock *sk, struct sk_buff *skb)
* Ian Heavens has since shown this is an inadequate fix for the protocol
* bug in question.
*/
- if(sk->state!=TCP_TIME_WAIT)
- {
+ if(sk->state!=TCP_TIME_WAIT) {
tcp_set_state(sk,TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
}
@@ -242,34 +279,30 @@ static int tcp_reset(struct sock *sk, struct sk_buff *skb)
return(0);
}
-
/*
- * Look for tcp options. Parses everything but only knows about MSS.
- * This routine is always called with the packet containing the SYN.
- * However it may also be called with the ack to the SYN. So you
- * can't assume this is always the SYN. It's always called after
- * we have set up sk->mtu to our own MTU.
- *
- * We need at minimum to add PAWS support here. Possibly large windows
- * as Linux gets deployed on 100Mb/sec networks.
+ * Look for tcp options. Normally only called on SYN and SYNACK packets.
+ * But, this can also be called on packets in the established flow when
+ * the fast version below fails.
+ * FIXME: surely this can be more efficient. -- erics
*/
-int tcp_parse_options(struct tcphdr *th)
+void tcp_parse_options(struct tcphdr *th, struct tcp_opt *tp)
{
unsigned char *ptr;
int length=(th->doff*4)-sizeof(struct tcphdr);
- int mss = 0;
-
+
ptr = (unsigned char *)(th + 1);
-
- while(length>0)
- {
+ tp->sacks = 0;
+ tp->saw_tstamp = 0;
+
+ while(length>0) {
int opcode=*ptr++;
int opsize=*ptr++;
- switch(opcode)
- {
+ if (length - opsize < 0) /* Don't parse partial options */
+ break;
+ switch(opcode) {
case TCPOPT_EOL:
- return 0;
+ return;
case TCPOPT_NOP: /* Ref: RFC 793 section 3.1 */
length--;
ptr--; /* the opsize=*ptr++ above was a mistake */
@@ -277,25 +310,86 @@ int tcp_parse_options(struct tcphdr *th)
default:
if(opsize<=2) /* Avoid silly options looping forever */
- return 0;
- switch(opcode)
- {
+ return;
+ switch(opcode) {
case TCPOPT_MSS:
- if(opsize==TCPOLEN_MSS && th->syn)
- {
- mss = ntohs(*(unsigned short *)ptr);
- }
+ if(opsize==TCPOLEN_MSS && th->syn) {
+ tp->in_mss = ntohs(*(__u16 *)ptr);
+ if (tp->in_mss == 0)
+ tp->in_mss = 536;
+ }
break;
- /* Add other options here as people feel the urge to implement stuff like large windows */
+ case TCPOPT_WINDOW:
+ if(opsize==TCPOLEN_WINDOW && th->syn)
+ if (sysctl_tcp_window_scaling)
+ tp->snd_wscale = *(__u8 *)ptr;
+ break;
+ case TCPOPT_SACK_PERM:
+ if(opsize==TCPOLEN_SACK_PERM && th->syn)
+ if (sysctl_tcp_sack)
+ tp->sack_ok = 1;
+ case TCPOPT_TIMESTAMP:
+ if(opsize==TCPOLEN_TIMESTAMP) {
+ /* Cheaper to set again then to
+ * test syn. Optimize this?
+ */
+ if (sysctl_tcp_timestamps)
+ tp->tstamp_ok = 1;
+ tp->saw_tstamp = 1;
+ tp->rcv_tsval = ntohl(*(__u32 *)ptr);
+ tp->rcv_tsecr = ntohl(*(__u32 *)(ptr+4));
+ }
+ break;
+ case TCPOPT_SACK:
+ tp->sacks = (opsize-2)>>3;
+ if (tp->sacks<<3 == opsize-2) {
+ int i;
+ for (i = 0; i < tp->sacks; i++) {
+ tp->left_sack[i] = ntohl(((__u32 *)ptr)[2*i]);
+ tp->right_sack[i] = ntohl(((__u32 *)ptr)[2*i+1]);
+ }
+ } else
+ tp->sacks = 0;
}
ptr+=opsize-2;
length-=opsize;
- }
+ };
}
+}
- return mss;
+/* Fast parse options. This hopes to only see timestamps.
+ * If it is wrong it falls back on tcp_parse_option().
+ * This should probably get extended for timestamps + SACK as well.
+ * Assembly code anyone? -- erics
+ */
+static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *tp)
+{
+ if (tp->tcp_header_len == sizeof(struct tcphdr))
+ return 0;
+ if (th->doff == sizeof(struct tcphdr)>>2) {
+ tp->saw_tstamp = 0;
+ tp->sacks = 0;
+ return 0;
+ } else if (th->doff == (sizeof(struct tcphdr)>>2)+3) {
+ __u32 *ptr = (__u32 *)(th + 1);
+ if (*ptr == htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+ | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
+ tp->saw_tstamp = 1;
+ tp->sacks = 0;
+ tp->rcv_tsval = ntohl(*++ptr);
+ tp->rcv_tsecr = ntohl(*++ptr);
+ return 1;
+ }
+ }
+ tcp_parse_options(th,tp);
+ return 1;
}
+#if 0
+
+/*
+ * This is the old fast retransmit code. It will go away eventually. -- erics
+ */
/*
* See draft-stevens-tcpca-spec-01 for documentation.
@@ -305,6 +399,15 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
{
struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ /* FIXME: if we are already retransmitting should this code
+ * be skipped? [Floyd high_seq check sort of does this]
+ * The case I'm worried about is falling into a fast
+ * retransmit on a link with a congestion window of 1 or 2.
+ * There was some evidence in 2.0.x that this was problem
+ * on really slow links (1200 or 2400 baud). I need to
+ * try this situation again and see what happens.
+ */
+
/*
* An ACK is a duplicate if:
* (1) it has the same sequence number as the largest number we've
@@ -316,55 +419,169 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
* The packet acked data after high_seq;
*/
- if (ack == tp->snd_una && sk->packets_out && (not_dup == 0) &&
- after(ack, tp->high_seq))
- {
-
- sk->dup_acks++;
-
-
- /*
- * 1. When the third duplicate ack is received, set ssthresh
- * to one half the current congestion window, but no less
- * than two segments. Retransmit the missing segment.
+ if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) {
+ /* 1. When the third duplicate ack is received, set ssthresh
+ * to one half the current congestion window, but no less
+ * than two segments. Retransmit the missing segment.
*/
-
- if (sk->dup_acks == 3)
- {
- sk->ssthresh = max(sk->cong_window >> 1, 2);
- sk->cong_window = sk->ssthresh + 3;
- tcp_do_retransmit(sk, 0);
+ if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
+ tp->dup_acks++;
+
+ if (tp->dup_acks == 3) {
+ tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+ tp->snd_cwnd = tp->snd_ssthresh + 3;
+ tcp_do_retransmit(sk, 0);
+
+ /* Careful not to timeout just after fast
+ * retransmit!
+ */
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
}
- /*
- * 2. Each time another duplicate ACK arrives, increment
- * cwnd by the segment size. [...] Transmit a packet...
+ /* 2. Each time another duplicate ACK arrives, increment
+ * cwnd by the segment size. [...] Transmit a packet...
*
- * Packet transmission will be done on normal flow processing
- * since we're not in "retransmit mode"
+ * Packet transmission will be done on normal flow processing
+ * since we're not in "retransmit mode".
*/
-
- if (sk->dup_acks > 3)
- {
- sk->cong_window++;
+ if (tp->dup_acks >= 3) {
+ tp->dup_acks++;
+ tp->snd_cwnd++;
+ }
+ } else {
+ /* 3. When the next ACK arrives that acknowledges new data,
+ * set cwnd to ssthresh.
+ */
+ if (tp->dup_acks >= 3) {
+ tp->retrans_head = NULL;
+ tp->snd_cwnd = max(tp->snd_ssthresh, 1);
+ tp->retransmits = 0;
}
+ tp->dup_acks = 0;
+
+ /* FIXME: This is wrong if the new ack that arrives
+ * is below the value for high_seq.
+ */
+ tp->high_seq = 0;
}
- else
- {
- /*
- * 3. When the next ACK arrives that acknowledges new data,
- * set cwnd to ssthresh
+}
+#endif
+
+#define FLAG_DATA 0x01
+#define FLAG_WIN_UPDATE 0x02
+#define FLAG_DATA_ACKED 0x04
+
+static __inline__ void clear_fast_retransmit(struct sock *sk) {
+ struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ if (tp->dup_acks > 3) {
+ tp->retrans_head = NULL;
+ tp->snd_cwnd = max(tp->snd_ssthresh, 1);
+ }
+ tp->dup_acks = 0;
+}
+
+/*
+ * NOTE: This code assumes that tp->dup_acks gets cleared when a
+ * retransmit timer fires.
+ */
+
+static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
+{
+ struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+
+ /*
+ * Note: If not_dup is set this implies we got a
+ * data carrying packet or a window update.
+ * This carries no new information about possible
+ * lost packets, so we have to ignore it for the purposes
+ * of counting duplicate acks. Ideally this does not imply we
+ * should stop our fast retransmit phase, more acks may come
+ * later without data to help us. Unfortunately this would make
+ * the code below much more complex. For now if I see such
+ * a packet I clear the fast retransmit phase.
+ */
+
+ if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) {
+ /* This is the standard reno style fast retransmit branch. */
+
+ /* 1. When the third duplicate ack is received, set ssthresh
+ * to one half the current congestion window, but no less
+ * than two segments. Retransmit the missing segment.
+ */
+ if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
+ tp->dup_acks++;
+ if (tp->dup_acks == 3) {
+ tp->dup_acks++;
+ tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+ tp->snd_cwnd = tp->snd_ssthresh + 3;
+ tp->high_seq = tp->snd_nxt;
+ tcp_do_retransmit(sk, 0);
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ }
+ }
+
+ /* 2. Each time another duplicate ACK arrives, increment
+ * cwnd by the segment size. [...] Transmit a packet...
+ *
+ * Packet transmission will be done on normal flow processing
+ * since we're not in "retransmit mode"
+ */
+ if (tp->dup_acks > 3)
+ tp->snd_cwnd++;
+ } else if (tp->high_seq != 0) {
+ /* In this branch we deal with clearing the Floyd style
+ * block on duplicate fast retransmits, and if requested
+ * we do Hoe style secondary fast retransmits.
*/
+ if (!before(ack,tp->high_seq) || (not_dup&FLAG_DATA) != 0) {
+ /* Once we have acked all the packets up to high_seq
+ * we are done this fast retransmit phase.
+ * Alternatively data arrived. In this case we
+ * Have to abort the fast retransmit attempt.
+ * Note that we do want to accept a window
+ * update since this is expected with Hoe's algorithm.
+ */
+ clear_fast_retransmit(sk);
- if (sk->dup_acks >= 3)
- {
- sk->tp_pinfo.af_tcp.retrans_head = NULL;
- sk->cong_window = sk->ssthresh;
- sk->retransmits = 0;
+ /* After we have cleared up to high_seq we can
+ * clear the Floyd style block.
+ */
+ if (after(ack,tp->high_seq))
+ tp->high_seq = 0;
+ } else if (tp->dup_acks >= 3) {
+ if (sysctl_tcp_hoe_retransmits) {
+ /* Hoe Style. We didn't ack the whole
+ * window. Take this as a cue that
+ * another packet was lost and retransmit it.
+ * Don't muck with the congestion window here.
+ * Note that we have to be careful not to
+ * act if this was a window update and it
+ * didn't ack new data, since this does
+ * not indicate a packet left the system.
+ * We can test this by just checking
+ * if ack changed from snd_una, since
+ * the only way to get here without changing
+ * advancing from snd_una is if this was a
+ * window update.
+ */
+ if (ack != tp->snd_una && before(ack,tp->high_seq)) {
+ tcp_do_retransmit(sk, 0);
+ tcp_reset_xmit_timer(sk, TIME_RETRANS,
+ tp->rto);
+ }
+ } else {
+ /* Reno style. We didn't ack the whole
+ * window, now we have to drop out of
+ * fast retransmit and wait for a timeout.
+ */
+ clear_fast_retransmit(sk);
+ }
}
- sk->dup_acks = 0;
+ } else {
+ /* Clear any aborted fast retransmit starts. */
+ tp->dup_acks = 0;
}
-
}
/*
@@ -379,148 +596,114 @@ static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
static void tcp_cong_avoid_vegas(struct sock *sk, u32 seq, u32 ack,
u32 seq_rtt)
{
- /*
- * From:
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ unsigned int actual, expected;
+ unsigned int inv_rtt, inv_basertt, inv_basebd;
+ u32 snt_bytes;
+
+ /* From:
* TCP Vegas: New Techniques for Congestion
* Detection and Avoidance.
- *
*
* Warning: This code is a scratch implementation taken
* from the paper only. The code they distribute seams
* to have improved several things over the initial spec.
*/
- struct tcp_opt * tp;
- unsigned int Actual, Expected;
- unsigned int inv_rtt, inv_basertt;
- u32 snt_bytes;
-
-
- tp = &(sk->tp_pinfo.af_tcp);
-
if (!seq_rtt)
seq_rtt = 1;
-
+
if (tp->basertt)
tp->basertt = min(seq_rtt, tp->basertt);
else
tp->basertt = seq_rtt;
- /*
- *
- * Actual = throughput for this segment.
- * Expected = number_of_bytes in transit / BaseRTT
- *
+ /* actual = throughput for this segment.
+ * expected = number_of_bytes in transit / BaseRTT
*/
- snt_bytes = (ack - seq) << SHIFT_FACTOR;
- inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt;
-
- Actual = snt_bytes * inv_rtt;
+ snt_bytes = ack - seq;
+ inv_rtt = (1 << SHIFT_FACTOR) / seq_rtt;
inv_basertt = (1 << SHIFT_FACTOR) / tp->basertt;
- Expected = ((tp->snd_nxt - tp->snd_una) << SHIFT_FACTOR) * inv_basertt;
- /*
- * Slow Start
- */
-
- if (sk->cong_window < sk->ssthresh &&
+ actual = snt_bytes * inv_rtt;
+
+ expected = (tp->snd_nxt - tp->snd_una) * inv_basertt;
+
+ /* XXX sk->mss should move into tcp_opt as well -DaveM */
+ inv_basebd = sk->mss * inv_basertt;
+
+ /* Slow Start */
+ if (tp->snd_cwnd < tp->snd_ssthresh &&
(seq == tp->snd_nxt ||
- (((Expected - Actual) <=
- ((TCP_VEGAS_GAMMA << SHIFT_FACTOR) * sk->mss * inv_basertt))
- )
- ))
- {
- /*
- * "Vegas allows exponential growth only every other
- * RTT"
- */
-
- if (!(sk->cong_count++))
- {
- sk->cong_window++;
- sk->cong_count = 0;
+ (expected - actual <= TCP_VEGAS_GAMMA * inv_basebd))) {
+ /* "Vegas allows exponential growth only every other RTT" */
+ if (tp->snd_cwnd_cnt++) {
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt = 0;
}
- }
- else
- {
- /*
- * Congestion Avoidance
- */
-
- if (Expected - Actual <=
- ((TCP_VEGAS_ALPHA << SHIFT_FACTOR) * sk->mss * inv_basertt))
- {
+ } else {
+ /* Congestion Avoidance */
+ if (expected - actual <= TCP_VEGAS_ALPHA * inv_basebd) {
/* Increase Linearly */
-
- if (sk->cong_count++ >= sk->cong_window)
- {
- sk->cong_window++;
- sk->cong_count = 0;
+ if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) {
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt = 0;
}
}
-
- if (Expected - Actual >=
- ((TCP_VEGAS_BETA << SHIFT_FACTOR) * sk->mss * inv_basertt))
- {
+
+ if (expected - actual >= TCP_VEGAS_BETA * inv_basebd) {
/* Decrease Linearly */
-
- if (sk->cong_count++ >= sk->cong_window)
- {
- sk->cong_window--;
- sk->cong_count = 0;
+ if (tp->snd_cwnd_cnt++ >= tp->snd_cwnd) {
+ tp->snd_cwnd--;
+ tp->snd_cwnd_cnt = 0;
}
-
- /* Never less than 2 segments */
- if (sk->cong_window < 2)
- sk->cong_window = 2;
+
+ /* Never less than 2 segments. */
+ if (tp->snd_cwnd < 2)
+ tp->snd_cwnd = 2;
}
}
}
static void tcp_cong_avoid_vanj(struct sock *sk, u32 seq, u32 ack, u32 seq_rtt)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- /*
- * This is Jacobson's slow start and congestion avoidance.
+ /* This is Jacobson's slow start and congestion avoidance.
* SIGCOMM '88, p. 328. Because we keep cong_window in
* integral mss's, we can't do cwnd += 1 / cwnd.
* Instead, maintain a counter and increment it once every
* cwnd times.
+ * FIXME: Check to be sure the mathematics works out right
+ * on this trick when we have to reduce the congestion window.
+ * The snd_cwnd_cnt has to be reset properly when reduction events
+ * happen.
+ * FIXME: What happens when the congestion window gets larger
+ * than the maximum receiver window by some large factor
+ * Suppose the pipeline never looses packets for a long
+ * period of time, then traffic increases causing packet loss.
+ * The congestion window should be reduced, but what it should
+ * be reduced to is not clear, since 1/2 the old window may
+ * still be larger than the maximum sending rate we ever achieved.
*/
-
- if (sk->cong_window <= sk->ssthresh)
- {
- /*
- * In "safe" area, increase
- */
-
- sk->cong_window++;
- }
- else
- {
- /*
- * In dangerous area, increase slowly.
- * In theory this is
- * sk->cong_window += 1 / sk->cong_window
+ if (tp->snd_cwnd <= tp->snd_ssthresh) {
+ /* In "safe" area, increase. */
+ tp->snd_cwnd++;
+ } else {
+ /* In dangerous area, increase slowly. In theory this is
+ * tp->snd_cwnd += 1 / tp->snd_cwnd
*/
-
- if (sk->cong_count >= sk->cong_window) {
-
- sk->cong_window++;
- sk->cong_count = 0;
- }
- else
- sk->cong_count++;
+ if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
+ tp->snd_cwnd++;
+ tp->snd_cwnd_cnt = 0;
+ } else
+ tp->snd_cwnd_cnt++;
}
}
-#define FLAG_DATA 0x01
-#define FLAG_WIN_UPDATE 0x02
-#define FLAG_DATA_ACKED 0x04
-
static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq,
__u32 *seq_rtt)
{
@@ -528,52 +711,46 @@ static int tcp_clean_rtx_queue(struct sock *sk, __u32 ack, __u32 *seq,
struct sk_buff *skb;
unsigned long now = jiffies;
int acked = 0;
-
- while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head))
- {
+ while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
#ifdef TCP_DEBUG
/* Check for a bug. */
-
if (skb->next != (struct sk_buff*) &sk->write_queue &&
- after(skb->end_seq, skb->next->seq))
- printk("INET: tcp_input.c: *** "
+ after(skb->end_seq, skb->next->seq))
+ printk(KERN_DEBUG "INET: tcp_input.c: *** "
"bug send_list out of order.\n");
#endif
- /*
- * If our packet is before the ack sequence we can
- * discard it as it's confirmed to have arrived the
- * other end.
+ /* If our packet is before the ack sequence we can
+ * discard it as it's confirmed to have arrived the
+ * other end.
*/
-
if (after(skb->end_seq, ack))
break;
-
- if (sk->debug)
- {
- printk(KERN_DEBUG "removing seg %x-%x from "
- "retransmit queue\n", skb->seq, skb->end_seq);
- }
-
+
+ SOCK_DEBUG(sk, "removing seg %x-%x from retransmit queue\n",
+ skb->seq, skb->end_seq);
+
acked = FLAG_DATA_ACKED;
- atomic_dec(&sk->packets_out);
+ /* FIXME: packet counting may break if we have to
+ * do packet "repackaging" for stacks that don't
+ * like overlapping packets.
+ */
+ tp->packets_out--;
*seq = skb->seq;
*seq_rtt = now - skb->when;
-
- skb_unlink(skb);
- skb->free = 1;
+
+ skb_unlink(skb);
kfree_skb(skb, FREE_WRITE);
}
- if (acked && !sk->dead)
- {
+ if (acked) {
tp->retrans_head = NULL;
- sk->write_space(sk);
+ if (!sk->dead)
+ sk->write_space(sk);
}
-
return acked;
}
@@ -581,27 +758,18 @@ static void tcp_ack_probe(struct sock *sk, __u32 ack)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- /*
- * Our probe was answered
- */
+ /* Our probe was answered. */
tp->probes_out = 0;
- /*
- * Was it a usable window open ?
- */
+ /* Was it a usable window open? */
/* should always be non-null */
if (tp->send_head != NULL &&
- !before (ack + tp->snd_wnd, tp->send_head->end_seq))
- {
+ !before (ack + tp->snd_wnd, tp->send_head->end_seq)) {
tp->backoff = 0;
tp->pending = 0;
-
tcp_clear_xmit_timer(sk, TIME_PROBE0);
-
- }
- else
- {
+ } else {
tcp_reset_xmit_timer(sk, TIME_PROBE0,
min(tp->rto << tp->backoff, 120*HZ));
}
@@ -614,192 +782,164 @@ static void tcp_ack_probe(struct sock *sk, __u32 ack)
static int tcp_ack(struct sock *sk, struct tcphdr *th,
u32 ack_seq, u32 ack, int len)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
int flag = 0;
u32 seq = 0;
u32 seq_rtt = 0;
struct sk_buff *skb;
- struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
if(sk->zapped)
return(1); /* Dead, can't ack any more so why bother */
-
- if (tp->pending == TIME_KEEPOPEN)
- {
+ if (tp->pending == TIME_KEEPOPEN)
tp->probes_out = 0;
- }
tp->rcv_tstamp = jiffies;
-
- /*
- * If the ack is newer than sent or older than previous acks
- * then we can probably ignore it.
+
+ /* If the ack is newer than sent or older than previous acks
+ * then we can probably ignore it.
*/
-
if (after(ack, tp->snd_nxt) || before(ack, tp->snd_una))
goto uninteresting_ack;
- /*
- * If there is data set flag 1
- */
-
- if (len != th->doff*4)
- {
+ /* If there is data set flag 1 */
+ if (len != th->doff*4) {
flag |= FLAG_DATA;
tcp_delack_estimator(tp);
}
- /*
- * Update our send window
- */
+ /* Update our send window. */
- /*
- * This is the window update code as per RFC 793
- * snd_wl{1,2} are used to prevent unordered
- * segments from shrinking the window
+ /* This is the window update code as per RFC 793
+ * snd_wl{1,2} are used to prevent unordered
+ * segments from shrinking the window
*/
+ if (before(tp->snd_wl1, ack_seq) ||
+ (tp->snd_wl1 == ack_seq && !after(tp->snd_wl2, ack))) {
+ unsigned long nwin = ntohs(th->window);
- if ((tp->snd_wl1 == 0) || before(tp->snd_wl1, ack_seq) ||
- (tp->snd_wl1 == ack_seq && !after(tp->snd_wl2, ack)))
- {
- tp->snd_wnd = ntohs(th->window);
- tp->snd_wl1 = ack_seq;
- tp->snd_wl2 = ack;
+ if ((tp->snd_wl2 != ack) || (nwin > tp->snd_wnd)) {
+ flag |= FLAG_WIN_UPDATE;
+ tp->snd_wnd = nwin;
- flag |= FLAG_WIN_UPDATE;
+ tp->snd_wl1 = ack_seq;
+ tp->snd_wl2 = ack;
- if (tp->snd_wnd > sk->max_window)
- {
- sk->max_window = tp->snd_wnd;
+ if (nwin > tp->max_window)
+ tp->max_window = nwin;
}
}
-
- /*
- * We passed data and got it acked, remove any soft error
- * log. Something worked...
+ /* We passed data and got it acked, remove any soft error
+ * log. Something worked...
*/
-
sk->err_soft = 0;
- /*
- * If this ack opens up a zero window, clear backoff. It was
- * being used to time the probes, and is probably far higher than
- * it needs to be for normal retransmission.
+ /* If this ack opens up a zero window, clear backoff. It was
+ * being used to time the probes, and is probably far higher than
+ * it needs to be for normal retransmission.
*/
-
if (tp->pending == TIME_PROBE0)
- {
tcp_ack_probe(sk, ack);
- }
-
- /*
- * See if we can take anything off of the retransmit queue.
- */
+ /* See if we can take anything off of the retransmit queue. */
if (tcp_clean_rtx_queue(sk, ack, &seq, &seq_rtt))
flag |= FLAG_DATA_ACKED;
-
- /*
- * if we where retransmiting don't count rtt estimate
- */
-
- if (sk->retransmits)
- {
- if (sk->packets_out == 0)
- sk->retransmits = 0;
- }
- else
- {
- /*
- * Note that we only reset backoff and rto in the
- * rtt recomputation code. And that doesn't happen
- * if there were retransmissions in effect. So the
- * first new packet after the retransmissions is
- * sent with the backoff still in effect. Not until
- * we get an ack from a non-retransmitted packet do
- * we reset the backoff and rto. This allows us to deal
- * with a situation where the network delay has increased
- * suddenly. I.e. Karn's algorithm. (SIGCOMM '87, p5.)
+ /* If we have a timestamp, we always do rtt estimates. */
+ if (tp->saw_tstamp) {
+ /* Read draft-ietf-tcplw-high-performance before mucking
+ * with this code. (Superceeds RFC1323)
*/
-
- if (flag & FLAG_DATA_ACKED)
- {
- tcp_rtt_estimator(tp, seq_rtt);
-
- (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+ seq_rtt = (jiffies-tp->rcv_tsecr);
+ tcp_rtt_estimator(tp, seq_rtt);
+ if (tp->retransmits) {
+ if (tp->packets_out == 0) {
+ tp->retransmits = 0;
+ tp->backoff = 0;
+ tcp_set_rto(tp);
+ } else {
+ /* Still retransmitting, use backoff */
+ tcp_set_rto(tp);
+ tp->rto = tp->rto << tp->backoff;
+ }
+ } else {
+ tcp_set_rto(tp);
+ if (flag && FLAG_DATA_ACKED)
+ (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+ }
+ /* NOTE: safe here so long as cong_ctl doesn't use rto */
+ tcp_bound_rto(tp);
+ } else {
+ /* If we were retransmiting don't count rtt estimate. */
+ if (tp->retransmits) {
+ if (tp->packets_out == 0)
+ tp->retransmits = 0;
+ } else {
+ /* We don't have a timestamp. Can only use
+ * packets that are not retransmitted to determine
+ * rtt estimates. Also, we must not reset the
+ * backoff for rto until we get a non-retransmitted
+ * packet. This allows us to deal with a situation
+ * where the network delay has increased suddenly.
+ * I.e. Karn's algorithm. (SIGCOMM '87, p5.)
+ */
+ if (flag & FLAG_DATA_ACKED) {
+ tp->backoff = 0;
+ tcp_rtt_estimator(tp, seq_rtt);
+ tcp_set_rto(tp);
+ tcp_bound_rto(tp);
+ (*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+ }
}
}
-
-#ifdef TCP_DEBUG
-
- /* Sanity check out packets_out counter */
- if (skb_queue_len(&sk->write_queue) == 0 ||
- ack == tp->snd_nxt )
- {
- if (sk->packets_out)
- {
- printk(KERN_DEBUG "tcp_ack: packets_out %d\n",
- sk->packets_out);
- sk->packets_out = 0;
- }
- }
-#endif
-
- if (sk->packets_out)
- {
- if (flag & FLAG_DATA_ACKED)
- {
+ if (tp->packets_out) {
+ if (flag & FLAG_DATA_ACKED) {
long when;
-
+
skb = skb_peek(&sk->write_queue);
-
when = tp->rto - (jiffies - skb->when);
-
- if (when <= 0)
- {
+
+ /* FIXME: This assumes that when we are retransmitting
+ * we should only ever respond with one packet.
+ * This means congestion windows should not grow
+ * during recovery. In 2.0.X we allow the congestion
+ * window to grow. It is not clear to me which
+ * decision is correct. The RFCs should be double
+ * checked as should the behavior of other stacks.
+ * Also note that if we do want to allow the
+ * congestion window to grow during retransmits
+ * we have to fix the call to congestion window
+ * updates so that it works during retransmission.
+ */
+ if (tp->retransmits) {
tp->retrans_head = NULL;
- /*
- * This is tricky. We are retransmiting a
+
+ /* This is tricky. We are retransmiting a
* segment of a window when congestion occured.
*/
tcp_do_retransmit(sk, 0);
- tcp_reset_xmit_timer(sk, TIME_RETRANS,
- tp->rto);
- }
- else
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+ } else
tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
}
- }
- else
+ } else
tcp_clear_xmit_timer(sk, TIME_RETRANS);
-
-
- /*
- * Remember the highest ack received.
- */
-
- tp->snd_una = ack;
tcp_fast_retrans(sk, ack, (flag & (FLAG_DATA|FLAG_WIN_UPDATE)));
+ /* Remember the highest ack received. */
+ tp->snd_una = ack;
return 1;
uninteresting_ack:
- tcp_fast_retrans(sk, ack, 0);
-
- if(sk->debug)
- printk("Ack ignored %u %u\n",ack,tp->snd_nxt);
-
+ SOCK_DEBUG(sk, "Ack ignored %u %u\n", ack, tp->snd_nxt);
return 0;
}
-
/*
* Process the FIN bit. This now behaves as it is supposed to work
* and the FIN takes effect when it is validly part of sequence
@@ -813,53 +953,46 @@ uninteresting_ack:
* close and we go into CLOSING (and later onto TIME-WAIT)
*
* If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
- *
*/
static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
{
- sk->fin_seq = skb->end_seq;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* XXX This fin_seq thing should disappear... -DaveM */
+ tp->fin_seq = skb->end_seq;
tcp_send_ack(sk);
- if (!sk->dead)
- {
+ if (!sk->dead) {
sk->state_change(sk);
sock_wake_async(sk->socket, 1);
}
- switch(sk->state)
- {
+ switch(sk->state) {
case TCP_SYN_RECV:
case TCP_SYN_SENT:
case TCP_ESTABLISHED:
- /*
- * move to CLOSE_WAIT
- */
-
+ /* Move to CLOSE_WAIT */
tcp_set_state(sk, TCP_CLOSE_WAIT);
-
if (th->rst)
sk->shutdown = SHUTDOWN_MASK;
break;
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
- /*
- * received a retransmission of the FIN, do
+ /* Received a retransmission of the FIN, do
* nothing.
*/
break;
case TCP_TIME_WAIT:
- /*
- * received a retransmission of the FIN,
+ /* Received a retransmission of the FIN,
* restart the TIME_WAIT timer.
*/
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
return(0);
case TCP_FIN_WAIT1:
- /*
- * This case occurs when a simultaneous close
+ /* This case occurs when a simultaneous close
* happens, we must ack the received FIN and
* enter the CLOSING state.
*
@@ -869,202 +1002,128 @@ static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
* FIN lost hang). The TIME_WRITE code is already
* correct for handling this timeout.
*/
-
tcp_set_state(sk, TCP_CLOSING);
break;
case TCP_FIN_WAIT2:
- /*
- * received a FIN -- send ACK and enter TIME_WAIT
- */
+ /* Received a FIN -- send ACK and enter TIME_WAIT. */
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
- sk->shutdown|=SHUTDOWN_MASK;
+ sk->shutdown |= SHUTDOWN_MASK;
tcp_set_state(sk,TCP_TIME_WAIT);
break;
case TCP_CLOSE:
- /*
- * already in CLOSE
- */
+ /* Already in CLOSE. */
break;
default:
+ /* FIXME: Document whats happening in this case. -DaveM */
tcp_set_state(sk,TCP_LAST_ACK);
/* Start the timers. */
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
return(0);
- }
+ };
return(0);
}
-
-
- /*
- * This one checks to see if we can put data from the
- * out_of_order queue into the receive_queue
- */
-
-static void tcp_ofo_queue(struct sock *sk)
+/* This one checks to see if we can put data from the
+ * out_of_order queue into the receive_queue.
+ */
+static void tcp_ofo_queue(struct sock *sk)
{
- struct sk_buff * skb;
- struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+ /* FIXME: out_of_order_queue is a strong tcp_opt candidate... -DaveM */
while ((skb = skb_peek(&sk->out_of_order_queue))) {
-
if (after(skb->seq, tp->rcv_nxt))
break;
if (!after(skb->end_seq, tp->rcv_nxt)) {
-
- if (sk->debug)
- printk("ofo packet was allready received \n");
-
+ SOCK_DEBUG(sk, "ofo packet was allready received \n");
skb_unlink(skb);
kfree_skb(skb, FREE_READ);
-
continue;
}
+ SOCK_DEBUG(sk, "ofo requeuing : rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, skb->seq, skb->end_seq);
- if (sk->debug)
- printk("ofo requeuing : rcv_next %X seq %X - %X\n",
- tp->rcv_nxt, skb->seq, skb->end_seq);
-
skb_unlink(skb);
-
-
skb_queue_tail(&sk->receive_queue, skb);
-
-
tp->rcv_nxt = skb->end_seq;
}
}
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
{
- struct sk_buff * skb1;
- struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ struct sk_buff *skb1;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- /*
- * Queue data for delivery to the user
- * Packets in sequence go to the receive queue
- * Out of sequence packets to out_of_order_queue
+ /* Queue data for delivery to the user.
+ * Packets in sequence go to the receive queue.
+ * Out of sequence packets to out_of_order_queue.
*/
-
-
if (skb->seq == tp->rcv_nxt) {
-
- /*
- * Ok. In sequence.
- */
-
-
+ /* Ok. In sequence. */
+queue_and_out:
skb_queue_tail(&sk->receive_queue, skb);
-
-
tp->rcv_nxt = skb->end_seq;
-
tcp_ofo_queue(sk);
-
if (skb_queue_len(&sk->out_of_order_queue) == 0)
tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd);
-
return;
}
- /*
- * Not in sequence
- * either a retransmit or some packet got lost
- */
-
+ /* Not in sequence, either a retransmit or some packet got lost. */
if (!after(skb->end_seq, tp->rcv_nxt)) {
-
- /*
- * A retransmit.
- * 2nd most common case.
- * force an imediate ack
- */
+ /* A retransmit, 2nd most common case. Force an imediate ack. */
+ SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq);
- if (sk->debug)
- printk("retransmit received: seq %X\n", skb->seq);
-
- sk->delayed_acks = MAX_DELAY_ACK;
+ tp->delayed_acks = MAX_DELAY_ACK;
kfree_skb(skb, FREE_READ);
-
return;
}
-
if (before(skb->seq, tp->rcv_nxt)) {
+ /* Partial packet, seq < rcv_next < end_seq */
+ SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, skb->seq, skb->end_seq);
- /*
- * Partial packet
- * seq < rcv_next < end_seq
- */
-
- if (sk->debug)
- printk("partial packet: rcv_next %X seq %X - %X\n",
- tp->rcv_nxt, skb->seq, skb->end_seq);
-
- skb_queue_tail(&sk->receive_queue, skb);
-
-
- tp->rcv_nxt = skb->end_seq;
-
- tcp_ofo_queue(sk);
-
- if (skb_queue_len(&sk->out_of_order_queue) == 0)
- tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd);
-
- return;
+ goto queue_and_out;
}
- /*
- * Ok. This is an out_of_order segment
- */
-
- /* Force an ack */
-
- sk->delayed_acks = MAX_DELAY_ACK;
-
- /*
- * disable header predition
- */
+ /* Ok. This is an out_of_order segment, force an ack. */
+ tp->delayed_acks = MAX_DELAY_ACK;
+ /* Disable header predition. */
tp->pred_flags = 0;
- if (sk->debug)
- printk("out of order segment: rcv_next %X seq %X - %X\n",
- tp->rcv_nxt, skb->seq, skb->end_seq);
+ SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
+ tp->rcv_nxt, skb->seq, skb->end_seq);
if (skb_peek(&sk->out_of_order_queue) == NULL) {
skb_queue_head(&sk->out_of_order_queue,skb);
- }
- else
+ } else {
for(skb1=sk->out_of_order_queue.prev; ; skb1 = skb1->prev) {
-
- /* allready there */
- if (skb->seq==skb1->seq && skb->len>=skb1->len)
- {
- skb_append(skb1,skb);
+ /* Already there. */
+ if (skb->seq == skb1->seq && skb->len >= skb1->len) {
+ skb_append(skb1, skb);
skb_unlink(skb1);
- kfree_skb(skb1,FREE_READ);
+ kfree_skb(skb1, FREE_READ);
break;
}
- if (after(skb->seq, skb1->seq))
- {
+ if (after(skb->seq, skb1->seq)) {
skb_append(skb1,skb);
break;
}
-
- /*
- * See if we've hit the start. If so insert.
- */
+
+ /* See if we've hit the start. If so insert. */
if (skb1 == skb_peek(&sk->out_of_order_queue)) {
skb_queue_head(&sk->out_of_order_queue,skb);
break;
}
}
-
+ }
}
@@ -1077,53 +1136,36 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
static int tcp_data(struct sk_buff *skb, struct sock *sk, unsigned int len)
{
struct tcphdr *th;
- struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
th = skb->h.th;
- skb_pull(skb,th->doff*4);
- skb_trim(skb,len-(th->doff*4));
+ skb_pull(skb, th->doff*4);
+ skb_trim(skb, len - (th->doff*4));
if (skb->len == 0 && !th->fin)
- {
return(0);
- }
-
- /*
- * FIXME: don't accept data after the receved fin
- */
- /*
- * The bytes in the receive read/assembly queue has increased.
- * Needed for the low memory discard algorithm
- */
-
- sk->bytes_rcv += skb->len;
-
- /*
- * We no longer have anyone receiving data on this connection.
+ /* FIXME: don't accept data after the received fin.
+ *
+ * Would checking snd_seq against fin_seq be enough?
+ * If so, how do we handle that case exactly? -DaveM
*/
+ /* We no longer have anyone receiving data on this connection. */
tcp_data_queue(sk, skb);
- if (before(tp->rcv_nxt, sk->copied_seq))
- {
- printk("*** tcp.c:tcp_data bug acked < copied\n");
+ if (before(tp->rcv_nxt, sk->copied_seq)) {
+ printk(KERN_DEBUG "*** tcp.c:tcp_data bug acked < copied\n");
tp->rcv_nxt = sk->copied_seq;
}
- sk->delayed_acks++;
-
+ tp->delayed_acks++;
- /*
- * Now tell the user we may have some data.
- */
-
- if (!sk->dead)
- {
- if(sk->debug)
- printk("Data wakeup.\n");
+ /* Now tell the user we may have some data. */
+ if (!sk->dead) {
+ SOCK_DEBUG(sk, "Data wakeup.\n");
sk->data_ready(sk,0);
- }
+ }
return(1);
}
@@ -1132,51 +1174,52 @@ static void tcp_data_snd_check(struct sock *sk)
struct sk_buff *skb;
struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
- if ((skb = tp->send_head))
- {
+ if ((skb = tp->send_head)) {
if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
- sk->packets_out < sk->cong_window )
- {
- /*
- * Add more data to the send queue.
- */
+ tp->packets_out < tp->snd_cwnd ) {
+ /* Add more data to the send queue. */
+ /* FIXME: the congestion window is checked
+ * again in tcp_write_xmit anyway?! -- erics
+ *
+ * I think it must, it bumps tp->packets_out for
+ * each packet it fires onto the wire. -DaveM
+ */
tcp_write_xmit(sk);
- wake_up_interruptible(sk->sleep);
- }
- else if (sk->packets_out == 0 && !tp->pending)
- {
- /*
- * Data to queue but no room.
- */
+ if(!sk->dead)
+ sk->write_space(sk);
+ } else if (tp->packets_out == 0 && !tp->pending) {
+ /* Data to queue but no room. */
+
+ /* FIXME: Is it right to do a zero window probe into
+ * a congestion window limited window??? -- erics
+ */
tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
- }
+ }
}
-}
+}
static __inline__ void tcp_ack_snd_check(struct sock *sk)
{
- /*
- * This also takes care of updating the window.
- * This if statement needs to be simplified.
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ /* This also takes care of updating the window.
+ * This if statement needs to be simplified.
*
- * rules for delaying an ack:
+ * Rules for delaying an ack:
* - delay time <= 0.5 HZ
* - we don't have a window update to send
* - must send at least every 2 full sized packets
*/
-
- if (sk->delayed_acks == 0)
+ if (tp->delayed_acks == 0) {
+ /* We sent a data segment already. */
return;
+ }
- if (sk->delayed_acks >= MAX_DELAY_ACK || tcp_raise_window(sk))
- {
+ if (tp->delayed_acks >= MAX_DELAY_ACK || tcp_raise_window(sk))
tcp_send_ack(sk);
- }
- else
- {
- tcp_send_delayed_ack(sk, HZ/2);
- }
+ else
+ tcp_send_delayed_ack(sk, HZ/2);
}
/*
@@ -1198,62 +1241,49 @@ static void tcp_check_urg(struct sock * sk, struct tcphdr * th)
ptr--;
ptr += ntohl(th->seq);
- /* ignore urgent data that we've already seen and read */
+ /* Ignore urgent data that we've already seen and read. */
if (after(sk->copied_seq, ptr))
return;
- /* do we already have a newer (or duplicate) urgent pointer? */
+ /* Do we already have a newer (or duplicate) urgent pointer? */
if (sk->urg_data && !after(ptr, sk->urg_seq))
return;
- /* tell the world about our new urgent pointer */
+ /* Tell the world about our new urgent pointer. */
if (sk->proc != 0) {
- if (sk->proc > 0) {
+ if (sk->proc > 0)
kill_proc(sk->proc, SIGURG, 1);
- } else {
+ else
kill_pg(-sk->proc, SIGURG, 1);
- }
}
- /*
- * We may be adding urgent data when the last byte read was
- * urgent. To do this requires some care. We cannot just ignore
- * sk->copied_seq since we would read the last urgent byte again
- * as data, nor can we alter copied_seq until this data arrives
- * or we break the sematics of SIOCATMARK (and thus sockatmark())
+
+ /* We may be adding urgent data when the last byte read was
+ * urgent. To do this requires some care. We cannot just ignore
+ * sk->copied_seq since we would read the last urgent byte again
+ * as data, nor can we alter copied_seq until this data arrives
+ * or we break the sematics of SIOCATMARK (and thus sockatmark())
*/
if (sk->urg_seq == sk->copied_seq)
sk->copied_seq++; /* Move the copied sequence on correctly */
sk->urg_data = URG_NOTYET;
sk->urg_seq = ptr;
- /* disable header prediction */
+ /* Disable header prediction. */
tp->pred_flags = 0;
}
-/*
- * This is the 'fast' part of urgent handling.
- */
-
+/* This is the 'fast' part of urgent handling. */
static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len)
{
- /*
- * Check if we get a new urgent pointer - normally not
- */
-
+ /* Check if we get a new urgent pointer - normally not. */
if (th->urg)
tcp_check_urg(sk,th);
- /*
- * Do we wait for any urgent data? - normally not
- */
-
+ /* Do we wait for any urgent data? - normally not... */
if (sk->urg_data == URG_NOTYET) {
- u32 ptr;
+ u32 ptr = sk->urg_seq - ntohl(th->seq) + (th->doff*4);
- /*
- * Is the urgent pointer pointing into this packet?
- */
- ptr = sk->urg_seq - ntohl(th->seq) + th->doff*4;
+ /* Is the urgent pointer pointing into this packet? */
if (ptr < len) {
sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
if (!sk->dead)
@@ -1262,26 +1292,19 @@ static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len
}
}
-
static void prune_queue(struct sock *sk)
{
struct sk_buff * skb;
- /*
- * clean the out_of_order queue
- */
-
+ /* Clean the out_of_order queue. */
while ((skb = skb_dequeue(&sk->out_of_order_queue)))
- {
kfree_skb(skb, FREE_READ);
- }
}
-
-void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
- struct tcphdr *th, __u16 len)
+int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ struct tcphdr *th, __u16 len)
{
- struct tcp_opt *tp;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
int queued = 0;
u32 flg;
@@ -1301,6 +1324,23 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
*/
tp = &(sk->tp_pinfo.af_tcp);
+
+ /*
+ * RFC1323: H1. Apply PAWS check first.
+ */
+ if (tcp_fast_parse_options(th,tp)) {
+ if (tp->saw_tstamp) {
+ if (tcp_paws_discard(tp)) {
+ if (!th->rst) {
+ tcp_send_ack(sk);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ }
+ tcp_replace_ts_recent(tp,skb->end_seq);
+ }
+ }
+
flg = *(((u32 *)th) + 3);
/*
@@ -1311,127 +1351,88 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
* space for instance)
*/
- if (flg == tp->pred_flags && skb->seq == tp->rcv_nxt)
- {
- if (len <= sizeof(struct tcphdr))
- {
- if (len == sizeof(struct tcphdr))
- {
+ if (flg == tp->pred_flags && skb->seq == tp->rcv_nxt) {
+ if (len <= th->doff*4) {
+ /* Bulk data transfer: sender */
+ if (len == th->doff*4) {
tcp_ack(sk, th, skb->seq, skb->ack_seq, len);
+ tcp_data_snd_check(sk);
}
- tcp_data_snd_check(sk);
-
kfree_skb(skb, FREE_READ);
- return;
-
- }
- else if (skb->ack_seq == tp->snd_una)
- {
- /*
- * Bulk data transfer: receiver
- */
+ return 0;
+ } else if (skb->ack_seq == tp->snd_una) {
+ /* Bulk data transfer: receiver */
- skb_pull(skb,sizeof(struct tcphdr));
+ skb_pull(skb,th->doff*4);
skb_queue_tail(&sk->receive_queue, skb);
tp->rcv_nxt = skb->end_seq;
- sk->bytes_rcv += len - sizeof(struct tcphdr);
-
+
sk->data_ready(sk, 0);
tcp_delack_estimator(tp);
- if (sk->delayed_acks++)
- {
+ if (tp->delayed_acks++ == 0)
tcp_send_delayed_ack(sk, HZ/2);
- }
else
tcp_send_ack(sk);
- return;
+ return 0;
}
}
- if (!tcp_sequence(tp, skb->seq, skb->end_seq))
- {
- if (!th->rst)
- {
- if (after(skb->seq, tp->rcv_nxt))
- {
- printk(KERN_DEBUG "->seq:%d end:%d "
- "wup:%d wnd:%d\n",
- skb->seq, skb->end_seq,
- tp->rcv_wup, tp->rcv_wnd);
+ if (!tcp_sequence(tp, skb->seq, skb->end_seq)) {
+ if (!th->rst) {
+ if (after(skb->seq, tp->rcv_nxt)) {
+ SOCK_DEBUG(sk, "seq:%d end:%d wup:%d wnd:%d\n",
+ skb->seq, skb->end_seq,
+ tp->rcv_wup, tp->rcv_wnd);
}
tcp_send_ack(sk);
kfree_skb(skb, FREE_READ);
- return;
+ return 0;
}
}
- if(th->syn && skb->seq != sk->syn_seq)
- {
+ if(th->syn && skb->seq != sk->syn_seq) {
printk(KERN_DEBUG "syn in established state\n");
tcp_reset(sk, skb);
kfree_skb(skb, FREE_READ);
- return;
+ return 1;
}
- if(th->rst)
- {
+ if(th->rst) {
tcp_reset(sk,skb);
kfree_skb(skb, FREE_READ);
- return;
+ return 0;
}
if(th->ack)
- {
tcp_ack(sk, th, skb->seq, skb->ack_seq, len);
- }
-
- /*
- * Process urgent data
- */
-
+ /* Process urgent data. */
tcp_urg(sk, th, len);
- /*
- * step 7: process the segment text
- */
-
-
+ /* step 7: process the segment text */
queued = tcp_data(skb, sk, len);
- /*
- * step 8: check the FIN bit
- */
-
+ /* step 8: check the FIN bit */
if (th->fin)
- {
tcp_fin(skb, sk, th);
- }
tcp_data_snd_check(sk);
tcp_ack_snd_check(sk);
- /*
- * If our receive queue has grown past its limits,
- * try to prune away duplicates etc..
+ /* If our receive queue has grown past its limits,
+ * try to prune away duplicates etc..
*/
- if (sk->rmem_alloc > sk->rcvbuf)
+ if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf)
prune_queue(sk);
- /*
- * And done
- */
-
- if (queued)
- return;
-
- kfree_skb(skb, FREE_READ);
+ if (!queued)
+ kfree_skb(skb, FREE_READ);
+ return 0;
}
-
/*
* This function implements the receiving procedure of RFC 793.
@@ -1444,49 +1445,26 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
int queued = 0;
- int rcv_mss;
-
- /*
- * state == CLOSED
- * tested in tcp_v{4,6}_rcv
- */
+ /* state == CLOSED, hash lookup always fails, so no worries. -DaveM */
switch (sk->state) {
-
-
case TCP_LISTEN:
-
if (th->rst)
goto discard;
- /*
- * These use the socket TOS..
+ /* These use the socket TOS..
* might want to be the received TOS
*/
-
if(th->ack)
- {
- /*
- * send reset
- */
-
- return 1;
- }
-
+ return 1; /* send reset */
- if(th->syn)
- {
- int err;
- __u32 isn;
-
- isn = tp->af_specific->init_sequence(sk, skb);
- err = tp->af_specific->conn_request(sk, skb, opt, isn);
+ if(th->syn) {
+ __u32 isn = tp->af_specific->init_sequence(sk, skb);
- if (err < 0)
+ if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0)
return 1;
- /*
- * Now we have several options: In theory there is
+ /* Now we have several options: In theory there is
* nothing else in the frame. KA9Q has an option to
* send data with the syn, BSD accepts data with the
* syn up to the [to be] advertised window and
@@ -1498,7 +1476,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
* Now that TTCP is starting to be used we ought to
* queue this data.
*/
-
return 0;
}
@@ -1506,45 +1483,36 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
break;
case TCP_SYN_SENT:
-
- /*
- * SYN sent means we have to look for a suitable ack and
- * either reset for bad matches or go to connected.
- * The SYN_SENT case is unusual and should
- * not be in line code. [AC]
+ /* SYN sent means we have to look for a suitable ack and
+ * either reset for bad matches or go to connected.
+ * The SYN_SENT case is unusual and should
+ * not be in line code. [AC]
*/
-
- if(th->ack)
- {
- /* We got an ack, but it's not a good ack */
- if(!tcp_ack(sk,th, skb->seq, skb->ack_seq, len))
- {
+ if(th->ack) {
+ tp->snd_wl1 = skb->seq;
+
+ /* We got an ack, but it's not a good ack. */
+ if(!tcp_ack(sk,th, skb->seq, skb->ack_seq, len)) {
tcp_statistics.TcpAttemptFails++;
return 1;
}
- if(th->rst)
- {
+ if(th->rst) {
tcp_reset(sk,skb);
goto discard;
}
- if(!th->syn)
- {
- /*
- * A valid ack from a different connection
- * start. Shouldn't happen but cover it
+ if(!th->syn) {
+ /* A valid ack from a different connection
+ * start. Shouldn't happen but cover it.
*/
tcp_statistics.TcpAttemptFails++;
return 1;
}
- /*
- * Ok.. it's good. Set up sequence
- * numbers and
- * move to established.
+ /* Ok.. it's good. Set up sequence numbers and
+ * move to established.
*/
-
tp->rcv_nxt = skb->seq+1;
tp->rcv_wnd = 0;
tp->rcv_wup = skb->seq+1;
@@ -1553,43 +1521,53 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
tp->snd_wl1 = skb->seq;
tp->snd_wl2 = skb->ack_seq;
- sk->fin_seq = skb->seq;
- tcp_send_ack(sk);
+ tp->fin_seq = skb->seq;
tcp_set_state(sk, TCP_ESTABLISHED);
- rcv_mss = tcp_parse_options(th);
-
- if (rcv_mss == 0)
- {
- rcv_mss = 536;
+ tcp_parse_options(th,tp);
+ /* FIXME: need to make room for SACK still */
+ if (tp->tstamp_ok) {
+ tp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: Define constant! */
+ sk->dummy_th.doff += 3; /* reserve space of options */
+ } else
+ tp->tcp_header_len = sizeof(struct tcphdr);
+ if (tp->saw_tstamp) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = jiffies;
}
- sk->mss = min(sk->mss, rcv_mss);
+ /* Can't be earlier, doff would be wrong. */
+ tcp_send_ack(sk);
+
+ if (tp->in_mss)
+ sk->mss = min(sk->mss, tp->in_mss);
+
+ /* Take out space for tcp options. */
+ sk->mss -= tp->tcp_header_len - sizeof(struct tcphdr);
sk->dummy_th.dest = th->source;
sk->copied_seq = tp->rcv_nxt;
- if(!sk->dead)
- {
+ if(!sk->dead) {
sk->state_change(sk);
sock_wake_async(sk->socket, 0);
}
/* Drop through step 6 */
goto step6;
- }
- else
- {
- if(th->syn && !th->rst)
- {
- /*
- * the previous version of the code
+ } else {
+ if(th->syn && !th->rst) {
+ /* The previous version of the code
* checked for "connecting to self"
* here. that check is done now in
- * tcp_connect
+ * tcp_connect.
*/
-
tcp_set_state(sk, TCP_SYN_RECV);
+ tcp_parse_options(th,tp);
+ if (tp->saw_tstamp) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = jiffies;
+ }
tp->rcv_nxt = skb->seq + 1;
tp->rcv_wup = skb->seq + 1;
@@ -1605,8 +1583,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
break;
case TCP_TIME_WAIT:
- /*
- * RFC 1122:
+ /* RFC 1122:
* "When a connection is [...] on TIME-WAIT state [...]
* [a TCP] MAY accept a new SYN from the remote TCP to
* reopen the connection directly, if it:
@@ -1619,14 +1596,10 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
* (2) returns to TIME-WAIT state if the SYN turns out
* to be an old duplicate".
*/
-
- if (th->syn && !th->rst && after(skb->seq, tp->rcv_nxt))
- {
+ if (th->syn && !th->rst && after(skb->seq, tp->rcv_nxt)) {
__u32 isn;
- int err;
- atomic_sub(skb->truesize, &sk->rmem_alloc);
- skb->sk = NULL;
+ skb_orphan(skb);
sk->err = ECONNRESET;
tcp_set_state(sk, TCP_CLOSE);
sk->shutdown = SHUTDOWN_MASK;
@@ -1635,56 +1608,58 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
sk = tp->af_specific->get_sock(skb, th);
- if (sk == NULL)
+ if (sk == NULL || !ipsec_sk_policy(sk,skb))
goto discard;
- skb->sk = sk;
+ skb_set_owner_r(skb, sk);
tp = &sk->tp_pinfo.af_tcp;
- atomic_add(skb->truesize, &sk->rmem_alloc);
-
- err = tp->af_specific->conn_request(sk, skb, opt, isn);
- if (err < 0)
+ if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0)
return 1;
-
return 0;
}
break;
-
}
- /*
- * step 1: check sequence number
+ /* Parse the tcp_options present on this header.
+ * By this point we really only expect timestamps and SACKs.
+ * Note that this really has to be here and not later for PAWS
+ * (RFC1323) to work.
*/
+ if (tcp_fast_parse_options(th,tp)) {
+ /* NOTE: assumes saw_tstamp is never set if we didn't
+ * negotiate the option. tcp_fast_parse_options() must
+ * guarantee this.
+ */
+ if (tp->saw_tstamp) {
+ if (tcp_paws_discard(tp)) {
+ if (!th->rst) {
+ tcp_send_ack(sk);
+ goto discard;
+ }
+ }
+ tcp_replace_ts_recent(tp,skb->end_seq);
+ }
+ }
- if (!tcp_sequence(tp, skb->seq, skb->end_seq))
- {
- if (!th->rst)
- {
+ /* step 1: check sequence number */
+ if (!tcp_sequence(tp, skb->seq, skb->end_seq)) {
+ if (!th->rst) {
tcp_send_ack(sk);
goto discard;
}
}
-
- /*
- * step 2: check RST bit
- */
-
- if(th->rst)
- {
+ /* step 2: check RST bit */
+ if(th->rst) {
tcp_reset(sk,skb);
goto discard;
}
- /*
- * step 3: check security and precedence
- * [ignored]
- */
+ /* step 3: check security and precedence [ignored] */
- /*
- * step 4:
+ /* step 4:
*
* Check for a SYN, and ensure it matches the SYN we were
* first sent. We have to handle the rather unusual (but valid)
@@ -1700,24 +1675,18 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
* original syn.
*/
- if (th->syn && skb->seq!=sk->syn_seq)
- {
+ if (th->syn && skb->seq!=sk->syn_seq) {
tcp_reset(sk, skb);
return 1;
}
- /*
- * step 5: check the ACK field
- */
-
- if (th->ack)
- {
+ /* step 5: check the ACK field */
+ if (th->ack) {
int acceptable = tcp_ack(sk,th,skb->seq, skb->ack_seq,len);
switch(sk->state) {
case TCP_SYN_RECV:
- if (acceptable)
- {
+ if (acceptable) {
tcp_set_state(sk, TCP_ESTABLISHED);
sk->dummy_th.dest=th->source;
sk->copied_seq = tp->rcv_nxt;
@@ -1730,36 +1699,26 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
tp->snd_wl1 = skb->seq;
tp->snd_wl2 = skb->ack_seq;
- }
- else
+ } else
return 1;
break;
case TCP_FIN_WAIT1:
-
- if (tp->snd_una == sk->write_seq)
- {
+ if (tp->snd_una == sk->write_seq) {
sk->shutdown |= SEND_SHUTDOWN;
tcp_set_state(sk, TCP_FIN_WAIT2);
- if (!sk->dead)
+ if (!sk->dead)
sk->state_change(sk);
}
break;
- case TCP_CLOSING:
-
- if (tp->snd_una == sk->write_seq)
- {
+ case TCP_CLOSING:
+ if (tp->snd_una == sk->write_seq)
tcp_time_wait(sk);
- if (!sk->dead)
- sk->state_change(sk);
- }
break;
case TCP_LAST_ACK:
-
- if (tp->snd_una == sk->write_seq)
- {
+ if (tp->snd_una == sk->write_seq) {
sk->shutdown = SHUTDOWN_MASK;
tcp_set_state(sk,TCP_CLOSE);
if (!sk->dead)
@@ -1769,49 +1728,34 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
break;
case TCP_TIME_WAIT:
- /*
- * keep us in TIME_WAIT until we stop getting
+ /* Keep us in TIME_WAIT until we stop getting
* packets, reset the timeout.
*/
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
break;
-
}
- }
- else
+ } else
goto discard;
- step6:
-
- /*
- * step 6: check the URG bit
- */
-
+step6:
+ /* step 6: check the URG bit */
tcp_urg(sk, th, len);
- /*
- * step 7: process the segment text
- */
-
+ /* step 7: process the segment text */
switch (sk->state) {
case TCP_CLOSE_WAIT:
case TCP_CLOSING:
- if (!before(skb->seq, sk->fin_seq))
+ if (!before(skb->seq, tp->fin_seq))
break;
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
-
- /*
- * RFC 793 says to queue data in this states,
- * RFC 1122 says we MUST send a reset.
- * BSD 4.4 also does reset.
+ /* RFC 793 says to queue data in these states,
+ * RFC 1122 says we MUST send a reset.
+ * BSD 4.4 also does reset.
*/
-
- if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead)
- {
- if (after(skb->end_seq - th->fin, tp->rcv_nxt))
- {
+ if ((sk->shutdown & RCV_SHUTDOWN) && sk->dead) {
+ if (after(skb->end_seq - th->fin, tp->rcv_nxt)) {
tcp_reset(sk, skb);
return 1;
}
@@ -1819,25 +1763,19 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
case TCP_ESTABLISHED:
queued = tcp_data(skb, sk, len);
- break;
+ break;
}
- /*
- * step 8: check the FIN bit
- */
-
+ /* step 8: check the FIN bit */
if (th->fin)
- {
tcp_fin(skb, sk, th);
- }
tcp_data_snd_check(sk);
tcp_ack_snd_check(sk);
if (queued)
return 0;
- discard:
-
+discard:
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -1847,30 +1785,22 @@ int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
{
int val = sysctl_tcp_cong_avoidance;
int retv;
-
- retv = proc_dointvec(ctl, write, filp, buffer, lenp);
-
- if (write)
- {
+
+ retv = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+ if (write) {
switch (sysctl_tcp_cong_avoidance) {
- case 0:
- tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
- break;
- case 1:
- tcp_sys_cong_ctl_f = &tcp_cong_avoid_vegas;
- break;
- default:
- retv = -EINVAL;
- sysctl_tcp_cong_avoidance = val;
- }
+ case 0:
+ tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
+ break;
+ case 1:
+ tcp_sys_cong_ctl_f = &tcp_cong_avoid_vegas;
+ break;
+ default:
+ retv = -EINVAL;
+ sysctl_tcp_cong_avoidance = val;
+ };
}
-
+
return retv;
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -c -o tcp_input.o tcp_input.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index a36860c7a..f4528f552 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -5,6 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
+ * Version: $Id: tcp_ipv4.c,v 1.39 1997/04/22 02:53:14 davem Exp $
*
* IPv4 specific functions
*
@@ -22,10 +23,20 @@
* 2 of the License, or (at your option) any later version.
*/
+/*
+ * Changes:
+ * David S. Miller : New socket lookup architecture.
+ * This code is dedicated to John Dyson.
+ * David S. Miller : Change semantics of established hash,
+ * half is devoted to TIME_WAIT sockets
+ * and the rest go in the other half.
+ */
+
#include <linux/config.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/random.h>
+#include <linux/ipsec.h>
#include <net/icmp.h>
#include <net/tcp.h>
@@ -33,54 +44,365 @@
#include <asm/segment.h>
-static void tcp_v4_send_reset(unsigned long saddr, unsigned long daddr,
- struct tcphdr *th, struct proto *prot,
- struct options *opt,
- struct device *dev, int tos, int ttl);
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_tsack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+
+static void tcp_v4_send_reset(struct sk_buff *skb);
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb);
-/*
- * Cached last hit socket
+/* This is for sockets with full identity only. Sockets here will always
+ * be without wildcards and will have the following invariant:
+ * TCP_ESTABLISHED <= sk->state < TCP_CLOSE
+ *
+ * First half of the table is for sockets not in TIME_WAIT, second half
+ * is for TIME_WAIT sockets only.
*/
-
-static volatile unsigned long th_cache_saddr, th_cache_daddr;
-static volatile unsigned short th_cache_dport, th_cache_sport;
-static volatile struct sock *th_cache_sk;
+struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
-void tcp_cache_zap(void)
+/* All sockets in TCP_LISTEN state will be in here. This is the only table
+ * where wildcard'd TCP sockets can exist. Hash function here is just local
+ * port number.
+ */
+struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
+
+/* Ok, let's try this, I give up, we do need a local binding
+ * TCP hash as well as the others for fast bind/connect.
+ */
+struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
+static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
+ __u32 faddr, __u16 fport)
{
- th_cache_sk=NULL;
+ return ((laddr ^ lport) ^ (faddr ^ fport)) & ((TCP_HTABLE_SIZE/2) - 1);
}
-/*
- * Find the socket, using the last hit cache if applicable.
- * The cache is not quite right...
+static __inline__ int tcp_sk_hashfn(struct sock *sk)
+{
+ __u32 laddr = sk->rcv_saddr;
+ __u16 lport = sk->num;
+ __u32 faddr = sk->daddr;
+ __u16 fport = sk->dummy_th.dest;
+
+ return tcp_hashfn(laddr, lport, faddr, fport);
+}
+
+static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ sk2 = tcp_bound_hash[tcp_bhashfn(snum)];
+ for(; sk2 != NULL; sk2 = sk2->bind_next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+
+ if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(sk2->rcv_saddr == sk->rcv_saddr) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+
+ return retval;
+}
+
+static __inline__ int tcp_lport_inuse(int num)
+{
+ struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)];
+
+ for(; sk != NULL; sk = sk->bind_next) {
+ if(sk->num == num)
+ return 1;
+ }
+ return 0;
+}
+
+/* Find a "good" local port, this is family independant.
+ * There are several strategies working in unison here to
+ * get the best possible performance. The current socket
+ * load is kept track of, if it is zero there is a strong
+ * likely hood that there is a zero length chain we will
+ * find with a small amount of searching, else the load is
+ * what we shoot for for when the chains all have at least
+ * one entry. The base helps us walk the chains in an
+ * order such that a good chain is found as quickly as possible. -DaveM
*/
+unsigned short tcp_good_socknum(void)
+{
+ static int start = PROT_SOCK;
+ static int binding_contour = 0;
+ int best = 0;
+ int size = 32767; /* a big num. */
+ int retval = 0, i, end, bc;
+
+ SOCKHASH_LOCK();
+ i = tcp_bhashfn(start);
+ end = i + TCP_BHTABLE_SIZE;
+ bc = binding_contour;
+ do {
+ struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)];
+ if(!sk) {
+ retval = (start + i);
+ start = (retval + 1);
+
+ /* Check for decreasing load. */
+ if(bc != 0)
+ binding_contour = 0;
+ goto done;
+ } else {
+ int j = 0;
+ do { sk = sk->bind_next; } while(++j < size && sk);
+ if(j < size) {
+ best = (start + i);
+ size = j;
+ if(bc && size <= bc) {
+ start = best + 1;
+ goto verify;
+ }
+ }
+ }
+ } while(++i != end);
+
+ /* Socket load is increasing, adjust our load average. */
+ binding_contour = size;
+verify:
+ if(size < binding_contour)
+ binding_contour = size;
+
+ if(best > 32767)
+ best -= (32768 - PROT_SOCK);
+
+ while(tcp_lport_inuse(best))
+ best += TCP_BHTABLE_SIZE;
+ retval = best;
+done:
+ if(start > 32767)
+ start -= (32768 - PROT_SOCK);
+
+ SOCKHASH_UNLOCK();
+
+ return retval;
+}
-static inline struct sock * get_tcp_sock(u32 saddr, u16 sport,
- u32 daddr, u16 dport,
- u32 paddr, u16 pport)
+static void tcp_v4_hash(struct sock *sk)
{
- struct sock * sk;
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(state != TCP_CLOSE || !sk->dead) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN)
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ else
+ skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
+
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v4_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v4_rehash(struct sock *sk)
+{
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ if(state != TCP_CLOSE || !sk->dead) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN) {
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ } else {
+ int hash= tcp_sk_hashfn(sk);
+ if(state == TCP_TIME_WAIT)
+ hash += (TCP_HTABLE_SIZE/2);
+ skp = &tcp_established_hash[hash];
+ }
+
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+/* Don't inline this cruft. Here are some nice properties to
+ * exploit here. The BSD API does not allow a listening TCP
+ * to specify the remote port nor the remote address for the
+ * connection. So always assume those are both wildcarded
+ * during the search since they can never be otherwise.
+ */
+static struct sock *tcp_v4_lookup_listener(u32 daddr, unsigned short hnum)
+{
+ struct sock *sk;
+ struct sock *result = NULL;
+
+ for(sk = tcp_listening_hash[tcp_lhashfn(hnum)]; sk; sk = sk->next) {
+ if(sk->num == hnum) {
+ __u32 rcv_saddr = sk->rcv_saddr;
- sk = (struct sock *) th_cache_sk;
- if (!sk || saddr != th_cache_saddr || daddr != th_cache_daddr ||
- sport != th_cache_sport || dport != th_cache_dport) {
- sk = get_sock(&tcp_prot, dport, saddr, sport, daddr,
- paddr, pport);
- if (sk) {
- th_cache_saddr=saddr;
- th_cache_daddr=daddr;
- th_cache_dport=dport;
- th_cache_sport=sport;
- th_cache_sk=sk;
+ if(rcv_saddr) {
+ if(rcv_saddr == daddr)
+ return sk; /* Best possible match. */
+ } else if(!result)
+ result = sk;
}
}
+ return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
+ u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+ unsigned short hnum = ntohs(dport);
+ struct sock *sk;
+ int hash = tcp_hashfn(daddr, hnum, saddr, sport);
+
+ /* Optimize here for direct hit, only listening connections can
+ * have wildcards anyways. It is assumed that this code only
+ * gets called from within NET_BH.
+ */
+ for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+ if(sk->daddr == saddr && /* remote address */
+ sk->dummy_th.dest == sport && /* remote port */
+ sk->num == hnum && /* local port */
+ sk->rcv_saddr == daddr) /* local address */
+ goto hit; /* You sunk my battleship! */
+
+ /* Must check for a TIME_WAIT'er before going to listener hash. */
+ for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+ if(sk->daddr == saddr && /* remote address */
+ sk->dummy_th.dest == sport && /* remote port */
+ sk->num == hnum && /* local port */
+ sk->rcv_saddr == daddr) /* local address */
+ goto hit;
+
+ sk = tcp_v4_lookup_listener(daddr, hnum);
+hit:
return sk;
}
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+ return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+ s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
+ else \
+ s1 = (sk); \
+ s1; \
+})
+
+#define tcp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass))
+
+#define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum),(sk)->bind_next,(fpass))
+
+struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+ unsigned short rnum, unsigned long laddr,
+ unsigned long paddr, unsigned short pnum)
+{
+ struct sock *s, *result = NULL;
+ int badness = -1;
+ unsigned short hnum = ntohs(num);
+ unsigned short hpnum = ntohs(pnum);
+ int firstpass = 1;
+
+ /* This code must run only from NET_BH. */
+ for(s = tcp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+ s != NULL;
+ s = tcp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+ if(s->num == hnum || s->num == hpnum) {
+ int score = 0;
+ if(s->dead && (s->state == TCP_CLOSE))
+ continue;
+ if(s->rcv_saddr) {
+ if((s->num != hpnum || s->rcv_saddr != paddr) &&
+ (s->num != hnum || s->rcv_saddr != laddr))
+ continue;
+ score++;
+ }
+ if(s->daddr) {
+ if(s->daddr != raddr)
+ continue;
+ score++;
+ }
+ if(s->dummy_th.dest) {
+ if(s->dummy_th.dest != rnum)
+ continue;
+ score++;
+ }
+ if(score == 3 && s->num == hnum) {
+ result = s;
+ break;
+ } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+ result = s;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
+
+#undef secondlist
+#undef tcp_v4_proxy_loop_init
+#undef tcp_v4_proxy_loop_next
+
+#endif
+
static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
{
return secure_tcp_sequence_number(sk->saddr, sk->daddr,
@@ -99,30 +421,41 @@ static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
static int tcp_unique_address(u32 saddr, u16 snum, u32 daddr, u16 dnum)
{
- int retval = 1;
+ int retval = 1, hashent = tcp_hashfn(saddr, snum, daddr, dnum);
struct sock * sk;
- /* Make sure we are allowed to connect here. */
- cli();
- for (sk = tcp_prot.sock_array[snum & (SOCK_ARRAY_SIZE -1)];
- sk != NULL; sk = sk->next)
- {
- /* hash collision? */
- if (sk->num != snum)
- continue;
- if (sk->saddr != saddr)
- continue;
- if (sk->daddr != daddr)
- continue;
- if (sk->dummy_th.dest != dnum)
- continue;
- retval = 0;
- break;
+ /* Make sure we are allowed to connect here.
+ * But freeze the hash while we snoop around.
+ */
+ SOCKHASH_LOCK();
+ sk = tcp_established_hash[hashent];
+ for (; sk != NULL; sk = sk->next) {
+ if(sk->daddr == daddr && /* remote address */
+ sk->dummy_th.dest == dnum && /* remote port */
+ sk->num == snum && /* local port */
+ sk->saddr == saddr) { /* local address */
+ retval = 0;
+ goto out;
+ }
+ }
+
+ /* Must check TIME_WAIT'ers too. */
+ sk = tcp_established_hash[hashent + (TCP_HTABLE_SIZE/2)];
+ for (; sk != NULL; sk = sk->next) {
+ if(sk->daddr == daddr && /* remote address */
+ sk->dummy_th.dest == dnum && /* remote port */
+ sk->num == snum && /* local port */
+ sk->saddr == saddr) { /* local address */
+ retval = 0;
+ goto out;
+ }
}
- sti();
+out:
+ SOCKHASH_UNLOCK();
return retval;
}
+
/*
* This will initiate an outgoing connection.
*/
@@ -131,10 +464,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sk_buff *buff;
struct sk_buff *skb1;
- struct device *dev=NULL;
- unsigned char *ptr;
int tmp;
- int atype;
struct tcphdr *t1;
struct rtable *rt;
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
@@ -143,45 +473,52 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (sk->state != TCP_CLOSE)
return(-EISCONN);
- /*
- * Don't allow a double connect.
- */
-
- if(sk->daddr)
+ /* Don't allow a double connect. */
+ if (sk->daddr)
return -EINVAL;
-
- if (addr_len < sizeof(struct sockaddr_in))
+
+ if (addr_len < sizeof(struct sockaddr_in))
return(-EINVAL);
- if (usin->sin_family && usin->sin_family != AF_INET)
- return(-EAFNOSUPPORT);
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (usin->sin_family)
+ return(-EAFNOSUPPORT);
+ if (!complained++)
+ printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm);
+ }
- /*
- * connect() to INADDR_ANY means loopback (BSD'ism).
- */
-
- if (usin->sin_addr.s_addr==INADDR_ANY)
- usin->sin_addr.s_addr=ip_my_addr();
-
- /*
- * Don't want a TCP connection going to a broadcast address
- */
+ if (sk->dst_cache) {
+ dst_release(sk->dst_cache);
+ sk->dst_cache = NULL;
+ }
- if ((atype=ip_chk_addr(usin->sin_addr.s_addr)) == IS_BROADCAST
- || atype==IS_MULTICAST)
- {
+ tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+ RT_TOS(sk->ip_tos)|(sk->localroute || 0));
+ if (tmp < 0)
+ return tmp;
+
+ if (rt->rt_flags&(RTF_MULTICAST|RTF_BROADCAST)) {
+ ip_rt_put(rt);
return -ENETUNREACH;
}
- if (!tcp_unique_address(sk->saddr, sk->num, usin->sin_addr.s_addr,
+ if (!tcp_unique_address(rt->rt_src, sk->num, rt->rt_dst,
usin->sin_port))
- {
return -EADDRNOTAVAIL;
- }
lock_sock(sk);
- sk->daddr = usin->sin_addr.s_addr;
+ sk->dst_cache = &rt->u.dst;
+ sk->daddr = rt->rt_dst;
+ if (!sk->saddr)
+ sk->saddr = rt->rt_src;
+ sk->rcv_saddr = sk->saddr;
+
+ if (sk->priority == SOPRI_NORMAL)
+ sk->priority = rt->u.dst.priority;
+
sk->dummy_th.dest = usin->sin_port;
+
sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
sk->dummy_th.source,
usin->sin_port);
@@ -195,34 +532,19 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sk->err = 0;
- buff = sock_wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
- if (buff == NULL)
- {
+ buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
+ if (buff == NULL) {
release_sock(sk);
return(-ENOBUFS);
}
- buff->sk = sk;
- buff->free = 0;
- buff->localroute = sk->localroute;
-
- /*
- * Put in the IP header and routing stuff.
- */
-
- tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev,
- IPPROTO_TCP, NULL, MAX_SYN_SIZE, sk->ip_tos,
- sk->ip_ttl,&sk->ip_route_cache);
-
- if (tmp < 0)
- {
- sock_wfree(sk, buff);
+ /* Put in the IP header and routing stuff. */
+ tmp = ip_build_header(buff, sk);
+ if (tmp < 0) {
+ kfree_skb(buff, FREE_WRITE);
release_sock(sk);
return(-ENETUNREACH);
}
- if ((rt = sk->ip_route_cache) != NULL && !sk->saddr)
- sk->saddr = rt->rt_src;
- sk->rcv_saddr = sk->saddr;
t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
buff->h.th = t1;
@@ -235,70 +557,57 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
t1->ack = 0;
t1->window = htons(512);
t1->syn = 1;
- t1->doff = 6;
- /* use 512 or whatever user asked for */
-
- if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
- sk->window_clamp=rt->rt_window;
- else
- sk->window_clamp=0;
+ /* Use 512 or whatever user asked for. */
+ tp->window_clamp = rt->u.dst.window;
+ sk->mtu = rt->u.dst.pmtu;
+ if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+ (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+ rt->rt_flags&RTF_NOPMTUDISC)) &&
+ rt->u.dst.pmtu > 576)
+ sk->mtu = 576;
- if (rt)
- sk->mtu = rt->rt_mtu;
- else
- sk->mtu = dev->mtu;
-
if(sk->mtu < 64)
sk->mtu = 64; /* Sanity limit */
if (sk->user_mss)
sk->mss = sk->user_mss;
else
- sk->mss = (sk->mtu - sizeof(struct iphdr) -
+ sk->mss = (sk->mtu - sizeof(struct iphdr) -
sizeof(struct tcphdr));
- /*
- * Put in the TCP options to say MSS.
- */
-
- ptr = skb_put(buff,4);
- ptr[0] = TCPOPT_MSS;
- ptr[1] = TCPOLEN_MSS;
- ptr[2] = (sk->mss) >> 8;
- ptr[3] = (sk->mss) & 0xff;
- buff->csum = csum_partial(ptr, 4, 0);
- tcp_v4_send_check(sk, t1, sizeof(struct tcphdr) + 4, buff);
+ tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
+ sysctl_tcp_timestamps,
+ sysctl_tcp_window_scaling?tp->rcv_wscale:0);
+ buff->csum = 0;
+ t1->doff = (sizeof(*t1)+ tmp)>>2;
- /*
- * This must go first otherwise a really quick response
- * will get reset.
- */
+ tcp_v4_send_check(sk, t1, sizeof(struct tcphdr) + tmp, buff);
- tcp_cache_zap();
tcp_set_state(sk,TCP_SYN_SENT);
- if(rt && (rt->rt_flags&RTF_IRTT))
- tp->rto = rt->rt_irtt;
- else
- tp->rto = TCP_TIMEOUT_INIT;
+ /* Socket identity change complete, no longer
+ * in TCP_CLOSE, so rehash.
+ */
+ tcp_v4_rehash(sk);
+
+ tp->rto = rt->u.dst.rtt;
tcp_init_xmit_timers(sk);
-
- /* Now works the right way instead of a hacked initial setting */
- sk->retransmits = 0;
+
+ /* Now works the right way instead of a hacked initial setting. */
+ tp->retransmits = 0;
skb_queue_tail(&sk->write_queue, buff);
- sk->packets_out++;
+ tp->packets_out++;
buff->when = jiffies;
skb1 = skb_clone(buff, GFP_KERNEL);
- sk->wmem_alloc += skb1->truesize;
- ip_queue_xmit(sk, dev, skb1, 1);
+ ip_queue_xmit(skb1);
- /* Timer for repeating the SYN until an answer */
+ /* Timer for repeating the SYN until an answer. */
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_statistics.TcpActiveOpens++;
tcp_statistics.TcpOutSegs++;
@@ -307,16 +616,12 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
return(0);
}
-static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg,
- int len, int nonblock, int flags)
+static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
int retval = -EINVAL;
- /*
- * Do sanity checking for sendmsg/sendto/send
- */
-
- if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+ /* Do sanity checking for sendmsg/sendto/send. */
+ if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
goto out;
if (msg->msg_name) {
struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name;
@@ -336,8 +641,8 @@ static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg,
}
lock_sock(sk);
- retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
- len, nonblock, flags);
+ retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
+ msg->msg_flags);
release_sock(sk);
@@ -354,203 +659,119 @@ out:
* to find the appropriate port.
*/
-void tcp_v4_err(int type, int code, unsigned char *header, __u32 info,
- __u32 daddr, __u32 saddr, struct inet_protocol *protocol, int len)
+void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
{
- struct tcphdr *th = (struct tcphdr *)header;
+ struct iphdr *iph = (struct iphdr*)dp;
+ struct tcphdr *th = (struct tcphdr*)(dp+(iph->ihl<<2));
struct tcp_opt *tp;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
struct sock *sk;
- if(len<8) /* We use the first 8 bytes only */
- return;
-
- th =(struct tcphdr *)header;
- sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr, 0, 0);
+ sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source);
if (sk == NULL)
return;
- if (type == ICMP_SOURCE_QUENCH)
- {
- /*
- * FIXME:
- * Follow BSD for now and just reduce cong_window to 1 again.
- * It is possible that we just want to reduce the
- * window by 1/2, or that we want to reduce ssthresh by 1/2
- * here as well.
- */
-
- tp = &sk->tp_pinfo.af_tcp;
-
- sk->cong_window = 1;
+ tp = &sk->tp_pinfo.af_tcp;
+ if (type == ICMP_SOURCE_QUENCH) {
+ tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+ tp->snd_cwnd = tp->snd_ssthresh;
tp->high_seq = tp->snd_nxt;
-
return;
}
- if (type == ICMP_PARAMETERPROB)
- {
+ if (type == ICMP_PARAMETERPROB) {
sk->err=EPROTO;
sk->error_report(sk);
}
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
- if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
- {
- struct rtable * rt;
-
- unsigned short new_mtu = info;
-
- if ((rt = sk->ip_route_cache) != NULL)
- if (rt->rt_mtu > new_mtu)
- rt->rt_mtu = new_mtu;
-
- if ((sk->mtu > new_mtu) &&
- (new_mtu > sizeof(struct iphdr)+sizeof(struct tcphdr)))
- {
- sk->mss = (new_mtu - sizeof(struct iphdr)
- - sizeof(struct tcphdr));
+ /* FIXME: What about the IP layer options size here? */
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+ int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - tp->tcp_header_len;
+ if (new_mtu < sk->mss && new_mtu > 0) {
+ sk->mss = new_mtu;
+ }
}
-
return;
}
-#endif
- /*
- * If we've already connected we will keep trying
+ /* If we've already connected we will keep trying
* until we time out, or the user gives up.
*/
-
- if (code <= NR_ICMP_UNREACH)
- {
- if(icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- {
+ if (code <= NR_ICMP_UNREACH) {
+ if(icmp_err_convert[code].fatal || sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
sk->err = icmp_err_convert[code].errno;
- if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV)
- {
+ if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
tcp_statistics.TcpAttemptFails++;
tcp_set_state(sk,TCP_CLOSE);
sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */
}
- }
- else /* Only an error on timeout */
+ } else /* Only an error on timeout */
sk->err_soft = icmp_err_convert[code].errno;
}
}
-/*
- * This routine computes a TCP checksum.
- *
- * Modified January 1995 from a go-faster DOS routine by
- * Jorge Cwik <jorge@laser.satlink.net>
- */
+/* This routine computes an IPv4 TCP checksum. */
void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len,
struct sk_buff *skb)
{
- __u32 saddr = sk->saddr;
- __u32 daddr = sk->daddr;
-#ifdef DEBUG_TCP_CHECK
- u16 check;
-#endif
- th->check = 0;
- th->check = tcp_v4_check(th, len, saddr, daddr,
- csum_partial((char *)th, sizeof(*th),
- skb->csum));
-
-#ifdef DEBUG_TCP_CHECK
- check = th->check;
th->check = 0;
- th->check = tcp_v4_check(th, len, saddr, daddr,
- csum_partial((char *)th,len,0));
- if (check != th->check) {
- static int count = 0;
- if (++count < 10) {
- printk("Checksum %x (%x) from %p\n", th->check, check,
- __builtin_return_address(0));
- printk("TCP=<off:%d a:%d s:%d f:%d> len=%d\n", th->doff*4, th->ack, th->syn, th->fin, len);
- }
- }
-#endif
+ th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+ csum_partial((char *)th, th->doff<<2, skb->csum));
}
/*
- * This routine will send an RST to the other tcp.
+ * This routine will send an RST to the other tcp.
+ *
+ * Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
+ * for reset.
+ * Answer: if a packet caused RST, it is not for a socket
+ * existing in our system, if it is matched to a socket,
+ * it is just duplicate segment or bug in other side's TCP.
+ * So that we build reply only basing on parameters
+ * arrived with segment.
+ * Exception: precedence violation. We do not implement it in any case.
*/
-
-static void tcp_v4_send_reset(unsigned long saddr, unsigned long daddr,
- struct tcphdr *th, struct proto *prot,
- struct options *opt,
- struct device *dev, int tos, int ttl)
+
+static void tcp_v4_send_reset(struct sk_buff *skb)
{
- struct sk_buff *buff;
- struct tcphdr *t1;
- int tmp;
- struct device *ndev=NULL;
+ struct tcphdr *th = skb->h.th;
+ struct sk_buff *skb1;
+ struct tcphdr *th1;
- /*
- * Cannot reset a reset (Think about it).
- */
-
- if(th->rst)
+ if (th->rst)
return;
-
- /*
- * We need to grab some memory, and put together an RST,
- * and then put it into the queue to be sent.
- */
-
- buff = alloc_skb(MAX_RESET_SIZE, GFP_ATOMIC);
- if (buff == NULL)
- return;
- buff->sk = NULL;
- buff->dev = dev;
- buff->localroute = 0;
-
-
- /*
- * Put in the IP header and routing stuff.
- */
-
- tmp = ip_build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
- sizeof(struct tcphdr),tos,ttl,NULL);
- if (tmp < 0)
- {
- buff->free = 1;
- sock_wfree(NULL, buff);
+ skb1 = ip_reply(skb, sizeof(struct tcphdr));
+ if (skb1 == NULL)
return;
- }
-
- t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
- memset(t1, 0, sizeof(*t1));
-
- /*
- * Swap the send and the receive.
- */
-
- t1->dest = th->source;
- t1->source = th->dest;
- t1->doff = sizeof(*t1)/4;
- t1->rst = 1;
-
- if(th->ack)
- {
- t1->seq = th->ack_seq;
- }
- else
- {
- t1->ack = 1;
- if(!th->syn)
- t1->ack_seq = th->seq;
+
+ skb1->h.th = th1 = (struct tcphdr *)skb_put(skb1, sizeof(struct tcphdr));
+ memset(th1, 0, sizeof(*th1));
+
+ /* Swap the send and the receive. */
+ th1->dest = th->source;
+ th1->source = th->dest;
+ th1->doff = sizeof(*th1)/4;
+ th1->rst = 1;
+
+ if (th->ack)
+ th1->seq = th->ack_seq;
+ else {
+ th1->ack = 1;
+ if (!th->syn)
+ th1->ack_seq = th->seq;
else
- t1->ack_seq = htonl(ntohl(th->seq)+1);
+ th1->ack_seq = htonl(ntohl(th->seq)+1);
}
-
- buff->csum = csum_partial((u8 *) t1, sizeof(*t1), 0);
- t1->check = tcp_v4_check(t1, sizeof(*t1), saddr, daddr, buff->csum);
-
- ip_queue_xmit(NULL, ndev, buff, 1);
+ skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0);
+ th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr,
+ skb1->nh.iph->daddr, skb1->csum);
+ /* FIXME: should this carry an options packet? */
+ ip_queue_xmit(skb1);
tcp_statistics.TcpOutSegs++;
}
@@ -562,12 +783,11 @@ static void tcp_v4_send_reset(unsigned long saddr, unsigned long daddr,
int tcp_chkaddr(struct sk_buff *skb)
{
- struct iphdr *iph = skb->h.iph;
- struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
+ struct iphdr *iph = skb->nh.iph;
+ struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
struct sock *sk;
- sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr,
- 0, 0);
+ sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
if (!sk)
return 0;
@@ -583,94 +803,69 @@ int tcp_chkaddr(struct sk_buff *skb)
static void tcp_v4_send_synack(struct sock *sk, struct open_request *req)
{
- struct tcp_v4_open_req *af_req = (struct tcp_v4_open_req *) req;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct sk_buff * skb;
- struct device *dev = NULL;
- struct rtable *rt = NULL;
struct tcphdr *th;
- unsigned char *ptr;
- int mss;
int tmp;
+ int mss;
skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-
if (skb == NULL)
- {
return;
- }
-
- tmp = ip_build_header(skb, af_req->loc_addr, af_req->rmt_addr, &dev,
- IPPROTO_TCP, af_req->opt, skb->truesize,
- sk->ip_tos, sk->ip_ttl, &rt);
- if (tmp < 0)
- {
- skb->free = 1;
+ if(ip_build_pkt(skb, sk, req->af.v4_req.loc_addr,
+ req->af.v4_req.rmt_addr, req->af.v4_req.opt) < 0) {
kfree_skb(skb, FREE_WRITE);
return;
}
- skb->dev = dev;
-
- if (rt)
- mss = rt->rt_mtu;
- else
- mss = dev->mtu;
-
- mss -= sizeof(struct iphdr) + sizeof(struct tcphdr);
-
+ mss = (skb->dst->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
if (sk->user_mss)
mss = min(mss, sk->user_mss);
-
- ip_rt_put(rt);
-
- th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
- skb->h.th = th;
+ skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
+
+ /* Don't offer more than they did.
+ * This way we don't have to memorize who said what.
+ */
+ req->mss = min(mss, req->mss);
+
+ /* Yuck, make this header setup more efficient... -DaveM */
memset(th, 0, sizeof(struct tcphdr));
-
th->syn = 1;
th->ack = 1;
-
th->source = sk->dummy_th.source;
th->dest = req->rmt_port;
-
skb->seq = req->snt_isn;
skb->end_seq = skb->seq + 1;
-
th->seq = ntohl(skb->seq);
th->ack_seq = htonl(req->rcv_isn + 1);
- th->doff = sizeof(*th)/4 + 1;
-
th->window = ntohs(tp->rcv_wnd);
- ptr = skb_put(skb, TCPOLEN_MSS);
- ptr[0] = TCPOPT_MSS;
- ptr[1] = TCPOLEN_MSS;
- ptr[2] = (mss >> 8) & 0xff;
- ptr[3] = mss & 0xff;
- skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
+ /* XXX Partial csum of 4 byte quantity is itself! -DaveM
+ * Yes, but it's a bit harder to special case now. It's
+ * now computed inside the tcp_v4_send_check() to clean up
+ * updating the options fields in the mainline send code.
+ * If someone thinks this is really bad let me know and
+ * I'll try to do it a different way. -- erics
+ */
- th->check = tcp_v4_check(th, sizeof(*th) + TCPOLEN_MSS, af_req->loc_addr,
- af_req->rmt_addr,
- csum_partial((char *)th, sizeof(*th), skb->csum));
+ tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok,
+ (req->snd_wscale)?tp->rcv_wscale:0);
+ skb->csum = 0;
+ th->doff = (sizeof(*th) + tmp)>>2;
+ th->check = tcp_v4_check(th, sizeof(*th) + tmp,
+ req->af.v4_req.loc_addr, req->af.v4_req.rmt_addr,
+ csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
- ip_queue_xmit(sk, dev, skb, 1);
+ ip_queue_xmit(skb);
tcp_statistics.TcpOutSegs++;
-
}
static void tcp_v4_or_free(struct open_request *req)
{
- struct tcp_v4_open_req *af_req = (struct tcp_v4_open_req *) req;
-
- if (af_req->req.sk)
- return;
-
- if (af_req->opt)
- {
- kfree_s(af_req->opt, sizeof(struct options) + af_req->opt->optlen);
- }
+ if(!req->sk && req->af.v4_req.opt)
+ kfree_s(req->af.v4_req.opt,
+ sizeof(struct options) + req->af.v4_req.opt->optlen);
}
static struct or_calltable or_ipv4 = {
@@ -685,29 +880,24 @@ static int tcp_v4_syn_filter(struct sock *sk, struct sk_buff *skb, __u32 saddr)
int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn)
{
- struct options *opt = (struct options *) ptr;
- struct tcp_v4_open_req *af_req;
+ struct ip_options *opt = (struct ip_options *) ptr;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
struct open_request *req;
struct tcphdr *th = skb->h.th;
- __u32 saddr = skb->saddr;
- __u32 daddr = skb->daddr;
+ __u32 saddr = skb->nh.iph->saddr;
+ __u32 daddr = skb->nh.iph->daddr;
/* If the socket is dead, don't accept the connection. */
- if (sk->dead)
- {
- if(sk->debug)
- {
- printk("Reset on %p: Connect on dead socket.\n",sk);
- }
+ if (sk->dead) {
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n",sk);
tcp_statistics.TcpAttemptFails++;
- return -ENOTCONN;
+ return -ENOTCONN;
}
- if (sk->ack_backlog >= sk->max_ack_backlog ||
- tcp_v4_syn_filter(sk, skb, saddr))
- {
- printk(KERN_DEBUG "droping syn ack:%d max:%d\n",
- sk->ack_backlog, sk->max_ack_backlog);
+ if (sk->ack_backlog >= sk->max_ack_backlog ||
+ tcp_v4_syn_filter(sk, skb, saddr)) {
+ SOCK_DEBUG(sk, "dropping syn ack:%d max:%d\n", sk->ack_backlog,
+ sk->max_ack_backlog);
#ifdef CONFIG_IP_TCPSF
tcp_v4_random_drop(sk);
#endif
@@ -715,67 +905,57 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
goto exit;
}
-
- af_req = kmalloc(sizeof(struct tcp_v4_open_req), GFP_ATOMIC);
-
- if (af_req == NULL)
- {
+ req = tcp_openreq_alloc();
+ if (req == NULL) {
tcp_statistics.TcpAttemptFails++;
goto exit;
}
sk->ack_backlog++;
- req = (struct open_request *) af_req;
-
- memset(af_req, 0, sizeof(struct tcp_v4_open_req));
req->rcv_isn = skb->seq;
req->snt_isn = isn;
-
- /* mss */
- req->mss = tcp_parse_options(th);
-
- if (!req->mss)
- {
- req->mss = 536;
+ tp->tstamp_ok = tp->sack_ok = tp->snd_wscale = 0;
+ tcp_parse_options(th,tp);
+ if (tp->saw_tstamp) {
+ tp->ts_recent = tp->rcv_tsval;
+ tp->ts_recent_stamp = jiffies;
}
-
+ req->mss = tp->in_mss;
+ req->tstamp_ok = tp->tstamp_ok;
+ req->sack_ok = tp->sack_ok;
+ req->snd_wscale = tp->snd_wscale;
+ req->ts_recent = tp->ts_recent;
req->rmt_port = th->source;
-
- af_req->loc_addr = daddr;
- af_req->rmt_addr = saddr;
-
- /*
- * options
- */
-
- if (opt && opt->optlen)
- {
- af_req->opt = (struct options*) kmalloc(sizeof(struct options) +
- opt->optlen, GFP_ATOMIC);
- if (af_req->opt)
- {
- if (ip_options_echo(af_req->opt, opt, skb->daddr,
- skb->saddr, skb))
- {
- kfree_s(af_req->opt, sizeof(struct options) +
- opt->optlen);
- af_req->opt = NULL;
+ req->af.v4_req.loc_addr = daddr;
+ req->af.v4_req.rmt_addr = saddr;
+
+ /* IPv4 options */
+ req->af.v4_req.opt = NULL;
+ if (opt && opt->optlen) {
+ int opt_size = sizeof(struct ip_options) + opt->optlen;
+
+ req->af.v4_req.opt = kmalloc(opt_size, GFP_ATOMIC);
+ if (req->af.v4_req.opt) {
+ if (ip_options_echo(req->af.v4_req.opt, skb)) {
+ kfree_s(req->af.v4_req.opt, opt_size);
+ req->af.v4_req.opt = NULL;
}
}
}
-
req->class = &or_ipv4;
+ req->retrans = 0;
+ req->sk = NULL;
tcp_v4_send_synack(sk, req);
-
+
req->expires = jiffies + TCP_TIMEOUT_INIT;
tcp_inc_slow_timer(TCP_SLT_SYNACK);
- tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
+ tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
sk->data_ready(sk, 0);
- exit:
+exit:
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -783,32 +963,28 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 i
struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct open_request *req)
{
- struct tcp_v4_open_req *af_req = (struct tcp_v4_open_req *) req;
struct tcp_opt *newtp;
struct sock *newsk;
struct rtable *rt;
int snd_mss;
- newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+ newsk = sk_alloc(GFP_ATOMIC);
if (newsk == NULL)
- {
return NULL;
- }
memcpy(newsk, sk, sizeof(*newsk));
+
+ /* Or else we die! -DaveM */
+ newsk->sklist_next = NULL;
+
newsk->opt = NULL;
- newsk->ip_route_cache = NULL;
+ newsk->dst_cache = NULL;
skb_queue_head_init(&newsk->write_queue);
skb_queue_head_init(&newsk->receive_queue);
skb_queue_head_init(&newsk->out_of_order_queue);
-
- /*
- * Unused
- */
-
- newsk->send_head = NULL;
- newsk->send_tail = NULL;
+ skb_queue_head_init(&newsk->error_queue);
+ /* Unused */
newtp = &(newsk->tp_pinfo.af_tcp);
newtp->send_head = NULL;
newtp->retrans_head = NULL;
@@ -819,17 +995,13 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->prot->init(newsk);
- newsk->cong_count = 0;
- newsk->ssthresh = 0;
+ newtp->snd_cwnd_cnt = 0;
newtp->backoff = 0;
- newsk->blog = 0;
- newsk->intr = 0;
newsk->proc = 0;
newsk->done = 0;
- newsk->partial = NULL;
newsk->pair = NULL;
- newsk->wmem_alloc = 0;
- newsk->rmem_alloc = 0;
+ atomic_set(&newsk->wmem_alloc, 0);
+ atomic_set(&newsk->rmem_alloc, 0);
newsk->localroute = sk->localroute;
newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
@@ -838,24 +1010,23 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->shutdown = 0;
newsk->ack_backlog = 0;
- newsk->fin_seq = req->rcv_isn;
+ newtp->fin_seq = req->rcv_isn;
newsk->syn_seq = req->rcv_isn;
newsk->state = TCP_SYN_RECV;
newsk->timeout = 0;
- newsk->ip_xmit_timeout = 0;
newsk->write_seq = req->snt_isn;
newtp->snd_wnd = ntohs(skb->h.th->window);
- newsk->max_window = newtp->snd_wnd;
+ newtp->max_window = newtp->snd_wnd;
newtp->snd_wl1 = req->rcv_isn;
newtp->snd_wl2 = newsk->write_seq;
newtp->snd_una = newsk->write_seq++;
newtp->snd_nxt = newsk->write_seq;
newsk->urg_data = 0;
- newsk->packets_out = 0;
- newsk->retransmits = 0;
+ newtp->packets_out = 0;
+ newtp->retransmits = 0;
newsk->linger=0;
newsk->destroy = 0;
init_timer(&newsk->timer);
@@ -866,384 +1037,283 @@ struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->dummy_th.source = sk->dummy_th.source;
newsk->dummy_th.dest = req->rmt_port;
-
- newtp->rcv_nxt = req->rcv_isn + 1;
+ newsk->sock_readers=0;
+
+ newtp->last_ack_sent = newtp->rcv_nxt = req->rcv_isn + 1;
newtp->rcv_wup = req->rcv_isn + 1;
newsk->copied_seq = req->rcv_isn + 1;
newsk->socket = NULL;
- newsk->daddr = af_req->rmt_addr;
- newsk->saddr = af_req->loc_addr;
- newsk->rcv_saddr = af_req->loc_addr;
-
- /*
- * options / mss / route_cache
- */
- newsk->opt = af_req->opt;
- rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr :
- newsk->daddr, 0);
+ newsk->daddr = req->af.v4_req.rmt_addr;
+ newsk->saddr = req->af.v4_req.loc_addr;
+ newsk->rcv_saddr = req->af.v4_req.loc_addr;
- newsk->ip_route_cache = rt;
-
- if(rt != NULL && (rt->rt_flags&RTF_WINDOW))
- newsk->window_clamp = rt->rt_window;
- else
- newsk->window_clamp = 0;
+ /* options / mss / route_cache */
+ newsk->opt = req->af.v4_req.opt;
+ if (ip_route_output(&rt,
+ newsk->opt && newsk->opt->srr ? newsk->opt->faddr : newsk->daddr,
+ newsk->saddr, newsk->ip_tos, NULL)) {
+ kfree(newsk);
+ return NULL;
+ }
- if (rt)
- snd_mss = rt->rt_mtu;
- else
- snd_mss = skb->dev->mtu;
-
+ newsk->dst_cache = &rt->u.dst;
+
+ newtp->window_clamp = rt->u.dst.window;
+ snd_mss = rt->u.dst.pmtu;
+
+ /* FIXME: is mtu really the same as snd_mss? */
newsk->mtu = snd_mss;
+ /* FIXME: where does mtu get used after this? */
/* sanity check */
if (newsk->mtu < 64)
- {
newsk->mtu = 64;
+
+ newtp->sack_ok = req->sack_ok;
+ newtp->tstamp_ok = req->tstamp_ok;
+ newtp->snd_wscale = req->snd_wscale;
+ newtp->ts_recent = req->ts_recent;
+ newtp->ts_recent_stamp = jiffies;
+ if (newtp->tstamp_ok) {
+ newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define constant! */
+ newsk->dummy_th.doff += 3;
+ } else {
+ newtp->tcp_header_len = sizeof(struct tcphdr);
}
snd_mss -= sizeof(struct iphdr) + sizeof(struct tcphdr);
-
if (sk->user_mss)
- {
snd_mss = min(snd_mss, sk->user_mss);
- }
-
- newsk->mss = min(req->mss, snd_mss);
-
- inet_put_sock(newsk->num, newsk);
- tcp_cache_zap();
+ /* Make sure our mtu is adjusted for headers. */
+ newsk->mss = min(req->mss, snd_mss) + sizeof(struct tcphdr) - newtp->tcp_header_len;
+ tcp_v4_hash(newsk);
+ add_to_prot_sklist(newsk);
return newsk;
}
struct sock *tcp_v4_check_req(struct sock *sk, struct sk_buff *skb)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- struct open_request *req;
-
+ struct open_request *req = tp->syn_wait_queue;
- /*
- * assumption: the socket is not in use.
+ /* assumption: the socket is not in use.
* as we checked the user count on tcp_rcv and we're
* running from a soft interrupt.
*/
-
- req = tp->syn_wait_queue;
-
-
- if (!req)
- {
+ if(!req)
return sk;
- }
-
- do {
- struct tcp_v4_open_req *af_req;
- af_req = (struct tcp_v4_open_req *) req;
-
- if (af_req->rmt_addr == skb->saddr &&
- af_req->loc_addr == skb->daddr &&
- req->rmt_port == skb->h.th->source)
- {
+ while(req) {
+ if (req->af.v4_req.rmt_addr == skb->nh.iph->saddr &&
+ req->af.v4_req.loc_addr == skb->nh.iph->daddr &&
+ req->rmt_port == skb->h.th->source) {
u32 flg;
-
- if (req->sk)
- {
- printk(KERN_DEBUG "BUG: syn_recv:"
- "socket exists\n");
+
+ if (req->sk) {
+ /* socket already created but not
+ * yet accepted()...
+ */
+ sk = req->sk;
break;
}
- /* match */
-
- /*
- * Check for syn retransmission
- */
+ /* Check for syn retransmission */
flg = *(((u32 *)skb->h.th) + 3);
flg &= __constant_htonl(0x002f0000);
-
if ((flg == __constant_htonl(0x00020000)) &&
- (!after(skb->seq, req->rcv_isn)))
- {
- /*
- * retransmited syn
+ (!after(skb->seq, req->rcv_isn))) {
+ /* retransmited syn
* FIXME: must send an ack
*/
return NULL;
}
- atomic_sub(skb->truesize, &sk->rmem_alloc);
sk = tp->af_specific->syn_recv_sock(sk, skb, req);
-
tcp_dec_slow_timer(TCP_SLT_SYNACK);
-
if (sk == NULL)
- {
return NULL;
- }
- atomic_add(skb->truesize, &sk->rmem_alloc);
req->expires = 0UL;
req->sk = sk;
- skb->sk = sk;
break;
}
-
req = req->dl_next;
- } while (req != tp->syn_wait_queue);
-
+ }
+ skb_orphan(skb);
+ skb_set_owner_r(skb, sk);
return sk;
}
-/*
- * From tcp_input.c
- */
-
-int tcp_v4_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo, struct inet_protocol * protocol)
+int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
{
- struct tcphdr *th;
- struct sock *sk;
+ skb_set_owner_r(skb, sk);
/*
- * "redo" is 1 if we have already seen this skb but couldn't
- * use it at that time (the socket was locked). In that case
- * we have already done a lot of the work (looked up the socket
- * etc).
+ * socket locking is here for SMP purposes as backlog rcv
+ * is currently called with bh processing disabled.
*/
+ lock_sock(sk);
- th = skb->h.th;
-
- sk = skb->sk;
-
- if (!redo)
+ if (sk->state == TCP_ESTABLISHED)
{
+ if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
+ goto reset;
+ goto ok;
+ }
- if (skb->pkt_type!=PACKET_HOST)
+ if (sk->state == TCP_LISTEN) {
+ struct sock *nsk;
+
+ /* Find possible connection requests. */
+ nsk = tcp_v4_check_req(sk, skb);
+ if (nsk == NULL)
goto discard_it;
- /*
- * Pull up the IP header.
- */
-
- skb_pull(skb, skb->h.raw-skb->data);
-
- /*
- * Try to use the device checksum if provided.
- */
-
- switch (skb->ip_summed)
- {
- case CHECKSUM_NONE:
- skb->csum = csum_partial((char *)th, len, 0);
- case CHECKSUM_HW:
- if (tcp_v4_check(th,len,saddr,daddr,skb->csum))
- goto discard_it;
- default:
- /* CHECKSUM_UNNECESSARY */
- }
+ release_sock(sk);
+ lock_sock(nsk);
+ sk = nsk;
+ }
- sk = get_tcp_sock(saddr, th->source, daddr, th->dest,
- dev->pa_addr, skb->redirport);
+ if (tcp_rcv_state_process(sk, skb, skb->h.th, NULL, skb->len) == 0)
+ goto ok;
- if (!sk)
- goto no_tcp_socket;
+reset:
+ tcp_v4_send_reset(skb);
- skb->sk = sk;
- skb->seq = ntohl(th->seq);
- skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
- skb->ack_seq = ntohl(th->ack_seq);
-
- skb->acked = 0;
- skb->used = 0;
- skb->free = 1;
- skb->saddr = saddr;
- skb->daddr = daddr;
- }
+discard_it:
+ /* Discard frame. */
+ kfree_skb(skb, FREE_READ);
- /*
- * We may need to add it to the backlog here.
- */
+ok:
+ release_sock(sk);
+ return 0;
+}
- if (sk->users)
- {
- __skb_queue_tail(&sk->back_log, skb);
- return(0);
- }
+/*
+ * From tcp_input.c
+ */
- if (!sk->prot)
- {
- printk(KERN_DEBUG "tcp_rcv: sk->prot == NULL\n");
- return(0);
- }
+int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+{
+ struct tcphdr *th;
+ struct sock *sk;
+ u32 saddr = skb->nh.iph->saddr;
+ u32 daddr = skb->nh.iph->daddr;
- atomic_add(skb->truesize, &sk->rmem_alloc);
+ th = skb->h.th;
- if (sk->state == TCP_ESTABLISHED)
- {
- tcp_rcv_established(sk, skb, th, len);
- return 0;
- }
+ if (skb->pkt_type!=PACKET_HOST)
+ goto discard_it;
+
+ /* Pull up the IP header. */
+ skb_pull(skb, skb->h.raw-skb->data);
+
+ /* Try to use the device checksum if provided. */
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)th, len, 0);
+ case CHECKSUM_HW:
+ if (tcp_v4_check(th,len,saddr,daddr,skb->csum)) {
+ struct iphdr * iph = skb->nh.iph;
+ printk(KERN_DEBUG "TCPv4 bad checksum from %08x:%04x to %08x:%04x, ack = %u, seq = %u, len=%d/%d/%d\n",
+ saddr, ntohs(th->source), daddr,
+ ntohl(th->ack_seq), ntohl(th->seq),
+ ntohs(th->dest), len, skb->len, ntohs(iph->tot_len));
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ };
- if (sk->state == TCP_LISTEN)
- {
- /*
- * find possible connection requests
- */
- sk = tcp_v4_check_req(sk, skb);
+ tcp_statistics.TcpInSegs++;
- if (sk == NULL)
- {
- goto discard_it;
- }
- }
-
- if (tcp_rcv_state_process(sk, skb, th, opt, len) == 0)
- return 0;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (IPCB(skb)->redirport)
+ sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+ skb->dev->pa_addr, IPCB(skb)->redirport);
+ else
+#endif
+ sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
+ if (!sk)
+ goto no_tcp_socket;
+ if(!ipsec_sk_policy(sk,skb))
+ goto discard_it;
-no_tcp_socket:
+ skb->seq = ntohl(th->seq);
+ skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
+ skb->ack_seq = ntohl(th->ack_seq);
- /*
- * No such TCB. If th->rst is 0 send a reset
- * (checked in tcp_send_reset)
- */
+ skb->used = 0;
- tcp_v4_send_reset(daddr, saddr, th, &tcp_prot, opt, dev,
- skb->ip_hdr->tos, 255);
+ if (!sk->sock_readers)
+ return tcp_v4_do_rcv(sk, skb);
-discard_it:
+ __skb_queue_tail(&sk->back_log, skb);
+ return 0;
- /*
- * Discard frame
- */
+no_tcp_socket:
+ tcp_v4_send_reset(skb);
+discard_it:
+ /* Discard frame. */
kfree_skb(skb, FREE_READ);
- return 0;
+ return 0;
+}
+
+int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
+{
+ return ip_build_header(skb, sk);
}
int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
-{
- struct options * opt = (struct options*)skb->proto_priv;
- struct device * dev;
+{
struct rtable *rt;
struct iphdr *iph;
struct tcphdr *th;
int size;
- /*
- * Discard the surplus MAC header
- */
-
- skb_pull(skb, ((unsigned char *)skb->ip_hdr)-skb->data);
+ /* Check route */
- iph = skb->ip_hdr;
- th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));
- size = skb->tail - (unsigned char *) th;
-
- dev = skb->dev;
-
- rt = ip_check_route(&sk->ip_route_cache,
- opt->srr?opt->faddr:iph->daddr,
- skb->localroute);
-
-
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
- if (rt && ntohs(iph->tot_len) > rt->rt_mtu)
- iph->frag_off &= ~htons(IP_DF);
-#endif
-
- if (rt==NULL) /* Deep poo */
- {
- if(skb->sk)
- {
- skb->sk->err_soft=ENETUNREACH;
- skb->sk->error_report(skb->sk);
+ rt = (struct rtable*)skb->dst;
+ if (rt->u.dst.obsolete) {
+ int err;
+ err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos, rt->key.dst_dev);
+ if (err) {
+ sk->err_soft=-err;
+ sk->error_report(skb->sk);
+ return -1;
}
- return -1;
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
}
+ /* Discard the surplus MAC header. */
+ skb_pull(skb, skb->nh.raw-skb->data);
- dev=rt->rt_dev;
- skb->raddr=rt->rt_gateway;
- skb->dev=dev;
- skb->arp=1;
-
- if (rt->rt_hh)
- {
- memcpy(skb_push(skb, dev->hard_header_len),
- rt->rt_hh->hh_data, dev->hard_header_len);
-
- if (!rt->rt_hh->hh_uptodate)
- {
- skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
- printk("tcp_do_rebuild_header: "
- "hh miss %08x via %08x\n",
- iph->daddr, rt->rt_gateway);
-#endif
- }
- }
- else if (dev->hard_header)
- {
- if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL,
- skb->len)<0)
- skb->arp=0;
- }
-
- return 0;
-}
+ iph = skb->nh.iph;
+ th = skb->h.th;
+ size = skb->tail - skb->h.raw;
-int tcp_v4_backlog_rcv(struct sock *sk, struct sk_buff *skb)
-{
- return tcp_v4_rcv(skb, skb->dev, (struct options *) skb->proto_priv,
- skb->daddr, skb->len, skb->saddr, 1,
- (struct inet_protocol *) sk->pair);
+ return 0;
}
static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
{
- struct sock *sk;
-
- sk = get_tcp_sock(skb->saddr, th->source, skb->daddr, th->dest, 0, 0);
-
- return sk;
-}
-
-int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
-{
- struct device *dev = NULL;
- int tmp;
-
- tmp = ip_build_header(skb, sk->saddr, sk->daddr, &dev,
- IPPROTO_TCP, sk->opt, skb->truesize,
- sk->ip_tos, sk->ip_ttl,
- &sk->ip_route_cache);
- skb->dev = dev;
-
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
- if (tmp > 0)
- {
- skb->ip_hdr->frag_off |= htons(IP_DF);
- }
-#endif
-
- return tmp;
+ return tcp_v4_lookup(skb->nh.iph->saddr, th->source,
+ skb->nh.iph->daddr, th->dest);
}
-
static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
{
struct sockaddr_in *sin = (struct sockaddr_in *) uaddr;
-
+
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = sk->daddr;
sin->sin_port = sk->dummy_th.dest;
-
}
struct tcp_func ipv4_specific = {
@@ -1258,6 +1328,7 @@ struct tcp_func ipv4_specific = {
ip_setsockopt,
ip_getsockopt,
v4_addr2sockaddr,
+ tcp_v4_send_reset,
sizeof(struct sockaddr_in)
};
@@ -1276,35 +1347,38 @@ static int tcp_v4_init_sock(struct sock *sk)
tp->iat = (HZ/5) << 3;
tp->rcv_wnd = 8192;
+ tp->tstamp_ok = 0;
+ tp->sack_ok = 0;
+ tp->in_mss = 0;
+ tp->snd_wscale = 0;
+ tp->sacks = 0;
+ tp->saw_tstamp = 0;
/*
* See draft-stevens-tcpca-spec-01 for discussion of the
* initialization of these values.
*/
- sk->cong_window = 1;
- sk->ssthresh = 0x7fffffff;
-
+ tp->snd_cwnd = 1;
+ tp->snd_ssthresh = 0x7fffffff; /* Infinity */
+
sk->priority = 1;
sk->state = TCP_CLOSE;
- /* this is how many unacked bytes we will accept for this socket. */
+ /* This is how many unacked bytes we will accept for this socket. */
sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
sk->max_ack_backlog = SOMAXCONN;
-
+
sk->mtu = 576;
sk->mss = 536;
- sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-
-
- /*
- * Speed up by setting some standard state for the dummy_th
- * if TCP uses it (maybe move to tcp_init later)
- */
-
- sk->dummy_th.ack=1;
+ /* Speed up by setting some standard state for the dummy_th. */
+ sk->dummy_th.ack=1;
sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
+ /* Init SYN queue. */
+ tp->syn_wait_queue = NULL;
+ tp->syn_wait_last = &tp->syn_wait_queue;
+
sk->tp_pinfo.af_tcp.af_specific = &ipv4_specific;
return 0;
@@ -1317,60 +1391,47 @@ static int tcp_v4_destroy_sock(struct sock *sk)
tcp_clear_xmit_timers(sk);
if (sk->keepopen)
- {
tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
- }
-
- /*
- * Cleanup up the write buffer.
- */
-
- while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
- IS_SKB(skb);
- skb->free = 1;
- kfree_skb(skb, FREE_WRITE);
- }
- /*
- * Cleans up our, hopefuly empty, out_of_order_queue
- */
+ /* Cleanup up the write buffer. */
+ while((skb = skb_dequeue(&sk->write_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
- while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) {
- IS_SKB(skb);
+ /* Cleans up our, hopefuly empty, out_of_order_queue. */
+ while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
return 0;
}
struct proto tcp_prot = {
- tcp_close,
- tcp_v4_connect,
- tcp_accept,
- NULL,
- tcp_write_wakeup,
- tcp_read_wakeup,
- tcp_select,
- tcp_ioctl,
- tcp_v4_init_sock,
- tcp_v4_destroy_sock,
- tcp_shutdown,
- tcp_setsockopt,
- tcp_getsockopt,
- tcp_v4_sendmsg,
- tcp_recvmsg,
- NULL, /* No special bind() */
- tcp_v4_backlog_rcv,
- 128,
- 0,
- "TCP",
- 0, 0,
- NULL
+ (struct sock *)&tcp_prot, /* sklist_next */
+ (struct sock *)&tcp_prot, /* sklist_prev */
+ tcp_close, /* close */
+ tcp_v4_connect, /* connect */
+ tcp_accept, /* accept */
+ NULL, /* retransmit */
+ tcp_write_wakeup, /* write_wakeup */
+ tcp_read_wakeup, /* read_wakeup */
+ tcp_poll, /* poll */
+ tcp_ioctl, /* ioctl */
+ tcp_v4_init_sock, /* init */
+ tcp_v4_destroy_sock, /* destroy */
+ tcp_shutdown, /* shutdown */
+ tcp_setsockopt, /* setsockopt */
+ tcp_getsockopt, /* getsockopt */
+ tcp_v4_sendmsg, /* sendmsg */
+ tcp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ tcp_v4_do_rcv, /* backlog_rcv */
+ tcp_v4_hash, /* hash */
+ tcp_v4_unhash, /* unhash */
+ tcp_v4_rehash, /* rehash */
+ tcp_good_socknum, /* good_socknum */
+ tcp_v4_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "TCP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -c -o tcp_ipv4.o tcp_ipv4.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 016cee54c..7f157abe2 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -5,7 +5,7 @@
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: @(#)tcp_input.c 1.0.16 05/25/93
+ * Version: $Id: tcp_output.c,v 1.42 1997/04/22 01:06:33 davem Exp $
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -27,19 +27,25 @@
* : AF independence
*
* Linus Torvalds : send_delayed_ack
+ * David S. Miller : Charge memory using the right skb
+ * during syn/ack processing.
*
*/
#include <net/tcp.h>
-/*
- * Get rid of any delayed acks, we sent one already..
- */
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_tsack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
+
+/* Get rid of any delayed acks, we sent one already.. */
static __inline__ void clear_delayed_acks(struct sock * sk)
{
- sk->delayed_acks = 0;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
+ tp->delayed_acks = 0;
sk->ack_backlog = 0;
- sk->bytes_rcv = 0;
tcp_clear_xmit_timer(sk, TIME_DACK);
}
@@ -48,12 +54,8 @@ static __inline__ void update_send_head(struct sock *sk)
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
tp->send_head = tp->send_head->next;
-
if (tp->send_head == (struct sk_buff *) &sk->write_queue)
- {
tp->send_head = NULL;
- }
-
}
static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
@@ -62,8 +64,7 @@ static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
int nagle_check = 1;
int len;
- /*
- * RFC 1122 - section 4.2.3.4
+ /* RFC 1122 - section 4.2.3.4
*
* We must queue if
*
@@ -74,17 +75,41 @@ static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
* c) We are retransmiting [Nagle]
* d) We have too many packets 'in flight'
*/
-
len = skb->end_seq - skb->seq;
-
- if (!sk->nonagle && len < (sk->mss >> 1) && sk->packets_out)
- {
+ if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out)
nagle_check = 0;
- }
- return (nagle_check && sk->packets_out < sk->cong_window &&
+ return (nagle_check && tp->packets_out < tp->snd_cwnd &&
!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
- sk->retransmits == 0);
+ tp->retransmits == 0);
+}
+
+static __inline__ void tcp_build_options(__u32 *ptr, struct tcp_opt *tp)
+{
+ /* FIXME: We will still need to do SACK here. */
+ if (tp->tstamp_ok) {
+ *ptr++ = ntohl((TCPOPT_NOP << 24)
+ | (TCPOPT_NOP << 16)
+ | (TCPOPT_TIMESTAMP << 8)
+ | TCPOLEN_TIMESTAMP);
+ /* WARNING: If HZ is ever larger than 1000 on some system,
+ * then we will be violating RFC1323 here because our timestamps
+ * will be moving too fast.
+ * FIXME: code TCP so it uses at most ~ 1000 ticks a second?
+ * (I notice alpha is 1024 ticks now). -- erics
+ */
+ *ptr++ = htonl(jiffies);
+ *ptr = htonl(tp->ts_recent);
+ }
+}
+
+static __inline__ void tcp_update_options(__u32 *ptr, struct tcp_opt *tp)
+{
+ /* FIXME: We will still need to do SACK here. */
+ if (tp->tstamp_ok) {
+ *++ptr = htonl(jiffies);
+ *++ptr = htonl(tp->ts_recent);
+ }
}
/*
@@ -98,102 +123,76 @@ int tcp_send_skb(struct sock *sk, struct sk_buff *skb)
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
int size;
- /*
- * length of packet (not counting length of pre-tcp headers)
- */
-
+ /* Length of packet (not counting length of pre-tcp headers). */
size = skb->len - ((unsigned char *) th - skb->data);
- /*
- * Sanity check it..
- */
-
- if (size < sizeof(struct tcphdr) || size > skb->len)
- {
- printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %u)\n",
- skb, skb->data, th, skb->len);
+ /* Sanity check it.. */
+ if (size < sizeof(struct tcphdr) || size > skb->len) {
+ printk(KERN_DEBUG "tcp_send_skb: bad skb "
+ "(skb = %p, data = %p, th = %p, len = %u)\n",
+ skb, skb->data, th, skb->len);
kfree_skb(skb, FREE_WRITE);
return 0;
}
- /*
- * If we have queued a header size packet.. (these crash a few
- * tcp stacks if ack is not set)
+ /* If we have queued a header size packet.. (these crash a few
+ * tcp stacks if ack is not set)
+ * FIXME: What is the equivalent below when we have options?
*/
-
- if (size == sizeof(struct tcphdr))
- {
- /*
- * If it's got a syn or fin discard
- */
- if(!th->syn && !th->fin)
- {
- printk("tcp_send_skb: attempt to queue a bogon.\n");
+ if (size == sizeof(struct tcphdr)) {
+ /* If it's got a syn or fin discard. */
+ if(!th->syn && !th->fin) {
+ printk(KERN_DEBUG "tcp_send_skb: attempt to queue a bogon.\n");
kfree_skb(skb,FREE_WRITE);
return 0;
}
}
-
- /*
- * Actual processing.
- */
-
- tcp_statistics.TcpOutSegs++;
+ /* Actual processing. */
skb->seq = ntohl(th->seq);
skb->end_seq = skb->seq + size - 4*th->doff;
-
- if (tp->send_head || !tcp_snd_test(sk, skb))
- {
- /*
- * Remember where we must start sending
- */
-
- if (tp->send_head == NULL)
- tp->send_head = skb;
-
- skb_queue_tail(&sk->write_queue, skb);
-
- if (sk->packets_out == 0 && !tp->pending)
- {
- tp->pending = TIME_PROBE0;
- tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
- }
+ skb_queue_tail(&sk->write_queue, skb);
- }
- else
- {
+ if (tp->send_head == NULL && tcp_snd_test(sk, skb)) {
struct sk_buff * buff;
- /*
- * This is going straight out
- */
-
- skb_queue_tail(&sk->write_queue, skb);
-
- clear_delayed_acks(sk);
-
- th->ack_seq = htonl(tp->rcv_nxt);
+ /* This is going straight out. */
+ tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
th->window = htons(tcp_select_window(sk));
+ tcp_update_options((__u32 *)(th+1),tp);
tp->af_specific->send_check(sk, th, size, skb);
- tp->snd_nxt = skb->end_seq;
+ buff = skb_clone(skb, GFP_KERNEL);
+ if (buff == NULL)
+ goto queue;
- atomic_inc(&sk->packets_out);
+ clear_delayed_acks(sk);
+ skb_set_owner_w(buff, sk);
+
+ tp->snd_nxt = skb->end_seq;
+ tp->packets_out++;
skb->when = jiffies;
-
- buff = skb_clone(skb, GFP_ATOMIC);
- atomic_add(buff->truesize, &sk->wmem_alloc);
- tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+ tcp_statistics.TcpOutSegs++;
+ tp->af_specific->queue_xmit(buff);
if (!tcp_timer_is_set(sk, TIME_RETRANS))
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+
+ return 0;
}
+queue:
+ /* Remember where we must start sending. */
+ if (tp->send_head == NULL)
+ tp->send_head = skb;
+ if (tp->packets_out == 0 && !tp->pending) {
+ tp->pending = TIME_PROBE0;
+ tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
+ }
return 0;
}
@@ -214,89 +213,61 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len)
th = skb->h.th;
- /* size of new segment */
- nsize = skb->tail - ((unsigned char *) (th + 1)) - len;
-
- if (nsize <= 0)
- {
+ /* Size of new segment. */
+ nsize = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len) - len;
+ if (nsize <= 0) {
printk(KERN_DEBUG "tcp_fragment: bug size <= 0\n");
return -1;
}
- /*
- * Get a new skb... force flag on
- */
+ /* Get a new skb... force flag on. */
buff = sock_wmalloc(sk, nsize + 128 + sk->prot->max_header + 15, 1,
GFP_ATOMIC);
-
if (buff == NULL)
return -1;
- buff->sk = sk;
- buff->localroute = sk->localroute;
-
- /*
- * Put headers on the new packet
- */
-
+ /* Put headers on the new packet. */
tmp = tp->af_specific->build_net_header(sk, buff);
-
- if (tmp < 0)
- {
- sock_wfree(sk, buff);
+ if (tmp < 0) {
+ kfree_skb(buff, FREE_WRITE);
return -1;
}
- /*
- * Move the TCP header over
- */
-
- nth = (struct tcphdr *) skb_put(buff, sizeof(*th));
-
+ /* Move the TCP header over. */
+ nth = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
buff->h.th = nth;
+ memcpy(nth, th, tp->tcp_header_len);
+
+ /* FIXME: Make sure this gets tcp options right. */
- memcpy(nth, th, sizeof(*th));
-
- /*
- * Correct the new header
- */
-
+ /* Correct the new header. */
buff->seq = skb->seq + len;
buff->end_seq = skb->end_seq;
nth->seq = htonl(buff->seq);
nth->check = 0;
- nth->doff = 5;
+ nth->doff = th->doff;
/* urg data is always an headache */
- if (th->urg)
- {
- if (th->urg_ptr > len)
- {
+ if (th->urg) {
+ if (th->urg_ptr > len) {
th->urg = 0;
nth->urg_ptr -= len;
- }
- else
- {
+ } else {
nth->urg = 0;
}
}
- /*
- * Copy TCP options and data start to our new buffer
- */
-
- buff->csum = csum_partial_copy(((u8 *)(th + 1)) + len,
+ /* Copy data tail to our new buffer. */
+ buff->csum = csum_partial_copy(((u8 *)(th)+tp->tcp_header_len) + len,
skb_put(buff, nsize),
nsize, 0);
-
skb->end_seq -= nsize;
-
skb_trim(skb, skb->len - nsize);
- /* remember to checksum this packet afterwards */
+ /* Remember to checksum this packet afterwards. */
th->check = 0;
- skb->csum = csum_partial((u8*) (th + 1), skb->tail - ((u8 *) (th + 1)),
+ skb->csum = csum_partial((u8*)(th) + tp->tcp_header_len, skb->tail - ((u8 *) (th)+tp->tcp_header_len),
0);
skb_append(skb, buff);
@@ -306,20 +277,16 @@ static int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len)
static void tcp_wrxmit_prob(struct sock *sk, struct sk_buff *skb)
{
- /*
- * This is acked data. We can discard it. This
- * cannot currently occur.
- */
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- sk->retransmits = 0;
+ /* This is acked data. We can discard it. This cannot currently occur. */
+ tp->retransmits = 0;
printk(KERN_DEBUG "tcp_write_xmit: bug skb in write queue\n");
update_send_head(sk);
skb_unlink(skb);
- skb->sk = NULL;
- skb->free = 1;
kfree_skb(skb, FREE_WRITE);
if (!sk->dead)
@@ -330,20 +297,16 @@ static int tcp_wrxmit_frag(struct sock *sk, struct sk_buff *skb, int size)
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- printk(KERN_DEBUG "tcp_write_xmit: frag needed size=%d mss=%d\n",
- size, sk->mss);
-
- if (tcp_fragment(sk, skb, sk->mss))
- {
+ SOCK_DEBUG(sk, "tcp_write_xmit: frag needed size=%d mss=%d\n",
+ size, sk->mss);
+
+ if (tcp_fragment(sk, skb, sk->mss)) {
/* !tcp_frament Failed! */
tp->send_head = skb;
- atomic_dec(&sk->packets_out);
+ tp->packets_out--;
return -1;
- }
- else
- {
- /*
- * If tcp_fragment succeded then
+ } else {
+ /* If tcp_fragment succeded then
* the send head is the resulting
* fragment
*/
@@ -361,79 +324,52 @@ static int tcp_wrxmit_frag(struct sock *sk, struct sk_buff *skb, int size)
void tcp_write_xmit(struct sock *sk)
{
struct sk_buff *skb;
- struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
u16 rcv_wnd;
int sent_pkts = 0;
- /*
- * The bytes will have to remain here. In time closedown will
- * empty the write queue and all will be happy
+ /* The bytes will have to remain here. In time closedown will
+ * empty the write queue and all will be happy.
*/
-
if(sk->zapped)
return;
- /*
- * Anything on the transmit queue that fits the window can
+ /* Anything on the transmit queue that fits the window can
* be added providing we are:
*
* a) following SWS avoidance [and Nagle algorithm]
* b) not exceeding our congestion window.
* c) not retransmiting [Nagle]
*/
-
- start_bh_atomic();
-
rcv_wnd = htons(tcp_select_window(sk));
-
- while((skb = tp->send_head) && tcp_snd_test(sk, skb))
- {
+ while((skb = tp->send_head) && tcp_snd_test(sk, skb)) {
struct tcphdr *th;
struct sk_buff *buff;
int size;
- IS_SKB(skb);
-
- /*
- * See if we really need to send the packet.
- */
-
- if (!after(skb->end_seq, tp->snd_una))
- {
+ /* See if we really need to send the packet. (debugging code) */
+ if (!after(skb->end_seq, tp->snd_una)) {
tcp_wrxmit_prob(sk, skb);
continue;
- }
-
+ }
- /*
- * Advance the send_head
- * This one is going out.
+ /* Put in the ack seq and window at this point rather
+ * than earlier, in order to keep them monotonic.
+ * We really want to avoid taking back window allocations.
+ * That's legal, but RFC1122 says it's frowned on.
+ * Ack and window will in general have changed since
+ * this packet was put on the write queue.
*/
-
- update_send_head(sk);
-
- atomic_inc(&sk->packets_out);
-
-
-/*
- * put in the ack seq and window at this point rather than earlier,
- * in order to keep them monotonic. We really want to avoid taking
- * back window allocations. That's legal, but RFC1122 says it's frowned on.
- * Ack and window will in general have changed since this packet was put
- * on the write queue.
- */
-
th = skb->h.th;
size = skb->len - (((unsigned char *) th) - skb->data);
-
- if (size - (th->doff << 2) > sk->mss)
- {
+ if (size - (th->doff << 2) > sk->mss) {
if (tcp_wrxmit_frag(sk, skb, size))
break;
}
- th->ack_seq = htonl(tp->rcv_nxt);
+ tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
th->window = rcv_wnd;
+ tcp_update_options((__u32 *)(th+1),tp);
tp->af_specific->send_check(sk, th, size, skb);
@@ -441,27 +377,29 @@ void tcp_write_xmit(struct sock *sk)
if (before(skb->end_seq, tp->snd_nxt))
printk(KERN_DEBUG "tcp_write_xmit:"
" sending already sent seq\n");
-#endif
+#endif
- tp->snd_nxt = skb->end_seq;
+ buff = skb_clone(skb, GFP_ATOMIC);
+ if (buff == NULL)
+ break;
- skb->when = jiffies;
+ /* Advance the send_head. This one is going out. */
+ update_send_head(sk);
clear_delayed_acks(sk);
- buff = skb_clone(skb, GFP_ATOMIC);
- atomic_add(buff->truesize, &sk->wmem_alloc);
+ tp->packets_out++;
+ skb_set_owner_w(buff, sk);
- sent_pkts = 1;
- tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+ tp->snd_nxt = skb->end_seq;
+
+ skb->when = jiffies;
+ sent_pkts = 1;
+ tp->af_specific->queue_xmit(buff);
}
-
+
if (sent_pkts && !tcp_timer_is_set(sk, TIME_RETRANS))
- {
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
- }
-
- end_bh_atomic();
}
@@ -474,33 +412,25 @@ void tcp_write_xmit(struct sock *sk)
* 2. We limit memory per socket
*/
-
unsigned short tcp_select_window(struct sock *sk)
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
int mss = sk->mss;
long free_space = sock_rspace(sk);
- long window;
- long cur_win;
- long usable;
+ long window, cur_win, usable;
-
- if (sk->window_clamp)
- {
- free_space = min(sk->window_clamp, free_space);
- mss = min(sk->window_clamp, mss);
+ if (tp->window_clamp) {
+ free_space = min(tp->window_clamp, free_space);
+ mss = min(tp->window_clamp, mss);
}
- /*
- * compute the actual window i.e.
+ /* compute the actual window i.e.
* old_window - received_bytes_on_that_win
*/
-
cur_win = tp->rcv_wup - (tp->rcv_nxt - tp->rcv_wnd);
window = tp->rcv_wnd;
- if ( cur_win < 0 )
- {
+ if (cur_win < 0) {
cur_win = 0;
printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n",
tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup);
@@ -516,49 +446,33 @@ unsigned short tcp_select_window(struct sock *sk)
* it MSS bytes
*/
- /*
- * It would be a good idea if it didn't break header prediction.
+ /* It would be a good idea if it didn't break header prediction.
* and BSD made the header predition standard...
* It expects the same value in the header i.e. th->window to be
* constant
*/
-
usable = free_space - cur_win;
if (usable < 0)
- {
usable = 0;
- }
- if ( window < usable )
- {
- /*
- * Window is not blocking the sender
+ if (window < usable) {
+ /* Window is not blocking the sender
* and we have enought free space for it
*/
-
if (cur_win > (sk->mss << 1))
goto out;
}
-
- if (window >= usable)
- {
- /*
- * We are offering too much, cut it down...
+ if (window >= usable) {
+ /* We are offering too much, cut it down...
* but don't shrink the window
*/
-
window = max(usable, cur_win);
- }
- else
- {
+ } else {
if ((usable - window) >= mss)
- {
window += mss;
- }
}
-
- out:
+out:
tp->rcv_wnd = window;
tp->rcv_wup = tp->rcv_nxt;
return window;
@@ -566,6 +480,7 @@ unsigned short tcp_select_window(struct sock *sk)
static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
struct tcphdr *th1, *th2;
int size1, size2, avail;
struct sk_buff *buff = skb->next;
@@ -577,55 +492,35 @@ static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb)
avail = skb_tailroom(skb);
- /*
- * size of tcp payload
- */
-
- size1 = skb->tail - (u8 *) (th1 + 1);
+ /* Size of TCP payload. */
+ size1 = skb->tail - ((u8 *) (th1)+(th1->doff<<2));
th2 = buff->h.th;
-
- size2 = buff->tail - (u8 *) (th2 + 1);
+ size2 = buff->tail - ((u8 *) (th2)+(th2->doff<<2));
if (size2 > avail || size1 + size2 > sk->mss )
return -1;
- /*
- * ok. we will be able to collapse the packet
- */
-
+ /* Ok. We will be able to collapse the packet. */
skb_unlink(buff);
-
memcpy(skb_put(skb, size2), ((char *) th2) + (th2->doff << 2), size2);
- /*
- * update sizes on original skb. both TCP and IP
- */
-
+ /* Update sizes on original skb, both TCP and IP. */
skb->end_seq += size2;
-
- if (th2->urg)
- {
+ if (th2->urg) {
th1->urg = 1;
th1->urg_ptr = th2->urg_ptr + size1;
}
- /*
- * ... and off you go.
- */
-
- buff->free = 1;
+ /* ... and off you go. */
kfree_skb(buff, FREE_WRITE);
- atomic_dec(&sk->packets_out);
+ tp->packets_out--;
- /*
- * Header checksum will be set by the retransmit procedure
- * after calling rebuild header
+ /* Header checksum will be set by the retransmit procedure
+ * after calling rebuild header.
*/
-
th1->check = 0;
- skb->csum = csum_partial((u8*) (th1+1), size1 + size2, 0);
-
+ skb->csum = csum_partial((u8*)(th1)+(th1->doff<<2), size1 + size2, 0);
return 0;
}
@@ -643,25 +538,19 @@ void tcp_do_retransmit(struct sock *sk, int all)
int ct=0;
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- start_bh_atomic();
-
if (tp->retrans_head == NULL)
tp->retrans_head = skb_peek(&sk->write_queue);
if (tp->retrans_head == tp->send_head)
tp->retrans_head = NULL;
- while ((skb = tp->retrans_head) != NULL)
- {
+ while ((skb = tp->retrans_head) != NULL) {
struct sk_buff *buff;
struct tcphdr *th;
- int tcp_size;
+ int tcp_size;
int size;
- IS_SKB(skb);
-
- /*
- * In general it's OK just to use the old packet. However we
+ /* In general it's OK just to use the old packet. However we
* need to use the current ack and window fields. Urg and
* urg_ptr could possibly stand to be updated as well, but we
* don't keep the necessary data. That shouldn't be a problem,
@@ -671,98 +560,69 @@ void tcp_do_retransmit(struct sock *sk, int all)
th = skb->h.th;
- tcp_size = skb->tail - ((unsigned char *) (th + 1));
+ tcp_size = skb->tail - ((unsigned char *)(th)+tp->tcp_header_len);
- if (tcp_size > sk->mss)
- {
- if (tcp_fragment(sk, skb, sk->mss))
- {
+ if (tcp_size > sk->mss) {
+ if (tcp_fragment(sk, skb, sk->mss)) {
printk(KERN_DEBUG "tcp_fragment failed\n");
return;
}
- atomic_inc(&sk->packets_out);
+ tp->packets_out++;
}
if (!th->syn &&
tcp_size < (sk->mss >> 1) &&
skb->next != tp->send_head &&
skb->next != (struct sk_buff *)&sk->write_queue)
- {
tcp_retrans_try_collapse(sk, skb);
- }
- if (tp->af_specific->rebuild_header(sk, skb))
- {
+ if (tp->af_specific->rebuild_header(sk, skb)) {
#ifdef TCP_DEBUG
printk(KERN_DEBUG "tcp_do_rebuild_header failed\n");
#endif
break;
}
- if (sk->debug)
- printk("retransmit sending\n");
-
- /*
- * update ack and window
- */
+ SOCK_DEBUG(sk, "retransmit sending\n");
- th->ack_seq = htonl(tp->rcv_nxt);
+ /* Update ack and window. */
+ tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
th->window = ntohs(tcp_select_window(sk));
+ tcp_update_options((__u32 *)(th+1),tp);
size = skb->tail - (unsigned char *) th;
tp->af_specific->send_check(sk, th, size, skb);
-
+
skb->when = jiffies;
+
buff = skb_clone(skb, GFP_ATOMIC);
- atomic_add(buff->truesize, &sk->wmem_alloc);
-
+ if (buff == NULL)
+ break;
+
+ skb_set_owner_w(buff, sk);
+
clear_delayed_acks(sk);
+ tp->af_specific->queue_xmit(buff);
- tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
-
- /*
- * Count retransmissions
- */
-
+ /* Count retransmissions. */
ct++;
- sk->prot->retransmits ++;
+ sk->prot->retransmits++;
tcp_statistics.TcpRetransSegs++;
- /*
- * Record the high sequence number to help avoid doing
- * to much fast retransmission.
- */
-
- if (sk->retransmits)
- tp->high_seq = tp->snd_nxt;
-
- /*
- * Only one retransmit requested.
- */
-
+ /* Only one retransmit requested. */
if (!all)
break;
- /*
- * This should cut it off before we send too many packets.
- */
-
- if (ct >= sk->cong_window)
+ /* This should cut it off before we send too many packets. */
+ if (ct >= tp->snd_cwnd)
break;
- /*
- * Advance the pointer
- */
-
+ /* Advance the pointer. */
tp->retrans_head = skb->next;
if ((tp->retrans_head == tp->send_head) ||
(tp->retrans_head == (struct sk_buff *) &sk->write_queue))
- {
tp->retrans_head = NULL;
- }
}
-
- end_bh_atomic();
}
/*
@@ -777,56 +637,40 @@ void tcp_send_fin(struct sock *sk)
struct sk_buff *buff;
int tmp;
-
- buff = sock_wmalloc(sk, MAX_RESET_SIZE, 1, GFP_KERNEL);
-
- if (buff == NULL)
- {
- /* This is a disaster if it occurs */
- printk("tcp_send_fin: Impossible malloc failure");
+ buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_KERNEL);
+ if (buff == NULL) {
+ /* FIXME: This is a disaster if it occurs. */
+ printk(KERN_INFO "tcp_send_fin: Impossible malloc failure");
return;
}
- /*
- * Administrivia
- */
-
- buff->sk = sk;
- buff->localroute = sk->localroute;
+ /* Administrivia. */
buff->csum = 0;
- /*
- * Put in the IP header and routing stuff.
- */
-
+ /* Put in the IP header and routing stuff. */
tmp = tp->af_specific->build_net_header(sk, buff);
-
- if (tmp < 0)
- {
+ if (tmp < 0) {
int t;
- /*
- * Finish anyway, treat this as a send that got lost.
- * (Not good).
+
+ /* FIXME: We must not throw this out. Eventually we must
+ * put a FIN into the queue, otherwise it never gets queued.
*/
-
- buff->free = 1;
- sock_wfree(sk,buff);
+ kfree_skb(buff, FREE_WRITE);
sk->write_seq++;
- t=del_timer(&sk->timer);
- if(t)
+ t = del_timer(&sk->timer);
+ if (t)
add_timer(&sk->timer);
else
tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
return;
}
- /*
- * We ought to check if the end of the queue is a buffer and
- * if so simply add the fin to that buffer, not send it ahead.
+ /* We ought to check if the end of the queue is a buffer and
+ * if so simply add the fin to that buffer, not send it ahead.
*/
-
- t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
+ t1 =(struct tcphdr *)skb_put(buff,tp->tcp_header_len);
buff->h.th = t1;
+ tcp_build_options((__u32 *)(t1+1),tp);
memcpy(t1, th, sizeof(*t1));
buff->seq = sk->write_seq;
@@ -837,26 +681,22 @@ void tcp_send_fin(struct sock *sk)
t1->window = htons(tcp_select_window(sk));
t1->fin = 1;
- tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
+ tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff);
- /*
- * The fin can only be transmited after the data.
- */
-
+ /* The fin can only be transmited after the data. */
skb_queue_tail(&sk->write_queue, buff);
-
- if (tp->send_head == NULL)
- {
+ if (tp->send_head == NULL) {
struct sk_buff *skb1;
- atomic_inc(&sk->packets_out);
+ tp->packets_out++;
tp->snd_nxt = sk->write_seq;
buff->when = jiffies;
skb1 = skb_clone(buff, GFP_KERNEL);
- atomic_add(skb1->truesize, &sk->wmem_alloc);
-
- tp->af_specific->queue_xmit(sk, skb1->dev, skb1, 1);
+ if (skb1) {
+ skb_set_owner_w(skb1, sk);
+ tp->af_specific->queue_xmit(skb1);
+ }
if (!tcp_timer_is_set(sk, TIME_RETRANS))
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -869,24 +709,14 @@ int tcp_send_synack(struct sock *sk)
struct sk_buff * skb;
struct sk_buff * buff;
struct tcphdr *th;
- unsigned char *ptr;
int tmp;
skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-
if (skb == NULL)
- {
return -ENOMEM;
- }
-
- skb->sk = sk;
- skb->localroute = sk->localroute;
tmp = tp->af_specific->build_net_header(sk, skb);
-
- if (tmp < 0)
- {
- skb->free = 1;
+ if (tmp < 0) {
kfree_skb(skb, FREE_WRITE);
return tmp;
}
@@ -907,33 +737,30 @@ int tcp_send_synack(struct sock *sk)
th->window = ntohs(tp->rcv_wnd);
- th->ack_seq = htonl(tp->rcv_nxt);
- th->doff = sizeof(*th)/4 + 1;
+ tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
- ptr = skb_put(skb, TCPOLEN_MSS);
- ptr[0] = TCPOPT_MSS;
- ptr[1] = TCPOLEN_MSS;
- ptr[2] = ((sk->mss) >> 8) & 0xff;
- ptr[3] = (sk->mss) & 0xff;
- skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
+ tmp = tcp_syn_build_options(skb, sk->mss,
+ tp->sack_ok, tp->tstamp_ok,
+ tp->snd_wscale?tp->rcv_wscale:0);
+ skb->csum = 0;
+ th->doff = (sizeof(*th) + tmp)>>2;
- tp->af_specific->send_check(sk, th, sizeof(*th)+4, skb);
+ tp->af_specific->send_check(sk, th, sizeof(*th)+tmp, skb);
skb_queue_tail(&sk->write_queue, skb);
-
- atomic_inc(&sk->packets_out);
- skb->when = jiffies;
buff = skb_clone(skb, GFP_ATOMIC);
+ if (buff) {
+ skb_set_owner_w(buff, sk);
- atomic_add(skb->truesize, &sk->wmem_alloc);
-
- tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
-
- tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
+ tp->packets_out++;
+ skb->when = jiffies;
- tcp_statistics.TcpOutSegs++;
+ tp->af_specific->queue_xmit(buff);
+ tcp_statistics.TcpOutSegs++;
+ tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
+ }
return 0;
}
@@ -951,22 +778,19 @@ void tcp_send_delayed_ack(struct sock * sk, int max_timeout)
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
unsigned long timeout, now;
- /* Calculate new timeout */
+ /* Calculate new timeout. */
now = jiffies;
timeout = tp->ato;
- if (timeout > max_timeout || sk->bytes_rcv > (sk->mss << 2))
- {
+ if (timeout > max_timeout ||
+ ((tp->rcv_nxt - tp->rcv_wup) > (sk->mss << 2)))
timeout = now;
- }
else
timeout += now;
- /* Use new timeout only if there wasn't a older one earlier */
+ /* Use new timeout only if there wasn't a older one earlier. */
if (!del_timer(&tp->delack_timer) || timeout < tp->delack_timer.expires)
- {
tp->delack_timer.expires = timeout;
- }
add_timer(&tp->delack_timer);
}
@@ -984,79 +808,53 @@ void tcp_send_ack(struct sock *sk)
struct tcphdr *th;
int tmp;
-
if(sk->zapped)
- {
- /* We have been reset, we may not send again */
- return;
- }
+ return; /* We have been reset, we may not send again. */
- /*
- * We need to grab some memory, and put together an ack,
+ /* We need to grab some memory, and put together an ack,
* and then put it into the queue to be sent.
+ * FIXME: is it better to waste memory here and use a
+ * constant sized ACK?
*/
-
- buff = sock_wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
- if (buff == NULL)
- {
- /*
- * Force it to send an ack. We don't have to do this
- * (ACK is unreliable) but it's much better use of
+ buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_ATOMIC);
+ if (buff == NULL) {
+ /* Force it to send an ack. We don't have to do this
+ * (ACK is unreliable) but it's much better use of
* bandwidth on slow links to send a spare ack than
- * resend packets.
+ * resend packets.
*/
-
tcp_send_delayed_ack(sk, HZ/2);
return;
}
clear_delayed_acks(sk);
- /*
- * Assemble a suitable TCP frame
- */
-
- buff->sk = sk;
- buff->localroute = sk->localroute;
+ /* Assemble a suitable TCP frame. */
buff->csum = 0;
- /*
- * Put in the IP header and routing stuff.
- */
-
+ /* Put in the IP header and routing stuff. */
tmp = tp->af_specific->build_net_header(sk, buff);
-
- if (tmp < 0)
- {
- buff->free = 1;
- sock_wfree(sk, buff);
+ if (tmp < 0) {
+ kfree_skb(buff, FREE_WRITE);
return;
}
- th =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
-
+ th = (struct tcphdr *)skb_put(buff,tp->tcp_header_len);
memcpy(th, &sk->dummy_th, sizeof(struct tcphdr));
+ tcp_build_options((__u32 *)(th+1),tp);
- /*
- * Swap the send and the receive.
- */
-
+ /* Swap the send and the receive. */
th->window = ntohs(tcp_select_window(sk));
th->seq = ntohl(tp->snd_nxt);
- th->ack_seq = ntohl(tp->rcv_nxt);
-
- /*
- * Fill in the packet and send it
- */
+ tp->last_ack_sent = th->ack_seq = ntohl(tp->rcv_nxt);
- tp->af_specific->send_check(sk, th, sizeof(struct tcphdr), buff);
+ /* Fill in the packet and send it. */
+ tp->af_specific->send_check(sk, th, tp->tcp_header_len, buff);
- if (sk->debug)
- printk("\rtcp_send_ack: seq %x ack %x\n",
- tp->snd_nxt, tp->rcv_nxt);
-
- tp->af_specific->queue_xmit(sk, buff->dev, buff, 1);
+ SOCK_DEBUG(sk, "\rtcp_send_ack: seq %x ack %x\n",
+ tp->snd_nxt, tp->rcv_nxt);
+ tp->af_specific->queue_xmit(buff);
tcp_statistics.TcpOutSegs++;
}
@@ -1073,58 +871,44 @@ void tcp_write_wakeup(struct sock *sk)
int tmp;
if (sk->zapped)
- return; /* After a valid reset we can send no more */
+ return; /* After a valid reset we can send no more. */
- /*
- * Write data can still be transmitted/retransmitted in the
+ /* Write data can still be transmitted/retransmitted in the
* following states. If any other state is encountered, return.
* [listen/close will never occur here anyway]
*/
-
if (sk->state != TCP_ESTABLISHED &&
sk->state != TCP_CLOSE_WAIT &&
sk->state != TCP_FIN_WAIT1 &&
sk->state != TCP_LAST_ACK &&
- sk->state != TCP_CLOSING
- )
- {
+ sk->state != TCP_CLOSING)
return;
- }
-
- if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) &&
- (skb=tp->send_head))
- {
- /*
- * We are probing the opening of a window
- * but the window size is != 0
- * must have been a result SWS avoidance ( sender )
- */
+ if (before(tp->snd_nxt, tp->snd_una + tp->snd_wnd) && (skb=tp->send_head)) {
struct tcphdr *th;
unsigned long win_size;
+ /* We are probing the opening of a window
+ * but the window size is != 0
+ * must have been a result SWS avoidance ( sender )
+ */
win_size = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
-
- if (win_size < skb->end_seq - skb->seq)
- {
- if (tcp_fragment(sk, skb, win_size))
- {
+ if (win_size < skb->end_seq - skb->seq) {
+ if (tcp_fragment(sk, skb, win_size)) {
printk(KERN_DEBUG "tcp_write_wakeup: "
"fragment failed\n");
return;
}
}
-
th = skb->h.th;
-
- tp->af_specific->send_check(sk, th, th->doff * 4 + win_size,
- skb);
-
+ tp->af_specific->send_check(sk, th, th->doff * 4 + win_size, skb);
buff = skb_clone(skb, GFP_ATOMIC);
+ if (buff == NULL)
+ return;
- atomic_add(buff->truesize, &sk->wmem_alloc);
- atomic_inc(&sk->packets_out);
+ skb_set_owner_w(buff, sk);
+ tp->packets_out++;
clear_delayed_acks(sk);
@@ -1132,39 +916,29 @@ void tcp_write_wakeup(struct sock *sk)
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
skb->when = jiffies;
-
update_send_head(sk);
-
tp->snd_nxt = skb->end_seq;
- }
- else
- {
- buff = sock_wmalloc(sk,MAX_ACK_SIZE, 1, GFP_ATOMIC);
+ } else {
+ buff = sock_wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);
if (buff == NULL)
return;
- buff->free = 1;
- buff->sk = sk;
- buff->localroute = sk->localroute;
buff->csum = 0;
- /*
- * Put in the IP header and routing stuff.
- */
-
+ /* Put in the IP header and routing stuff. */
tmp = tp->af_specific->build_net_header(sk, buff);
-
- if (tmp < 0)
- {
- sock_wfree(sk, buff);
+ if (tmp < 0) {
+ kfree_skb(buff, FREE_WRITE);
return;
}
t1 = (struct tcphdr *) skb_put(buff, sizeof(struct tcphdr));
memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
+ /* FIXME: should zero window probes have SACK and/or TIMESTAMP data?
+ * If so we have to tack them on here.
+ */
- /*
- * Use a previous sequence.
+ /* Use a previous sequence.
* This should cause the other end to send an ack.
*/
@@ -1172,15 +946,15 @@ void tcp_write_wakeup(struct sock *sk)
/* t1->fin = 0; -- We are sending a 'previous' sequence, and 0 bytes of data - thus no FIN bit */
t1->ack_seq = htonl(tp->rcv_nxt);
t1->window = htons(tcp_select_window(sk));
-
- tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
- }
- /*
- * Send it.
- */
+ /* Value from dummy_th may be larger. */
+ t1->doff = sizeof(struct tcphdr)/4;
- tp->af_specific->queue_xmit(sk, buff->dev, buff, 1);
+ tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
+ }
+
+ /* Send it. */
+ tp->af_specific->queue_xmit(buff);
tcp_statistics.TcpOutSegs++;
}
@@ -1195,16 +969,12 @@ void tcp_send_probe0(struct sock *sk)
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
if (sk->zapped)
- return; /* After a valid reset we can send no more */
-
+ return; /* After a valid reset we can send no more. */
tcp_write_wakeup(sk);
-
tp->pending = TIME_PROBE0;
-
tp->backoff++;
tp->probes_out++;
-
tcp_reset_xmit_timer (sk, TIME_PROBE0,
min(tp->rto << tp->backoff, 120*HZ));
}
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index e96089fab..365d3dac2 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -34,8 +34,8 @@ struct timer_list tcp_slow_timer = {
struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = {
- {0, TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer}, /* SYNACK */
- {0, TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive} /* KEEPALIVE */
+ {ATOMIC_INIT(0), TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer},/* SYNACK */
+ {ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive} /* KEEPALIVE */
};
/*
@@ -67,16 +67,14 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when)
{
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- if((long)when <= 0)
- {
- printk("xmit_timer <= 0 - timer:%d when:%lx\n", what, when);
+ if((long)when <= 0) {
+ printk(KERN_DEBUG "xmit_timer <= 0 - timer:%d when:%lx\n", what, when);
when=HZ/50;
}
switch (what) {
case TIME_RETRANS:
- /*
- * When seting the transmit timer the probe timer
+ /* When seting the transmit timer the probe timer
* should not be set.
* The delayed ack timer can be set if we are changing the
* retransmit timer when removing acked frames.
@@ -100,12 +98,12 @@ void tcp_reset_xmit_timer(struct sock *sk, int what, unsigned long when)
break;
case TIME_WRITE:
- printk("bug: tcp_reset_xmit_timer TIME_WRITE\n");
+ printk(KERN_DEBUG "bug: tcp_reset_xmit_timer TIME_WRITE\n");
break;
default:
- printk("bug: unknown timer value\n");
- }
+ printk(KERN_DEBUG "bug: unknown timer value\n");
+ };
}
void tcp_clear_xmit_timer(struct sock *sk, int what)
@@ -123,8 +121,8 @@ void tcp_clear_xmit_timer(struct sock *sk, int what)
del_timer(&tp->probe_timer);
break;
default:
- printk("bug: unknown timer value\n");
- }
+ printk(KERN_DEBUG "bug: unknown timer value\n");
+ };
}
int tcp_timer_is_set(struct sock *sk, int what)
@@ -142,8 +140,8 @@ int tcp_timer_is_set(struct sock *sk, int what)
return tp->probe_timer.next != NULL;
break;
default:
- printk("bug: unknown timer value\n");
- }
+ printk(KERN_DEBUG "bug: unknown timer value\n");
+ };
return 0;
}
@@ -162,25 +160,25 @@ void tcp_clear_xmit_timers(struct sock *sk)
static int tcp_write_timeout(struct sock *sk)
{
+ struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
/*
* Look for a 'soft' timeout.
*/
- if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
- || (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1))
- {
- /*
- * Attempt to recover if arp has changed (unlikely!) or
+ if ((sk->state == TCP_ESTABLISHED &&
+
+ /* Eric, what the heck is this doing?!?! */
+ tp->retransmits && !(tp->retransmits & 7)) ||
+
+ (sk->state != TCP_ESTABLISHED && tp->retransmits > TCP_RETR1)) {
+ /* Attempt to recover if arp has changed (unlikely!) or
* a route has shifted (not supported prior to 1.3).
*/
- ip_rt_advice(&sk->ip_route_cache, 0);
+ ip_rt_advice((struct rtable**)&sk->dst_cache, 0);
}
- /*
- * Have we tried to SYN too many times (repent repent 8))
- */
-
- if(sk->retransmits > TCP_SYN_RETRIES && sk->state==TCP_SYN_SENT)
- {
+ /* Have we tried to SYN too many times (repent repent 8)) */
+ if(tp->retransmits > TCP_SYN_RETRIES && sk->state==TCP_SYN_SENT) {
if(sk->err_soft)
sk->err=sk->err_soft;
else
@@ -196,11 +194,9 @@ static int tcp_write_timeout(struct sock *sk)
/* Don't FIN, we got nothing back */
return 0;
}
- /*
- * Has it gone just too far ?
- */
- if (sk->retransmits > TCP_RETR2)
- {
+
+ /* Has it gone just too far? */
+ if (tp->retransmits > TCP_RETR2) {
if(sk->err_soft)
sk->err = sk->err_soft;
else
@@ -209,19 +205,12 @@ static int tcp_write_timeout(struct sock *sk)
tcp_clear_xmit_timers(sk);
- /*
- * Time wait the socket
- */
- if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING )
- {
+ /* Time wait the socket. */
+ if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2 || sk->state == TCP_CLOSING) {
tcp_set_state(sk,TCP_TIME_WAIT);
tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
- }
- else
- {
- /*
- * Clean up time.
- */
+ } else {
+ /* Clean up time. */
tcp_set_state(sk, TCP_CLOSE);
return 0;
}
@@ -235,14 +224,10 @@ void tcp_delack_timer(unsigned long data) {
struct sock *sk = (struct sock*)data;
if(sk->zapped)
- {
return;
- }
- if (sk->delayed_acks)
- {
+ if (sk->tp_pinfo.af_tcp.delayed_acks)
tcp_read_wakeup(sk);
- }
}
void tcp_probe_timer(unsigned long data) {
@@ -251,16 +236,10 @@ void tcp_probe_timer(unsigned long data) {
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
if(sk->zapped)
- {
return;
- }
- if (sk->users)
- {
- /*
- * Try again in second
- */
-
+ if (sk->sock_readers) {
+ /* Try again in second. */
tcp_reset_xmit_timer(sk, TIME_PROBE0, HZ);
return;
}
@@ -270,28 +249,20 @@ void tcp_probe_timer(unsigned long data) {
* FIXME: We ought not to do it, Solaris 2.5 actually has fixing
* this behaviour in Solaris down as a bug fix. [AC]
*/
- if (tp->probes_out > TCP_RETR2)
- {
+ if (tp->probes_out > TCP_RETR2) {
if(sk->err_soft)
sk->err = sk->err_soft;
else
sk->err = ETIMEDOUT;
sk->error_report(sk);
- /*
- * Time wait the socket
- */
+ /* Time wait the socket. */
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2
- || sk->state == TCP_CLOSING )
- {
+ || sk->state == TCP_CLOSING) {
tcp_set_state(sk, TCP_TIME_WAIT);
tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
- }
- else
- {
- /*
- * Clean up time.
- */
+ } else {
+ /* Clean up time. */
tcp_set_state(sk, TCP_CLOSE);
}
}
@@ -303,24 +274,20 @@ static __inline__ int tcp_keepopen_proc(struct sock *sk)
{
int res = 0;
- if (sk->state == TCP_ESTABLISHED || sk->state == TCP_CLOSE_WAIT)
- {
+ if (sk->state == TCP_ESTABLISHED || sk->state == TCP_CLOSE_WAIT ||
+ sk->state == TCP_FIN_WAIT2) {
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
__u32 elapsed = jiffies - tp->rcv_tstamp;
- if (elapsed >= TCP_KEEPALIVE_TIME)
- {
- if (tp->probes_out > TCP_KEEPALIVE_PROBES)
- {
+ if (elapsed >= TCP_KEEPALIVE_TIME) {
+ if (tp->probes_out > TCP_KEEPALIVE_PROBES) {
if(sk->err_soft)
sk->err = sk->err_soft;
else
sk->err = ETIMEDOUT;
tcp_set_state(sk, TCP_CLOSE);
- }
- else
- {
+ } else {
tp->probes_out++;
tp->pending = TIME_KEEPOPEN;
tcp_write_wakeup(sk);
@@ -347,28 +314,40 @@ static __inline__ int tcp_keepopen_proc(struct sock *sk)
*/
#define MAX_KA_PROBES 5
+/* Keepopen's are only valid for "established" TCP's, nicely our listener
+ * hash gets rid of most of the useless testing, so we run through a couple
+ * of the established hash chains each clock tick. -DaveM
+ *
+ * And now, even more magic... TIME_WAIT TCP's cannot have keepalive probes
+ * going off for them, so we only need check the first half of the established
+ * hash table, even less testing under heavy load.
+ *
+ * I _really_ would rather do this by adding a new timer_struct to struct sock,
+ * and this way only those who set the keepalive option will get the overhead.
+ * The idea is you set it for 2 hours when the sock is first connected, when it
+ * does fire off (if at all, most sockets die earlier) you check for the keepalive
+ * option and also if the sock has been idle long enough to start probing.
+ */
static void tcp_keepalive(unsigned long data)
{
- struct sock *sk;
+ static int chain_start = 0;
int count = 0;
int i;
- for(i=0; i < SOCK_ARRAY_SIZE; i++)
- {
- sk = tcp_prot.sock_array[i];
- while (sk)
- {
- if (sk->keepopen)
- {
+ for(i = chain_start; i < (chain_start + ((TCP_HTABLE_SIZE/2) >> 2)); i++) {
+ struct sock *sk = tcp_established_hash[i];
+ while(sk) {
+ if(sk->keepopen) {
count += tcp_keepopen_proc(sk);
+ if(count == MAX_KA_PROBES)
+ goto out;
}
-
- if (count == MAX_KA_PROBES)
- return;
-
- sk = sk->next;
+ sk = sk->next;
}
}
+out:
+ chain_start = ((chain_start + ((TCP_HTABLE_SIZE/2)>>2)) &
+ ((TCP_HTABLE_SIZE/2) - 1));
}
/*
@@ -389,47 +368,35 @@ void tcp_retransmit_timer(unsigned long data)
struct sock *sk = (struct sock*)data;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- /*
- * We are reset. We will send no more retransmits.
- */
-
- if(sk->zapped)
- {
+ /* We are reset. We will send no more retransmits. */
+ if(sk->zapped) {
tcp_clear_xmit_timer(sk, TIME_RETRANS);
return;
}
- /*
- * Clear delay ack timer
- */
+ lock_sock(sk);
+ /* Clear delay ack timer. */
tcp_clear_xmit_timer(sk, TIME_DACK);
- /*
- * Retransmission
- */
-
+ /* Retransmission. */
tp->retrans_head = NULL;
-
-
- if (sk->retransmits == 0)
- {
- /*
- * remember window where we lost
+ if (tp->retransmits == 0) {
+ /* remember window where we lost
* "one half of the current window but at least 2 segments"
*/
-
- sk->ssthresh = max(sk->cong_window >> 1, 2);
- sk->cong_count = 0;
- sk->cong_window = 1;
+ tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
+ tp->snd_cwnd_cnt = 0;
+ tp->snd_cwnd = 1;
}
- atomic_inc(&sk->retransmits);
+ tp->retransmits++;
+ tp->dup_acks = 0;
+ tp->high_seq = tp->snd_nxt;
tcp_do_retransmit(sk, 0);
- /*
- * Increase the timeout each time we retransmit. Note that
+ /* Increase the timeout each time we retransmit. Note that
* we do not increase the rtt estimate. rto is initialized
* from rtt, but increases here. Jacobson (SIGCOMM 88) suggests
* that doubling rto each time is the least we can get away with.
@@ -444,38 +411,35 @@ void tcp_retransmit_timer(unsigned long data)
* implemented ftp to mars will work nicely. We will have to fix
* the 120 second clamps though!
*/
-
- tp->backoff++;
+ tp->backoff++; /* FIXME: always same as retransmits? -- erics */
tp->rto = min(tp->rto << 1, 120*HZ);
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_write_timeout(sk);
+
+ release_sock(sk);
}
/*
* Slow timer for SYN-RECV sockets
*/
+/* This now scales very nicely. -DaveM */
static void tcp_syn_recv_timer(unsigned long data)
{
struct sock *sk;
unsigned long now = jiffies;
int i;
- for(i=0; i < SOCK_ARRAY_SIZE; i++)
- {
- sk = tcp_prot.sock_array[i];
- while (sk)
- {
+ for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
+ sk = tcp_listening_hash[i];
+
+ while(sk) {
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- if (sk->state == TCP_LISTEN && !sk->users &&
- tp->syn_wait_queue)
- {
- struct open_request *req;
-
- req = tp->syn_wait_queue;
-
+ /* TCP_LISTEN is implied. */
+ if (!sk->sock_readers && tp->syn_wait_queue) {
+ struct open_request *req = tp->syn_wait_queue;
do {
struct open_request *conn;
@@ -483,17 +447,13 @@ static void tcp_syn_recv_timer(unsigned long data)
req = req->dl_next;
if (conn->sk)
- {
continue;
- }
-
+
if ((long)(now - conn->expires) <= 0)
break;
tcp_synq_unlink(tp, conn);
-
- if (conn->retrans >= TCP_RETR1)
- {
+ if (conn->retrans >= TCP_RETR1) {
#ifdef TCP_DEBUG
printk(KERN_DEBUG "syn_recv: "
"too many retransmits\n");
@@ -501,20 +461,19 @@ static void tcp_syn_recv_timer(unsigned long data)
(*conn->class->destructor)(conn);
tcp_dec_slow_timer(TCP_SLT_SYNACK);
sk->ack_backlog--;
- kfree(conn);
+ tcp_openreq_free(conn);
if (!tp->syn_wait_queue)
break;
- }
- else
- {
+ } else {
__u32 timeo;
-
+
(*conn->class->rtx_syn_ack)(sk, conn);
conn->retrans++;
#ifdef TCP_DEBUG
- printk(KERN_DEBUG "syn_ack rtx %d\n", conn->retrans);
+ printk(KERN_DEBUG "syn_ack rtx %d\n",
+ conn->retrans);
#endif
timeo = min((TCP_TIMEOUT_INIT
<< conn->retrans),
@@ -522,9 +481,8 @@ static void tcp_syn_recv_timer(unsigned long data)
conn->expires = now + timeo;
tcp_synq_queue(tp, conn);
}
- } while (req != tp->syn_wait_queue);
+ } while (req);
}
-
sk = sk->next;
}
}
@@ -537,16 +495,13 @@ void tcp_sltimer_handler(unsigned long data)
unsigned long now = jiffies;
int i;
- for (i=0; i < TCP_SLT_MAX; i++, slt++)
- {
- if (slt->count)
- {
+ for (i=0; i < TCP_SLT_MAX; i++, slt++) {
+ if (atomic_read(&slt->count)) {
long trigger;
trigger = slt->period - ((long)(now - slt->last));
- if (trigger <= 0)
- {
+ if (trigger <= 0) {
(*slt->handler)((unsigned long) slt);
slt->last = now;
trigger = slt->period;
@@ -555,8 +510,7 @@ void tcp_sltimer_handler(unsigned long data)
}
}
- if (next != ~0UL)
- {
+ if (next != ~0UL) {
tcp_slow_timer.expires = now + next;
add_timer(&tcp_slow_timer);
}
@@ -572,13 +526,10 @@ void __tcp_inc_slow_timer(struct tcp_sl_timer *slt)
when = now + slt->period;
if (del_timer(&tcp_slow_timer))
- {
next = tcp_slow_timer.expires;
- }
+
if (next && ((long)(next - when) < 0))
- {
when = next;
- }
tcp_slow_timer.expires = when;
add_timer(&tcp_slow_timer);
diff --git a/net/ipv4/timer.c b/net/ipv4/timer.c
index 664d81167..3a2927528 100644
--- a/net/ipv4/timer.c
+++ b/net/ipv4/timer.c
@@ -92,7 +92,7 @@ void net_timer (unsigned long data)
* only process if socket is not in use
*/
- if (sk->users)
+ if (sk->sock_readers)
{
sk->timer.expires = jiffies+HZ;
add_timer(&sk->timer);
@@ -122,7 +122,7 @@ void net_timer (unsigned long data)
if (sk->state != TCP_CLOSE)
{
- printk ("non CLOSE socket in time_done\n");
+ printk (KERN_DEBUG "non CLOSE socket in time_done\n");
break;
}
destroy_sock (sk);
@@ -148,7 +148,7 @@ void net_timer (unsigned long data)
break;
default:
- printk ("net_timer: timer expired - reason %d is unknown\n", why);
+ printk (KERN_DEBUG "net_timer: timer expired - reason %d is unknown\n", why);
break;
}
}
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 29e44e88a..9ca5f3045 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -49,6 +49,10 @@
* Mike Shaver : RFC1122 checks.
* Alan Cox : Nonblocking error fix.
* Willy Konynenberg : Transparent proxying support.
+ * David S. Miller : New socket lookup architecture.
+ * Last socket cache retained as it
+ * does have a high hit rate.
+ * Olaf Kirch : Don't linearise iovec on sendmsg.
*
*
* This program is free software; you can redistribute it and/or
@@ -108,6 +112,7 @@
#include <net/icmp.h>
#include <net/route.h>
#include <net/checksum.h>
+#include <linux/ipsec.h>
/*
* Snmp MIB for the UDP layer
@@ -115,29 +120,302 @@
struct udp_mib udp_statistics;
-/*
- * Cached last hit socket
+struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+
+ if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(sk2->rcv_saddr == sk->rcv_saddr) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
+static inline int udp_lport_inuse(int num)
+{
+ struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+
+ for(; sk != NULL; sk = sk->next) {
+ if(sk->num == num)
+ return 1;
+ }
+ return 0;
+}
+
+/* Shared by v4/v6 tcp. */
+unsigned short udp_good_socknum(void)
+{
+ static int start = 0;
+ unsigned short base;
+ int i, best = 0, size = 32767; /* a big num. */
+ int result;
+
+ base = PROT_SOCK + (start & 1023) + 1;
+
+ SOCKHASH_LOCK();
+ for(i = 0; i < UDP_HTABLE_SIZE; i++) {
+ struct sock *sk = udp_hash[i];
+ if(!sk) {
+ start = (i + 1 + start) & 1023;
+ result = i + base + 1;
+ goto out;
+ } else {
+ int j = 0;
+ do {
+ if(++j >= size)
+ goto next;
+ } while((sk = sk->next));
+ best = i;
+ size = j;
+ }
+ next:
+ }
+
+ while(udp_lport_inuse(base + best + 1))
+ best += UDP_HTABLE_SIZE;
+ result = (best + base + 1);
+out:
+ SOCKHASH_UNLOCK();
+ return result;
+}
+
+/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
+static u32 uh_cache_saddr, uh_cache_daddr;
+static u16 uh_cache_dport, uh_cache_sport;
+static struct sock *uh_cache_sk = NULL;
+
+static void udp_v4_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = udp_hash[num];
+ udp_hash[num] = sk;
+ sk->hashent = num;
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+ SOCKHASH_UNLOCK();
+}
+
+/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this here plus the last hit cache. -DaveM
*/
-
-volatile unsigned long uh_cache_saddr,uh_cache_daddr;
-volatile unsigned short uh_cache_dport, uh_cache_sport;
-volatile struct sock *uh_cache_sk;
+struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+ struct sock *sk, *result = NULL;
+ unsigned short hnum = ntohs(dport);
+ int badness = -1;
+
+ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+ if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) {
+ int score = 0;
+ if(sk->rcv_saddr) {
+ if(sk->rcv_saddr != daddr)
+ continue;
+ score++;
+ }
+ if(sk->daddr) {
+ if(sk->daddr != saddr)
+ continue;
+ score++;
+ }
+ if(sk->dummy_th.dest) {
+ if(sk->dummy_th.dest != sport)
+ continue;
+ score++;
+ }
+ if(score == 3) {
+ result = sk;
+ break;
+ } else if(score > badness) {
+ result = sk;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
-void udp_cache_zap(void)
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
{
- unsigned long flags;
- save_flags(flags);
- cli();
- uh_cache_saddr=0;
- uh_cache_daddr=0;
- uh_cache_dport=0;
- uh_cache_sport=0;
- uh_cache_sk=NULL;
- restore_flags(flags);
+ struct sock *sk;
+
+ if(uh_cache_sk &&
+ uh_cache_saddr == saddr &&
+ uh_cache_sport == sport &&
+ uh_cache_dport == dport &&
+ uh_cache_daddr == daddr)
+ return uh_cache_sk;
+
+ sk = udp_v4_lookup_longway(saddr, sport, daddr, dport);
+ uh_cache_sk = sk;
+ uh_cache_saddr = saddr;
+ uh_cache_daddr = daddr;
+ uh_cache_sport = sport;
+ uh_cache_dport = dport;
+ return sk;
}
-#define min(a,b) ((a)<(b)?(a):(b))
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+ s1 = udp_hash[(hpnum) & (TCP_HTABLE_SIZE - 1)]; \
+ else \
+ s1 = (sk); \
+ s1; \
+})
+
+#define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum), udp_hash[(hnum)&(TCP_HTABLE_SIZE-1)],(fpass))
+
+#define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+ secondlist((hpnum),(sk)->next,(fpass))
+
+struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+ unsigned short rnum, unsigned long laddr,
+ unsigned long paddr, unsigned short pnum)
+{
+ struct sock *s, *result = NULL;
+ int badness = -1;
+ unsigned short hnum = ntohs(num);
+ unsigned short hpnum = ntohs(pnum);
+ int firstpass = 1;
+
+ SOCKHASH_LOCK();
+ for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+ s != NULL;
+ s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+ if(s->num == hnum || s->num == hpnum) {
+ int score = 0;
+ if(s->dead && (s->state == TCP_CLOSE))
+ continue;
+ if(s->rcv_saddr) {
+ if((s->num != hpnum || s->rcv_saddr != paddr) &&
+ (s->num != hnum || s->rcv_saddr != laddr))
+ continue;
+ score++;
+ }
+ if(s->daddr) {
+ if(s->daddr != raddr)
+ continue;
+ score++;
+ }
+ if(s->dummy_th.dest) {
+ if(s->dummy_th.dest != rnum)
+ continue;
+ score++;
+ }
+ if(score == 3 && s->num == hnum) {
+ result = s;
+ break;
+ } else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+ result = s;
+ badness = score;
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return result;
+}
+
+#undef secondlist
+#undef udp_v4_proxy_loop_init
+#undef udp_v4_proxy_loop_next
+
+#endif
+static inline struct sock *udp_v4_mcast_next(struct sock *sk,
+ unsigned short num,
+ unsigned long raddr,
+ unsigned short rnum,
+ unsigned long laddr)
+{
+ struct sock *s = sk;
+ unsigned short hnum = ntohs(num);
+ for(; s; s = s->next) {
+ if ((s->num != hnum) ||
+ (s->dead && (s->state == TCP_CLOSE)) ||
+ (s->daddr && s->daddr!=raddr) ||
+ (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+ (s->rcv_saddr && s->rcv_saddr != laddr))
+ continue;
+ break;
+ }
+ return s;
+}
+
+#define min(a,b) ((a)<(b)?(a):(b))
/*
* This routine is called by the ICMP module when it gets some
@@ -150,30 +428,34 @@ void udp_cache_zap(void)
* to find the appropriate port.
*/
-void udp_err(int type, int code, unsigned char *header, __u32 info,
- __u32 daddr, __u32 saddr, struct inet_protocol *protocol, int len)
+void udp_err(struct sk_buff *skb, unsigned char *dp)
{
- struct udphdr *uh;
+ struct iphdr *iph = (struct iphdr*)dp;
+ struct udphdr *uh = (struct udphdr*)(dp+(iph->ihl<<2));
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
struct sock *sk;
- /*
- * Find the 8 bytes of post IP header ICMP included for us
- */
-
- if(len<sizeof(struct udphdr))
- return;
-
- uh = (struct udphdr *)header;
-
- sk = get_sock(&udp_prot, uh->source, daddr, uh->dest, saddr, 0, 0);
-
- if (sk == NULL)
+ sk = udp_v4_lookup(iph->daddr, uh->dest, iph->saddr, uh->source);
+ if (sk == NULL)
return; /* No socket for error */
+
+ if (sk->ip_recverr && !sk->sock_readers) {
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2 && sock_queue_err_skb(sk, skb2))
+ kfree_skb(skb2, FREE_READ);
+ }
- if (type == ICMP_SOURCE_QUENCH)
- { /* Slow down! */
+ if (type == ICMP_SOURCE_QUENCH) {
+#if 0 /* FIXME: If you check the rest of the code, this is a NOP!
+ * Someone figure out what we were trying to be doing
+ * here. Besides, cong_window is a TCP thing and thus
+ * I moved it out of normal sock and into tcp_opt.
+ */
+ /* Slow down! */
if (sk->cong_window > 1)
sk->cong_window = sk->cong_window/2;
+#endif
return;
}
@@ -183,6 +465,15 @@ void udp_err(int type, int code, unsigned char *header, __u32 info,
sk->error_report(sk);
return;
}
+
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+ {
+ if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+ sk->err = EMSGSIZE;
+ sk->error_report(sk);
+ }
+ return;
+ }
/*
* Various people wanted BSD UDP semantics. Well they've come
@@ -196,7 +487,7 @@ void udp_err(int type, int code, unsigned char *header, __u32 info,
/* 4.1.3.3. */
/* After the comment above, that should be no surprise. */
- if(code<=NR_ICMP_UNREACH && icmp_err_convert[code].fatal)
+ if (code < NR_ICMP_UNREACH && icmp_err_convert[code].fatal)
{
/*
* 4.x BSD compatibility item. Break RFC1122 to
@@ -218,43 +509,58 @@ static unsigned short udp_check(struct udphdr *uh, int len, unsigned long saddr,
struct udpfakehdr
{
struct udphdr uh;
- __u32 daddr;
- __u32 other;
- const char *from;
- __u32 wcheck;
+ u32 saddr;
+ u32 daddr;
+ u32 other;
+ struct iovec *iov;
+ int nriov;
+ u32 wcheck;
};
/*
- * Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to
- * get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode
- * for direct user->board I/O transfers. That one will be fun.
+ * Copy and checksum a UDP packet from user space into a buffer. We still have
+ * to do the planning to get ip_build_xmit to spot direct transfer to network
+ * card and provide an additional callback mode for direct user->board I/O
+ * transfers. That one will be fun.
*/
-static int udp_getfrag(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen)
+static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen)
{
struct udpfakehdr *ufh = (struct udpfakehdr *)p;
- const char *src;
- char *dst;
+ struct iovec *iov;
+ char *src;
+ char *dst = to;
unsigned int len;
- if (offset)
- {
- len = fraglen;
- src = ufh->from+(offset-sizeof(struct udphdr));
- dst = to;
- }
- else
- {
- len = fraglen-sizeof(struct udphdr);
- src = ufh->from;
- dst = to+sizeof(struct udphdr);
+ if (offset == 0) {
+ fraglen -= sizeof(struct udphdr);
+ dst += sizeof(struct udphdr);
}
- ufh->wcheck = csum_partial_copy_fromuser(src, dst, len, ufh->wcheck);
- if (offset == 0)
- {
+
+ iov = ufh->iov;
+ do {
+ if ((len = iov->iov_len) > fraglen)
+ len = fraglen;
+ src = (char *) iov->iov_base + iov->iov_len - len;
+ ufh->wcheck = csum_partial_copy_fromuser(src,
+ dst + fraglen - len, len,
+ ufh->wcheck);
+ if ((iov->iov_len -= len) == 0) {
+ if (--(ufh->nriov) < 0) {
+ printk(KERN_NOTICE "udp_getfrag: nriov = %d\n",
+ ufh->nriov);
+ return -EINVAL;
+ }
+ iov--;
+ }
+ fraglen -= len;
+ } while (fraglen);
+ ufh->iov = iov;
+
+ if (offset == 0) {
ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr),
ufh->wcheck);
- ufh->uh.check = csum_tcpudp_magic(saddr, ufh->daddr,
+ ufh->uh.check = csum_tcpudp_magic(ufh->saddr, ufh->daddr,
ntohs(ufh->uh.len),
IPPROTO_UDP, ufh->wcheck);
if (ufh->uh.check == 0)
@@ -266,217 +572,172 @@ static int udp_getfrag(const void *p, __u32 saddr, char * to, unsigned int offse
/*
* Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
- * that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET
- * set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if
- * this is a valid decision.
+ * that we use two routines for this for speed. Probably we ought to have a
+ * CONFIG_FAST_NET set for >10Mb/second boards to activate this sort of coding.
+ * Timing needed to verify if this is a valid decision.
*/
-static int udp_getfrag_nosum(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen)
+static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen)
{
struct udpfakehdr *ufh = (struct udpfakehdr *)p;
- const char *src;
- char *dst;
+ struct iovec *iov;
+ char *src;
+ char *dst = to;
+ int err;
unsigned int len;
- int err;
- if (offset)
- {
- len = fraglen;
- src = ufh->from+(offset-sizeof(struct udphdr));
- dst = to;
+ if (offset == 0) {
+ fraglen -= sizeof(struct udphdr);
+ dst += sizeof(struct udphdr);
}
- else
- {
- len = fraglen-sizeof(struct udphdr);
- src = ufh->from;
- dst = to+sizeof(struct udphdr);
- }
- err = copy_from_user(dst,src,len);
+
+ iov = ufh->iov;
+ do {
+ if ((len = iov->iov_len) > fraglen)
+ len = fraglen;
+ src = (char *) iov->iov_base + iov->iov_len - len;
+ err = copy_from_user(dst + fraglen - len, src, len);
+ fraglen -= len;
+ if ((iov->iov_len -= len) == 0) {
+ if (--(ufh->nriov) < 0) {
+ printk(KERN_NOTICE "udp_getfrag: nriov = %d\n",
+ ufh->nriov);
+ return -EINVAL;
+ }
+ iov--;
+ }
+ } while (fraglen && err >= 0);
+ ufh->iov = iov;
+
if (offset == 0)
memcpy(to, ufh, sizeof(struct udphdr));
- return err;
+ return err;
}
-/*
- * Send UDP frames.
- */
-
-static int udp_send(struct sock *sk, struct sockaddr_in *sin,
- const unsigned char *from, int len, int rt,
- __u32 saddr, int noblock)
+int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
int ulen = len + sizeof(struct udphdr);
- int a;
+ struct device *dev = NULL;
+ struct ipcm_cookie ipc;
struct udpfakehdr ufh;
-
- if(ulen>65535-sizeof(struct iphdr))
- return -EMSGSIZE;
-
- ufh.uh.source = sk->dummy_th.source;
- ufh.uh.dest = sin->sin_port;
- ufh.uh.len = htons(ulen);
- ufh.uh.check = 0;
- ufh.daddr = sin->sin_addr.s_addr;
- ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
- ufh.from = from;
- ufh.wcheck = 0;
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- if (rt&MSG_PROXY)
- {
- /*
- * We map the first 8 bytes of a second sockaddr_in
- * into the last 8 (unused) bytes of a sockaddr_in.
- * This _is_ ugly, but it's the only way to do it
- * easily, without adding system calls.
- */
- struct sockaddr_in *sinfrom =
- (struct sockaddr_in *) sin->sin_zero;
-
- if (!suser())
- return(-EPERM);
- if (sinfrom->sin_family && sinfrom->sin_family != AF_INET)
- return(-EINVAL);
- if (sinfrom->sin_port == 0)
- return(-EINVAL);
- saddr = sinfrom->sin_addr.s_addr;
- ufh.uh.source = sinfrom->sin_port;
- }
-#endif
-
- /* RFC1122: OK. Provides the checksumming facility (MUST) as per */
- /* 4.1.3.4. It's configurable by the application via setsockopt() */
- /* (MAY) and it defaults to on (MUST). Almost makes up for the */
- /* violation above. -- MS */
-
- if(sk->no_check)
- a = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen,
- sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
- else
- a = ip_build_xmit(sk, udp_getfrag, &ufh, ulen,
- sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
- if(a<0)
- return a;
- udp_statistics.UdpOutDatagrams++;
- return len;
-}
-
+ struct rtable *rt;
+ int free = 0;
+ u32 daddr;
+ u8 tos;
+ int err;
-static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int noblock,
- unsigned flags, struct sockaddr_in *usin, int addr_len)
-{
- struct sockaddr_in sin;
- int tmp;
- __u32 saddr=0;
+ if (len>65535)
+ return -EMSGSIZE;
/*
- * Check the flags. We support no flags for UDP sending
+ * Check the flags.
*/
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- if (flags&~(MSG_DONTROUTE|MSG_PROXY))
-#else
- if (flags&~MSG_DONTROUTE)
-#endif
- return(-EINVAL);
+ if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */
+ return -EOPNOTSUPP;
+
+ if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT))
+ return -EINVAL;
+
/*
* Get and verify the address.
*/
- if (usin)
- {
- if (addr_len < sizeof(sin))
- return(-EINVAL);
- if (usin->sin_family && usin->sin_family != AF_INET)
+ if (msg->msg_namelen) {
+ struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
+ if (msg->msg_namelen < sizeof(*usin))
return(-EINVAL);
- if (usin->sin_port == 0)
- return(-EINVAL);
- }
- else
- {
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
- /* We need to provide a sockaddr_in when using MSG_PROXY. */
- if (flags&MSG_PROXY)
- return(-EINVAL);
-#endif
- if (sk->state != TCP_ESTABLISHED)
- return(-EINVAL);
- sin.sin_family = AF_INET;
- sin.sin_port = sk->dummy_th.dest;
- sin.sin_addr.s_addr = sk->daddr;
- usin = &sin;
+ if (usin->sin_family != AF_INET) {
+ static int complained;
+ if (!complained++)
+ printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm);
+ if (usin->sin_family)
+ return -EINVAL;
+ }
+ ufh.daddr = usin->sin_addr.s_addr;
+ ufh.uh.dest = usin->sin_port;
+ if (ufh.uh.dest == 0)
+ return -EINVAL;
+ } else {
+ if (sk->state != TCP_ESTABLISHED)
+ return -EINVAL;
+ ufh.daddr = sk->daddr;
+ ufh.uh.dest = sk->dummy_th.dest;
}
-
- /*
- * BSD socket semantics. You must set SO_BROADCAST to permit
- * broadcasting of data.
- */
-
- /* RFC1122: OK. Allows the application to select the specific */
- /* source address for an outgoing packet (MUST) as per 4.1.3.5. */
- /* Optional addition: a mechanism for telling the application what */
- /* address was used. (4.1.3.5, MAY) -- MS */
-
- /* RFC1122: MUST ensure that all outgoing packets have one */
- /* of this host's addresses as a source addr.(4.1.3.6) - bind in */
- /* af_inet.c checks these. It does need work to allow BSD style */
- /* bind to multicast as is done by xntpd */
-
- if(usin->sin_addr.s_addr==INADDR_ANY)
- usin->sin_addr.s_addr=ip_my_addr();
-
- if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
- return -EACCES; /* Must turn broadcast on first */
- lock_sock(sk);
+ ipc.addr = sk->saddr;
+ ipc.opt = NULL;
+ if (msg->msg_controllen) {
+ err = ip_cmsg_send(msg, &ipc, &dev);
+ if (err)
+ return err;
+ if (ipc.opt)
+ free = 1;
+ }
+ if (!ipc.opt)
+ ipc.opt = sk->opt;
- /* Send the packet. */
- tmp = udp_send(sk, usin, from, len, flags, saddr, noblock);
+ ufh.saddr = ipc.addr;
+ ipc.addr = daddr = ufh.daddr;
- /* The datagram has been sent off. Release the socket. */
- release_sock(sk);
- return(tmp);
-}
+ if (ipc.opt && ipc.opt->srr) {
+ if (!daddr)
+ return -EINVAL;
+ daddr = ipc.opt->faddr;
+ }
+ tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) ||
+ (ipc.opt && ipc.opt->is_strictroute));
-/*
- * Temporary
- */
-
-int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock,
- int flags)
-{
- if(msg->msg_iovlen==1)
- return udp_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
+ if (MULTICAST(daddr) && sk->ip_mc_index && dev == NULL)
+ err = ip_route_output_dev(&rt, daddr, ufh.saddr, tos, sk->ip_mc_index);
else
- {
- /*
- * For awkward cases we linearise the buffer first. In theory this is only frames
- * whose iovec's don't split on 4 byte boundaries, and soon encrypted stuff (to keep
- * skip happy). We are a bit more general about it.
- */
-
- unsigned char *buf;
- int fs;
- int err;
- if(len>65515)
- return -EMSGSIZE;
- buf=kmalloc(len, GFP_KERNEL);
- if(buf==NULL)
- return -ENOBUFS;
- err = memcpy_fromiovec(buf, msg->msg_iov, len);
- if (err)
- err = -EFAULT;
- if (!err)
- {
- fs=get_fs();
- set_fs(get_ds());
- err=udp_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
- set_fs(fs);
- }
- kfree_s(buf,len);
+ err = ip_route_output(&rt, daddr, ufh.saddr, tos, dev);
+
+ if (err) {
+ if (free) kfree(ipc.opt);
return err;
}
+
+ if (rt->rt_flags&RTF_BROADCAST && !sk->broadcast) {
+ if (free) kfree(ipc.opt);
+ ip_rt_put(rt);
+ return -EACCES;
+ }
+
+ ufh.saddr = rt->rt_src;
+ if (!ipc.addr)
+ ufh.daddr = ipc.addr = rt->rt_dst;
+ ufh.uh.source = sk->dummy_th.source;
+ ufh.uh.len = htons(ulen);
+ ufh.uh.check = 0;
+ ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
+ ufh.iov = msg->msg_iov + msg->msg_iovlen - 1;
+ ufh.nriov = msg->msg_iovlen;
+ ufh.wcheck = 0;
+
+ /* RFC1122: OK. Provides the checksumming facility (MUST) as per */
+ /* 4.1.3.4. It's configurable by the application via setsockopt() */
+ /* (MAY) and it defaults to on (MUST). Almost makes up for the */
+ /* violation above. -- MS */
+
+ lock_sock(sk);
+ if (sk->no_check)
+ err = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen,
+ &ipc, rt, msg->msg_flags);
+ else
+ err = ip_build_xmit(sk, udp_getfrag, &ufh, ulen,
+ &ipc, rt, msg->msg_flags);
+ ip_rt_put(rt);
+ release_sock(sk);
+
+ if (free)
+ kfree(ipc.opt);
+ if (!err) {
+ udp_statistics.UdpOutDatagrams++;
+ return len;
+ }
+ return err;
}
/*
@@ -542,6 +803,17 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
if (addr_len)
*addr_len=sizeof(*sin);
+
+ if (sk->ip_recverr && (skb = skb_dequeue(&sk->error_queue)) != NULL) {
+ er = sock_error(sk);
+ if (msg->msg_controllen == 0) {
+ skb_free_datagram(sk, skb);
+ return er;
+ }
+ put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
+ skb_free_datagram(sk, skb);
+ return 0;
+ }
/*
* From here the generic datagram does a lot of the work. Come
@@ -553,13 +825,12 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
return er;
truesize = skb->len - sizeof(struct udphdr);
- copied = truesize;
-
- if(len<truesize)
- {
- copied=len;
- msg->msg_flags|=MSG_TRUNC;
- }
+ copied = truesize;
+ if (len < truesize)
+ {
+ msg->msg_flags |= MSG_TRUNC;
+ copied = len;
+ }
/*
* FIXME : should use udp header size info value
@@ -571,11 +842,11 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sk->stamp=skb->stamp;
/* Copy the address. */
- if (sin)
+ if (sin)
{
sin->sin_family = AF_INET;
sin->sin_port = skb->h.uh->source;
- sin->sin_addr.s_addr = skb->daddr;
+ sin->sin_addr.s_addr = skb->nh.iph->saddr;
#ifdef CONFIG_IP_TRANSPARENT_PROXY
if (flags&MSG_PROXY)
{
@@ -590,10 +861,12 @@ int udp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sinto->sin_family = AF_INET;
sinto->sin_port = skb->h.uh->dest;
- sinto->sin_addr.s_addr = skb->saddr;
+ sinto->sin_addr.s_addr = skb->nh.iph->daddr;
}
#endif
}
+ if (sk->ip_cmsg_flags)
+ ip_cmsg_recv(msg, skb);
skb_free_datagram(sk, skb);
return(copied);
@@ -603,7 +876,12 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
struct rtable *rt;
+ int err;
+
+ if (addr_len < sizeof(*usin))
+ return(-EINVAL);
+
/*
* 1003.1g - break association.
*/
@@ -614,33 +892,32 @@ int udp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
sk->rcv_saddr=INADDR_ANY;
sk->daddr=INADDR_ANY;
sk->state = TCP_CLOSE;
- udp_cache_zap();
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
return 0;
}
-
- if (addr_len < sizeof(*usin))
- return(-EINVAL);
if (usin->sin_family && usin->sin_family != AF_INET)
return(-EAFNOSUPPORT);
- if (usin->sin_addr.s_addr==INADDR_ANY)
- usin->sin_addr.s_addr=ip_my_addr();
- if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
- return -EACCES; /* Must turn broadcast on first */
-
- rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
- if (rt==NULL)
- return -ENETUNREACH;
+ err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+ sk->ip_tos|sk->localroute);
+ if (err)
+ return err;
+ if ((rt->rt_flags&RTF_BROADCAST) && !sk->broadcast) {
+ ip_rt_put(rt);
+ return -EACCES;
+ }
if(!sk->saddr)
sk->saddr = rt->rt_src; /* Update source address */
if(!sk->rcv_saddr)
sk->rcv_saddr = rt->rt_src;
- sk->daddr = usin->sin_addr.s_addr;
+ sk->daddr = rt->rt_dst;
sk->dummy_th.dest = usin->sin_port;
sk->state = TCP_ESTABLISHED;
- udp_cache_zap();
- sk->ip_route_cache = rt;
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
+ ip_rt_put(rt);
return(0);
}
@@ -649,29 +926,36 @@ static void udp_close(struct sock *sk, unsigned long timeout)
{
lock_sock(sk);
sk->state = TCP_CLOSE;
- if(uh_cache_sk==sk)
- udp_cache_zap();
- release_sock(sk);
+ if(uh_cache_sk == sk)
+ uh_cache_sk = NULL;
sk->dead = 1;
+ release_sock(sk);
+ udp_v4_unhash(sk);
destroy_sock(sk);
}
-static inline int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
{
/*
+ * Check the security clearance
+ */
+
+ if(!ipsec_sk_policy(sk,skb))
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return(0);
+ }
+
+ /*
* Charge it to the socket, dropping if the queue is full.
*/
- /* I assume this includes the IP options, as per RFC1122 (4.1.3.2). */
- /* If not, please let me know. -- MS */
-
if (__sock_queue_rcv_skb(sk,skb)<0) {
udp_statistics.UdpInErrors++;
ip_statistics.IpInDiscards++;
ip_statistics.IpInDelivers--;
- skb->sk = NULL;
kfree_skb(skb, FREE_WRITE);
- return 0;
+ return -1;
}
udp_statistics.UdpInDatagrams++;
return 0;
@@ -680,15 +964,48 @@ static inline int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
{
- skb->sk = sk;
-
- if (sk->users) {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return;
}
udp_queue_rcv_skb(sk, skb);
}
+/*
+ * Multicasts and broadcasts go to each listener.
+ */
+static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
+ u32 saddr, u32 daddr)
+{
+ struct sock *sk;
+ int given = 0;
+
+ SOCKHASH_LOCK();
+ sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+ sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+ if(sk) {
+ struct sock *sknext = NULL;
+
+ do {
+ struct sk_buff *skb1 = skb;
+
+ sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
+ uh->source, daddr);
+ if(sknext)
+ skb1 = skb_clone(skb, GFP_ATOMIC);
+
+ if(skb1)
+ udp_deliver(sk, skb1);
+ sk = sknext;
+ } while(sknext);
+ given = 1;
+ }
+ SOCKHASH_UNLOCK();
+ if(!given)
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
#ifdef CONFIG_IP_TRANSPARENT_PROXY
/*
* Check whether a received UDP packet might be for one of our
@@ -697,15 +1014,18 @@ static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
int udp_chkaddr(struct sk_buff *skb)
{
- struct iphdr *iph = skb->h.iph;
- struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
+ struct iphdr *iph = skb->nh.iph;
+ struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
struct sock *sk;
- sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr, 0, 0);
+ sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+ if (!sk)
+ return 0;
- if (!sk) return 0;
/* 0 means accept all LOCAL addresses here, not all the world... */
- if (sk->rcv_saddr == 0) return 0;
+ if (sk->rcv_saddr == 0)
+ return 0;
+
return 1;
}
#endif
@@ -714,29 +1034,25 @@ int udp_chkaddr(struct sk_buff *skb)
* All we need to do is get the socket, and then do a checksum.
*/
-int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo, struct inet_protocol *protocol)
+int udp_rcv(struct sk_buff *skb, unsigned short len)
{
struct sock *sk;
struct udphdr *uh;
unsigned short ulen;
- int addr_type;
+ struct rtable *rt = (struct rtable*)skb->dst;
+ u32 saddr = skb->nh.iph->saddr;
+ u32 daddr = skb->nh.iph->daddr;
/*
* First time through the loop.. Do all the setup stuff
* (including finding out the socket we go to etc)
*/
- addr_type = IS_MYADDR;
- if(!dev || dev->pa_addr!=daddr)
- addr_type=ip_chk_addr(daddr);
-
/*
* Get the header.
*/
- uh = (struct udphdr *) skb->h.uh;
+ uh = skb->h.uh;
ip_statistics.IpInDelivers++;
@@ -746,9 +1062,8 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
ulen = ntohs(uh->len);
- if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh))
- {
- NETDEBUG(printk("UDP: short packet: %d/%d\n", ulen, len));
+ if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) {
+ NETDEBUG(printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
udp_statistics.UdpInErrors++;
kfree_skb(skb, FREE_WRITE);
return(0);
@@ -761,20 +1076,17 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
/* FIXME list for IP, though, so I wouldn't worry about it. */
/* (That's the Right Place to do it, IMHO.) -- MS */
- if (uh->check && (
- ( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
- ( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
- /* skip if CHECKSUM_UNNECESSARY */
- )
- )
- {
+ if (uh->check &&
+ (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) ||
+ ((skb->ip_summed==CHECKSUM_NONE) &&
+ (udp_check(uh,len,saddr,daddr, csum_partial((char*)uh, len, 0)))))) {
/* <mea@utu.fi> wants to know, who sent it, to
go and stomp on the garbage sender... */
- /* RFC1122: OK. Discards the bad packet silently (as far as */
- /* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
+ /* RFC1122: OK. Discards the bad packet silently (as far as */
+ /* the network is concerned, anyway) as per 4.1.3.4 (MUST). */
- NETDEBUG(printk("UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
+ NETDEBUG(printk(KERN_DEBUG "UDP: bad checksum. From %08lX:%d to %08lX:%d ulen %d\n",
ntohl(saddr),ntohs(uh->source),
ntohl(daddr),ntohs(uh->dest),
ulen));
@@ -783,73 +1095,38 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
return(0);
}
+
+ len = ulen;
+
/*
- * These are supposed to be switched.
+ * FIXME:
+ * Trimming things wrongly. We must adjust the base/end to allow
+ * for the headers we keep!
+ * --ANK
*/
-
- skb->daddr = saddr;
- skb->saddr = daddr;
+ skb_trim(skb,len);
- len=ulen;
- skb->dev = dev;
- skb_trim(skb,len);
+ if(rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST))
+ return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
-#ifdef CONFIG_IP_MULTICAST
- if (addr_type==IS_BROADCAST || addr_type==IS_MULTICAST)
- {
- /*
- * Multicasts and broadcasts go to each listener.
- */
- struct sock *sknext=NULL;
- sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
- saddr, uh->source, daddr);
- if(sk)
- {
- do
- {
- struct sk_buff *skb1;
-
- sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
- if(sknext)
- skb1=skb_clone(skb,GFP_ATOMIC);
- else
- skb1=skb;
- if(skb1)
- udp_deliver(sk, skb1);
- sk=sknext;
- }
- while(sknext!=NULL);
- }
- else
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-#endif
- if(saddr==uh_cache_saddr && daddr==uh_cache_daddr && uh->dest==uh_cache_dport && uh->source==uh_cache_sport)
- sk=(struct sock *)uh_cache_sk;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+ if (IPCB(skb)->redirport)
+ sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
+ daddr, skb->dev->pa_addr,
+ IPCB(skb)->redirport);
else
- {
- sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr, dev->pa_addr, skb->redirport);
- uh_cache_saddr=saddr;
- uh_cache_daddr=daddr;
- uh_cache_dport=uh->dest;
- uh_cache_sport=uh->source;
- uh_cache_sk=sk;
- }
+#endif
+ sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
- if (sk == NULL)
- {
+ if (sk == NULL) {
udp_statistics.UdpNoPorts++;
- if (addr_type != IS_BROADCAST && addr_type != IS_MULTICAST)
- {
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
- }
+ icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
/*
* Hmm. We got an UDP broadcast to a port to which we
* don't wanna listen. Ignore it.
*/
- skb->sk = NULL;
kfree_skb(skb, FREE_WRITE);
return(0);
}
@@ -858,27 +1135,33 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
}
struct proto udp_prot = {
- udp_close,
- udp_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
- udp_ioctl,
- NULL,
- NULL,
- NULL,
- ip_setsockopt,
- ip_getsockopt,
- udp_sendmsg,
- udp_recvmsg,
- NULL, /* No special bind function */
- udp_queue_rcv_skb,
- 128,
- 0,
- "UDP",
- 0, 0,
- NULL
+ (struct sock *)&udp_prot, /* sklist_next */
+ (struct sock *)&udp_prot, /* sklist_prev */
+ udp_close, /* close */
+ udp_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ udp_ioctl, /* ioctl */
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ ip_setsockopt, /* setsockopt */
+ ip_getsockopt, /* getsockopt */
+ udp_sendmsg, /* sendmsg */
+ udp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ udp_queue_rcv_skb, /* backlog_rcv */
+ udp_v4_hash, /* hash */
+ udp_v4_unhash, /* unhash */
+ udp_v4_rehash, /* rehash */
+ udp_good_socknum, /* good_socknum */
+ udp_v4_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "UDP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index e11c5eee7..15ef93ac6 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -8,12 +8,18 @@
O_TARGET := ipv6.o
-O_OBJS := af_inet6.o ipv6_output.o ipv6_input.o addrconf.o sit.o \
- ipv6_route.o ipv6_sockglue.o ndisc.o udp.o raw.o \
- protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
- exthdrs.o sysctl_net_ipv6.o datagram.o
+IPV6_OBJS := af_inet6.o ip6_output.o ip6_input.o addrconf.o sit.o \
+ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \
+ protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o
MOD_LIST_NAME := IPV6_MODULES
M_OBJS := $(O_TARGET)
+#ifeq ($(CONFIG_IPV6_FIREWALL),y)
+# IPV6_OBJS += ip6_fw.o
+#endif
+
+O_OBJS := $(IPV6_OBJS)
+
include $(TOPDIR)/Rules.make
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 6b7ec0ad4..9173a7760 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5,12 +5,14 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: addrconf.c,v 1.18 1997/04/16 05:58:03 davem Exp $
*
* This program 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 of the License, or (at your option) any later version.
*/
+
/*
* Changes:
*
@@ -18,6 +20,7 @@
* <chexum@bankinf.banki.hu>
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -27,6 +30,7 @@
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/route.h>
#include <linux/proc_fs.h>
#include <net/sock.h>
@@ -35,30 +39,37 @@
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/sit.h>
#include <asm/uaccess.h>
-#define HASH_SIZE 16
+/* Set to 3 to get tracing... */
+#define ACONF_DEBUG 2
+
+#if ACONF_DEBUG >= 3
+#define ADBG(x) printk x
+#else
+#define ADBG(x)
+#endif
+
/*
* Configured unicast address list
*/
-struct inet6_ifaddr *inet6_addr_lst[HASH_SIZE];
+struct inet6_ifaddr *inet6_addr_lst[IN6_ADDR_HSIZE];
/*
* Hash list of configured multicast addresses
*/
-struct ipv6_mc_list *inet6_mcast_lst[HASH_SIZE];
+struct ifmcaddr6 *inet6_mcast_lst[IN6_ADDR_HSIZE];
/*
* AF_INET6 device list
*/
-struct inet6_dev *inet6_dev_lst;
-int in6_ifnum = 0;
+struct inet6_dev *inet6_dev_lst[IN6_ADDR_HSIZE];
-atomic_t addr_list_lock = 0;
+static atomic_t addr_list_lock = ATOMIC_INIT(0);
void addrconf_verify(unsigned long);
@@ -67,15 +78,11 @@ static struct timer_list addr_chk_timer = {
0, 0, addrconf_verify
};
-
-int DupAddrDetectTransmits = 1;
-
-/*
- * /proc/sys switch for autoconf (enabled by default)
- */
-int addrconf_sys_autoconf = 1;
+static int addrconf_ifdown(struct device *dev);
static void addrconf_dad_start(struct inet6_ifaddr *ifp);
+static void addrconf_dad_timer(unsigned long data);
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
static void addrconf_rs_timer(unsigned long data);
int ipv6_addr_type(struct in6_addr *addr)
@@ -89,58 +96,41 @@ int ipv6_addr_type(struct in6_addr *addr)
* 0x4/3
*/
- if ((st & __constant_htonl(0xE0000000)) ==
- __constant_htonl(0x40000000))
- {
+ if ((st & __constant_htonl(0xE0000000)) == __constant_htonl(0x40000000))
return IPV6_ADDR_UNICAST;
- }
- if ((st & __constant_htonl(0xFF000000)) ==
- __constant_htonl(0xFF000000))
- {
+ if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
int type = IPV6_ADDR_MULTICAST;
- switch((st >> 16) & 0x0f)
- {
- case 0x01:
+ switch((st & __constant_htonl(0x00FF0000))) {
+ case __constant_htonl(0x00010000):
type |= IPV6_ADDR_LOOPBACK;
break;
- case 0x02:
+
+ case __constant_htonl(0x00020000):
type |= IPV6_ADDR_LINKLOCAL;
break;
- case 0x05:
+
+ case __constant_htonl(0x00050000):
type |= IPV6_ADDR_SITELOCAL;
break;
- }
+ };
return type;
}
- if ((st & __constant_htonl(0xFFC00000)) ==
- __constant_htonl(0xFE800000))
- {
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
- }
- if ((st & __constant_htonl(0xFFC00000)) ==
- __constant_htonl(0xFEC00000))
- {
+ if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
- }
- if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0)
- {
- if (addr->s6_addr32[2] == 0)
- {
+ if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
+ if (addr->s6_addr32[2] == 0) {
if (addr->in6_u.u6_addr32[3] == 0)
- {
return IPV6_ADDR_ANY;
- }
if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
- {
- return (IPV6_ADDR_LOOPBACK |
- IPV6_ADDR_UNICAST);
- }
+ return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
}
@@ -152,81 +142,64 @@ int ipv6_addr_type(struct in6_addr *addr)
return IPV6_ADDR_RESERVED;
}
-struct inet6_dev * ipv6_add_dev(struct device *dev)
+static struct inet6_dev * ipv6_add_dev(struct device *dev)
{
- struct inet6_dev *dev6;
-
- /*
- * called by netdev notifier from a syscall
- */
- dev6 = (struct inet6_dev *) kmalloc(sizeof(struct inet6_dev),
- GFP_ATOMIC);
+ struct inet6_dev *ndev, **bptr, *iter;
+ int hash;
- if (dev6 == NULL)
- return NULL;
+ ndev = kmalloc(sizeof(struct inet6_dev), gfp_any());
- memset(dev6, 0, sizeof(struct inet6_dev));
- dev6->dev = dev;
- dev6->if_index = ++in6_ifnum;
+ if (ndev) {
+ memset(ndev, 0, sizeof(struct inet6_dev));
- /*
- * insert at head.
- */
+ ndev->dev = dev;
+ hash = ipv6_devindex_hash(dev->ifindex);
+ bptr = &inet6_dev_lst[hash];
+ iter = *bptr;
- dev6->next = inet6_dev_lst;
- inet6_dev_lst = dev6;
+ for (; iter; iter = iter->next)
+ bptr = &iter->next;
- return dev6;
-}
-
-struct inet6_dev * ipv6_dev_by_index(int index)
-{
- struct inet6_dev *in6_dev;
-
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- if (in6_dev->if_index == index)
- return in6_dev;
+ *bptr = ndev;
}
-
- return NULL;
+ return ndev;
}
void addrconf_forwarding_on(void)
{
- struct inet6_dev *in6_dev;
- struct in6_addr maddr;
+ struct inet6_dev *idev;
+ int i;
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- printk(KERN_DEBUG "dev %s\n", in6_dev->dev->name);
+ for (i = 0; i < IN6_ADDR_HSIZE; i++) {
+ for (idev = inet6_dev_lst[i]; idev; idev = idev->next) {
+#if ACONF_DEBUG >= 2
+ printk(KERN_DEBUG "dev %s\n", idev->dev->name);
+#endif
- if (in6_dev->dev->type == ARPHRD_ETHER)
- {
- printk(KERN_DEBUG "joining all-routers\n");
- in6_dev->router = 1;
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(in6_dev->dev, &maddr);
- }
- }
+ if (idev->dev->type == ARPHRD_ETHER) {
+ struct in6_addr maddr;
- if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
- {
- rt_release(last_resort_rt);
- last_resort_rt = NULL;
+#if ACONF_DEBUG >= 2
+ printk(KERN_DEBUG "joining all-routers\n");
+#endif
+ idev->router = 1;
+ ipv6_addr_all_routers(&maddr);
+ ipv6_dev_mc_inc(idev->dev, &maddr);
+ }
+ }
}
}
struct inet6_dev * ipv6_get_idev(struct device *dev)
{
- struct inet6_dev *in6_dev;
+ struct inet6_dev *idev;
+ int hash;
- for (in6_dev = inet6_dev_lst; in6_dev; in6_dev = in6_dev->next)
- {
- if (in6_dev->dev == dev)
- {
- return in6_dev;
- }
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+ if (idev->dev == dev)
+ return idev;
}
return NULL;
}
@@ -234,45 +207,34 @@ struct inet6_dev * ipv6_get_idev(struct device *dev)
struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev,
struct in6_addr *addr, int scope)
{
- struct inet6_ifaddr * ifaddr;
+ struct inet6_ifaddr *ifa;
int hash;
- unsigned long flags;
- save_flags(flags);
- cli();
+ ifa = kmalloc(sizeof(struct inet6_ifaddr), gfp_any());
- ifaddr = (struct inet6_ifaddr *) kmalloc(sizeof(struct inet6_ifaddr),
- GFP_ATOMIC);
-
- if (ifaddr == NULL)
- {
- printk(KERN_DEBUG "ipv6_add_addr: malloc failed\n");
- restore_flags(flags);
+ if (ifa == NULL) {
+ ADBG(("ipv6_add_addr: malloc failed\n"));
return NULL;
}
- memset(ifaddr, 0, sizeof(struct inet6_ifaddr));
- memcpy(&ifaddr->addr, addr, sizeof(struct in6_addr));
+ memset(ifa, 0, sizeof(struct inet6_ifaddr));
+ memcpy(&ifa->addr, addr, sizeof(struct in6_addr));
- ifaddr->scope = scope;
- ifaddr->idev = idev;
-
-
- /* add to list */
+ init_timer(&ifa->timer);
+ ifa->scope = scope;
+ ifa->idev = idev;
+ /* Add to list. */
hash = ipv6_addr_hash(addr);
- ifaddr->lst_next = inet6_addr_lst[hash];
- inet6_addr_lst[hash] = ifaddr;
-
+ ifa->lst_next = inet6_addr_lst[hash];
+ inet6_addr_lst[hash] = ifa;
- /* add to inet6_dev unicast addr list */
- ifaddr->if_next = idev->addr_list;
- idev->addr_list = ifaddr;
+ /* Add to inet6_dev unicast addr list. */
+ ifa->if_next = idev->addr_list;
+ idev->addr_list = ifa;
- restore_flags(flags);
- return ifaddr;
-
+ return ifa;
}
void ipv6_del_addr(struct inet6_ifaddr *ifp)
@@ -280,8 +242,7 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
struct inet6_ifaddr *iter, **back;
int hash;
- if (addr_list_lock)
- {
+ if (atomic_read(&addr_list_lock)) {
ifp->flags |= ADDR_INVALID;
return;
}
@@ -291,10 +252,8 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
iter = inet6_addr_lst[hash];
back = &inet6_addr_lst[hash];
- for (; iter; iter = iter->lst_next)
- {
- if (iter == ifp)
- {
+ for (; iter; iter = iter->lst_next) {
+ if (iter == ifp) {
*back = ifp->lst_next;
ifp->lst_next = NULL;
break;
@@ -305,10 +264,8 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
iter = ifp->idev->addr_list;
back = &ifp->idev->addr_list;
- for (; iter; iter = iter->if_next)
- {
- if (iter == ifp)
- {
+ for (; iter; iter = iter->if_next) {
+ if (iter == ifp) {
*back = ifp->if_next;
ifp->if_next = NULL;
break;
@@ -327,30 +284,26 @@ void ipv6_del_addr(struct inet6_ifaddr *ifp)
* an address of the attached interface
* iii) don't use deprecated addresses
*
- * at the moment i believe only iii) is missing.
+ * at the moment I believe only iii) is missing.
*/
-struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr)
+struct inet6_ifaddr * ipv6_get_saddr(struct dst_entry *dst,
+ struct in6_addr *daddr)
{
int scope;
- struct inet6_ifaddr * ifp = NULL;
- struct inet6_dev * i6dev;
- struct inet6_ifaddr * match = NULL;
+ struct inet6_ifaddr *ifp = NULL;
+ struct inet6_ifaddr *match = NULL;
struct device *dev = NULL;
+ struct rt6_info *rt;
int i;
+ rt = (struct rt6_info *) dst;
if (rt)
- {
- dev = rt->rt_dev;
- }
+ dev = rt->rt6i_dev;
atomic_inc(&addr_list_lock);
- scope = ipv6_addr_type(daddr);
-
- scope &= IPV6_ADDR_SCOPE_MASK;
-
- if (rt && (rt->rt_flags & RTI_ALLONLINK))
- {
+ scope = ipv6_addr_scope(daddr);
+ if (rt && (rt->rt6i_flags & RTF_ALLONLINK)) {
/*
* route for the "all destinations on link" rule
* when no routers are present
@@ -363,30 +316,23 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
* search dev and walk through dev addresses
*/
- if (dev)
- {
+ if (dev) {
+ struct inet6_dev *idev;
+ int hash;
+
if (dev->flags & IFF_LOOPBACK)
- {
scope = IFA_HOST;
- }
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- {
- if (i6dev->dev == dev)
- {
- for (ifp=i6dev->addr_list; ifp;
- ifp=ifp->if_next)
- {
- if (ifp->scope == scope)
- {
+ hash = ipv6_devindex_hash(dev->ifindex);
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+ if (idev->dev == dev) {
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == scope) {
if (!(ifp->flags & ADDR_STATUS))
- {
goto out;
- }
+
if (!(ifp->flags & ADDR_INVALID))
- {
match = ifp;
- }
}
}
break;
@@ -395,37 +341,27 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
}
if (scope == IFA_LINK)
- {
goto out;
- }
/*
* dev == NULL or search failed for specified dev
*/
- for (i=0; i < HASH_SIZE; i++)
- {
- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
- {
- if (ifp->scope == scope)
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
+ if (ifp->scope == scope) {
if (!(ifp->flags & ADDR_STATUS))
- {
goto out;
- }
+
if (!(ifp->flags & ADDR_INVALID))
- {
match = ifp;
- }
}
}
}
- out:
+out:
if (ifp == NULL && match)
- {
ifp = match;
- }
atomic_dec(&addr_list_lock);
return ifp;
}
@@ -433,14 +369,14 @@ struct inet6_ifaddr * ipv6_get_saddr(struct rt6_info *rt, struct in6_addr *daddr
struct inet6_ifaddr * ipv6_get_lladdr(struct device *dev)
{
struct inet6_ifaddr *ifp;
- struct inet6_dev *i6dev;
-
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- {
- if (i6dev->dev == dev)
- {
- for (ifp=i6dev->addr_list; ifp; ifp=ifp->if_next)
- {
+ struct inet6_dev *idev;
+ int hash;
+
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next) {
+ if (idev->dev == dev) {
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
if (ifp->scope == IFA_LINK)
return ifp;
}
@@ -464,154 +400,15 @@ struct inet6_ifaddr * ipv6_chk_addr(struct in6_addr *addr)
atomic_inc(&addr_list_lock);
hash = ipv6_addr_hash(addr);
-
- for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next)
- {
+ for(ifp = inet6_addr_lst[hash]; ifp; ifp=ifp->lst_next) {
if (ipv6_addr_cmp(&ifp->addr, addr) == 0)
- {
break;
- }
}
atomic_dec(&addr_list_lock);
return ifp;
}
-static void sit_route_add(struct inet6_dev *idev)
-{
- struct in6_rtmsg rtmsg;
- struct device *dev = idev->dev;
- int err;
-
- rtmsg.rtmsg_type = RTMSG_NEWROUTE;
-
- memset(&rtmsg.rtmsg_dst, 0, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- if (dev->pa_dstaddr == 0)
- {
- /* prefix length - 96 bytes "::d.d.d.d" */
- rtmsg.rtmsg_prefixlen = 96;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
- }
- else
- {
- rtmsg.rtmsg_prefixlen = 10;
- rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
- rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_UP;
- }
-
- rtmsg.rtmsg_ifindex = idev->if_index;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "sit_route_add: error in route_add\n");
- }
-}
-
-static void init_loopback(struct device *dev)
-{
- struct in6_addr addr;
- struct inet6_dev *idev;
- struct inet6_ifaddr * ifp;
- struct in6_rtmsg rtmsg;
- int err;
-
- /* ::1 */
-
- memset(&addr, 0, sizeof(struct in6_addr));
- addr.s6_addr[15] = 1;
-
- idev = ipv6_add_dev(dev);
-
- if (idev == NULL)
- {
- printk(KERN_DEBUG "init loopback: add_dev failed\n");
- return;
- }
-
- ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
-
- if (ifp == NULL)
- {
- printk(KERN_DEBUG "init_loopback: add_addr failed\n");
- return;
- }
-
- ifp->flags |= ADDR_PERMANENT;
-
- memcpy(&rtmsg.rtmsg_dst, &addr, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- rtmsg.rtmsg_prefixlen = 128;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_ifindex = idev->if_index;
-
- rtmsg.rtmsg_flags = RTF_NEXTHOP|RTF_HOST|RTF_UP;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "init_loopback: error in route_add\n");
- }
-
- /* add route for ::127.0.0.1 */
-}
-
-static void addrconf_eth_config(struct device *dev)
-{
- struct in6_addr addr;
- struct in6_addr maddr;
- struct inet6_ifaddr * ifp;
- struct inet6_dev * idev;
-
- memset(&addr, 0, sizeof(struct in6_addr));
-
- /* generate link local address*/
- addr.s6_addr[0] = 0xFE;
- addr.s6_addr[1] = 0x80;
-
- memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len),
- dev->dev_addr, dev->addr_len);
-
- idev = ipv6_add_dev(dev);
-
- if (idev == NULL)
- return;
-
- ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
-
- if (ifp == NULL)
- return;
-
- ifp->flags |= (DAD_INCOMPLETE | ADDR_PERMANENT);
- ifp->prefix_len = 10;
-
- /* join to all nodes multicast group */
- ipv6_addr_all_nodes(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
-
- if (ipv6_forwarding)
- {
- idev->router = 1;
- ipv6_addr_all_routers(&maddr);
- ipv6_dev_mc_inc(dev, &maddr);
- }
-
- /* join to solicited addr multicast group */
- addrconf_addr_solict_mult(&addr, &maddr);
- ipv6_dev_mc_inc(dev, &maddr);
-
- /* start dad */
- addrconf_dad_start(ifp);
-}
-
void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
{
struct prefix_info *pinfo;
@@ -623,9 +420,8 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
pinfo = (struct prefix_info *) opt;
- if (len < sizeof(struct prefix_info))
- {
- printk(KERN_DEBUG "addrconf: prefix option too short\n");
+ if (len < sizeof(struct prefix_info)) {
+ ADBG(("addrconf: prefix option too short\n"));
return;
}
@@ -636,17 +432,13 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
addr_type = ipv6_addr_type(&pinfo->prefix);
if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
return;
- }
valid_lft = ntohl(pinfo->valid);
prefered_lft = ntohl(pinfo->prefered);
- if (prefered_lft > valid_lft)
- {
- printk(KERN_WARNING
- "addrconf: prefix option has invalid lifetime\n");
+ if (prefered_lft > valid_lft) {
+ printk(KERN_WARNING "addrconf: prefix option has invalid lifetime\n");
return;
}
@@ -655,11 +447,7 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
* delete it
*/
- if (last_resort_rt && (last_resort_rt->rt_flags & RTI_ALLONLINK))
- {
- rt_release(last_resort_rt);
- last_resort_rt = NULL;
- }
+ rt6_purge_dflt_routers(RTF_ALLONLINK);
/*
* Two things going on here:
@@ -669,178 +457,86 @@ void addrconf_prefix_rcv(struct device *dev, u8 *opt, int len)
rt_expires = jiffies + valid_lft * HZ;
if (rt_expires < jiffies)
- {
rt_expires = ~0;
- }
- rt = fibv6_lookup(&pinfo->prefix, dev, RTI_DYNAMIC|RTI_GATEWAY);
-
- if (rt)
- {
- if (pinfo->onlink == 0 || valid_lft == 0)
- {
- /*
- * delete route
- */
- fib6_del_rt(rt);
+ rt = rt6_lookup(&pinfo->prefix, NULL, dev, RTF_LINKRT);
+
+ if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
+ if (pinfo->onlink == 0 || valid_lft == 0) {
+ ip6_del_rt(rt);
rt = NULL;
+ } else {
+ rt->rt6i_expires = rt_expires;
}
- else
- {
- rt->rt_expires = rt_expires;
- }
- }
- else if (pinfo->onlink && valid_lft)
- {
+ } else if (pinfo->onlink && valid_lft) {
struct in6_rtmsg rtmsg;
- struct inet6_dev *idev;
+ int err;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
printk(KERN_DEBUG "adding on link route\n");
- ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
- rtmsg.rtmsg_prefixlen = pinfo->prefix_len;
- rtmsg.rtmsg_metric = 1;
-
- if ((idev = ipv6_get_idev(dev)))
- {
- rtmsg.rtmsg_ifindex = idev->if_index;
- }
- rtmsg.rtmsg_flags = RTF_UP | RTF_ADDRCONF;
- rtmsg.rtmsg_info = rt_expires;
+ ipv6_addr_copy(&rtmsg.rtmsg_dst, &pinfo->prefix);
+ rtmsg.rtmsg_dst_len = pinfo->prefix_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ rtmsg.rtmsg_flags = RTF_UP | RTF_ADDRCONF;
+ rtmsg.rtmsg_info = rt_expires;
- ipv6_route_add(&rtmsg);
+ ip6_route_add(&rtmsg, &err);
}
- if (pinfo->autoconf && addrconf_sys_autoconf)
- {
+ if (pinfo->autoconf && ipv6_config.autoconf) {
struct inet6_ifaddr * ifp;
struct in6_addr addr;
int plen;
plen = pinfo->prefix_len >> 3;
- if (plen + dev->addr_len == sizeof(struct in6_addr))
- {
+ if (plen + dev->addr_len == sizeof(struct in6_addr)) {
memcpy(&addr, &pinfo->prefix, plen);
memcpy(addr.s6_addr + plen, dev->dev_addr,
dev->addr_len);
- }
- else
- {
- printk(KERN_DEBUG
- "addrconf: prefix_len invalid\n");
+ } else {
+ ADBG(("addrconf: prefix_len invalid\n"));
return;
}
ifp = ipv6_chk_addr(&addr);
- if (ifp == NULL && valid_lft)
- {
- /* create */
-
- struct inet6_dev *in6_dev;
-
- in6_dev = ipv6_get_idev(dev);
+ if (ifp == NULL && valid_lft) {
+ struct inet6_dev *in6_dev = ipv6_get_idev(dev);
if (in6_dev == NULL)
- {
- printk(KERN_DEBUG
- "addrconf: device not configured\n");
- }
+ ADBG(("addrconf: device not configured\n"));
ifp = ipv6_add_addr(in6_dev, &addr,
addr_type & IPV6_ADDR_SCOPE_MASK);
- if (dev->flags & IFF_MULTICAST)
- {
+ if (dev->flags & IFF_MULTICAST) {
struct in6_addr maddr;
- /* join to solicited addr multicast group */
+ /* Join to solicited addr multicast group. */
addrconf_addr_solict_mult(&addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
}
- ifp->flags |= DAD_INCOMPLETE;
ifp->prefix_len = pinfo->prefix_len;
addrconf_dad_start(ifp);
-
}
- if (ifp && valid_lft == 0)
- {
+ if (ifp && valid_lft == 0) {
ipv6_del_addr(ifp);
ifp = NULL;
}
- if (ifp)
- {
+ if (ifp) {
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
ifp->tstamp = jiffies;
}
}
-
-}
-
-static int addrconf_ifdown(struct device *dev)
-{
- struct inet6_dev *idev, **bidev;
- struct inet6_ifaddr *ifa, **bifa;
- int i;
-
- start_bh_atomic();
-
- bidev = &inet6_dev_lst;
-
- for (idev = inet6_dev_lst; idev; idev = idev->next)
- {
- if (idev->dev == dev)
- {
- *bidev = idev->next;
- break;
- }
- bidev = &idev;
- }
-
- if (idev == NULL)
- {
- printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
- end_bh_atomic();
- return -ENODEV;
- }
-
- /*
- * FIXME: clear multicast group membership
- */
-
- /*
- * clean addr_list
- */
-
- for (i=0; i<16; i++)
- {
- bifa = &inet6_addr_lst[i];
-
- for (ifa=inet6_addr_lst[i]; ifa; )
- {
- if (ifa->idev == idev)
- {
- *bifa = ifa->lst_next;
- del_timer(&ifa->timer);
- kfree(ifa);
- ifa = *bifa;
- continue;
- }
- bifa = &ifa;
- ifa = ifa->lst_next;
- }
- }
-
- kfree(idev);
- end_bh_atomic();
- return 0;
}
/*
@@ -852,67 +548,36 @@ int addrconf_set_dstaddr(void *arg)
{
struct in6_ifreq ireq;
struct device *dev;
- int err;
+ int err = -EINVAL;
- err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
-
- if (err)
- return -EFAULT;
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) {
+ err = -EFAULT;
+ goto err_exit;
+ }
- dev = dev_get(ireq.devname);
+ dev = dev_get_by_index(ireq.ifr6_ifindex);
- if (dev->type == ARPHRD_SIT)
- {
+ if (dev == NULL) {
+ err = -ENODEV;
+ goto err_exit;
+ }
+
+ if (dev->type == ARPHRD_SIT) {
struct device *dev;
- if (!(ipv6_addr_type(&ireq.addr) & IPV6_ADDR_COMPATv4))
- {
+ if (!(ipv6_addr_type(&ireq.ifr6_addr) & IPV6_ADDR_COMPATv4))
return -EADDRNOTAVAIL;
- }
- dev = sit_add_tunnel(ireq.addr.s6_addr32[3]);
+ dev = sit_add_tunnel(ireq.ifr6_addr.s6_addr32[3]);
if (dev == NULL)
- return -ENODEV;
-
- return 0;
- }
-
- return -EINVAL;
-}
-
-/*
- * Obtain if_index from device name
- */
-int addrconf_get_ifindex(void *arg)
-{
- struct ifreq ifr;
- int res = -ENODEV;
-
- if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
- {
- res = -EFAULT;
- }
- else
- {
- struct inet6_dev *idev;
-
- for (idev = inet6_dev_lst; idev; idev=idev->next)
- {
- if (!strncmp(ifr.ifr_name, idev->dev->name, IFNAMSIZ))
- {
- res = 0;
- ifr.ifr_ifindex = idev->if_index;
- if (copy_to_user(arg, &ifr, sizeof(ifr)))
- {
- res = -EFAULT;
- }
- break;
- }
- }
+ err = -ENODEV;
+ else
+ err = 0;
}
- return res;
+err_exit:
+ return err;
}
/*
@@ -920,61 +585,118 @@ int addrconf_get_ifindex(void *arg)
*/
int addrconf_add_ifaddr(void *arg)
{
- struct inet6_dev *in6_dev;
+ struct inet6_dev *idev;
struct in6_ifreq ireq;
struct inet6_ifaddr *ifp;
struct device *dev;
- int addr_type;
- int err;
+ int scope;
if (!suser())
return -EPERM;
- err = copy_from_user(&ireq, arg, sizeof(struct in6_ifreq));
- if (err)
+ if(copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
return -EFAULT;
- dev = dev_get(ireq.devname);
-
- if (dev == NULL)
+ if((dev = dev_get_by_index(ireq.ifr6_ifindex)) == NULL)
return -EINVAL;
- in6_dev = ipv6_get_idev(dev);
-
- if (in6_dev == NULL)
+ if ((idev = ipv6_get_idev(dev)) == NULL)
return -EINVAL;
- addr_type = ipv6_addr_type(&ireq.addr);
- addr_type &= IPV6_ADDR_SCOPE_MASK;
-
- ifp = ipv6_add_addr(in6_dev, &ireq.addr, addr_type);
+ scope = ipv6_addr_scope(&ireq.ifr6_addr);
- if (ifp == NULL)
+ if((ifp = ipv6_add_addr(idev, &ireq.ifr6_addr, scope)) == NULL)
return -ENOMEM;
ifp->prefix_len = 128;
- if (dev->flags & IFF_MULTICAST)
- {
+ if (dev->flags & IFF_MULTICAST) {
struct in6_addr maddr;
- /* join to solicited addr multicast group */
- addrconf_addr_solict_mult(&ireq.addr, &maddr);
+ /* Join to solicited addr multicast group. */
+ addrconf_addr_solict_mult(&ireq.ifr6_addr, &maddr);
ipv6_dev_mc_inc(dev, &maddr);
}
-
- ifp->prefix_len = ireq.prefix_len;
+ ifp->prefix_len = ireq.ifr6_prefixlen;
ifp->flags |= ADDR_PERMANENT;
if (!(dev->flags & (IFF_NOARP|IFF_LOOPBACK)))
- {
- ifp->flags |= DAD_INCOMPLETE;
addrconf_dad_start(ifp);
- }
+ else
+ ip6_rt_addr_add(&ifp->addr, dev);
+
return 0;
}
+static void sit_route_add(struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *rt;
+ int err;
+
+ ADBG(("sit_route_add(%s): ", dev->name));
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+ if (dev->pa_dstaddr == 0) {
+ ADBG(("pa_dstaddr=0, "));
+ /* prefix length - 96 bytes "::d.d.d.d" */
+ rtmsg.rtmsg_dst_len = 96;
+ rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_UP;
+ } else {
+ ADBG(("pa_dstaddr=%08x, ", dev->pa_dstaddr));
+ rtmsg.rtmsg_dst_len = 10;
+ rtmsg.rtmsg_dst.s6_addr32[0] = __constant_htonl(0xfe800000);
+ rtmsg.rtmsg_dst.s6_addr32[3] = dev->pa_dstaddr;
+ rtmsg.rtmsg_gateway.s6_addr32[3]= dev->pa_dstaddr;
+ rtmsg.rtmsg_flags = RTF_UP;
+ }
+
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+ ADBG(("doing ip6_route_add()\n"));
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (err) {
+#if ACONF_DEBUG >= 1
+ printk(KERN_DEBUG "sit_route_add: error %d in route_add\n", err);
+#endif
+ }
+
+ ADBG(("sit_route_add(cont): "));
+ if (dev->pa_dstaddr) {
+ struct rt6_info *mrt;
+
+ ADBG(("pa_dstaddr != 0, "));
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_gateway);
+ if (rt->rt6i_nexthop == NULL) {
+ ADBG(("can't get neighbour\n"));
+ printk(KERN_DEBUG "sit_route: get_neigh failed\n");
+ }
+
+ /*
+ * Add multicast route.
+ */
+ ADBG(("add MULT, "));
+ ipv6_addr_set(&rtmsg.rtmsg_dst, __constant_htonl(0xFF000000), 0, 0, 0);
+
+ rtmsg.rtmsg_dst_len = 8;
+ rtmsg.rtmsg_flags = RTF_UP;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+
+ memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
+ ADBG(("doing ip6_route_add()\n"));
+ mrt = ip6_route_add(&rtmsg, &err);
+
+ if (mrt)
+ mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ } else {
+ ADBG(("pa_dstaddr==0\n"));
+ }
+}
+
static void sit_add_v4_addrs(struct inet6_dev *idev)
{
struct inet6_ifaddr * ifp;
@@ -984,26 +706,20 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
memset(&addr, 0, sizeof(struct in6_addr));
- if (idev->dev->pa_dstaddr)
- {
+ if (idev->dev->pa_dstaddr) {
addr.s6_addr32[0] = __constant_htonl(0xfe800000);
scope = IFA_LINK;
- }
- else
- {
+ } else {
scope = IPV6_ADDR_COMPATv4;
}
- for (dev = dev_base; dev != NULL; dev = dev->next)
- {
- if (dev->family == AF_INET && (dev->flags & IFF_UP))
- {
+ for (dev = dev_base; dev != NULL; dev = dev->next) {
+ if (dev->family == AF_INET && (dev->flags & IFF_UP)) {
int flag = scope;
addr.s6_addr32[3] = dev->pa_addr;
- if (dev->flags & IFF_LOOPBACK)
- {
+ if (dev->flags & IFF_LOOPBACK) {
if (idev->dev->pa_dstaddr)
continue;
@@ -1011,15 +727,94 @@ static void sit_add_v4_addrs(struct inet6_dev *idev)
}
ifp = ipv6_add_addr(idev, &addr, flag);
-
+
if (ifp == NULL)
continue;
ifp->flags |= ADDR_PERMANENT;
+ ip6_rt_addr_add(&ifp->addr, dev);
}
}
}
+static void init_loopback(struct device *dev)
+{
+ struct in6_addr addr;
+ struct inet6_dev *idev;
+ struct inet6_ifaddr * ifp;
+ int err;
+
+ /* ::1 */
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+ addr.s6_addr[15] = 1;
+
+ idev = ipv6_add_dev(dev);
+
+ if (idev == NULL) {
+ printk(KERN_DEBUG "init loopback: add_dev failed\n");
+ return;
+ }
+
+ ifp = ipv6_add_addr(idev, &addr, IFA_HOST);
+
+ if (ifp == NULL) {
+ printk(KERN_DEBUG "init_loopback: add_addr failed\n");
+ return;
+ }
+
+ ifp->flags |= ADDR_PERMANENT;
+
+ err = ip6_rt_addr_add(&addr, dev);
+ if (err)
+ printk(KERN_DEBUG "init_loopback: error in route_add\n");
+}
+
+static void addrconf_eth_config(struct device *dev)
+{
+ struct in6_addr addr;
+ struct in6_addr maddr;
+ struct inet6_ifaddr * ifp;
+ struct inet6_dev * idev;
+
+ memset(&addr, 0, sizeof(struct in6_addr));
+
+ /* Generate link local address. */
+ addr.s6_addr[0] = 0xFE;
+ addr.s6_addr[1] = 0x80;
+
+ memcpy(addr.s6_addr + (sizeof(struct in6_addr) - dev->addr_len),
+ dev->dev_addr, dev->addr_len);
+
+ idev = ipv6_add_dev(dev);
+ if (idev == NULL)
+ return;
+
+ ifp = ipv6_add_addr(idev, &addr, IFA_LINK);
+ if (ifp == NULL)
+ return;
+
+ ifp->flags = ADDR_PERMANENT;
+ ifp->prefix_len = 10;
+
+ /* Join to all nodes multicast group. */
+ ipv6_addr_all_nodes(&maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+
+ if (ipv6_config.forwarding) {
+ idev->router = 1;
+ ipv6_addr_all_routers(&maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+ }
+
+ /* Join to solicited addr multicast group. */
+ addrconf_addr_solict_mult(&addr, &maddr);
+ ipv6_dev_mc_inc(dev, &maddr);
+
+ /* Start duplicate address detection. */
+ addrconf_dad_start(ifp);
+}
+
int addrconf_notify(struct notifier_block *this, unsigned long event,
void * data)
{
@@ -1050,7 +845,7 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
* route.
*/
- sit_route_add(idev);
+ sit_route_add(dev);
break;
case ARPHRD_LOOPBACK:
@@ -1058,12 +853,12 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
break;
case ARPHRD_ETHER:
-
printk(KERN_DEBUG "Configuring eth interface\n");
addrconf_eth_config(dev);
break;
- }
- rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, 0, dev, 0, 0);
+ };
+
+ rt6_sndmsg(RTMSG_NEWDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
break;
case NETDEV_DOWN:
@@ -1071,104 +866,72 @@ int addrconf_notify(struct notifier_block *this, unsigned long event,
* Remove all addresses from this interface
* and take the interface out of the list.
*/
- if (addrconf_ifdown(dev) == 0)
- {
+ if (addrconf_ifdown(dev) == 0) {
+#if 0
rt6_ifdown(dev);
- rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, 0, dev, 0, 0);
+#endif
+ rt6_sndmsg(RTMSG_DELDEVICE, NULL, NULL, NULL, dev, 0, 0, 0, 0);
}
break;
- }
+ };
return NOTIFY_OK;
}
-static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
-{
- struct in6_rtmsg rtmsg;
- struct device *dev;
- int err;
-
-
- if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)
- {
- struct in6_addr all_routers;
-
- /*
- * 1) configure a link route for this interface
- * 2) send a (delayed) router solicitation
- */
-
- memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
- memset(&rtmsg.rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- dev = ifp->idev->dev;
- rtmsg.rtmsg_prefixlen = ifp->prefix_len;
- rtmsg.rtmsg_metric = 1;
- rtmsg.rtmsg_ifindex = ifp->idev->if_index;
-
- rtmsg.rtmsg_flags = RTF_UP;
-
- err = ipv6_route_add(&rtmsg);
-
- if (err)
- {
- printk(KERN_DEBUG "dad_complete: error in route_add\n");
- }
+static int addrconf_ifdown(struct device *dev)
+{
+ struct inet6_dev *idev, **bidev;
+ struct inet6_ifaddr *ifa, **bifa;
+ int i, hash;
- if (ipv6_forwarding == 0)
- {
- ipv6_addr_set(&all_routers,
- __constant_htonl(0xff020000U), 0, 0,
- __constant_htonl(0x2U));
+ start_bh_atomic();
- /*
- * If a host as already performed a random delay
- * [...] as part of DAD [...] there is no need
- * to delay again before sending the first RS
- */
- ndisc_send_rs(ifp->idev->dev, &ifp->addr,
- &all_routers);
+ hash = ipv6_devindex_hash(dev->ifindex);
+ bidev = &inet6_dev_lst[hash];
- ifp->probes = 1;
- ifp->timer.function = addrconf_rs_timer;
- ifp->timer.expires = (jiffies +
- RTR_SOLICITATION_INTERVAL);
- ifp->idev->if_flags |= IF_RS_SENT;
- add_timer(&ifp->timer);
+ for (idev = inet6_dev_lst[hash]; idev; idev = idev->next) {
+ if (idev->dev == dev) {
+ *bidev = idev->next;
+ break;
}
+ bidev = &idev->next;
}
-}
+ if (idev == NULL) {
+ printk(KERN_DEBUG "addrconf_ifdown: device not found\n");
+ end_bh_atomic();
+ return -ENODEV;
+ }
-static void addrconf_dad_timer(unsigned long data)
-{
- struct inet6_ifaddr *ifp;
- struct in6_addr unspec;
- struct in6_addr mcaddr;
+ /*
+ * FIXME: clear multicast group membership
+ */
- ifp = (struct inet6_ifaddr *) data;
+ /*
+ * clean addr_list
+ */
- if (ifp->probes-- == 0)
- {
- /*
- * DAD was successful
- */
+ for (i=0; i<16; i++) {
+ bifa = &inet6_addr_lst[i];
- ifp->flags &= ~DAD_INCOMPLETE;
- addrconf_dad_completed(ifp);
- return;
+ for (ifa=inet6_addr_lst[i]; ifa; ) {
+ if (ifa->idev == idev) {
+ *bifa = ifa->lst_next;
+ del_timer(&ifa->timer);
+ kfree(ifa);
+ ifa = *bifa;
+ continue;
+ }
+ ifa = ifa->lst_next;
+ bifa = &ifa->lst_next;
+ }
}
- /* send a neighbour solicitation for our addr */
- memset(&unspec, 0, sizeof(unspec));
- addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
-
- ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
-
- ifp->timer.expires = jiffies + RETRANS_TIMER;
- add_timer(&ifp->timer);
+ kfree(idev);
+ end_bh_atomic();
+ return 0;
}
static void addrconf_rs_timer(unsigned long data)
@@ -1177,11 +940,10 @@ static void addrconf_rs_timer(unsigned long data)
ifp = (struct inet6_ifaddr *) data;
- if (ipv6_forwarding)
+ if (ipv6_config.forwarding)
return;
- if (ifp->idev->if_flags & IF_RA_RCVD)
- {
+ if (ifp->idev->if_flags & IF_RA_RCVD) {
/*
* Announcement received after solicitation
* was sent
@@ -1189,8 +951,7 @@ static void addrconf_rs_timer(unsigned long data)
return;
}
- if (ifp->probes++ <= MAX_RTR_SOLICITATIONS)
- {
+ if (ifp->probes++ <= ipv6_config.rtr_solicits) {
struct in6_addr all_routers;
ipv6_addr_set(&all_routers,
@@ -1199,54 +960,77 @@ static void addrconf_rs_timer(unsigned long data)
ndisc_send_rs(ifp->idev->dev, &ifp->addr,
&all_routers);
-
ifp->timer.function = addrconf_rs_timer;
- ifp->timer.expires = jiffies + RTR_SOLICITATION_INTERVAL;
+ ifp->timer.expires = (jiffies +
+ ipv6_config.rtr_solicit_interval);
add_timer(&ifp->timer);
- }
- else
- {
+ } else {
+ struct in6_rtmsg rtmsg;
+ int err;
+
+#if ACONF_DEBUG >= 2
printk(KERN_DEBUG "%s: no IPv6 routers present\n",
ifp->idev->dev->name);
+#endif
- if (!default_rt_list && !last_resort_rt)
- {
- struct rt6_info *rt;
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_flags = (RTF_ALLONLINK | RTF_ADDRCONF |
+ RTF_DEFAULT | RTF_UP);
- /*
- * create a last resort route with all
- * destinations on link
- */
- rt = kmalloc(sizeof(struct rt6_info), GFP_ATOMIC);
-
- if (rt)
- {
- memset(rt, 0, sizeof(struct rt6_info));
- rt->rt_dev = ifp->idev->dev;
- rt->rt_ref = 1;
- rt->rt_flags = (RTI_ALLONLINK | RTF_UP);
- last_resort_rt = rt;
- }
- }
+ rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex;
+
+ ip6_route_add(&rtmsg, &err);
}
}
+/*
+ * Duplicate Address Detection
+ */
static void addrconf_dad_start(struct inet6_ifaddr *ifp)
{
static int rand_seed = 1;
- int rand_num;
+ struct device *dev;
+ unsigned long rand_num;
+
+ dev = ifp->idev->dev;
+
+ if (dev->flags & IFF_MULTICAST) {
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *mrt;
+ int err;
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ ipv6_addr_set(&rtmsg.rtmsg_dst,
+ __constant_htonl(0xFF000000), 0, 0, 0);
+
+ rtmsg.rtmsg_dst_len = 8;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rtmsg.rtmsg_flags = RTF_UP;
- if (rand_seed)
- {
+ mrt = ip6_route_add(&rtmsg, &err);
+
+ if (err)
+ printk(KERN_DEBUG "dad_start: mcast route add failed\n");
+ else
+ mrt->rt6i_nexthop = ndisc_get_neigh(dev, &rtmsg.rtmsg_dst);
+ }
+
+ if (rand_seed) {
rand_seed = 0;
nd_rand_seed = ifp->addr.s6_addr32[3];
}
init_timer(&ifp->timer);
- ifp->probes = DupAddrDetectTransmits;
- rand_num = ipv6_random() % MAX_RTR_SOLICITATION_DELAY;
+ ifp->probes = ipv6_config.dad_transmits;
+ ifp->flags |= DAD_INCOMPLETE;
+
+ rand_num = ipv6_random() % ipv6_config.rtr_solicit_delay;
ifp->timer.function = addrconf_dad_timer;
ifp->timer.data = (unsigned long) ifp;
@@ -1255,6 +1039,97 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp)
add_timer(&ifp->timer);
}
+static void addrconf_dad_timer(unsigned long data)
+{
+ struct inet6_ifaddr *ifp;
+ struct in6_addr unspec;
+ struct in6_addr mcaddr;
+
+ ifp = (struct inet6_ifaddr *) data;
+
+ if (ifp->probes == 0) {
+ /*
+ * DAD was successful
+ */
+
+ ifp->flags &= ~DAD_INCOMPLETE;
+ addrconf_dad_completed(ifp);
+ return;
+ }
+
+ ifp->probes--;
+
+ /* send a neighbour solicitation for our addr */
+ memset(&unspec, 0, sizeof(unspec));
+ addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
+
+ ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);
+
+ ifp->timer.expires = jiffies + ipv6_config.rtr_solicit_interval;
+ add_timer(&ifp->timer);
+}
+
+static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
+{
+ struct device *dev;
+ int err;
+
+ dev = ifp->idev->dev;
+
+ if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+ struct in6_rtmsg rtmsg;
+ struct in6_addr all_routers;
+
+ /*
+ * 1) configure a link route for this interface
+ * 2) send a (delayed) router solicitation
+ */
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+
+ memcpy(&rtmsg.rtmsg_dst, &ifp->addr, sizeof(struct in6_addr));
+
+ rtmsg.rtmsg_dst_len = ifp->prefix_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rtmsg.rtmsg_flags = RTF_UP;
+
+ ip6_route_add(&rtmsg, &err);
+
+ if (err)
+ printk(KERN_DEBUG "dad_complete: error in route_add\n");
+
+ if (ipv6_config.forwarding == 0) {
+ ipv6_addr_set(&all_routers,
+ __constant_htonl(0xff020000U), 0, 0,
+ __constant_htonl(0x2U));
+
+ /*
+ * If a host as already performed a random delay
+ * [...] as part of DAD [...] there is no need
+ * to delay again before sending the first RS
+ */
+ ndisc_send_rs(ifp->idev->dev, &ifp->addr,
+ &all_routers);
+
+ ifp->probes = 1;
+ ifp->timer.function = addrconf_rs_timer;
+ ifp->timer.expires = (jiffies +
+ ipv6_config.rtr_solicit_interval);
+ ifp->idev->if_flags |= IF_RS_SENT;
+ add_timer(&ifp->timer);
+ }
+ }
+
+ /*
+ * configure the address for reception
+ */
+
+ ip6_rt_addr_add(&ifp->addr, dev);
+}
+
+#ifdef CONFIG_PROC_FS
static int iface_proc_info(char *buffer, char **start, off_t offset,
int length, int dummy)
{
@@ -1262,13 +1137,11 @@ static int iface_proc_info(char *buffer, char **start, off_t offset,
int i;
int len = 0;
- for (i=0; i < HASH_SIZE; i++)
- for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next)
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++)
+ for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) {
int j;
- for (j=0; j<16; j++)
- {
+ for (j=0; j<16; j++) {
sprintf(buffer + len, "%02x",
ifp->addr.s6_addr[j]);
len += 2;
@@ -1276,7 +1149,7 @@ static int iface_proc_info(char *buffer, char **start, off_t offset,
len += sprintf(buffer + len,
" %02x %02x %02x %02x %8s\n",
- ifp->idev->if_index,
+ ifp->idev->dev->ifindex,
ifp->prefix_len,
ifp->scope,
ifp->flags,
@@ -1299,7 +1172,7 @@ struct proc_dir_entry iface_proc_entry =
0, NULL,
&iface_proc_info
};
-
+#endif /* CONFIG_PROC_FS */
/*
* Periodic address status verification
@@ -1311,29 +1184,23 @@ void addrconf_verify(unsigned long foo)
unsigned long now = jiffies;
int i;
- for (i=0; i < HASH_SIZE; i++)
- {
- for (ifp=inet6_addr_lst[i]; ifp;)
- {
- if (!(ifp->flags & ADDR_PERMANENT))
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifp=inet6_addr_lst[i]; ifp;) {
+ if (!(ifp->flags & ADDR_PERMANENT)) {
struct inet6_ifaddr *bp;
unsigned long age;
age = (now - ifp->tstamp) / HZ;
if (age > ifp->prefered_lft)
- {
ifp->flags |= ADDR_DEPRECATED;
- }
bp = ifp;
ifp=ifp->lst_next;
if (age > bp->valid_lft)
- {
ipv6_del_addr(bp);
- }
+
continue;
}
ifp=ifp->lst_next;
@@ -1344,18 +1211,25 @@ void addrconf_verify(unsigned long foo)
add_timer(&addr_chk_timer);
}
+/*
+ * Init / cleanup code
+ */
+
void addrconf_init()
{
struct device *dev;
- /* init addr hash list */
- memset(inet6_addr_lst, 0, 16 * sizeof(struct inet6_ifaddr *));
+ /*
+ * init address and device hash lists
+ */
+
+ memset(inet6_addr_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_ifaddr *));
- memset(inet6_mcast_lst, 0, 16 * sizeof(struct ipv6_mc_list *));
+ memset(inet6_mcast_lst, 0, IN6_ADDR_HSIZE * sizeof(struct ifmcaddr6 *));
- inet6_dev_lst = NULL;
+ memset(inet6_dev_lst, 0, IN6_ADDR_HSIZE * sizeof(struct inet6_dev *));
- /*
+ /*
* Init loopback device
*/
@@ -1374,16 +1248,19 @@ void addrconf_init()
if (dev && (dev->flags & IFF_UP))
addrconf_eth_config(dev);
- proc_register_dynamic(&proc_net, &iface_proc_entry);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&iface_proc_entry);
+#endif
addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY;
add_timer(&addr_chk_timer);
}
+#ifdef MODULE
void addrconf_cleanup(void)
{
- struct inet6_dev *idev, *bidev;
- struct inet6_ifaddr *ifa, *bifa;
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
int i;
del_timer(&addr_chk_timer);
@@ -1392,32 +1269,32 @@ void addrconf_cleanup(void)
* clean dev list.
*/
- for (idev = inet6_dev_lst; idev; )
- {
- bidev = idev;
- idev = idev->next;
- kfree(bidev);
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (idev = inet6_dev_lst[i]; idev; ) {
+ struct inet6_dev *back;
+
+ back = idev;
+ idev = idev->next;
+ kfree(back);
+ }
}
/*
* clean addr_list
*/
- for (i=0; i<16; i++)
- {
- for (ifa=inet6_addr_lst[i]; ifa; )
- {
+ for (i=0; i < IN6_ADDR_HSIZE; i++) {
+ for (ifa=inet6_addr_lst[i]; ifa; ) {
+ struct inet6_ifaddr *bifa;
+
bifa = ifa;
ifa = ifa->lst_next;
kfree(bifa);
}
}
- proc_unregister(&proc_net, iface_proc_entry.low_ino);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(iface_proc_entry.low_ino);
+#endif
}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
+#endif /* MODULE */
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 2609e5294..0f6bbf4de 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/af_inet.c
*
- * $Id: af_inet6.c,v 1.13 1996/10/31 19:47:17 roque Exp $
+ * $Id: af_inet6.c,v 1.16 1997/03/18 18:24:26 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -35,293 +35,182 @@
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-
#include <linux/inet.h>
#include <linux/netdevice.h>
+#include <linux/icmpv6.h>
+
#include <net/ip.h>
#include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/arp.h>
-#include <net/rarp.h>
-#include <net/route.h>
-#include <net/tcp.h>
#include <net/udp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/raw.h>
-#include <net/icmp.h>
-#include <linux/icmpv6.h>
+#include <net/tcp.h>
+#include <net/sit.h>
+#include <net/protocol.h>
#include <net/inet_common.h>
#include <net/transp_v6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/sit.h>
-#include <linux/ip_fw.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
-/*
- * Default callbacks for user INET sockets. These just wake up
- * the user owning the socket.
- */
-
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk,int len)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 1);
- }
-}
+#include <asm/uaccess.h>
+#include <asm/system.h>
-static void def_callback3(struct sock *sk)
-{
- long wmem;
-
- wmem = (long) sk->wmem_alloc;
+extern struct proto_ops inet6_stream_ops;
+extern struct proto_ops inet6_dgram_ops;
- if (wmem < 0) {
- printk(KERN_DEBUG "bug wmem_alloc < 0\n");
- sk->wmem_alloc = 0;
- }
-
- if(!sk->dead && sk->wmem_alloc*2 <= sk->sndbuf)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 2);
- }
-}
+/* IPv6 procfs goodies... */
-struct sock * rawv6_sock_array[SOCK_ARRAY_SIZE];
+#ifdef CONFIG_PROC_FS
+extern int raw6_get_info(char *, char **, off_t, int, int);
+extern int tcp6_get_info(char *, char **, off_t, int, int);
+extern int udp6_get_info(char *, char **, off_t, int, int);
+extern int afinet6_get_info(char *, char **, off_t, int, int);
+#endif
static int inet6_create(struct socket *sock, int protocol)
{
struct sock *sk;
struct proto *prot;
- int err;
sk = sk_alloc(GFP_KERNEL);
if (sk == NULL)
- return(-ENOBUFS);
-
- /* Efficient way to set most fields to zero */
- memset(sk,0,sizeof(*sk));
-
- /*
- * Note for tcp that also wiped the dummy_th block for us.
- */
-
- switch(sock->type)
- {
- case SOCK_STREAM:
- case SOCK_SEQPACKET:
- if (protocol && protocol != IPPROTO_TCP)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_TCP;
- sk->no_check = TCP_NO_CHECK;
- prot = &tcpv6_prot;
- break;
-
- case SOCK_DGRAM:
- if (protocol && protocol != IPPROTO_UDP)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- protocol = IPPROTO_UDP;
- sk->no_check = UDP_NO_CHECK;
- prot=&udpv6_prot;
- break;
-
- case SOCK_RAW:
- if (!suser())
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPERM);
- }
- if (!protocol)
- {
- kfree_s((void *)sk, sizeof(*sk));
- return(-EPROTONOSUPPORT);
- }
- prot = &rawv6_prot;
- sk->reuse = 1;
- sk->num = protocol;
- break;
- default:
- kfree_s((void *)sk, sizeof(*sk));
- return(-ESOCKTNOSUPPORT);
+ goto do_oom;
+
+ /* Note for tcp that also wiped the dummy_th block for us. */
+ if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+ if (protocol && protocol != IPPROTO_TCP)
+ goto free_and_noproto;
+ protocol = IPPROTO_TCP;
+ sk->no_check = TCP_NO_CHECK;
+ prot = &tcpv6_prot;
+ sock->ops = &inet6_stream_ops;
+ } else if(sock->type == SOCK_DGRAM) {
+ if (protocol && protocol != IPPROTO_UDP)
+ goto free_and_noproto;
+ protocol = IPPROTO_UDP;
+ sk->no_check = UDP_NO_CHECK;
+ prot=&udpv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ } else if(sock->type == SOCK_RAW) {
+ if (!suser())
+ goto free_and_badperm;
+ if (!protocol)
+ goto free_and_noproto;
+ prot = &rawv6_prot;
+ sock->ops = &inet6_dgram_ops;
+ sk->reuse = 1;
+ sk->num = protocol;
+ } else {
+ goto free_and_badtype;
}
+
+ sock_init_data(sock, sk);
- sk->socket = sock;
-
- sk->family = AF_INET6;
- sk->type = sock->type;
- sk->protocol = protocol;
- sk->allocation = GFP_KERNEL;
- sk->sndbuf = SK_WMEM_MAX;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->priority = 1;
-
- sk->prot = prot;
- sk->backlog_rcv = prot->backlog_rcv;
-
- sk->sleep = sock->wait;
- sock->data =(void *) sk;
-
- sk->state = TCP_CLOSE;
-
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->back_log);
+ sk->zapped = 0;
+ sk->family = AF_INET6;
+ sk->protocol = protocol;
- sk->timer.data = (unsigned long)sk;
- sk->timer.function = &net_timer;
- init_timer(&sk->timer);
+ sk->prot = prot;
+ sk->backlog_rcv = prot->backlog_rcv;
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback3;
- sk->error_report = def_callback1;
+ sk->timer.data = (unsigned long)sk;
+ sk->timer.function = &net_timer;
- sk->net_pinfo.af_inet6.hop_limit = ipv6_hop_limit;
+ sk->net_pinfo.af_inet6.hop_limit = ipv6_config.hop_limit;
sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
sk->net_pinfo.af_inet6.mc_loop = 1;
- /*
- * init the ipv4 part of the socket since
- * we can have sockets using v6 API for ipv4
+ /* Init the ipv4 part of the socket since we can have sockets
+ * using v6 API for ipv4.
*/
+ sk->ip_ttl = 64;
- sk->ip_ttl=64;
-
-#ifdef CONFIG_IP_MULTICAST
- sk->ip_mc_loop=1;
- sk->ip_mc_ttl=1;
- *sk->ip_mc_name=0;
- sk->ip_mc_list=NULL;
-#endif
-
+ sk->ip_mc_loop = 1;
+ sk->ip_mc_ttl = 1;
+ sk->ip_mc_index = 0;
+ sk->ip_mc_list = NULL;
if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
sk->ip_hdrincl=1;
- if (sk->num)
- {
- /*
- * It assumes that any protocol which allows
+ if (sk->num) {
+ /* It assumes that any protocol which allows
* the user to assign a number at socket
- * creation time automatically
- * shares.
+ * creation time automatically shares.
*/
-
- inet_put_sock(sk->num, sk);
sk->dummy_th.source = ntohs(sk->num);
+ if(sk->prot->hash)
+ sk->prot->hash(sk);
+ add_to_prot_sklist(sk);
}
- if (sk->prot->init)
- {
- err = sk->prot->init(sk);
- if (err != 0)
- {
+ if (sk->prot->init) {
+ int err = sk->prot->init(sk);
+ if (err != 0) {
destroy_sock(sk);
return(err);
}
}
MOD_INC_USE_COUNT;
return(0);
+
+free_and_badtype:
+ sk_free(sk);
+ return -ESOCKTNOSUPPORT;
+free_and_badperm:
+ sk_free(sk);
+ return -EPERM;
+free_and_noproto:
+ sk_free(sk);
+ return -EPROTONOSUPPORT;
+do_oom:
+ return -ENOBUFS;
}
static int inet6_dup(struct socket *newsock, struct socket *oldsock)
{
- return(inet6_create(newsock,
- ((struct sock *)(oldsock->data))->protocol));
+ return(inet6_create(newsock, oldsock->sk->protocol));
}
-
-/*
- * bind for INET6 API
- */
-
-static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
- int addr_len)
+/* bind for INET6 API */
+static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
- struct sock *sk=(struct sock *)sock->data, *sk2;
+ struct sock *sk = sock->sk;
__u32 v4addr = 0;
unsigned short snum = 0;
int addr_type = 0;
- /*
- * If the socket has its own bind function then use it.
- */
-
+ /* If the socket has its own bind function then use it. */
if(sk->prot->bind)
return sk->prot->bind(sk, uaddr, addr_len);
- /* check this error. */
- if (sk->state != TCP_CLOSE)
- return(-EINVAL);
-
- if(addr_len < sizeof(struct sockaddr_in6))
+ /* Check these errors (active socket, bad address length, double bind). */
+ if ((sk->state != TCP_CLOSE) ||
+ (addr_len < sizeof(struct sockaddr_in6)) ||
+ (sk->num != 0))
return -EINVAL;
- if(sock->type != SOCK_RAW)
- {
- if (sk->num != 0)
- return(-EINVAL);
-
- snum = ntohs(addr->sin6_port);
-
- if (snum == 0)
- snum = get_new_socknum(sk->prot, 0);
-
- if (snum < PROT_SOCK && !suser())
- return(-EACCES);
- }
+ snum = ntohs(addr->sin6_port);
+ if (snum == 0)
+ snum = sk->prot->good_socknum();
+ if (snum < PROT_SOCK && !suser())
+ return(-EACCES);
addr_type = ipv6_addr_type(&addr->sin6_addr);
-
if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
- {
return(-EINVAL);
- }
-
- /*
- * check if the address belongs to the host
- */
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
v4addr = addr->sin6_addr.s6_addr32[3];
-
- if (ip_chk_addr(v4addr) != IS_MYADDR)
+ if (__ip_chk_addr(v4addr) != IS_MYADDR)
return(-EADDRNOTAVAIL);
- }
- else
- {
- if (addr_type != IPV6_ADDR_ANY)
- {
- /*
- * ipv4 addr of the socket is invalid.
- * only the unpecified and mapped address
- * have a v4 equivalent.
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
*/
-
v4addr = LOOPBACK4_IPV6;
-
- if (!(addr_type & IPV6_ADDR_MULTICAST))
- {
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
return(-EADDRNOTAVAIL);
}
@@ -338,82 +227,16 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
sizeof(struct in6_addr));
- if(sock->type != SOCK_RAW)
- {
- /* Make sure we are allowed to bind here. */
- cli();
- for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
- sk2 != NULL; sk2 = sk2->next)
- {
- /*
- * Hash collision or real match ?
- */
-
- if (sk2->num != snum)
- continue;
-
- /*
- * Either bind on the port is wildcard means
- * they will overlap and thus be in error.
- * We use the sk2 v4 address to test the
- * other socket since addr_any in av4 implies
- * addr_any in v6
- */
-
- if (addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr))
- {
- /*
- * Allow only if both are setting reuse.
- */
- if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
- continue;
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Two binds match ?
- */
-
- if (ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
- &sk2->net_pinfo.af_inet6.rcv_saddr))
-
- continue;
- /*
- * Reusable port ?
- */
-
- if (!sk->reuse)
- {
- sti();
- return(-EADDRINUSE);
- }
-
- /*
- * Reuse ?
- */
-
- if (!sk2->reuse || sk2->state==TCP_LISTEN)
- {
- sti();
- return(-EADDRINUSE);
- }
- }
- sti();
+ /* Make sure we are allowed to bind here. */
+ if(sk->prot->verify_bind(sk, snum))
+ return -EADDRINUSE;
- inet_remove_sock(sk);
-
- /*
- if(sock->type==SOCK_DGRAM)
- udp_cache_zap();
- if(sock->type==SOCK_STREAM)
- tcp_cache_zap();
- */
- inet_put_sock(snum, sk);
- sk->dummy_th.source = ntohs(sk->num);
- sk->dummy_th.dest = 0;
- sk->daddr = 0;
- }
+ sk->num = snum;
+ sk->dummy_th.source = ntohs(sk->num);
+ sk->dummy_th.dest = 0;
+ sk->daddr = 0;
+ sk->prot->rehash(sk);
+ add_to_prot_sklist(sk);
return(0);
}
@@ -440,39 +263,32 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
struct sock *sk;
sin->sin6_family = AF_INET6;
- sk = (struct sock *) sock->data;
- if (peer)
- {
+ sk = sock->sk;
+ if (peer) {
if (!tcp_connected(sk->state))
return(-ENOTCONN);
sin->sin6_port = sk->dummy_th.dest;
memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
sizeof(struct in6_addr));
- }
- else
- {
- if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) ==
- IPV6_ADDR_ANY)
+ } else {
+ if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
memcpy(&sin->sin6_addr,
&sk->net_pinfo.af_inet6.saddr,
sizeof(struct in6_addr));
-
else
memcpy(&sin->sin6_addr,
&sk->net_pinfo.af_inet6.rcv_saddr,
sizeof(struct in6_addr));
sin->sin6_port = sk->dummy_th.source;
-
}
-
*uaddr_len = sizeof(*sin);
return(0);
}
static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk=(struct sock *)sock->data;
+ struct sock *sk = sock->sk;
int err;
int pid;
@@ -484,7 +300,7 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if(err)
return err;
- /* see inet_fcntl */
+ /* see sock_no_fcntl */
if (current->pid != pid && current->pgrp != -pid && !suser())
return -EPERM;
sk->proc = pid;
@@ -545,18 +361,10 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCGIFMAP:
case SIOCSIFSLAVE:
case SIOCGIFSLAVE:
+ case SIOGIFINDEX:
return(dev_ioctl(cmd,(void *) arg));
- return -EINVAL;
-
- case SIOGIFINDEX:
- /*
- * This one will be moved to the generic device
- * layer in the near future
- */
- return addrconf_get_ifindex((void *) arg);
-
case SIOCSIFADDR:
return addrconf_add_ifaddr((void *) arg);
case SIOCSIFDSTADDR:
@@ -574,276 +382,112 @@ static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return(0);
}
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *inet6_get_sock(struct proto *prot,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr,
- unsigned short loc_port,
- unsigned short rmt_port)
-{
- struct sock *s;
- struct sock *result = NULL;
- int badness = -1;
- unsigned short hnum;
- struct ipv6_pinfo *np;
- hnum = ntohs(loc_port);
-
- /*
- * SOCK_ARRAY_SIZE must be a power of two. This will work better
- * than a prime unless 3 or more sockets end up using the same
- * array entry. This should not be a problem because most
- * well known sockets don't overlap that much, and for
- * the other ones, we can just be careful about picking our
- * socket number when we choose an arbitrary one.
- */
-
- for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
- s != NULL; s = s->next)
- {
- int score = 0;
-
- if ((s->num != hnum) || s->family != AF_INET6)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- {
- printk(KERN_DEBUG "dead or closed socket\n");
- continue;
- }
-
- np = &s->net_pinfo.af_inet6;
-
- /* remote port matches? */
-
- if (s->dummy_th.dest) {
- if (s->dummy_th.dest != rmt_port)
- {
- continue;
- }
- score++;
- }
-
- /* local address matches? */
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
- {
- continue;
- }
- score++;
- }
-
- /* remote address matches? */
- if (!ipv6_addr_any(&np->daddr))
- {
- if (ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
- score++;
- }
-
- /* perfect match? */
- if (score == 3)
- return s;
- /* no, check if this is the best so far.. */
- if (score <= badness)
- continue;
- result = s;
- badness = score;
- }
- return result;
-}
-
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
- struct ipv6_mc_socklist *mc;
-
- for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next)
- {
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- return 1;
- }
-
- return 0;
-}
-
-/*
- * Deliver a datagram to raw sockets.
- */
-
-struct sock *inet6_get_sock_raw(struct sock *sk, unsigned short num,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr)
-
-{
- struct sock *s;
- struct ipv6_pinfo *np;
- int addr_type = 0;
-
- s=sk;
-
- addr_type = ipv6_addr_type(loc_addr);
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != num)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
-
- np = &s->net_pinfo.af_inet6;
-
- if (!ipv6_addr_any(&np->daddr) &&
- ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
- return(s);
-
- if ((addr_type & IPV6_ADDR_MULTICAST) &&
- inet6_mc_check(s, loc_addr))
- return (s);
-
- continue;
- }
-
- return(s);
- }
- return(NULL);
-}
-
-/*
- * inet6_get_sock_mcast for UDP sockets.
- */
-
-struct sock *inet6_get_sock_mcast(struct sock *sk,
- unsigned short num, unsigned short rmt_port,
- struct in6_addr *loc_addr,
- struct in6_addr *rmt_addr)
-{
- struct sock *s;
- struct ipv6_pinfo *np;
-
- s=sk;
-
- for(; s != NULL; s = s->next)
- {
- if (s->num != num)
- continue;
-
- if(s->dead && (s->state == TCP_CLOSE))
- continue;
-
- np = &s->net_pinfo.af_inet6;
-
- if (s->dummy_th.dest) {
- if (s->dummy_th.dest != rmt_port)
- {
- continue;
- }
- }
-
- if (!ipv6_addr_any(&np->daddr) &&
- ipv6_addr_cmp(&np->daddr, rmt_addr))
- {
- continue;
- }
-
-
- if (!ipv6_addr_any(&np->rcv_saddr))
- {
- if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
- return(s);
- }
-
- if (!inet6_mc_check(s, loc_addr))
- {
- continue;
- }
+struct proto_ops inet6_stream_ops = {
+ AF_INET6,
- return(s);
- }
- return(NULL);
-}
-
+ inet6_dup,
+ inet6_release,
+ inet6_bind,
+ inet_stream_connect, /* ok */
+ inet6_socketpair, /* a do nothing */
+ inet_accept, /* ok */
+ inet6_getname,
+ inet_poll, /* ok */
+ inet6_ioctl, /* must change */
+ inet_listen, /* ok */
+ inet_shutdown, /* ok */
+ inet_setsockopt, /* ok */
+ inet_getsockopt, /* ok */
+ sock_no_fcntl, /* ok */
+ inet_sendmsg, /* ok */
+ inet_recvmsg /* ok */
+};
-static struct proto_ops inet6_proto_ops = {
+struct proto_ops inet6_dgram_ops = {
AF_INET6,
- inet6_create,
inet6_dup,
inet6_release,
inet6_bind,
- inet_connect, /* ok */
+ inet_dgram_connect, /* ok */
inet6_socketpair, /* a do nothing */
inet_accept, /* ok */
inet6_getname,
- inet_select, /* ok */
+ datagram_poll, /* ok */
inet6_ioctl, /* must change */
- inet_listen, /* ok */
+ sock_no_listen, /* ok */
inet_shutdown, /* ok */
inet_setsockopt, /* ok */
inet_getsockopt, /* ok */
- inet_fcntl, /* ok */
+ sock_no_fcntl, /* ok */
inet_sendmsg, /* ok */
inet_recvmsg /* ok */
};
+struct net_proto_family inet6_family_ops = {
+ AF_INET6,
+ inet6_create
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_raw6 = {
+ PROC_NET_RAW6, 4, "raw6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ raw6_get_info
+};
+static struct proc_dir_entry proc_net_tcp6 = {
+ PROC_NET_TCP6, 4, "tcp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ tcp6_get_info
+};
+static struct proc_dir_entry proc_net_udp6 = {
+ PROC_NET_RAW6, 4, "udp6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ udp6_get_info
+};
+static struct proc_dir_entry proc_net_sockstat6 = {
+ PROC_NET_SOCKSTAT6, 9, "sockstat6",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ afinet6_get_info
+};
+#endif /* CONFIG_PROC_FS */
+
#ifdef MODULE
int init_module(void)
#else
void inet6_proto_init(struct net_proto *pro)
#endif
{
- int i;
+ struct sk_buff *dummy_skb;
- printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
+ printk(KERN_INFO "IPv6 v0.2 for NET3.037\n");
- sock_register(inet6_proto_ops.family, &inet6_proto_ops);
-
- for(i = 0; i < SOCK_ARRAY_SIZE; i++)
+ if (sizeof(struct ipv6_options) > sizeof(dummy_skb->cb))
{
- rawv6_sock_array[i] = NULL;
- }
+ printk(KERN_CRIT "inet6_proto_init: size fault\n");
+#ifdef MODULE
+ return -EINVAL;
+#else
+ return;
+#endif
+ }
+ (void) sock_register(&inet6_family_ops);
+
/*
* ipngwg API draft makes clear that the correct semantics
* for TCP and UDP is to consider one TCP and UDP instance
* in a host availiable by both INET and INET6 APIs and
- * hable to communicate via both network protocols.
+ * able to communicate via both network protocols.
*/
-
- tcpv6_prot.inuse = 0;
- tcpv6_prot.highestinuse = 0;
- tcpv6_prot.sock_array = tcp_sock_array;
- udpv6_prot.inuse = 0;
- udpv6_prot.highestinuse = 0;
- udpv6_prot.sock_array = udp_sock_array;
-
- rawv6_prot.inuse = 0;
- rawv6_prot.highestinuse = 0;
- rawv6_prot.sock_array = rawv6_sock_array;
-
ipv6_init();
- icmpv6_init(&inet6_proto_ops);
- ndisc_init(&inet6_proto_ops);
+ icmpv6_init(&inet6_family_ops);
addrconf_init();
@@ -856,6 +500,14 @@ void inet6_proto_init(struct net_proto *pro)
tcpv6_init();
+ /* Create /proc/foo6 entries. */
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_raw6);
+ proc_net_register(&proc_net_tcp6);
+ proc_net_register(&proc_net_udp6);
+ proc_net_register(&proc_net_sockstat6);
+#endif
+
#ifdef MODULE
return 0;
#endif
@@ -867,6 +519,11 @@ void cleanup_module(void)
sit_cleanup();
ipv6_cleanup();
sock_unregister(AF_INET6);
-}
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(proc_net_raw6.low_ino);
+ proc_net_unregister(proc_net_tcp6.low_ino);
+ proc_net_unregister(proc_net_udp6.low_ino);
+ proc_net_unregister(proc_net_sockstat6.low_ino);
#endif
-
+}
+#endif /* MODULE */
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 03a58e843..a898f6008 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: datagram.c,v 1.3 1996/10/11 16:03:05 roque Exp $
+ * $Id: datagram.c,v 1.10 1997/04/14 05:39:42 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,90 +19,51 @@
#include <linux/sockios.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
+#include <linux/route.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
-
int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
- struct cmsghdr *cmsg = msg->msg_control;
- int len = msg->msg_controllen;
-
- msg->msg_controllen = 0;
+ struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
- if (np->rxinfo && (len >= sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo)))
- {
- struct in6_pktinfo *src_info;
- struct inet6_dev *in6_dev;
-
- cmsg->cmsg_len = (sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo));
- cmsg->cmsg_level = SOL_IPV6;
- cmsg->cmsg_type = IPV6_RXINFO;
-
- src_info = (struct in6_pktinfo *) cmsg->cmsg_data;
- in6_dev = ipv6_get_idev(skb->dev);
-
- if (in6_dev == NULL)
- {
- printk(KERN_DEBUG "recv_ctl: unknown device\n");
- return -ENODEV;
- }
+ if (np->rxinfo) {
+ struct in6_pktinfo src_info;
- src_info->ipi6_ifindex = in6_dev->if_index;
- ipv6_addr_copy(&src_info->ipi6_addr,
- &skb->ipv6_hdr->daddr);
+ src_info.ipi6_ifindex = skb->dev->ifindex;
+ ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr);
+ put_cmsg(msg, SOL_IPV6, IPV6_RXINFO, sizeof(src_info), &src_info);
+ }
- len -= cmsg->cmsg_len;
- msg->msg_controllen += cmsg->cmsg_len;
- cmsg = (struct cmsghdr *)((u8*) cmsg + cmsg->cmsg_len);
+ if (np->rxhlim) {
+ int hlim = skb->nh.ipv6h->hop_limit;
+ put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
}
- if (opt->srcrt)
- {
+ if (opt->srcrt) {
int hdrlen = sizeof(struct rt0_hdr) + (opt->srcrt->hdrlen << 3);
- if (len >= sizeof(struct cmsghdr) + hdrlen)
- {
- struct rt0_hdr *rt0;
-
- cmsg->cmsg_len = sizeof(struct cmsghdr) + hdrlen;
- cmsg->cmsg_level = SOL_IPV6;
- cmsg->cmsg_type = IPV6_RXINFO;
-
- rt0 = (struct rt0_hdr *) cmsg->cmsg_data;
- memcpy(rt0, opt->srcrt, hdrlen);
-
- len -= cmsg->cmsg_len;
- msg->msg_controllen += cmsg->cmsg_len;
- cmsg = (struct cmsghdr *)((u8*) cmsg + cmsg->cmsg_len);
- }
+ put_cmsg(msg, SOL_IPV6, IPV6_RXSRCRT, hdrlen, opt->srcrt);
}
return 0;
}
-
int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
- struct in6_addr **src_addr, struct ipv6_options *opt)
+ struct in6_addr **src_addr, struct ipv6_options *opt,
+ int *hlimit)
{
- struct inet6_dev *in6_dev = NULL;
struct in6_pktinfo *src_info;
struct cmsghdr *cmsg;
struct ipv6_rt_hdr *rthdr;
int len;
- int err = -EINVAL;
+ int err = 0;
- for (cmsg = msg->msg_control; cmsg; cmsg = cmsg_nxthdr(msg, cmsg))
- {
- if (cmsg->cmsg_level != SOL_IPV6)
- {
+ for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_IPV6) {
printk(KERN_DEBUG "cmsg_level %d\n", cmsg->cmsg_level);
continue;
}
@@ -111,50 +72,43 @@ int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
case IPV6_TXINFO:
if (cmsg->cmsg_len < (sizeof(struct cmsghdr) +
- sizeof(struct in6_pktinfo)))
- {
+ sizeof(struct in6_pktinfo))) {
+ err = -EINVAL;
goto exit_f;
}
src_info = (struct in6_pktinfo *) cmsg->cmsg_data;
- if (src_info->ipi6_ifindex)
- {
- in6_dev = ipv6_dev_by_index(src_info->ipi6_ifindex);
- if (in6_dev == NULL)
- {
- goto exit_f;
- }
+ if (src_info->ipi6_ifindex) {
+ int index = src_info->ipi6_ifindex;
- *src_dev = in6_dev->dev;
+ *src_dev = dev_get_by_index(index);
}
- if (!ipv6_addr_any(&src_info->ipi6_addr))
- {
+ if (!ipv6_addr_any(&src_info->ipi6_addr)) {
struct inet6_ifaddr *ifp;
ifp = ipv6_chk_addr(&src_info->ipi6_addr);
- if ( ifp == NULL)
- {
+ if (ifp == NULL) {
+ err = -EINVAL;
goto exit_f;
}
*src_addr = &src_info->ipi6_addr;
- err = 0;
}
break;
- case SCM_SRCRT:
+ case IPV6_RXSRCRT:
len = cmsg->cmsg_len;
len -= sizeof(struct cmsghdr);
/* validate option length */
- if (len < sizeof(struct ipv6_rt_hdr))
- {
+ if (len < sizeof(struct ipv6_rt_hdr)) {
+ err = -EINVAL;
goto exit_f;
}
@@ -163,34 +117,48 @@ int datagram_send_ctl(struct msghdr *msg, struct device **src_dev,
/*
* TYPE 0
*/
- if (rthdr->type)
- {
+ if (rthdr->type) {
+ err = -EINVAL;
goto exit_f;
}
- if (((rthdr->hdrlen + 1) << 3) < len)
- {
+ if (((rthdr->hdrlen + 1) << 3) < len) {
+ err = -EINVAL;
goto exit_f;
}
/* segments left must also match */
- if ((rthdr->hdrlen >> 1) != rthdr->segments_left)
- {
+ if ((rthdr->hdrlen >> 1) != rthdr->segments_left) {
+ err = -EINVAL;
goto exit_f;
}
opt->opt_nflen += ((rthdr->hdrlen + 1) << 3);
opt->srcrt = rthdr;
- err = 0;
break;
+
+ case IPV6_HOPLIMIT:
+
+ len = cmsg->cmsg_len;
+ len -= sizeof(struct cmsghdr);
+
+ if (len < sizeof(int)) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
+ *hlimit = *((int *) cmsg->cmsg_data);
+ break;
+
default:
printk(KERN_DEBUG "invalid cmsg type: %d\n",
cmsg->cmsg_type);
+ err = -EINVAL;
break;
- }
+ };
}
- exit_f:
+exit_f:
return err;
}
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index 6c5c8ab7e..b2380fb78 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: exthdrs.c,v 1.7 1996/09/12 18:44:18 roque Exp $
+ * $Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -31,13 +31,13 @@
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
/*
* inbound
*/
-
+#if 0
int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
__u8 *nhptr, struct ipv6_options *opt)
{
@@ -53,11 +53,10 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
struct ipv6_rt_hdr *hdr = (struct ipv6_rt_hdr *) skb->h.raw;
struct rt0_hdr *rthdr;
- if (hdr->segments_left == 0)
- {
+ if (hdr->segments_left == 0) {
struct ipv6_options *opt;
- opt = (struct ipv6_options *) skb->proto_priv;
+ opt = (struct ipv6_options *) skb->cb;
opt->srcrt = hdr;
skb->h.raw += (hdr->hdrlen + 1) << 3;
@@ -65,13 +64,12 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
}
if (hdr->type != IPV6_SRCRT_TYPE_0 || hdr->hdrlen & 0x01 ||
- hdr->hdrlen > 46)
- {
+ hdr->hdrlen > 46) {
/*
* Discard
*/
- pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+ pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
if (hdr->type)
pos += 2;
@@ -90,9 +88,8 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
n = hdr->hdrlen >> 1;
- if (hdr->segments_left > n)
- {
- pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+ if (hdr->segments_left > n) {
+ pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
pos += 3;
@@ -109,15 +106,14 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
addr_type = ipv6_addr_type(addr);
- if (addr_type == IPV6_ADDR_MULTICAST)
- {
+ if (addr_type == IPV6_ADDR_MULTICAST) {
kfree_skb(skb, FREE_READ);
return 0;
}
ipv6_addr_copy(&daddr, addr);
- ipv6_addr_copy(addr, &skb->ipv6_hdr->daddr);
- ipv6_addr_copy(&skb->ipv6_hdr->daddr, &daddr);
+ ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
+ ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
/*
* Check Strick Source Route
@@ -126,9 +122,7 @@ int ipv6_routing_header(struct sk_buff **skb_ptr, struct device *dev,
bit_map = ntohl(rthdr->bitmap);
if ((bit_map & (1 << i)) == IPV6_SRCRT_STRICT)
- {
strict = 1;
- }
ipv6_forward(skb, dev, (strict ? IP6_FW_STRICT : 0) | IP6_FW_SRCRT);
@@ -154,10 +148,8 @@ int ipv6opt_bld_rthdr(struct sk_buff *skb, struct ipv6_options *opt,
hops = ihdr->rt_hdr.hdrlen >> 1;
if (hops > 1)
- {
memcpy(phdr->addr, ihdr->addr + 1,
(hops - 1) * sizeof(struct in6_addr));
- }
ipv6_addr_copy(phdr->addr + (hops - 1), addr);
@@ -165,9 +157,4 @@ int ipv6opt_bld_rthdr(struct sk_buff *skb, struct ipv6_options *opt,
return NEXTHDR_ROUTING;
}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
+#endif
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index f959189c6..37bd7f814 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: icmp.c,v 1.8 1997/03/18 18:24:30 davem Exp $
+ *
* Based on net/ipv4/icmp.c
*
* RFC 1885
@@ -28,17 +30,9 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
-#include <linux/major.h>
#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
#include <linux/skbuff.h>
#include <linux/inet.h>
@@ -49,15 +43,13 @@
#include <net/sock.h>
#include <net/ipv6.h>
+#include <net/checksum.h>
#include <net/protocol.h>
-#include <net/route.h>
-#include <net/ndisc.h>
#include <net/raw.h>
-#include <net/inet_common.h>
+#include <net/rawv6.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
-#include <net/rawv6.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -66,7 +58,8 @@
* ICMP socket for flow control.
*/
-static struct socket icmpv6_socket;
+struct inode icmpv6_inode;
+struct socket *icmpv6_socket=&icmpv6_inode.u.socket_i;
int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
struct in6_addr *saddr, struct in6_addr *daddr,
@@ -87,7 +80,7 @@ static struct inet6_protocol icmpv6_protocol =
struct icmpv6_msg {
- struct icmpv6hdr icmph;
+ struct icmp6hdr icmph;
__u8 *data;
struct in6_addr *daddr;
int len;
@@ -105,7 +98,7 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
char *buff, unsigned int offset, unsigned int len)
{
struct icmpv6_msg *msg = (struct icmpv6_msg *) data;
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
__u32 csum;
/*
@@ -114,26 +107,25 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
* on an echo reply. (those are the rules on RFC 1883)
*/
- if (offset)
- {
+ if (offset) {
csum = csum_partial_copy((void *) msg->data +
- offset - sizeof(struct icmpv6hdr),
+ offset - sizeof(struct icmp6hdr),
buff, len, msg->csum);
msg->csum = csum;
return 0;
}
csum = csum_partial_copy((void *) &msg->icmph, buff,
- sizeof(struct icmpv6hdr), msg->csum);
+ sizeof(struct icmp6hdr), msg->csum);
csum = csum_partial_copy((void *) msg->data,
- buff + sizeof(struct icmpv6hdr),
- len - sizeof(struct icmpv6hdr), csum);
+ buff + sizeof(struct icmp6hdr),
+ len - sizeof(struct icmp6hdr), csum);
- icmph = (struct icmpv6hdr *) buff;
+ icmph = (struct icmp6hdr *) buff;
- icmph->checksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
- IPPROTO_ICMPV6, csum);
+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len,
+ IPPROTO_ICMPV6, csum);
return 0;
}
@@ -145,7 +137,7 @@ static int icmpv6_getfrag(const void *data, struct in6_addr *saddr,
*/
static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
{
- char *buff = (char *) skb->ipv6_hdr;
+ char *buff = skb->nh.raw;
return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
}
@@ -157,11 +149,12 @@ static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
struct device *dev)
{
- struct ipv6hdr *hdr = skb->ipv6_hdr;
- struct sock *sk = (struct sock *) icmpv6_socket.data;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct sock *sk = icmpv6_socket->sk;
struct in6_addr *saddr = NULL;
struct device *src_dev = NULL;
struct icmpv6_msg msg;
+ struct flowi fl;
int addr_type = 0;
int optlen;
int len;
@@ -170,9 +163,8 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
* sanity check pointer in case of parameter problem
*/
- if (type == ICMPV6_PARAMETER_PROB &&
- (info > (skb->tail - ((unsigned char *) hdr))))
- {
+ if (type == ICMPV6_PARAMPROB &&
+ (info > (skb->tail - ((unsigned char *) hdr)))) {
printk(KERN_DEBUG "icmpv6_send: bug! pointer > skb\n");
return;
}
@@ -187,23 +179,18 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
addr_type = ipv6_addr_type(&hdr->daddr);
if (ipv6_chk_addr(&hdr->daddr))
- {
saddr = &hdr->daddr;
- }
/*
* Dest addr check
*/
- if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST))
- {
+ if ((addr_type & IPV6_ADDR_MULTICAST || skb->pkt_type != PACKET_HOST)) {
if (type != ICMPV6_PKT_TOOBIG &&
- !(type == ICMPV6_PARAMETER_PROB &&
+ !(type == ICMPV6_PARAMPROB &&
code == ICMPV6_UNK_OPTION &&
(opt_unrec(skb, info))))
- {
return;
- }
saddr = NULL;
}
@@ -215,16 +202,13 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
*/
if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
src_dev = skb->dev;
- }
/*
* Must not send if we know that source is Anycast also.
* for now we don't know that.
*/
- if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST))
- {
+ if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
printk(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
return;
}
@@ -234,12 +218,12 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
* getfrag_t callback.
*/
- msg.icmph.type = type;
- msg.icmph.code = code;
- msg.icmph.checksum = 0;
+ msg.icmph.icmp6_type = type;
+ msg.icmph.icmp6_code = code;
+ msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_pointer = htonl(info);
- msg.data = (__u8 *) skb->ipv6_hdr;
+ msg.data = skb->nh.raw;
msg.csum = 0;
msg.daddr = &hdr->saddr;
/*
@@ -251,31 +235,37 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info,
optlen = 0;
len = min(skb->tail - ((unsigned char *) hdr),
- 576 - sizeof(struct ipv6hdr) - sizeof(struct icmpv6hdr)
+ 576 - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr)
- optlen);
- if (len < 0)
- {
+ if (len < 0) {
printk(KERN_DEBUG "icmp: len problem\n");
return;
}
- len += sizeof(struct icmpv6hdr);
+ len += sizeof(struct icmp6hdr);
msg.len = len;
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = src_dev;
+ fl.uli_u.icmpt.type = type;
+ fl.uli_u.icmpt.code = code;
- ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len,
- saddr, src_dev, NULL, IPPROTO_ICMPV6, 1);
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
}
static void icmpv6_echo_reply(struct sk_buff *skb)
{
- struct sock *sk = (struct sock *) icmpv6_socket.data;
- struct ipv6hdr *hdr = skb->ipv6_hdr;
- struct icmpv6hdr *icmph = (struct icmpv6hdr *) skb->h.raw;
+ struct sock *sk = icmpv6_socket->sk;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw;
struct in6_addr *saddr;
struct icmpv6_msg msg;
+ struct flowi fl;
unsigned char *data;
int len;
@@ -287,11 +277,11 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
saddr = NULL;
len = skb->tail - data;
- len += sizeof(struct icmpv6hdr);
+ len += sizeof(struct icmp6hdr);
- msg.icmph.type = ICMPV6_ECHO_REPLY;
- msg.icmph.code = 0;
- msg.icmph.checksum = 0;
+ msg.icmph.icmp6_type = ICMPV6_ECHO_REPLY;
+ msg.icmph.icmp6_code = 0;
+ msg.icmph.icmp6_cksum = 0;
msg.icmph.icmp6_identifier = icmph->icmp6_identifier;
msg.icmph.icmp6_sequence = icmph->icmp6_sequence;
@@ -299,9 +289,16 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
msg.csum = 0;
msg.len = len;
msg.daddr = &hdr->saddr;
-
- ipv6_build_xmit(sk, icmpv6_getfrag, &msg, &hdr->saddr, len, saddr,
- skb->dev, NULL, IPPROTO_ICMPV6, 1);
+
+ fl.proto = IPPROTO_ICMPV6;
+ fl.nl_u.ip6_u.daddr = &hdr->saddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = skb->dev;
+ fl.uli_u.icmpt.type = ICMPV6_ECHO_REPLY;
+ fl.uli_u.icmpt.code = 0;
+
+ ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1,
+ MSG_DONTWAIT);
}
static __inline__ int ipv6_ext_hdr(u8 nexthdr)
@@ -338,56 +335,53 @@ static void icmpv6_notify(int type, int code, unsigned char *buff, int len,
pbuff = (char *) (hdr + 1);
len -= sizeof(struct ipv6hdr);
- while (ipv6_ext_hdr(nexthdr))
- {
+ while (ipv6_ext_hdr(nexthdr)) {
int hdrlen;
if (nexthdr == NEXTHDR_NONE)
return;
nexthdr = *pbuff;
+
+ /* Header length is size in 8-octet units, not
+ * including the first 8 octets.
+ */
hdrlen = *(pbuff+1);
+ hdrlen = (hdrlen + 1) << 3;
- if (((hdrlen + 1) << 3) > len)
+ if (hdrlen > len)
return;
+ /* Now this is right. */
pbuff += hdrlen;
len -= hdrlen;
}
- hash = nexthdr & (MAX_INET_PROTOS -1);
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
ipprot != NULL;
- ipprot=(struct inet6_protocol *)ipprot->next)
- {
+ ipprot=(struct inet6_protocol *)ipprot->next) {
if (ipprot->protocol != nexthdr)
continue;
if (ipprot->err_handler)
- {
ipprot->err_handler(type, code, pbuff, info,
saddr, daddr, ipprot);
- }
return;
}
/* delivery to upper layer protocols failed. try raw sockets */
- sk = rawv6_prot.sock_array[hash];
+ sk = raw_v6_htable[hash];
if (sk == NULL)
- {
return;
- }
- while ((sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr)))
- {
+ while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
rawv6_err(sk, type, code, pbuff, saddr, daddr);
sk = sk->next;
}
-
- return;
}
/*
@@ -400,32 +394,29 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
int redo, struct inet6_protocol *protocol)
{
struct ipv6hdr *orig_hdr;
- struct icmpv6hdr *hdr = (struct icmpv6hdr *) skb->h.raw;
+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw;
int ulen;
- /* perform checksum */
-
-
+ /* Perform checksum. */
switch (skb->ip_summed) {
case CHECKSUM_NONE:
skb->csum = csum_partial((char *)hdr, len, 0);
case CHECKSUM_HW:
if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_ICMPV6,
- skb->csum))
- {
+ skb->csum)) {
printk(KERN_DEBUG "icmpv6 checksum failed\n");
goto discard_it;
}
default:
/* CHECKSUM_UNNECESSARY */
- }
+ };
/*
* length of original packet carried in skb
*/
ulen = skb->tail - (unsigned char *) (hdr + 1);
- switch (hdr->type) {
+ switch (hdr->icmp6_type) {
case ICMPV6_ECHO_REQUEST:
icmpv6_echo_reply(skb);
@@ -438,20 +429,19 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
case ICMPV6_PKT_TOOBIG:
orig_hdr = (struct ipv6hdr *) (hdr + 1);
if (ulen >= sizeof(struct ipv6hdr))
- {
- rt6_handle_pmtu(&orig_hdr->daddr,
- ntohl(hdr->icmp6_mtu));
- }
+ rt6_pmtu_discovery(&orig_hdr->daddr, dev,
+ ntohl(hdr->icmp6_mtu));
/*
- * Drop through to notify
+ * Drop through to notify
*/
case ICMPV6_DEST_UNREACH:
- case ICMPV6_TIME_EXCEEDED:
- case ICMPV6_PARAMETER_PROB:
+ case ICMPV6_TIME_EXCEED:
+ case ICMPV6_PARAMPROB:
- icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+ icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen,
saddr, daddr, protocol);
break;
@@ -463,55 +453,67 @@ int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
ndisc_rcv(skb, dev, saddr, daddr, opt, len);
break;
- case ICMPV6_MEMBERSHIP_QUERY:
- case ICMPV6_MEMBERSHIP_REPORT:
- case ICMPV6_MEMBERSHIP_REDUCTION:
- /* forward the packet to the igmp module */
+ case ICMPV6_MGM_QUERY:
+ igmp6_event_query(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REPORT:
+ igmp6_event_report(skb, hdr, len);
+ break;
+
+ case ICMPV6_MGM_REDUCTION:
break;
default:
printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
/* informational */
- if (hdr->type & 0x80)
- {
+ if (hdr->icmp6_type & 0x80)
goto discard_it;
- }
/*
* error of unkown type.
* must pass to upper level
*/
- icmpv6_notify(hdr->type, hdr->code, (char *) (hdr + 1), ulen,
+ icmpv6_notify(hdr->icmp6_type, hdr->icmp6_code,
+ (char *) (hdr + 1), ulen,
saddr, daddr, protocol);
- }
-
- discard_it:
+ };
+discard_it:
kfree_skb(skb, FREE_READ);
return 0;
}
-void icmpv6_init(struct proto_ops *ops)
+void icmpv6_init(struct net_proto_family *ops)
{
struct sock *sk;
int err;
- icmpv6_socket.type=SOCK_RAW;
- icmpv6_socket.ops=ops;
+ icmpv6_inode.i_mode = S_IFSOCK;
+ icmpv6_inode.i_sock = 1;
+ icmpv6_inode.i_uid = 0;
+ icmpv6_inode.i_gid = 0;
- if((err=ops->create(&icmpv6_socket, IPPROTO_ICMPV6))<0)
+ icmpv6_socket->inode = &icmpv6_inode;
+ icmpv6_socket->state = SS_UNCONNECTED;
+ icmpv6_socket->type=SOCK_RAW;
+
+ if((err=ops->create(icmpv6_socket, IPPROTO_ICMPV6))<0)
printk(KERN_DEBUG
- "Failed to create the ICMP control socket.\n");
+ "Failed to create the ICMP6 control socket.\n");
MOD_DEC_USE_COUNT;
- sk = icmpv6_socket.data;
+ sk = icmpv6_socket->sk;
sk->allocation = GFP_ATOMIC;
sk->num = 256; /* Don't receive any data */
inet6_add_protocol(&icmpv6_protocol);
+
+ ndisc_init(ops);
+ igmp6_init(ops);
}
static struct icmp6_err {
@@ -533,8 +535,7 @@ int icmpv6_err_convert(int type, int code, int *err)
switch (type) {
case ICMPV6_DEST_UNREACH:
- if (code <= ICMPV6_PORT_UNREACH)
- {
+ if (code <= ICMPV6_PORT_UNREACH) {
*err = tab_unreach[code].err;
fatal = tab_unreach[code].fatal;
}
@@ -544,7 +545,7 @@ int icmpv6_err_convert(int type, int code, int *err)
*err = EMSGSIZE;
break;
- case ICMPV6_PARAMETER_PROB:
+ case ICMPV6_PARAMPROB:
*err = EPROTO;
fatal = 1;
break;
@@ -552,9 +553,3 @@ int icmpv6_err_convert(int type, int code, int *err)
return fatal;
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o icmp.o icmp.c"
- * End:
- */
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
new file mode 100644
index 000000000..25b34465c
--- /dev/null
+++ b/net/ipv6/ip6_fib.c
@@ -0,0 +1,927 @@
+/*
+ * Linux INET6 implementation
+ * Forwarding Information Database
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_fib.c,v 1.7 1997/04/12 04:32:46 davem Exp $
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+
+#define RT_DEBUG 2
+
+struct rt6_statistics rt6_stats;
+
+/*
+ * A routing update causes an increase of the serial number on the
+ * afected subtree. This allows for cached routes to be asynchronously
+ * tested when modifications are made to the destination cache as a
+ * result of redirects, path MTU changes, etc.
+ */
+
+static __u32 rt_sernum = 0;
+
+static void fib6_run_gc(unsigned long);
+
+static struct timer_list ip6_fib_timer = {
+ NULL, NULL,
+ 0,
+ 0,
+ fib6_run_gc
+};
+
+/*
+ * Auxiliary address test functions for the radix tree.
+ *
+ * These assume a 32bit processor (although it will work on
+ * 64bit processors)
+ */
+
+/*
+ * compare "prefix length" bits of an address
+ */
+
+static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int pdw;
+ int pbi;
+
+ pdw = prefixlen >> 0x05; /* num of whole __u32 in prefix */
+ pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (pdw)
+ if (memcmp(a1, a2, pdw << 2))
+ return 0;
+
+ if (pbi) {
+ __u32 w1, w2;
+ __u32 mask;
+
+ w1 = a1[pdw];
+ w2 = a2[pdw];
+
+ mask = htonl((0xffffffff) << (0x20 - pbi));
+
+ if ((w1 ^ w2) & mask)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * test bit
+ */
+
+static __inline__ int addr_bit_set(void *token, int fn_bit)
+{
+ int dw;
+ __u32 b1;
+ __u32 mask;
+ int bit = fn_bit;
+ __u32 *addr = token;
+
+ dw = bit >> 0x05;
+
+ b1 = addr[dw];
+
+ bit = ~bit;
+ bit &= 0x1f;
+ mask = htonl(1 << bit);
+ return (b1 & mask);
+}
+
+
+
+/*
+ * find the first different bit between two addresses
+ * length of address must be a multiple of 32bits
+ */
+
+static __inline__ int addr_diff(void *token1, void *token2, int addrlen)
+{
+ __u32 *a1 = token1;
+ __u32 *a2 = token2;
+ int i;
+
+ addrlen >>= 2;
+
+ for (i = 0; i < addrlen; i++) {
+ __u32 b1, b2;
+ __u32 xb;
+
+ b1 = a1[i];
+ b2 = a2[i];
+
+ xb = b1 ^ b2;
+
+ if (xb) {
+ int res = 0;
+ int j=31;
+
+ xb = ntohl(xb);
+
+ while (test_bit(j, &xb) == 0) {
+ res++;
+ j--;
+ }
+
+ return (i * 32 + res);
+ }
+ }
+
+ /*
+ * we should *never* get to this point since that
+ * would mean the addrs are equal
+ */
+
+ return -1;
+}
+
+static __inline__ struct fib6_node * node_alloc(void)
+{
+ struct fib6_node *fn;
+
+ if ((fn = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC))) {
+ memset(fn, 0, sizeof(struct fib6_node));
+ rt6_stats.fib_nodes++;
+ }
+
+ return fn;
+}
+
+static __inline__ void node_free(struct fib6_node * fn)
+{
+ rt6_stats.fib_nodes--;
+ kfree(fn);
+}
+
+/*
+ * Routing Table
+ *
+ * return the apropriate node for a routing tree "add" operation
+ * by either creating and inserting or by returning an existing
+ * node.
+ */
+
+static struct fib6_node * fib6_add_1(struct fib6_node *root, void *addr,
+ int addrlen, int plen,
+ unsigned long offset,
+ struct rt6_info *rt)
+
+{
+ struct fib6_node *fn;
+ struct fib6_node *pn = NULL;
+ struct fib6_node *in;
+ struct fib6_node *ln;
+ struct rt6key *key;
+ __u32 bit;
+ __u32 dir = 0;
+ __u32 sernum = ++rt_sernum;
+
+ /* insert node in tree */
+
+ fn = root;
+
+ if (plen == 0)
+ return fn;
+
+ for (;;) {
+ if (fn == NULL) {
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+ ln->fn_sernum = sernum;
+ rt->rt6i_node = ln;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+ return ln;
+ }
+
+ key = (struct rt6key *)((u8 *)fn->leaf + offset);
+
+ if (addr_match(&key->addr, addr, fn->fn_bit)) {
+ if (plen == fn->fn_bit) {
+ /* clean up an intermediate node */
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_release(fn->leaf);
+ fn->leaf = NULL;
+ }
+
+ fn->fn_sernum = sernum;
+
+ return fn;
+ }
+
+ if (plen > fn->fn_bit) {
+ /* Walk down on tree. */
+ fn->fn_sernum = sernum;
+ dir = addr_bit_set(addr, fn->fn_bit);
+ pn = fn;
+ fn = dir ? fn->right: fn->left;
+
+ continue;
+ }
+ }
+
+ /*
+ * split since we don't have a common prefix anymore or
+ * we have a less significant route.
+ * we've to insert an intermediate node on the list
+ * this new node will point to the one we need to create
+ * and the current
+ */
+
+ pn = fn->parent;
+
+ /* find 1st bit in difference between the 2 addrs */
+ bit = addr_diff(addr, &key->addr, addrlen);
+
+
+ /*
+ * (intermediate)
+ * / \
+ * (new leaf node) (old node)
+ */
+ if (plen > bit) {
+ in = node_alloc();
+
+ if (in == NULL)
+ return NULL;
+
+ /*
+ * new intermediate node.
+ * RTN_RTINFO will
+ * be off since that an address that chooses one of
+ * the branches would not match less specific routes
+ * int the other branch
+ */
+
+ in->fn_bit = bit;
+
+ in->parent = pn;
+ in->leaf = rt;
+
+ in->fn_sernum = sernum;
+ atomic_inc(&rt->rt6i_ref);
+
+ /* leaf node */
+ ln = node_alloc();
+
+ if (ln == NULL) {
+ node_free(in);
+ return NULL;
+ }
+
+ /* update parent pointer */
+ if (dir)
+ pn->right = in;
+ else
+ pn->left = in;
+
+ ln->fn_bit = plen;
+
+ ln->parent = in;
+ fn->parent = in;
+
+ ln->fn_sernum = sernum;
+
+ if (addr_bit_set(addr, bit)) {
+ in->right = ln;
+ in->left = fn;
+ } else {
+ in->left = ln;
+ in->right = fn;
+ }
+
+ return ln;
+ }
+
+ /*
+ * (new leaf node)
+ * / \
+ * (old node) NULL
+ */
+
+ ln = node_alloc();
+
+ if (ln == NULL)
+ return NULL;
+
+ ln->fn_bit = plen;
+
+ ln->parent = pn;
+
+ ln->fn_sernum = sernum;
+
+ if (dir)
+ pn->right = ln;
+ else
+ pn->left = ln;
+
+
+ if (addr_bit_set(&key->addr, plen))
+ ln->right = fn;
+ else
+ ln->left = fn;
+
+ fn->parent = ln;
+
+ return ln;
+ }
+
+ return NULL;
+}
+
+/*
+ * Insert routing information in a node.
+ */
+
+static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt)
+{
+ struct rt6_info *iter = NULL;
+ struct rt6_info **ins;
+
+ rt->rt6i_node = fn;
+ ins = &fn->leaf;
+
+ for (iter = fn->leaf; iter; iter=iter->u.next) {
+ /*
+ * Search for duplicates
+ */
+
+ if (iter->rt6i_metric == rt->rt6i_metric) {
+ /*
+ * Same priority level
+ */
+
+ if ((iter->rt6i_dev == rt->rt6i_dev) &&
+ (iter->rt6i_flowr == rt->rt6i_flowr) &&
+ (ipv6_addr_cmp(&iter->rt6i_gateway,
+ &rt->rt6i_gateway) == 0))
+ return -EEXIST;
+ }
+
+ if (iter->rt6i_metric > rt->rt6i_metric)
+ break;
+
+ ins = &iter->u.next;
+ }
+
+ /*
+ * insert node
+ */
+
+ *ins = rt;
+ rt->u.next = iter;
+ atomic_inc(&rt->rt6i_ref);
+ rt6_stats.fib_rt_entries++;
+
+ if ((fn->fn_flags & RTN_RTINFO) == 0) {
+ rt6_stats.fib_route_nodes++;
+ fn->fn_flags |= RTN_RTINFO;
+ }
+
+ return 0;
+}
+
+static __inline__ void fib6_start_gc(struct rt6_info *rt)
+{
+ if ((ip6_fib_timer.expires == 0) &&
+ (rt->rt6i_flags & (RTF_ADDRCONF | RTF_CACHE))) {
+ ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+ add_timer(&ip6_fib_timer);
+ }
+}
+
+/*
+ * Add routing information to the routing tree.
+ * <destination addr>/<source addr>
+ * with source addr info in sub-trees
+ */
+
+int fib6_add(struct fib6_node *root, struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+ int err = -ENOMEM;
+ unsigned long offset;
+
+ offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
+ rt->rt6i_dst.plen, offset, rt);
+
+ if (fn == NULL) {
+#if RT_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_add: fn == NULL\n");
+#endif
+ goto out;
+ }
+
+ if (rt->rt6i_src.plen) {
+ struct fib6_node *sn;
+
+#if RT_DEBUG >= 2
+ printk(KERN_DEBUG "fib6_add: src.len > 0\n");
+#endif
+
+ if (fn->subtree == NULL) {
+ struct fib6_node *sfn;
+
+ if (fn->leaf == NULL) {
+ fn->leaf = rt;
+ atomic_inc(&rt->rt6i_ref);
+ }
+
+ sfn = node_alloc();
+
+ if (sfn == NULL)
+ goto out;
+
+ sfn->parent = fn;
+ sfn->leaf = &ip6_null_entry;
+ sfn->fn_flags = RTN_ROOT;
+ sfn->fn_sernum = ++rt_sernum;
+
+ fn->subtree = sfn;
+ }
+
+ offset = (u8*) &rt->rt6i_src - (u8*) rt;
+
+ sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
+ sizeof(struct in6_addr), rt->rt6i_src.plen,
+ offset, rt);
+
+ if (sn == NULL)
+ goto out;
+
+ fn = sn;
+ }
+
+ err = fib6_add_rt2node(fn, rt);
+
+ if (err == 0)
+ fib6_start_gc(rt);
+out:
+ return err;
+}
+
+/*
+ * Routing tree lookup
+ *
+ */
+
+struct lookup_args {
+ unsigned long offset; /* key offset on rt6_info */
+ struct in6_addr *addr; /* search key */
+};
+
+static struct fib6_node * fib6_lookup_1(struct fib6_node *root,
+ struct lookup_args *args)
+{
+ struct fib6_node *fn;
+ int dir;
+
+ /*
+ * Descend on a tree
+ */
+
+ fn = root;
+
+ for (;;) {
+ struct fib6_node *next;
+
+ dir = addr_bit_set(args->addr, fn->fn_bit);
+
+ next = dir ? fn->right : fn->left;
+
+ if (next) {
+ fn = next;
+ continue;
+ }
+
+ break;
+ }
+
+ while ((fn->fn_flags & RTN_ROOT) == 0) {
+ if (fn->subtree) {
+ struct fib6_node *st;
+ struct lookup_args *narg;
+
+ narg = args + 1;
+
+ if (narg->addr) {
+ st = fib6_lookup_1(fn->subtree, narg);
+
+ if (!(st->fn_flags & RTN_ROOT))
+ {
+ return st;
+ }
+ }
+ }
+
+ if (fn->fn_flags & RTN_RTINFO) {
+ struct rt6key *key;
+
+ key = (struct rt6key *) ((u8 *) fn->leaf +
+ args->offset);
+
+ if (addr_match(&key->addr, args->addr, key->plen))
+ return fn;
+ }
+
+ fn = fn->parent;
+ }
+
+ return NULL;
+}
+
+struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ struct lookup_args args[2];
+ struct rt6_info *rt = NULL;
+ struct fib6_node *fn;
+
+ args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt;
+ args[0].addr = daddr;
+
+ args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt;
+ args[1].addr = saddr;
+
+ fn = fib6_lookup_1(root, args);
+
+ if (fn == NULL)
+ fn = root;
+
+ return fn;
+}
+
+/*
+ * Deletion
+ *
+ */
+
+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn)
+{
+ while(fn) {
+ if(fn->left)
+ return fn->left->leaf;
+
+ if(fn->right)
+ return fn->right->leaf;
+
+ fn = fn->subtree;
+ }
+ return NULL;
+}
+
+/*
+ * called to trim the tree of intermediate nodes when possible
+ */
+
+static void fib6_del_2(struct fib6_node *fn)
+{
+ struct rt6_info *rt;
+
+ fn->fn_flags &= ~RTN_RTINFO;
+ rt6_stats.fib_route_nodes--;
+
+ if (fn->fn_flags & RTN_TL_ROOT)
+ return;
+
+ do {
+ struct fib6_node *pn, *child;
+ int children = 0;
+
+ child = NULL;
+
+ if (fn->left) {
+ children++;
+ child = fn->left;
+ }
+
+ if (fn->right) {
+ children++;
+ child = fn->right;
+ }
+
+ if (children > 1 || (fn->fn_flags & RTN_RTINFO))
+ break;
+
+ if (fn->subtree)
+ goto stree_node;
+
+ pn = fn->parent;
+
+ if ((fn->fn_flags & RTN_ROOT) == 0) {
+ if (pn->left == fn)
+ pn->left = child;
+ else
+ pn->right = child;
+
+ if (child)
+ child->parent = pn;
+
+ if (fn->leaf)
+ rt6_release(fn->leaf);
+ } else {
+ if (children)
+ break;
+
+ pn->subtree = NULL;
+ }
+
+ node_free(fn);
+ fn = pn;
+
+ } while (!(fn->fn_flags & RTN_TL_ROOT));
+
+ return;
+
+stree_node:
+
+ rt6_release(fn->leaf);
+ rt = fib6_find_prefix(fn);
+
+ if (rt == NULL)
+ panic("fib6_del_2: inconsistent tree\n");
+
+ atomic_inc(&rt->rt6i_ref);
+ fn->leaf = rt;
+}
+
+static struct fib6_node * fib6_del_1(struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+
+ fn = rt->rt6i_node;
+
+ if (fn) {
+ struct rt6_info **back;
+ struct rt6_info *lf;
+
+ back = &fn->leaf;
+
+ for(lf = fn->leaf; lf; lf=lf->u.next) {
+ if (rt == lf) {
+ /*
+ * Delete this entry.
+ */
+
+ *back = lf->u.next;
+ rt6_release(lf);
+ return fn;
+ }
+ back = &lf->u.next;
+ }
+ }
+
+ return NULL;
+}
+
+int fib6_del(struct rt6_info *rt)
+{
+ struct fib6_node *fn;
+
+ fn = fib6_del_1(rt);
+
+ if (fn == NULL)
+ return -ENOENT;
+
+ if (fn->leaf == NULL)
+ fib6_del_2(fn);
+
+ return 0;
+}
+
+/*
+ * Tree transversal function
+ *
+ */
+
+void fib6_walk_tree(struct fib6_node *root, f_pnode func, void *arg,
+ int filter)
+{
+ struct fib6_node *fn;
+
+ fn = root;
+
+ do {
+ if (!(fn->fn_flags & RTN_TAG)) {
+ fn->fn_flags |= RTN_TAG;
+
+ if (fn->left) {
+ fn = fn->left;
+ continue;
+ }
+ }
+
+ fn->fn_flags &= ~RTN_TAG;
+
+ if (fn->right) {
+ fn = fn->right;
+ continue;
+ }
+
+ do {
+ struct fib6_node *node;
+
+ if (fn->fn_flags & RTN_ROOT)
+ break;
+ node = fn;
+ fn = fn->parent;
+
+ if (!(node->fn_flags & RTN_TAG)) {
+ if (node->subtree) {
+ fib6_walk_tree(node->subtree, func,
+ arg, filter);
+ }
+
+ if (!filter ||
+ (node->fn_flags & RTN_RTINFO))
+ (*func)(node, arg);
+ }
+
+ } while (!(fn->fn_flags & RTN_TAG));
+
+ } while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
+}
+
+/*
+ * Garbage collection
+ */
+
+static int fib6_gc_node(struct fib6_node *fn, int timeout)
+{
+ struct rt6_info *rt, **back;
+ int more = 0;
+ unsigned long now = jiffies;
+
+ back = &fn->leaf;
+
+ for (rt = fn->leaf; rt;) {
+ if ((rt->rt6i_flags & RTF_CACHE) && atomic_read(&rt->rt6i_use) == 0) {
+ if (now - rt->rt6i_tstamp > timeout) {
+ struct rt6_info *old;
+
+ old = rt;
+
+ rt = rt->u.next;
+
+ *back = rt;
+
+ old->rt6i_node = NULL;
+ rt6_release(old);
+ rt6_stats.fib_rt_entries--;
+ continue;
+ }
+ more++;
+ }
+
+ /*
+ * check addrconf expiration here.
+ */
+ back = &rt->u.next;
+ rt = rt->u.next;
+ }
+
+ return more;
+}
+
+struct fib6_gc_args {
+ unsigned long timeout;
+ int more;
+};
+
+static void fib6_garbage_collect(struct fib6_node *fn, void *p_arg)
+{
+ struct fib6_gc_args * args = (struct fib6_gc_args *) p_arg;
+
+ if (fn->fn_flags & RTN_RTINFO) {
+ int more;
+
+ more = fib6_gc_node(fn, args->timeout);
+
+ if (fn->leaf) {
+ args->more += more;
+ return;
+ }
+
+ rt6_stats.fib_route_nodes--;
+ fn->fn_flags &= ~RTN_RTINFO;
+ }
+
+ /*
+ * tree nodes (with no routing information)
+ */
+
+ if (!fn->subtree && !(fn->fn_flags & RTN_TL_ROOT)) {
+ int children = 0;
+ struct fib6_node *chld = NULL;
+
+ if (fn->left) {
+ children++;
+ chld = fn->left;
+ }
+
+ if (fn->right) {
+ children++;
+ chld = fn->right;
+ }
+
+ if ((fn->fn_flags & RTN_ROOT)) {
+ if (children == 0) {
+ struct fib6_node *pn;
+
+ pn = fn->parent;
+ pn->subtree = NULL;
+
+ node_free(fn);
+ }
+ return;
+ }
+
+ if (children <= 1) {
+ struct fib6_node *pn = fn->parent;
+
+ if (pn->left == fn)
+ pn->left = chld;
+ else
+ pn->right = chld;
+
+ if (chld)
+ chld->parent = pn;
+
+ if (fn->leaf)
+ rt6_release(fn->leaf);
+
+ node_free(fn);
+
+ return;
+ }
+ }
+
+ if (fn->leaf == NULL) {
+ struct rt6_info *nrt;
+
+ nrt = fib6_find_prefix(fn);
+
+ if (nrt == NULL)
+ panic("fib6: inconsistent tree\n");
+
+ atomic_inc(&nrt->rt6i_ref);
+ fn->leaf = nrt;
+ }
+}
+
+static void fib6_run_gc(unsigned long dummy)
+{
+ struct fib6_gc_args arg = {
+ ipv6_config.rt_cache_timeout,
+ 0
+ };
+
+ fib6_walk_tree(&ip6_routing_table, fib6_garbage_collect, &arg, 0);
+
+ if (arg.more) {
+ ip6_fib_timer.expires = jiffies + ipv6_config.rt_gc_period;
+ add_timer(&ip6_fib_timer);
+ } else {
+ ip6_fib_timer.expires = 0;
+ }
+}
diff --git a/net/ipv6/ip6_fw.c b/net/ipv6/ip6_fw.c
new file mode 100644
index 000000000..f6e7f8da4
--- /dev/null
+++ b/net/ipv6/ip6_fw.c
@@ -0,0 +1,378 @@
+/*
+ * IPv6 Firewall
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_fw.c,v 1.4 1997/03/18 18:24:34 davem Exp $
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/udp.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/ip6_fw.h>
+#include <net/netlink.h>
+
+static unsigned long ip6_fw_rule_cnt;
+static struct ip6_fw_rule ip6_fw_rule_list = {
+ {0},
+ NULL, NULL,
+ {0},
+ IP6_FW_REJECT
+};
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args);
+
+struct flow_rule_ops ip6_fw_ops = {
+ ip6_fw_accept
+};
+
+
+static struct rt6_info ip6_fw_null_entry = {
+ {{NULL, 0, 0, NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+ ip6_pkt_discard, ip6_pkt_discard, NULL}},
+ NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+ 0, &ip6_fw_rule_list, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+static struct fib6_node ip6_fw_fib = {
+ NULL, NULL, NULL, NULL,
+ &ip6_fw_null_entry,
+ 0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+static void ip6_rule_add(struct ip6_fw_rule *rl)
+{
+ struct ip6_fw_rule *next;
+
+ start_bh_atomic();
+ ip6_fw_rule_cnt++;
+ next = &ip6_fw_rule_list;
+ rl->next = next;
+ rl->prev = next->prev;
+ rl->prev->next = rl;
+ next->prev = rl;
+ end_bh_atomic();
+}
+
+static void ip6_rule_del(struct ip6_fw_rule *rl)
+{
+ struct ip6_fw_rule *next, *prev;
+
+ start_bh_atomic();
+ ip6_fw_rule_cnt--;
+ next = rl->next;
+ prev = rl->prev;
+ next->prev = prev;
+ prev->next = next;
+ end_bh_atomic();
+}
+
+static __inline__ struct ip6_fw_rule * ip6_fwrule_alloc(void)
+{
+ struct ip6_fw_rule *rl;
+
+ rl = kmalloc(sizeof(struct ip6_fw_rule), GFP_ATOMIC);
+
+ memset(rl, 0, sizeof(struct ip6_fw_rule));
+
+ if (rl)
+ rl->flowr.ops = &ip6_fw_ops;
+
+ return rl;
+}
+
+static __inline__ void ip6_fwrule_free(struct ip6_fw_rule * rl)
+{
+ kfree(rl);
+}
+
+static __inline__ int port_match(int rl_port, int fl_port)
+{
+ int res = 0;
+ if (rl_port == 0 || (rl_port == fl_port))
+ res = 1;
+ return res;
+}
+
+static int ip6_fw_accept_trans(struct ip6_fw_rule *rl,
+ struct fl_acc_args *args)
+{
+ int res = FLOWR_NODECISION;
+ int proto = 0;
+ int sport = 0;
+ int dport = 0;
+
+ switch (args->type) {
+ case FL_ARG_FORWARD:
+ {
+ struct sk_buff *skb = args->fl_u.skb;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ int len;
+
+ len = skb->len - sizeof(struct ipv6hdr);
+
+ proto = hdr->nexthdr;
+
+ switch (proto) {
+ case IPPROTO_TCP:
+ {
+ struct tcphdr *th;
+
+ if (len < sizeof(struct tcphdr)) {
+ res = FLOWR_ERROR;
+ goto out;
+ }
+ th = (struct tcphdr *)(hdr + 1);
+ sport = th->source;
+ dport = th->dest;
+ break;
+ }
+ case IPPROTO_UDP:
+ {
+ struct udphdr *uh;
+
+ if (len < sizeof(struct udphdr)) {
+ res = FLOWR_ERROR;
+ goto out;
+ }
+ uh = (struct udphdr *)(hdr + 1);
+ sport = uh->source;
+ dport = uh->dest;
+ break;
+ }
+ default:
+ goto out;
+ };
+ break;
+ }
+
+ case FL_ARG_ORIGIN:
+ {
+ proto = args->fl_u.fl_o.flow->proto;
+
+ if (proto == IPPROTO_ICMPV6) {
+ goto out;
+ } else {
+ sport = args->fl_u.fl_o.flow->uli_u.ports.sport;
+ dport = args->fl_u.fl_o.flow->uli_u.ports.dport;
+ }
+ break;
+ }
+
+ if (proto == rl->info.proto &&
+ port_match(args->fl_u.fl_o.flow->uli_u.ports.sport, sport) &&
+ port_match(args->fl_u.fl_o.flow->uli_u.ports.dport, dport)) {
+ if (rl->policy & IP6_FW_REJECT)
+ res = FLOWR_SELECT;
+ else
+ res = FLOWR_CLEAR;
+ }
+
+ default:
+#if IP6_FW_DEBUG >= 1
+ printk(KERN_DEBUG "ip6_fw_accept: unknown arg type\n");
+#endif
+ goto out;
+ };
+
+out:
+ return res;
+}
+
+static int ip6_fw_accept(struct dst_entry *dst, struct fl_acc_args *args)
+{
+ struct rt6_info *rt;
+ struct ip6_fw_rule *rl;
+ int proto;
+ int res = FLOWR_NODECISION;
+
+ rt = (struct rt6_info *) dst;
+ rl = (struct ip6_fw_rule *) rt->rt6i_flowr;
+
+ proto = rl->info.proto;
+
+ switch (proto) {
+ case 0:
+ if (rl->policy & IP6_FW_REJECT)
+ res = FLOWR_SELECT;
+ else
+ res = FLOWR_CLEAR;
+ break;
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ res = ip6_fw_accept_trans(rl, args);
+ break;
+ case IPPROTO_ICMPV6:
+ };
+
+ return res;
+}
+
+static struct dst_entry * ip6_fw_dup(struct dst_entry *frule,
+ struct dst_entry *rt,
+ struct fl_acc_args *args)
+{
+ struct ip6_fw_rule *rl;
+ struct rt6_info *nrt;
+ struct rt6_info *frt;
+
+ frt = (struct rt6_info *) frule;
+
+ rl = (struct ip6_fw_rule *) frt->rt6i_flowr;
+
+ nrt = ip6_rt_copy((struct rt6_info *) rt);
+
+ if (nrt) {
+ nrt->u.dst.input = frule->input;
+ nrt->u.dst.output = frule->output;
+
+ nrt->rt6i_flowr = flow_clone(frt->rt6i_flowr);
+
+ nrt->rt6i_flags |= RTF_CACHE;
+ nrt->rt6i_tstamp = jiffies;
+ }
+
+ return (struct dst_entry *) nrt;
+}
+
+int ip6_fw_reject(struct sk_buff *skb)
+{
+#if IP6_FW_DEBUG >= 1
+ printk(KERN_DEBUG "packet rejected: \n");
+#endif
+
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADM_PROHIBITED, 0,
+ skb->dev);
+ /*
+ * send it via netlink, as (rule, skb)
+ */
+
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+int ip6_fw_discard(struct sk_buff *skb)
+{
+ printk(KERN_DEBUG "ip6_fw: BUG fw_reject called\n");
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+int ip6_fw_msg_add(struct ip6_fw_msg *msg)
+{
+ struct in6_rtmsg rtmsg;
+ struct ip6_fw_rule *rl;
+ struct rt6_info *rt;
+ int err;
+
+ ipv6_addr_copy(&rtmsg.rtmsg_dst, &msg->dst);
+ ipv6_addr_copy(&rtmsg.rtmsg_src, &msg->src);
+ rtmsg.rtmsg_dst_len = msg->dst_len;
+ rtmsg.rtmsg_src_len = msg->src_len;
+ rtmsg.rtmsg_metric = IP6_RT_PRIO_FW;
+
+ rl = ip6_fwrule_alloc();
+
+ if (rl == NULL)
+ return -ENOMEM;
+
+ rl->policy = msg->policy;
+ rl->info.proto = msg->proto;
+ rl->info.uli_u.data = msg->u.data;
+
+ rtmsg.rtmsg_flags = RTF_NONEXTHOP|RTF_POLICY;
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (rt == NULL) {
+ ip6_fwrule_free(rl);
+ return -ENOMEM;
+ }
+
+ rt->u.dst.error = -EPERM;
+
+ if (msg->policy == IP6_FW_ACCEPT) {
+ /*
+ * Accept rules are never selected
+ * (i.e. packets use normal forwarding)
+ */
+ rt->u.dst.input = ip6_fw_discard;
+ rt->u.dst.output = ip6_fw_discard;
+ } else {
+ rt->u.dst.input = ip6_fw_reject;
+ rt->u.dst.output = ip6_fw_reject;
+ }
+
+ ip6_rule_add(rl);
+
+ rt->rt6i_flowr = flow_clone((struct flow_rule *)rl);
+
+ return 0;
+}
+
+static int ip6_fw_msgrcv(int unit, struct sk_buff *skb)
+{
+ int count = 0;
+
+ while (skb->len) {
+ struct ip6_fw_msg *msg;
+
+ if (skb->len < sizeof(struct ip6_fw_msg)) {
+ count = -EINVAL;
+ break;
+ }
+
+ msg = (struct ip6_fw_msg *) skb->data;
+ skb_pull(skb, sizeof(struct ip6_fw_msg));
+ count += sizeof(struct ip6_fw_msg);
+
+ switch (msg->action) {
+ case IP6_FW_MSG_ADD:
+ ip6_fw_msg_add(msg);
+ break;
+ case IP6_FW_MSG_DEL:
+ break;
+ default:
+ return -EINVAL;
+ };
+ }
+
+ return count;
+}
+
+static void ip6_fw_destroy(struct flow_rule *rl)
+{
+ ip6_fwrule_free((struct ip6_fw_rule *)rl);
+}
+
+#ifdef MODULE
+#define ip6_fw_init module_init
+#endif
+
+void ip6_fw_init(void)
+{
+ netlink_attach(NETLINK_IP6_FW, ip6_fw_msgrcv);
+}
+
+#ifdef MODULE
+void module_cleanup(void)
+{
+ netlink_detach(NETLINK_IP6_FW);
+}
+#endif
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
new file mode 100644
index 000000000..c5e21417d
--- /dev/null
+++ b/net/ipv6/ip6_input.c
@@ -0,0 +1,408 @@
+/*
+ * IPv6 input
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ * Ian P. Morris <I.P.Morris@soton.ac.uk>
+ *
+ * $Id: ip6_input.c,v 1.4 1997/03/18 18:24:35 davem Exp $
+ *
+ * Based in linux/net/ipv4/ip_input.c
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/sched.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/protocol.h>
+#include <net/transp_v6.h>
+#include <net/rawv6.h>
+#include <net/ndisc.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+ __u8 *nhptr, struct ipv6_options *opt);
+
+struct hdrtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+} hdrproc_lst[] = {
+
+ /*
+ TODO
+
+ {NEXTHDR_HOP, ipv6_hop_by_hop}
+ {NEXTHDR_ROUTING, ipv6_routing_header},
+ */
+ {NEXTHDR_FRAGMENT, ipv6_reassembly},
+
+ {NEXTHDR_DEST, ipv6_dest_opt},
+ /*
+ {NEXTHDR_AUTH, ipv6_auth_hdr},
+ {NEXTHDR_ESP, ipv6_esp_hdr},
+ */
+ {NEXTHDR_MAX, NULL}
+};
+
+/* New header structures */
+
+
+struct ipv6_tlvtype {
+ u8 type;
+ u8 len;
+};
+
+struct ipv6_destopt_hdr {
+ u8 nexthdr;
+ u8 hdrlen;
+};
+
+
+struct tlvtype_proc {
+ u8 type;
+ int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
+ struct ipv6_options *opt);
+ /*
+ * these functions do NOT update skb->h.raw
+ */
+
+} tlvprocdestopt_lst[] = {
+ {255, NULL}
+};
+
+static int ip6_dstopt_unknown(struct sk_buff *skb, struct ipv6_tlvtype *hdr)
+{
+ struct in6_addr *daddr;
+ int pos;
+
+ /*
+ * unkown destination option type
+ */
+
+ pos = (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
+
+ /* I think this is correct please check - IPM */
+
+ switch ((hdr->type & 0xC0) >> 6) {
+ case 0: /* ignore */
+ skb->h.raw += hdr->len+2;
+ return 1;
+
+ case 1: /* drop packet */
+ break;
+
+ case 2: /* send ICMP PARM PROB regardless and drop packet */
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_OPTION,
+ pos, skb->dev);
+ break;
+
+ case 3: /* Send ICMP if not a multicast address and drop packet */
+ daddr = &skb->nh.ipv6h->daddr;
+ if (!(ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST))
+ icmpv6_send(skb, ICMPV6_PARAMPROB,
+ ICMPV6_UNK_OPTION, pos, skb->dev);
+ };
+
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+static int ip6_parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
+ struct device *dev, __u8 *nhptr,
+ struct ipv6_options *opt, void *lastopt)
+{
+ struct ipv6_tlvtype *hdr;
+ struct tlvtype_proc *curr;
+
+ while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt) {
+ switch (hdr->type & 0x3F) {
+ case 0: /* TLV encoded Pad1 */
+ skb->h.raw++;
+ break;
+
+ case 1: /* TLV encoded PadN */
+ skb->h.raw += hdr->len+2;
+ break;
+
+ default: /* Other TLV code so scan list */
+ for (curr=procs; curr->type != 255; curr++) {
+ if (curr->type == (hdr->type & 0x3F)) {
+ curr->func(skb, dev, nhptr, opt);
+ skb->h.raw += hdr->len+2;
+ break;
+ }
+ }
+ if (curr->type==255) {
+ if (ip6_dstopt_unknown(skb, hdr) == 0)
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev,
+ __u8 *nhptr, struct ipv6_options *opt)
+{
+ struct sk_buff *skb=*skb_ptr;
+ struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
+ int res = 0;
+
+ if (ip6_parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,
+ skb->h.raw+hdr->hdrlen))
+ res = hdr->nexthdr;
+
+ return res;
+}
+
+
+int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ struct ipv6hdr *hdr;
+ int pkt_len;
+
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ hdr = skb->nh.ipv6h;
+
+ if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
+ goto err;
+
+ pkt_len = ntohs(hdr->payload_len);
+
+ if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+ goto err;
+
+ skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
+
+ ip6_route_input(skb);
+
+ return 0;
+err:
+ ipv6_statistics.Ip6InHdrErrors++;
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+/*
+ * 0 - deliver
+ * 1 - block
+ */
+static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+{
+ struct icmp6hdr *icmph;
+ struct raw6_opt *opt;
+
+ opt = &sk->tp_pinfo.tp_raw;
+ icmph = (struct icmp6hdr *) (skb->nh.ipv6h + 1);
+ return test_bit(icmph->icmp6_type, &opt->filter);
+}
+
+/*
+ * demultiplex raw sockets.
+ * (should consider queueing the skb in the sock receive_queue
+ * without calling rawv6.c)
+ */
+static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
+ struct ipv6_options *opt,
+ int nexthdr, int len)
+{
+ struct in6_addr *saddr;
+ struct in6_addr *daddr;
+ struct sock *sk, *sk2;
+ __u8 hash;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = saddr + 1;
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+
+ sk = raw_v6_htable[hash];
+
+ /*
+ * The first socket found will be delivered after
+ * delivery to transport protocols.
+ */
+
+ if (sk == NULL)
+ return NULL;
+
+ sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
+
+ if (sk) {
+ sk2 = sk;
+
+ while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr))) {
+ struct sk_buff *buff;
+
+ if (nexthdr == IPPROTO_ICMPV6 &&
+ icmpv6_filter(sk2, skb))
+ continue;
+
+ buff = skb_clone(skb, GFP_ATOMIC);
+ buff->sk = sk2;
+ rawv6_rcv(buff, skb->dev, saddr, daddr, opt, len);
+ }
+ }
+
+ if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
+ sk = NULL;
+
+ return sk;
+}
+
+/*
+ * Deliver the packet to the host
+ */
+
+int ip6_input(struct sk_buff *skb)
+{
+ struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ struct inet6_protocol *ipprot;
+ struct hdrtype_proc *hdrt;
+ struct sock *raw_sk;
+ __u8 *nhptr;
+ int nexthdr;
+ int found = 0;
+ u8 hash;
+ int len;
+
+ skb->h.raw += sizeof(struct ipv6hdr);
+
+ /*
+ * Parse extension headers
+ */
+
+ nexthdr = hdr->nexthdr;
+ nhptr = &hdr->nexthdr;
+
+ /*
+ * check for extension headers
+ */
+
+st_loop:
+
+ for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++) {
+ if (hdrt->type == nexthdr) {
+ if ((nexthdr = hdrt->func(&skb, skb->dev, nhptr, opt))) {
+ nhptr = skb->h.raw;
+ hdr = skb->nh.ipv6h;
+ goto st_loop;
+ }
+ return 0;
+ }
+ }
+
+ len = skb->tail - skb->h.raw;
+
+ raw_sk = ipv6_raw_deliver(skb, opt, nexthdr, len);
+
+ hash = nexthdr & (MAX_INET_PROTOS - 1);
+ for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
+ ipprot != NULL;
+ ipprot = (struct inet6_protocol *) ipprot->next) {
+ struct sk_buff *buff = skb;
+
+ if (ipprot->protocol != nexthdr)
+ continue;
+
+ if (ipprot->copy || raw_sk)
+ buff = skb_clone(skb, GFP_ATOMIC);
+
+
+ ipprot->handler(buff, skb->dev, &hdr->saddr, &hdr->daddr,
+ opt, len, 0, ipprot);
+ found = 1;
+ }
+
+ if (raw_sk) {
+ skb->sk = raw_sk;
+ rawv6_rcv(skb, skb->dev, &hdr->saddr, &hdr->daddr, opt, len);
+ found = 1;
+ }
+
+ /*
+ * not found: send ICMP parameter problem back
+ */
+
+ if (!found) {
+ unsigned long offset;
+#if IP6_DEBUG >= 2
+ printk(KERN_DEBUG "proto not found %d\n", nexthdr);
+#endif
+ offset = nhptr - (u8*) hdr;
+ icmpv6_send(skb, ICMPV6_PARAMPROB, ICMPV6_UNK_NEXTHDR,
+ offset, skb->dev);
+ kfree_skb(skb, FREE_READ);
+ }
+
+ return 0;
+}
+
+int ip6_mc_input(struct sk_buff *skb)
+{
+ struct ipv6hdr *hdr;
+ int deliver = 0;
+ int discard = 1;
+
+ hdr = skb->nh.ipv6h;
+ if (ipv6_chk_mcast_addr(skb->dev, &hdr->daddr))
+ deliver = 1;
+
+#if 0
+ if (ipv6_config.multicast_route) {
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&hdr->daddr);
+
+ if (!(addr_type & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
+ struct sk_buff *skb2;
+ struct dst_entry *dst;
+
+ dst = skb->dst;
+
+ if (deliver) {
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ } else {
+ discard = 0;
+ skb2 = skb;
+ }
+
+ dst->output(skb2);
+ }
+ }
+#endif
+
+ if (deliver) {
+ discard = 0;
+ ip6_input(skb);
+ }
+
+ if (discard)
+ kfree_skb(skb, FREE_READ);
+
+ return 0;
+}
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
new file mode 100644
index 000000000..6c14747eb
--- /dev/null
+++ b/net/ipv6/ip6_output.c
@@ -0,0 +1,629 @@
+/*
+ * IPv6 output functions
+ * Linux INET6 implementation
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: ip6_output.c,v 1.3 1997/03/18 18:24:37 davem Exp $
+ *
+ * Based on linux/net/ipv4/ip_output.c
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/route.h>
+
+#include <net/sock.h>
+#include <net/snmp.h>
+
+#include <net/ipv6.h>
+#include <net/ndisc.h>
+#include <net/protocol.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+static u32 ipv6_fragmentation_id = 1;
+
+static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
+ int len)
+{
+ struct device *dev;
+
+
+ dev = dst->dev;
+
+ skb->arp = 1;
+
+ if (dev->hard_header) {
+ int mac;
+
+#if 0
+ if (dst->hh)
+ hh_copy_header(dst->hh, skb);
+#endif
+ mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
+
+ if (mac < 0)
+ skb->arp = 0;
+ }
+
+ skb->mac.raw = skb->data;
+}
+
+/*
+ * xmit an sk_buff (used by TCP)
+ * sk can be NULL (for sending RESETs)
+ */
+
+int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ struct ipv6_options *opt)
+{
+ struct ipv6_pinfo *np = NULL;
+ struct dst_entry *dst = NULL;
+ struct ipv6hdr *hdr;
+ int seg_len;
+
+ hdr = skb->nh.ipv6h;
+
+ if (sk)
+ np = &sk->net_pinfo.af_inet6;
+
+ if (np && np->dst) {
+ /*
+ * dst_check returns NULL if route is no longer valid
+ */
+ dst = dst_check(&dst, np->dst_cookie);
+ }
+
+ if (dst == NULL) {
+ dst = ip6_route_output(sk, fl);
+
+ if (dst->error) {
+ /*
+ * NETUNREACH usually
+ */
+ return dst->error;
+ }
+ }
+
+ skb->dst = dst_clone(dst);
+ skb->dev = dst->dev;
+ seg_len = skb->tail - ((unsigned char *) hdr);
+
+ /*
+ * Link Layer headers
+ */
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ hdr = skb->nh.ipv6h;
+
+ ipv6_build_mac_hdr(skb, dst, seg_len);
+
+
+ /*
+ * Fill in the IPv6 header
+ */
+
+ hdr->version = 6;
+ hdr->priority = np ? np->priority : 0;
+
+ if (np)
+ memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
+ else
+ memset(hdr->flow_lbl, 0, 3);
+
+ hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
+ hdr->nexthdr = fl->proto;
+ hdr->hop_limit = np ? np->hop_limit : ipv6_config.hop_limit;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+
+ if (sk)
+ ip6_dst_store(sk, dst);
+ else
+ dst_release(dst);
+
+ return 0;
+}
+
+/*
+ * To avoid extra problems ND packets are send through this
+ * routine. It's code duplication but i really want to avoid
+ * extra checks since ipv6_build_header is used by TCP (which
+ * is for us performace critical)
+ */
+
+int ip6_nd_hdr(struct sock *sk, struct sk_buff *skb, struct device *dev,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ int proto, int len)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+ int totlen;
+
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->dev = dev;
+
+ totlen = len + sizeof(struct ipv6hdr);
+
+ skb->mac.raw = skb->data;
+
+ hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.ipv6h = hdr;
+
+ hdr->version = 6;
+ hdr->priority = np->priority & 0x0f;
+ memset(hdr->flow_lbl, 0, 3);
+
+ hdr->payload_len = htons(len);
+ hdr->nexthdr = proto;
+ hdr->hop_limit = np->hop_limit;
+
+ ipv6_addr_copy(&hdr->saddr, saddr);
+ ipv6_addr_copy(&hdr->daddr, daddr);
+
+ return 0;
+}
+
+static void ip6_bld_1(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
+ int hlimit, unsigned short pktlength)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+
+ skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
+ hdr = skb->nh.ipv6h;
+
+ hdr->version = 6;
+ hdr->priority = np->priority;
+
+ memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+
+ hdr->payload_len = htons(pktlength - sizeof(struct ipv6hdr));
+
+ /*
+ * FIXME: hop limit has default UNI/MCAST and
+ * msgctl settings
+ */
+ hdr->hop_limit = hlimit;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+}
+
+static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
+ const void *data, struct dst_entry *dst,
+ struct flowi *fl, struct ipv6_options *opt,
+ int hlimit, int flags, unsigned short length)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6hdr *hdr;
+ struct sk_buff *last_skb;
+ struct frag_hdr *fhdr;
+ int unfrag_len;
+ int payl_len;
+ int frag_len;
+ int last_len;
+ int nfrags;
+ int fhdr_dist;
+ int err;
+
+ /*
+ * Fragmentation
+ *
+ * Extension header order:
+ * Hop-by-hop -> Routing -> Fragment -> rest (...)
+ *
+ * We must build the non-fragmented part that
+ * will be in every packet... this also means
+ * that other extension headers (Dest, Auth, etc)
+ * must be considered in the data to be fragmented
+ */
+
+ unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
+ payl_len = length;
+
+ if (opt) {
+ unfrag_len += opt->opt_nflen;
+ payl_len += opt->opt_flen;
+ }
+
+ nfrags = payl_len / ((dst->pmtu - unfrag_len) & ~0x7);
+
+ /*
+ * Length of fragmented part on every packet but
+ * the last must be an:
+ * "integer multiple of 8 octects".
+ */
+
+ frag_len = (dst->pmtu - unfrag_len) & ~0x7;
+
+ /*
+ * We must send from end to start because of
+ * UDP/ICMP checksums. We do a funny trick:
+ * fill the last skb first with the fixed
+ * header (and its data) and then use it
+ * to create the following segments and send it
+ * in the end. If the peer is checking the M_flag
+ * to trigger the reassembly code then this
+ * might be a good idea.
+ */
+
+ last_len = payl_len - (nfrags * frag_len);
+
+ if (last_len == 0) {
+ last_len = frag_len;
+ nfrags--;
+ }
+
+ last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
+ dst->dev->hard_header_len + 15,
+ 0, flags & MSG_DONTWAIT, &err);
+
+ if (last_skb == NULL)
+ return err;
+
+ last_skb->dst = dst_clone(dst);
+ last_skb->dev = dst->dev;
+ last_skb->protocol = htons(ETH_P_IPV6);
+ last_skb->when = jiffies;
+ last_skb->arp = 0;
+
+ /*
+ * build the mac header...
+ */
+ if (dst->dev->hard_header_len) {
+ skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
+ ipv6_build_mac_hdr(last_skb, dst, unfrag_len + frag_len);
+ }
+
+ hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
+ last_skb->nh.ipv6h = hdr;
+
+ hdr->version = 6;
+ hdr->priority = np->priority;
+
+ memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
+ hdr->payload_len = htons(unfrag_len + frag_len - sizeof(struct ipv6hdr));
+
+ hdr->hop_limit = hlimit;
+
+ hdr->nexthdr = NEXTHDR_FRAGMENT;
+
+ ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
+ ipv6_addr_copy(&hdr->daddr, fl->nl_u.ip6_u.daddr);
+
+#if 0
+ if (opt && opt->srcrt) {
+ hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, daddr,
+ NEXTHDR_FRAGMENT);
+ }
+#endif
+
+ fhdr = (struct frag_hdr *) skb_put(last_skb, sizeof(struct frag_hdr));
+ memset(fhdr, 0, sizeof(struct frag_hdr));
+
+ fhdr->nexthdr = fl->proto;
+ fhdr->frag_off = ntohs(nfrags * frag_len);
+ fhdr->identification = ipv6_fragmentation_id++;
+
+ fhdr_dist = (unsigned char *) fhdr - last_skb->data;
+
+ err = getfrag(data, &hdr->saddr, last_skb->tail, nfrags * frag_len,
+ last_len);
+
+ if (!err) {
+ while (nfrags--) {
+ struct sk_buff *skb;
+
+ struct frag_hdr *fhdr2;
+
+ printk(KERN_DEBUG "sending frag %d\n", nfrags);
+ skb = skb_copy(last_skb, sk->allocation);
+
+ if (skb == NULL)
+ return -ENOMEM;
+
+ fhdr2 = (struct frag_hdr *) (skb->data + fhdr_dist);
+
+ /* more flag on */
+ fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
+
+ /*
+ * FIXME:
+ * if (nfrags == 0)
+ * put rest of headers
+ */
+
+ err = getfrag(data, &hdr->saddr,skb_put(skb, frag_len),
+ nfrags * frag_len, frag_len);
+
+ if (err) {
+ kfree_skb(skb, FREE_WRITE);
+ break;
+ }
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ }
+ }
+
+ if (err) {
+ kfree_skb(last_skb, FREE_WRITE);
+ return -EFAULT;
+ }
+
+ printk(KERN_DEBUG "sending last frag \n");
+
+ hdr->payload_len = htons(unfrag_len + last_len -
+ sizeof(struct ipv6hdr));
+
+ /*
+ * update last_skb to reflect the getfrag we did
+ * on start.
+ */
+
+ last_skb->tail += last_len;
+ last_skb->len += last_len;
+
+ /*
+ * toss the mac header out and rebuild it.
+ * needed because of the different frame length.
+ * ie: not needed for an ethernet.
+ */
+
+ if (dst->dev->type != ARPHRD_ETHER && last_len != frag_len) {
+ skb_pull(last_skb, (unsigned char *)last_skb->nh.ipv6h -
+ last_skb->data);
+ ipv6_build_mac_hdr(last_skb, dst, unfrag_len + last_len);
+ }
+
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(last_skb);
+
+ return 0;
+}
+
+int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
+ struct flowi *fl, unsigned short length,
+ struct ipv6_options *opt, int hlimit, int flags)
+{
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct in6_addr *final_dst = NULL;
+ struct dst_entry *dst;
+ int pktlength;
+ int err = 0;
+
+ if (opt && opt->srcrt) {
+ struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ final_dst = fl->nl_u.ip6_u.daddr;
+ fl->nl_u.ip6_u.daddr = rt0->addr;
+ }
+
+ dst = NULL;
+
+ if (np->dst)
+ dst = dst_check(&np->dst, np->dst_cookie);
+
+ if (dst == NULL)
+ dst = ip6_route_output(sk, fl);
+
+ if (dst->error) {
+ ipv6_statistics.Ip6OutNoRoutes++;
+ err = -ENETUNREACH;
+ goto out;
+ }
+
+ if (fl->nl_u.ip6_u.saddr == NULL) {
+ struct inet6_ifaddr *ifa;
+
+ ifa = ipv6_get_saddr(dst, fl->nl_u.ip6_u.daddr);
+
+ if (ifa == NULL) {
+#if IP6_DEBUG >= 2
+ printk(KERN_DEBUG "ip6_build_xmit: "
+ "no availiable source address\n");
+#endif
+ err = -ENETUNREACH;
+ goto out;
+ }
+ fl->nl_u.ip6_u.saddr = &ifa->addr;
+ }
+
+ pktlength = length;
+
+ if (hlimit < 0)
+ hlimit = np->hop_limit;
+
+ if (!sk->ip_hdrincl) {
+ pktlength += sizeof(struct ipv6hdr);
+ if (opt)
+ pktlength += opt->opt_flen + opt->opt_nflen;
+ }
+
+ if (pktlength <= dst->pmtu) {
+ struct sk_buff *skb;
+ struct ipv6hdr *hdr;
+ struct device *dev;
+
+ skb = sock_alloc_send_skb(sk, pktlength + 15 +
+ dst->dev->hard_header_len, 0,
+ flags & MSG_DONTWAIT, &err);
+
+ if (skb == NULL) {
+ ipv6_statistics.Ip6OutDiscards++;
+ goto out;
+ }
+
+ dev = dst->dev;
+ skb->dst = dst_clone(dst);
+
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->when = jiffies;
+ skb->arp = 0;
+
+ if (dev && dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ ipv6_build_mac_hdr(skb, dst, pktlength);
+ }
+
+ hdr = (struct ipv6hdr *) skb->tail;
+ skb->nh.ipv6h = hdr;
+
+ if (!sk->ip_hdrincl) {
+ ip6_bld_1(sk, skb, fl, hlimit, pktlength);
+#if 0
+ if (opt && opt->srcrt) {
+ hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
+ final_dst,
+ fl->proto);
+ }
+ else
+#endif
+ hdr->nexthdr = fl->proto;
+ }
+
+ skb_put(skb, length);
+ err = getfrag(data, &hdr->saddr,
+ ((char *) hdr) + (pktlength - length),
+ 0, length);
+
+ if (!err) {
+ ipv6_statistics.Ip6OutRequests++;
+ dst->output(skb);
+ } else {
+ err = -EFAULT;
+ kfree_skb(skb, FREE_WRITE);
+ }
+ } else {
+ if (sk->ip_hdrincl)
+ return -EMSGSIZE;
+
+ err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
+ flags, pktlength);
+ }
+
+ /*
+ * cleanup
+ */
+ out:
+
+ if (np->dst)
+ ip6_dst_store(sk, dst);
+ else
+ dst_release(dst);
+
+ return err;
+}
+
+int ip6_forward(struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb->dst;
+ struct ipv6hdr *hdr = skb->nh.ipv6h;
+ int size;
+
+ /*
+ * check hop-by-hop options present
+ */
+#if 0
+ if (hdr->nexthdr == NEXTHDR_HOP)
+ {
+ }
+#endif
+ /*
+ * check and decrement ttl
+ */
+ if (hdr->hop_limit <= 1) {
+ icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+ 0, skb->dev);
+
+ kfree_skb(skb, FREE_READ);
+ return -ETIMEDOUT;
+ }
+
+ hdr->hop_limit--;
+
+ if (skb->dev == dst->dev && dst->neighbour) {
+ struct in6_addr *target = NULL;
+ struct rt6_info *rt;
+ struct nd_neigh *ndn = (struct nd_neigh *) dst->neighbour;
+
+ /*
+ * incoming and outgoing devices are the same
+ * send a redirect.
+ */
+
+ rt = (struct rt6_info *) dst;
+ if ((rt->rt6i_flags & RTF_GATEWAY))
+ target = &ndn->ndn_addr;
+ else
+ target = &hdr->daddr;
+
+ ndisc_send_redirect(skb, dst->neighbour, target);
+ }
+
+ size = sizeof(struct ipv6hdr) + ntohs(hdr->payload_len);
+
+ if (size > dst->pmtu) {
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
+ kfree_skb(skb, FREE_READ);
+ return -EMSGSIZE;
+ }
+
+ skb->dev = dst->dev;
+
+ /*
+ * Rebuild the mac header
+ */
+ if (skb_headroom(skb) < dst->dev->hard_header_len) {
+ struct sk_buff *buff;
+
+ buff = alloc_skb(dst->dev->hard_header_len + skb->len + 15,
+ GFP_ATOMIC);
+
+ if (buff == NULL) {
+ kfree_skb(skb, FREE_WRITE);
+ return -ENOMEM;
+ }
+
+ skb_reserve(buff, (dst->dev->hard_header_len + 15) & ~15);
+
+ buff->protocol = __constant_htons(ETH_P_IPV6);
+ buff->h.raw = skb_put(buff, size);
+ buff->dst = dst_clone(dst);
+ buff->dev = dst->dev;
+
+ memcpy(buff->h.raw, hdr, size);
+ buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
+ kfree_skb(skb, FREE_READ);
+ skb = buff;
+ } else {
+ skb_pull(skb, skb->nh.raw - skb->data);
+ }
+
+ ipv6_build_mac_hdr(skb, dst, size);
+
+ if (dst->neighbour)
+ ndisc_event_send(dst->neighbour, skb);
+
+ ipv6_statistics.Ip6ForwDatagrams++;
+ dst->output(skb);
+
+ return 0;
+}
diff --git a/net/ipv6/ipv6_input.c b/net/ipv6/ipv6_input.c
deleted file mode 100644
index 64a9d79f0..000000000
--- a/net/ipv6/ipv6_input.c
+++ /dev/null
@@ -1,437 +0,0 @@
-/*
- * IPv6 input
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- * Ian P. Morris <I.P.Morris@soton.ac.uk>
- *
- * Based in linux/net/ipv4/ip_input.c
- *
- * $Id: ipv6_input.c,v 1.13 1996/10/11 16:03:06 roque Exp $
- *
- * This program 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 of the License, or (at your option) any later version.
- */
-
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-#include <linux/icmpv6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/rawv6.h>
-#include <net/ndisc.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-/*
- * Header processing function list
- * We process headers in order (as per RFC)
- * If the processing function returns 0 the packet is considered
- * delivered else it returns the value of the nexthdr.
- * The ptr field of the function points to the previous nexthdr field.
- * This is allows the processing function to change it if it's sematics
- * is: return a new packet without this header (like fragmentation).
- * When a next_header value is not within the list
- * the inet protocol list is searched (i.e. to deliver to
- * TCP for instance)
- */
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
- struct ipv6_options *opt);
-
-
-struct hdrtype_proc {
- u8 type;
- int (*func) (struct sk_buff **, struct device *dev, __u8 *ptr,
- struct ipv6_options *opt);
-} hdrproc_lst[] = {
- /*
- TODO
-
- {NEXTHDR_HOP, ipv6_hop_by_hop}
- */
- {NEXTHDR_ROUTING, ipv6_routing_header},
- {NEXTHDR_FRAGMENT, ipv6_reassembly},
-
- {NEXTHDR_DEST, ipv6_dest_opt},
- /*
- {NEXTHDR_AUTH, ipv6_auth_hdr},
- {NEXTHDR_ESP, ipv6_esp_hdr},
- */
- {NEXTHDR_MAX, NULL}
-};
-
-/* New header structures */
-
-
-struct ipv6_tlvtype {
- u8 type;
- u8 len;
-};
-
-struct ipv6_destopt_hdr {
- u8 nexthdr;
- u8 hdrlen;
-};
-
-
-struct tlvtype_proc {
- u8 type;
- int (*func) (struct sk_buff *, struct device *dev, __u8 *ptr,
- struct ipv6_options *opt);
-
- /* these functions do NOT update skb->h.raw */
-
-} tlvprocdestopt_lst[] = {
- {255, NULL}
-};
-
-
-static int parse_tlv(struct tlvtype_proc *procs, struct sk_buff *skb,
- struct device *dev, __u8 *nhptr, struct ipv6_options *opt,
- void *lastopt)
-{
- struct ipv6_tlvtype *hdr;
- struct tlvtype_proc *curr;
- int pos;
-
- while ((hdr=(struct ipv6_tlvtype *)skb->h.raw) != lastopt)
- switch (hdr->type & 0x3F)
- {
- case 0: /* TLV encoded Pad1 */
- skb->h.raw++;
- break;
-
- case 1: /* TLV encoded PadN */
- skb->h.raw += hdr->len+2;
- break;
-
- default: /* Other TLV code so scan list */
- for (curr=procs; curr->type != 255; curr++)
- if (curr->type == (hdr->type & 0x3F))
- {
- curr->func(skb, dev, nhptr, opt);
- skb->h.raw += hdr->len+2;
- break;
- }
-
- if (curr->type==255)
- {
- /* unkown type */
- pos= (__u8 *) skb->h.raw - (__u8 *) skb->ipv6_hdr;
- /* I think this is correct please check - IPM */
-
- switch ((hdr->type & 0xC0) >> 6) {
- case 0: /* ignore */
- skb->h.raw += hdr->len+2;
- break;
-
- case 1: /* drop packet */
- kfree_skb(skb, FREE_READ);
- return 0;
-
- case 2: /* send ICMP PARM PROB regardless and
- drop packet */
- icmpv6_send(skb, ICMPV6_PARAMETER_PROB,
- 2, pos, dev);
- kfree_skb(skb, FREE_READ);
- return 0;
-
- case 3: /* Send ICMP if not a multicast address
- and drop packet */
- if (!(ipv6_addr_type(&(skb->ipv6_hdr->daddr)) & IPV6_ADDR_MULTICAST) )
- icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 2, pos, dev);
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- }
- break;
- }
-
- return 1;
-}
-
-
-
-static int ipv6_dest_opt(struct sk_buff **skb_ptr, struct device *dev, __u8 *nhptr,
- struct ipv6_options *opt)
-{
- struct sk_buff *skb=*skb_ptr;
- struct ipv6_destopt_hdr *hdr = (struct ipv6_destopt_hdr *) skb->h.raw;
-
- if (parse_tlv(tlvprocdestopt_lst, skb, dev, nhptr, opt,skb->h.raw+hdr->hdrlen))
- return hdr->nexthdr;
- else
- return 0;
-}
-
-
-
-/*
- * 0 - deliver
- * 1 - block
- */
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
-{
- struct icmpv6hdr *icmph;
- struct raw6_opt *opt;
-
- opt = &sk->tp_pinfo.tp_raw;
- icmph = (struct icmpv6hdr *) (skb->ipv6_hdr + 1);
- return test_bit(icmph->type, &opt->filter);
-}
-
-/*
- * demultiplex raw sockets.
- * (should consider queueing the skb in the sock receive_queue
- * without calling rawv6.c)
- */
-static struct sock * ipv6_raw_deliver(struct sk_buff *skb,
- struct device *dev,
- struct ipv6_options *opt,
- __u16 nexthdr,
- __u16 len,
- struct in6_addr *saddr,
- struct in6_addr *daddr)
-{
- struct sock *sk, *sk2;
- __u8 hash;
-
- hash = nexthdr & (SOCK_ARRAY_SIZE-1);
-
- sk = rawv6_prot.sock_array[hash];
-
-
- /*
- * The first socket found will be delivered after
- * delivery to transport protocols.
- */
-
- if (sk == NULL)
- return NULL;
-
- sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr);
-
- if (sk)
- {
- sk2 = sk;
-
- while ((sk2 = inet6_get_sock_raw(sk2->next, nexthdr,
- daddr, saddr)))
- {
- struct sk_buff *buff;
-
- if (nexthdr == IPPROTO_ICMPV6 &&
- icmpv6_filter(sk2, skb))
- {
- continue;
- }
- buff = skb_clone(skb, GFP_ATOMIC);
- buff->sk = sk2;
- rawv6_rcv(buff, dev, saddr, daddr, opt, len);
- }
- }
-
- if (sk && nexthdr == IPPROTO_ICMPV6 && icmpv6_filter(sk, skb))
- {
- sk = NULL;
- }
-
- return sk;
-}
-
-int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
- struct inet6_ifaddr *ifp;
- struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
- struct ipv6hdr *hdr;
- u8 hash;
- u8 addr_type;
- struct inet6_protocol *ipprot;
- struct sock *raw_sk;
- int found = 0;
- int nexthdr = 0;
- __u8 *nhptr;
- int pkt_len;
-
- hdr = skb->ipv6_hdr = (struct ipv6hdr *) skb->h.raw;
-
- if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
- {
- ipv6_statistics.Ip6InHdrErrors++;
- printk(KERN_DEBUG "ipv6_rcv: broken header\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- pkt_len = ntohs(hdr->payload_len);
-
- if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
- {
- printk(KERN_DEBUG "ipv6_rcv: invalid payload length\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
-
- skb_trim(skb, pkt_len + sizeof(struct ipv6hdr));
-
- /* check daddr */
-
- /* Accounting & Firewall check */
-
- addr_type = ipv6_addr_type(&hdr->daddr);
-
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- /*
- * if mcast address is not for one of our groups
- * either pass it to mcast router or discard it
- */
-
- if (ipv6_chk_mcast_addr(dev, &hdr->daddr) == 0)
- {
- /* something like:
- if (acting_as_router)
- ipv6_mcast_route(skb, ...)
- else
- */
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- }
-
- if (addr_type & IPV6_ADDR_MULTICAST ||
- (ifp = ipv6_chk_addr(&hdr->daddr)))
- {
-
- /* loop in a cicle parsing nexthdrs */
-
- skb->h.raw += sizeof(struct ipv6hdr);
-
- /* extension header processing must update skb->h.raw */
-
- nexthdr = hdr->nexthdr;
- nhptr = &hdr->nexthdr;
-
-
- while(1)
- {
- struct hdrtype_proc *hdrt;
-
- /* check for extension header */
-
- for (hdrt=hdrproc_lst; hdrt->type != NEXTHDR_MAX; hdrt++)
- {
- if (hdrt->type == nexthdr)
- {
- if ((nexthdr = hdrt->func(&skb, dev, nhptr, opt)))
- {
- nhptr = skb->h.raw;
- hdr = skb->ipv6_hdr;
- continue;
- }
- return 0;
- }
- }
- break;
-
- }
-
- /*
- * deliver to raw sockets
- * should we deliver raw after or before parsing
- * extension headers ?
- * delivering after means we do reassembly of datagrams
- * in ip.
- */
-
- pkt_len = skb->tail - skb->h.raw;
-
- raw_sk = ipv6_raw_deliver(skb, dev, opt, nexthdr, pkt_len,
- &hdr->saddr, &hdr->daddr);
-
- /* check inet6_protocol list */
-
- hash = nexthdr & (MAX_INET_PROTOS -1);
- for (ipprot = (struct inet6_protocol *) inet6_protos[hash];
- ipprot != NULL;
- ipprot = (struct inet6_protocol *) ipprot->next)
- {
- struct sk_buff *buff = skb;
-
- if (ipprot->protocol != nexthdr)
- continue;
-
- if (ipprot->copy || raw_sk)
- buff = skb_clone(skb, GFP_ATOMIC);
-
-
- ipprot->handler(buff, dev,
- &hdr->saddr, &hdr->daddr,
- opt, pkt_len,
- 0, ipprot);
- found = 1;
- }
-
- if (raw_sk)
- {
- skb->sk = raw_sk;
- rawv6_rcv(skb, dev, &hdr->saddr, &hdr->daddr, opt,
- htons(hdr->payload_len));
- found = 1;
- }
-
- /* not found: send ICMP parameter problem back */
-
- if (!found)
- {
- printk(KERN_DEBUG "proto not found %d\n", nexthdr);
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- }
-
- }
- else
- {
- if (ipv6_forwarding)
- {
- if (addr_type & IPV6_ADDR_LINKLOCAL)
- {
- printk(KERN_DEBUG
- "link local pkt to forward\n");
- kfree_skb(skb, FREE_READ);
- return 0;
- }
- ipv6_forward(skb, dev, 0);
- }
- else
- {
- printk(KERN_WARNING "IPV6: packet to forward -"
- "host not configured as router\n");
- kfree_skb(skb, FREE_READ);
- }
- }
-
- return 0;
-}
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/ipv6_output.c b/net/ipv6/ipv6_output.c
deleted file mode 100644
index 7f82dba03..000000000
--- a/net/ipv6/ipv6_output.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * IPv6 output functions
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- *
- * Based on linux/net/ipv4/ip_output.c
- *
- * $Id: ipv6_output.c,v 1.19 1996/10/16 18:34:16 roque Exp $
- *
- * This program 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 of the License, or (at your option) any later version.
- */
-
-/*
- * Changes:
- *
- * Andi Kleen : exception handling
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/in6.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-static u32 ipv6_fragmentation_id = 1;
-int ipv6_forwarding = 0; /* default: host */
-
-static int __inline__ ipv6_build_mac_header(struct sk_buff *skb,
- struct device *dev,
- struct neighbour *neigh,
- int len)
-{
- int mac;
- int hdrlen = 0;
-
- skb->arp = 1;
- skb->nexthop = neigh;
-
-
- if (dev->hard_header_len)
- {
- skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
- if (neigh && (neigh->flags & NCF_HHVALID))
- {
- /*
- * Cached hardware header
- */
-
- memcpy(skb_push(skb, dev->hard_header_len),
- neigh->hh_data, dev->hard_header_len);
-
- return dev->hard_header_len;
- }
-
- if (dev->hard_header)
- {
- mac = dev->hard_header(skb, dev, ETH_P_IPV6,
- NULL, NULL, len);
-
- if (mac < 0)
- {
- hdrlen = -mac;
- skb->arp = 0;
- }
- else
- {
- hdrlen = mac;
- }
- }
- else
- hdrlen = dev->hard_header_len;
- }
-
- return hdrlen;
-}
-
-void ipv6_redo_mac_hdr(struct sk_buff *skb, struct neighbour *neigh, int len)
-{
- struct device *dev = neigh->dev;
- int mac;
-
- skb->dev = dev;
- skb->nexthop = neigh;
- skb->arp = 1;
-
- skb_pull(skb, (unsigned char *) skb->ipv6_hdr - skb->data);
-
- /*
- * neighbour cache should have the ether address
- * cached... use it
- */
-
- if (dev->hard_header)
- {
- if (neigh && (neigh->flags & NCF_HHVALID))
- {
- /*
- * Cached hardware header
- */
-
- memcpy(skb_push(skb, dev->hard_header_len),
- neigh->hh_data, dev->hard_header_len);
- return;
- }
-
- mac = dev->hard_header(skb, dev, ETH_P_IPV6,
- NULL, NULL, len);
-
- if (mac < 0)
- {
- skb->arp = 0;
- }
-
- }
-}
-
-void default_output_method(struct sk_buff *skb, struct rt6_info *rt)
-{
- struct sock *sk = skb->sk;
- struct device *dev = skb->dev;
-
- if (dev->flags & IFF_UP)
- {
- /*
- * If we have an owner use its priority setting,
- * otherwise use NORMAL
- */
-
- if (sk != NULL)
- {
- dev_queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
- }
- }
- else
- {
- if(sk)
- sk->err = ENETDOWN;
-
- ipv6_statistics.Ip6OutDiscards++;
-
- kfree_skb(skb, FREE_WRITE);
- }
-}
-
-/*
- * xmit an sk_buff (used by TCP)
- * sk can be NULL (for sending RESETs)
- */
-int ipv6_xmit(struct sock *sk, struct sk_buff *skb, struct in6_addr *saddr,
- struct in6_addr *daddr, struct ipv6_options *opt, int proto)
-{
- struct ipv6hdr *hdr;
- struct dest_entry *dc;
- struct ipv6_pinfo *np = NULL;
- struct device *dev = skb->dev;
- int seg_len;
- int addr_type;
- int rt_flags = 0;
-
-
- addr_type = ipv6_addr_type(daddr);
-
- if (addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_SITELOCAL))
- {
- /*
- * force device match on route lookup
- */
-
- rt_flags |= RTI_DEVRT;
- }
-
- if (skb->localroute)
- {
- rt_flags |= RTI_GATEWAY;
- }
-
- hdr = skb->ipv6_hdr;
-
-
- if (sk)
- {
- np = &sk->net_pinfo.af_inet6;
- }
-
- if (np && np->dest)
- {
- dc = ipv6_dst_check(np->dest, daddr, np->dc_sernum, rt_flags);
- }
- else
- {
- dc = ipv6_dst_route(daddr, dev, rt_flags);
- }
-
- if (dc == NULL)
- {
- ipv6_statistics.Ip6OutNoRoutes++;
- return(-ENETUNREACH);
- }
-
- dev = dc->rt.rt_dev;
-
- if (saddr == NULL)
- {
- struct inet6_ifaddr *ifa;
-
- ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
- if (ifa == NULL)
- {
- printk(KERN_DEBUG
- "ipv6_xmit: get_saddr failed\n");
- return -ENETUNREACH;
- }
-
- saddr = &ifa->addr;
-
- if (np)
- {
- ipv6_addr_copy(&np->saddr, saddr);
- }
- }
-
- seg_len = skb->tail - ((unsigned char *) hdr);
-
- /*
- * Link Layer headers
- */
-
- skb->sk = sk;
- skb->protocol = __constant_htons(ETH_P_IPV6);
- skb->free = 1;
- skb->dev = dev;
-
- ipv6_redo_mac_hdr(skb, dc->dc_nexthop, seg_len);
-
- /*
- * Fill in the IPv6 header
- */
-
- hdr->version = 6;
- hdr->priority = np ? np->priority : 0;
-
- if (np)
- memcpy(hdr->flow_lbl, (void *) &np->flow_lbl, 3);
- else
- memset(hdr->flow_lbl, 0, 3);
-
- hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
- hdr->nexthdr = proto;
- hdr->hop_limit = np ? np->hop_limit : ipv6_hop_limit;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
-
- /*
- * Options
- */
-
-
- /*
- * Output the packet
- */
-
- ipv6_statistics.Ip6OutRequests++;
-
- if (dc->rt.rt_output_method)
- {
- (*dc->rt.rt_output_method)(skb, (struct rt6_info *) dc);
- }
- else
- default_output_method(skb, (struct rt6_info *) dc);
-
- /*
- * Update serial number of cached dest_entry or
- * release destination cache entry
- */
-
- if (np)
- {
- np->dest = dc;
- if (dc->rt.fib_node)
- {
- np->dc_sernum = dc->rt.fib_node->fn_sernum;
- }
- }
- else
- {
- ipv6_dst_unlock(dc);
- }
-
- return 0;
-}
-
-/*
- * To avoid extra problems ND packets are send through this
- * routine. It's code duplication but i really want to avoid
- * extra checks since ipv6_build_header is used by TCP (which
- * is for us performace critical)
- */
-
-int ipv6_bld_hdr_2(struct sock *sk, struct sk_buff *skb, struct device *dev,
- struct neighbour *neigh,
- struct in6_addr *saddr, struct in6_addr *daddr,
- int proto, int len)
-{
- struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct ipv6hdr *hdr;
- int hdrlen = 0;
-
- skb->dev = dev;
-
- /* build MAC header */
- hdrlen += ipv6_build_mac_header(skb, dev, neigh, len);
-
- /* build fixed IPv6 header */
-
- if (proto == IPPROTO_RAW)
- return hdrlen;
-
-
- hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
- skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority & 0x0f;
-
- memset(hdr->flow_lbl, 0, 3);
-
- hdr->hop_limit = np->hop_limit;
-
- if (saddr == NULL)
- {
- printk(KERN_DEBUG "bug: bld_hdr called with no saddr\n");
- return -ENETUNREACH;
- }
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- hdrlen += sizeof(struct ipv6hdr);
-
- hdr->nexthdr = proto;
-
- return hdrlen;
-}
-
-void ipv6_queue_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
- int free)
-{
- struct ipv6hdr *hdr;
- u32 seg_len;
-
- hdr = skb->ipv6_hdr;
- skb->sk = sk;
- skb->protocol = __constant_htons(ETH_P_IPV6);
- skb->free=1;
-
- seg_len = skb->tail - ((unsigned char *) hdr);
-
- hdr->payload_len = htons(seg_len - sizeof(struct ipv6hdr));
-
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ipv6_queue_xmit: unknown device\n");
- return;
- }
-
- skb->dev = dev;
-
- ipv6_statistics.Ip6OutRequests++;
-
-
- /*
- * Multicast loopback
- */
-
- if (dev->flags & IFF_UP)
- {
- /*
- * If we have an owner use its priority setting,
- * otherwise use NORMAL
- */
-
- if (sk != NULL)
- {
- dev_queue_xmit(skb, dev, sk->priority);
- }
- else
- {
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
- }
- }
- else
- {
- if(sk)
- sk->err = ENETDOWN;
-
- ipv6_statistics.Ip6OutDiscards++;
-
- kfree_skb(skb, FREE_WRITE);
- }
-
-}
-
-
-int ipv6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
- struct in6_addr *dest, unsigned short int length,
- struct in6_addr *saddr, struct device *dev,
- struct ipv6_options *opt, int proto,
- int noblock)
-{
- rt6_output_method_t output_method = default_output_method;
- int hlimit;
- struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
- struct dest_entry *dc = NULL;
- struct in6_addr *daddr = dest;
- struct ipv6hdr *hdr;
- struct neighbour *neigh;
- int addr_type;
- int pktlength;
- int pmtu = 0;
- int rt_flags = 0;
- int error;
-
- if (opt && opt->srcrt)
- {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
- daddr = rt0->addr;
- }
-
- addr_type = ipv6_addr_type(daddr);
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- hlimit = np->mcast_hops;
- if (dev == NULL)
- {
- dev = np->mc_if;
- }
- }
- else
- hlimit = np->hop_limit;
-
- if (addr_type & (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_SITELOCAL |
- IPV6_ADDR_MULTICAST))
- {
- /*
- * force device match on route lookup
- */
-
- rt_flags |= RTI_DEVRT;
- }
-
- if (sk->localroute)
- {
- rt_flags |= RTI_GATEWAY;
- }
-
- if (np->dest)
- {
- np->dest = ipv6_dst_check(np->dest, daddr, np->dc_sernum,
- rt_flags);
-
- dc = np->dest;
-
- if (dc && dc->rt.fib_node)
- {
- np->dc_sernum = dc->rt.fib_node->fn_sernum;
- }
- else
- {
- printk(KERN_WARNING "dc entry not in table\n");
- }
- }
- else
- {
- dc = ipv6_dst_route(daddr, dev, rt_flags);
- }
-
- if (dc == NULL)
- {
- if ((addr_type & IPV6_ADDR_MULTICAST) && dev)
- {
- neigh = NULL;
- pmtu = dev->mtu;
- }
- else
- {
- ipv6_statistics.Ip6OutNoRoutes++;
- return(-ENETUNREACH);
- }
- }
- else
- {
- neigh = dc->dc_nexthop;
- dev = neigh->dev;
-
- if (dc->rt.rt_output_method)
- {
- output_method = dc->rt.rt_output_method;
- }
-
- if (dc->dc_flags & DCF_PMTU)
- pmtu = dc->dc_pmtu;
- else
- pmtu = dev->mtu;
- }
-
-
- if (saddr == NULL)
- {
- struct inet6_ifaddr *ifa;
-
- ifa = ipv6_get_saddr((struct rt6_info *) dc, daddr);
-
- if (ifa == NULL)
- {
- printk(KERN_DEBUG
- "ipv6_build_xmit: get_saddr failed\n");
- return -ENETUNREACH;
- }
-
- saddr = &ifa->addr;
- }
-
- if (dc && np->dest == NULL)
- {
- ipv6_dst_unlock(dc);
- }
-
- pktlength = length;
-
- if (!sk->ip_hdrincl)
- {
- pktlength += sizeof(struct ipv6hdr);
- if (opt)
- {
- pktlength += opt->opt_flen + opt->opt_nflen;
- }
- }
-
-
- dev_lock_list();
-
- /*
- * reminder: don't allow fragmentation for IPPROTO_RAW
- */
-
-
- if (pktlength <= pmtu)
- {
- struct sk_buff *skb =
- sock_alloc_send_skb(sk, pktlength+15+
- dev->hard_header_len,
- 0, noblock, &error);
-
- if (skb == NULL)
- {
- ipv6_statistics.Ip6OutDiscards++;
- dev_unlock_list();
- return error;
-
- }
-
- skb->dev=dev;
- skb->protocol = htons(ETH_P_IPV6);
- skb->free=1;
- skb->when=jiffies;
- skb->sk=sk;
- skb->arp=0;
-
- /* build the mac header... */
- ipv6_build_mac_header(skb, dev, neigh, pktlength);
-
- hdr = (struct ipv6hdr *) skb->tail;
-
- if (!sk->ip_hdrincl)
- {
- skb_put(skb, sizeof(struct ipv6hdr));
- skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority;
-
- memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
-
- hdr->payload_len = htons(pktlength -
- sizeof(struct ipv6hdr));
-
- hdr->hop_limit = hlimit;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- if (opt && opt->srcrt)
- {
- hdr->nexthdr = ipv6opt_bld_rthdr(skb, opt,
- dest, proto);
-
- }
- else
- hdr->nexthdr = proto;
- }
-
- skb_put(skb, length);
- error = getfrag(data, &hdr->saddr,
- ((char *) hdr) + (pktlength - length),
- 0, length);
-
- if (!error)
- {
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(skb, (struct rt6_info *) dc);
- } else
- {
- error = -EFAULT;
- kfree_skb(skb, FREE_WRITE);
- }
-
- dev_unlock_list();
- return error;
- }
- else
- {
- /*
- * Fragmentation
- */
-
- /*
- * Extension header order:
- * Hop-by-hop -> Routing -> Fragment -> rest (...)
- *
- * We must build the non-fragmented part that
- * will be in every packet... this also means
- * that other extension headers (Dest, Auth, etc)
- * must be considered in the data to be fragmented
- */
-
- struct sk_buff *last_skb;
- struct frag_hdr *fhdr;
- int unfrag_len;
- int payl_len;
- int frag_len;
- int last_len;
- int nfrags;
- int err;
- int fhdr_dist;
- __u32 id;
-
- if (sk->ip_hdrincl)
- {
- return -EMSGSIZE;
- }
-
- id = ipv6_fragmentation_id++;
-
- unfrag_len = sizeof(struct ipv6hdr) + sizeof(struct frag_hdr);
- payl_len = length;
-
- if (opt)
- {
- unfrag_len += opt->opt_nflen;
- payl_len += opt->opt_flen;
- }
-
- nfrags = payl_len / ((pmtu - unfrag_len) & ~0x7);
-
- /*
- * Length of fragmented part on every packet but
- * the last must be an:
- * "integer multiple of 8 octects".
- */
-
- frag_len = (pmtu - unfrag_len) & ~0x7;
-
- /*
- * We must send from end to start because of
- * UDP/ICMP checksums. We do a funny trick:
- * fill the last skb first with the fixed
- * header (and its data) and then use it
- * to create the following segments and send it
- * in the end. If the peer is checking the M_flag
- * to trigger the reassembly code then this
- * might be a good idea.
- */
-
- last_len = payl_len - (nfrags * frag_len);
-
- if (last_len == 0)
- {
- last_len = frag_len;
- nfrags--;
- }
-
- last_skb = sock_alloc_send_skb(sk, unfrag_len + frag_len +
- dev->hard_header_len + 15,
- 0, noblock, &err);
-
- if (last_skb == NULL)
- {
- dev_unlock_list();
- return err;
- }
-
- last_skb->dev=dev;
- last_skb->protocol = htons(ETH_P_IPV6);
- last_skb->free=1;
- last_skb->when=jiffies;
- last_skb->sk=sk;
- last_skb->arp=0;
-
- /*
- * build the mac header...
- */
- ipv6_build_mac_header(last_skb, dev, neigh,
- unfrag_len + frag_len);
-
- hdr = (struct ipv6hdr *) skb_put(last_skb,
- sizeof(struct ipv6hdr));
- last_skb->ipv6_hdr = hdr;
-
- hdr->version = 6;
- hdr->priority = np->priority;
-
- memcpy(hdr->flow_lbl, &np->flow_lbl, 3);
- hdr->payload_len = htons(unfrag_len + frag_len -
- sizeof(struct ipv6hdr));
-
- hdr->hop_limit = hlimit;
-
- hdr->nexthdr = NEXTHDR_FRAGMENT;
-
- memcpy(&hdr->saddr, saddr, sizeof(struct in6_addr));
- memcpy(&hdr->daddr, daddr, sizeof(struct in6_addr));
-
- if (opt && opt->srcrt)
- {
- hdr->nexthdr = ipv6opt_bld_rthdr(last_skb, opt, dest,
- NEXTHDR_FRAGMENT);
- }
-
- fhdr = (struct frag_hdr *)
- skb_put(last_skb, sizeof(struct frag_hdr));
-
- memset(fhdr, 0, sizeof(struct frag_hdr));
-
- fhdr->nexthdr = proto;
- fhdr->frag_off = ntohs(nfrags * frag_len);
- fhdr->identification = id;
-
- fhdr_dist = (unsigned char *) fhdr - last_skb->data;
-
- error = getfrag(data, &hdr->saddr, last_skb->tail,
- nfrags * frag_len, last_len);
-
- if (!error)
- {
- while (nfrags--)
- {
- struct sk_buff *skb;
-
- struct frag_hdr *fhdr2;
-
- printk(KERN_DEBUG "sending frag %d\n", nfrags);
- skb = skb_copy(last_skb, sk->allocation);
-
- fhdr2 = (struct frag_hdr *)
- (skb->data + fhdr_dist);
-
- /* more flag on */
- fhdr2->frag_off = ntohs(nfrags * frag_len + 1);
-
- /*
- * FIXME:
- * if (nfrags == 0)
- * put rest of headers
- */
-
- error = getfrag(data, &hdr->saddr,
- skb_put(skb, frag_len),
- nfrags * frag_len, frag_len);
-
- if (error)
- {
- kfree_skb(skb, FREE_WRITE);
- break;
- }
-
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(skb, (struct rt6_info *) dc);
- }
- }
-
- if (error)
- {
- kfree_skb(last_skb, FREE_WRITE);
- dev_unlock_list();
- return -EFAULT;
- }
-
- printk(KERN_DEBUG "sending last frag \n");
-
- hdr->payload_len = htons(unfrag_len + last_len -
- sizeof(struct ipv6hdr));
-
- /*
- * update last_skb to reflect the getfrag we did
- * on start.
- */
- last_skb->tail += last_len;
- last_skb->len += last_len;
-
- /*
- * toss the mac header out and rebuild it.
- * needed because of the different frame length.
- * ie: not needed for an ethernet.
- */
-
- if (dev->type != ARPHRD_ETHER && last_len != frag_len)
- {
- ipv6_redo_mac_hdr(last_skb, neigh,
- unfrag_len + last_len);
- }
-
- ipv6_statistics.Ip6OutRequests++;
- (*output_method)(last_skb, (struct rt6_info *) dc);
-
- dev_unlock_list();
- return 0;
- }
- return -1;
-}
-
-static int pri_values[4] =
-{
- SOPRI_BACKGROUND,
- SOPRI_NORMAL,
- SOPRI_NORMAL,
- SOPRI_INTERACTIVE
-};
-
-void ipv6_forward(struct sk_buff *skb, struct device *dev, int flags)
-{
- struct neighbour *neigh;
- struct dest_entry *dest;
- int priority;
- int rt_flags;
- int size;
- int pmtu;
-
- if (skb->ipv6_hdr->hop_limit <= 1)
- {
- icmpv6_send(skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
- 0, dev);
-
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- skb->ipv6_hdr->hop_limit--;
-
- if (ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL)
- {
- printk(KERN_DEBUG "ipv6_forward: link local source addr\n");
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR,
- 0, dev);
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- rt_flags = RTF_MODIFIED;
-
- if ((flags & IP6_FW_STRICT))
- {
- rt_flags |= RTF_GATEWAY;
- }
-
- dest = ipv6_dst_route(&skb->ipv6_hdr->daddr, NULL, rt_flags);
-
- if (dest == NULL)
- {
- int code;
-
- if (flags & IP6_FW_STRICT)
- code = ICMPV6_NOT_NEIGHBOUR;
- else
- code = ICMPV6_NOROUTE;
-
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, dev);
-
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- neigh = dest->dc_nexthop;
-
- if (neigh->dev == dev && (dev->flags & IFF_MULTICAST) &&
- !(flags & IP6_FW_SRCRT))
- {
- struct in6_addr *target = NULL;
-
- /*
- * outgoing device equal to incoming device
- * send a redirect
- */
-
- if ((dest->dc_flags & RTF_GATEWAY))
- {
- target = &neigh->addr;
- }
- else
- {
- target = &skb->ipv6_hdr->daddr;
- }
-
- ndisc_send_redirect(skb, neigh, target);
- }
-
- pmtu = neigh->dev->mtu;
-
- size = sizeof(struct ipv6hdr) + ntohs(skb->ipv6_hdr->payload_len);
-
- if (size > pmtu)
- {
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, pmtu, dev);
- kfree_skb(skb, FREE_READ);
- return;
- }
-
- ipv6_dst_unlock(dest);
-
- if (skb_headroom(skb) < neigh->dev->hard_header_len)
- {
- struct sk_buff *buff;
-
- buff = alloc_skb(neigh->dev->hard_header_len + skb->len + 15,
- GFP_ATOMIC);
-
- if (buff == NULL)
- {
- return;
- }
-
- skb_reserve(buff, (neigh->dev->hard_header_len + 15) & ~15);
-
- buff->protocol = __constant_htons(ETH_P_IPV6);
- buff->free = 1;
- buff->h.raw = skb_put(buff, size);
-
- memcpy(buff->h.raw, skb->ipv6_hdr, size);
- buff->ipv6_hdr = (struct ipv6hdr *) buff->h.raw;
- kfree_skb(skb, FREE_READ);
- skb = buff;
- }
-
- ipv6_redo_mac_hdr(skb, neigh, size);
-
- priority = skb->ipv6_hdr->priority;
-
- priority = (priority & 0x7) >> 1;
- priority = pri_values[priority];
-
- if (dev->flags & IFF_UP)
- {
- dev_queue_xmit(skb, neigh->dev, priority);
- }
- else
- {
- ipv6_statistics.Ip6OutDiscards++;
- kfree_skb(skb, FREE_READ);
- }
-}
-
-
-/*
- * Local variables:
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/ipv6_route.c b/net/ipv6/ipv6_route.c
deleted file mode 100644
index e68990a0f..000000000
--- a/net/ipv6/ipv6_route.c
+++ /dev/null
@@ -1,2056 +0,0 @@
-/*
- * IPv6 routing table
- * Linux INET6 implementation
- *
- * Authors:
- * Pedro Roque <roque@di.fc.ul.pt>
- *
- * This program 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 of the License, or (at your option) any later version.
- */
-
-/*
- * Changes:
- *
- * Masaki Hirabaru : Fix for /proc info > pagesize
- * <masaki@merit.edu>
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/sched.h>
-#include <linux/net.h>
-#include <linux/route.h>
-#include <linux/netdevice.h>
-#include <linux/in6.h>
-
-#ifdef CONFIG_PROC_FS
-#include <linux/proc_fs.h>
-#endif
-
-#include <net/tcp.h>
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
-#include <net/ndisc.h>
-#include <net/protocol.h>
-#include <net/ipv6_route.h>
-#include <net/addrconf.h>
-
-#include <net/netlink.h>
-
-#include <asm/uaccess.h>
-
-/*
- * Routing Table
- *
- * simplified version of a radix tree
- *
- * - every node shares it's acestors prefix
- * - the tree is ordered from less to most specific mask
- * - default routes are handled apart
- *
- * this facilitates recursion a lot
- */
-
-static struct rt6_info null_entry = {
- NULL, NULL,
- {{{0}}},
- 0, 1,
- NULL, NULL,
- 0, 0, RTF_REJECT
-};
-
-struct fib6_node routing_table = {
- NULL, NULL, NULL, &null_entry,
- 0, RTN_ROOT, 0
-};
-
-struct rt6_info *default_rt_list = NULL;
-struct rt6_info *loopback_rt = NULL;
-
-/*
- * last_resort_rt - no routers present.
- * Assume all destinations on link.
- */
-struct rt6_info *last_resort_rt = NULL;
-
-static struct rt6_req request_queue = {
- 0, NULL, &request_queue, &request_queue
-};
-
-
-/*
- * A routing update causes an increase of the serial number on the
- * afected subtree. This allows for cached routes to be asynchronously
- * tested when modifications are made to the destination cache as a
- * result of redirects, path MTU changes, etc.
- */
-
-static __u32 rt_sernum = 0;
-
-static atomic_t rt6_lock = 0;
-static int rt6_bh_mask = 0;
-
-#define RT_BH_REQUEST 1
-#define RT_BH_GC 2
-
-static void __rt6_run_bh(void);
-
-typedef void (*f_pnode)(struct fib6_node *fn, void *);
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter);
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg);
-static int rt6_msgrcv(int unit, struct sk_buff *skb);
-
-struct rt6_statistics rt6_stats = {
- 1, 0, 1, 1, 0
-};
-
-static atomic_t rt_clients = 0;
-
-void rt6_timer_handler(unsigned long data);
-
-struct timer_list rt6_gc_timer = {
- NULL,
- NULL,
- 0,
- 0,
- rt6_timer_handler
-};
-
-static __inline__ void rt6_run_bh(void)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (rt6_lock == 0 && rt6_bh_mask)
- {
- __rt6_run_bh();
- }
- restore_flags(flags);
-}
-
-/*
- * request queue operations
- * FIFO queue/dequeue
- */
-static __inline__ void rtreq_queue(struct rt6_req * req)
-{
- unsigned long flags;
- struct rt6_req *next = &request_queue;
-
- save_flags(flags);
- cli();
-
- req->prev = next->prev;
- req->prev->next = req;
- next->prev = req;
- req->next = next;
- restore_flags(flags);
-}
-
-static __inline__ struct rt6_req * rtreq_dequeue(void)
-{
- struct rt6_req *next = &request_queue;
- struct rt6_req *head;
-
- head = next->next;
-
- if (head == next)
- {
- return NULL;
- }
-
- head->next->prev = head->prev;
- next->next = head->next;
-
- head->next = NULL;
- head->prev = NULL;
-
- return head;
-}
-
-/*
- * compare "prefix length" bits of an address
- */
-static __inline__ int addr_match(struct in6_addr *a1, struct in6_addr *a2,
- int prefixlen)
-{
- int pdw;
- int pbi;
-
- pdw = prefixlen >> 0x05; /* num of whole __u32 in prefix */
- pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
-
- if (pdw)
- {
- if (memcmp(a1, a2, pdw << 2))
- return 0;
- }
-
- if (pbi)
- {
- __u32 w1, w2;
- __u32 mask;
-
- w1 = a1->s6_addr32[pdw];
- w2 = a2->s6_addr32[pdw];
-
- mask = htonl((0xffffffff) << (0x20 - pbi));
-
- if ((w1 ^ w2) & mask)
- return 0;
- }
-
- return 1;
-}
-
-/*
- * test bit. range [0-127]
- */
-
-static __inline__ int addr_bit_set(struct in6_addr *addr, int fn_bit)
-{
- int dw;
- __u32 b1;
- __u32 mask;
- int bit = fn_bit;
-
- dw = bit >> 0x05;
-
- b1 = addr->s6_addr32[dw];
-
- bit = ~bit;
- bit &= 0x1f;
- mask = htonl(1 << bit);
- return (b1 & mask);
-}
-
-static __inline__ int addr_bit_equal(struct in6_addr *a1, struct in6_addr *a2,
- int fn_bit)
-{
- int dw;
- __u32 b1, b2;
- __u32 mask;
- int bit = fn_bit;
-
- dw = bit >> 0x05;
-
- b1 = a1->s6_addr32[dw];
- b2 = a2->s6_addr32[dw];
-
- bit = ~bit;
- bit &= 0x1f;
- mask = htonl(1 << bit);
- return !((b1 ^ b2) & mask);
-}
-
-/*
- * find the first different bit between two addresses
- */
-static __inline__ int addr_diff(struct in6_addr *a1, struct in6_addr *a2)
-{
- int i;
-
- for (i = 0; i<4; i++)
- {
- __u32 b1, b2;
- __u32 xb;
-
- b1 = a1->s6_addr32[i];
- b2 = a2->s6_addr32[i];
-
- xb = b1 ^ b2;
-
- if (xb)
- {
- int res = 0;
- int j=31;
-
- xb = ntohl(xb);
-
- while (test_bit(j, &xb) == 0)
- {
- res++;
- j--;
- }
-
- return (i * 32 + res);
- }
- }
-
- /*
- * bit values are in range [0-127]
- * 128 is an ilegal value as we should *never* get to
- * this point since that would mean the addrs are equal
- */
- return 128;
-}
-
-/*
- * add a rt to a node that may already contain routes
- * sort routes in ascending metric order so that fib lookup
- * returns the smallest metric by default
- */
-
-static __inline__ void fib6_add_rt2node(struct fib6_node *fn,
- struct rt6_info *rt)
-{
- struct rt6_info *iter, **back;
-
- rt->fib_node = fn;
- back = &fn->leaf;
-
- for (iter = fn->leaf; iter; iter=iter->next)
- {
- if (iter->rt_metric > rt->rt_metric)
- {
- break;
- }
-
- back = &iter->next;
- }
-
- rt->next = iter;
- *back = rt;
-}
-
-/*
- * Routing Table
- */
-
-static int fib6_add_1(struct rt6_info *rt)
-{
- struct fib6_node *fn;
- struct fib6_node *pn = NULL;
- struct fib6_node *in;
- struct fib6_node *ln;
- struct in6_addr *addr;
- __u32 bit;
- __u32 dir = 0;
- __u32 sernum = ++rt_sernum;
- int pbit = rt->rt_prefixlen - 1;
-
- addr = &rt->rt_dst;
-
- /* insert node in tree */
-
- fn = &routing_table;
-
- for (;;)
- {
- if (fn == NULL)
- {
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- return (-ENOMEM);
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
- ln->parent = pn;
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- rt->fib_node = ln;
-
- atomic_inc(&rt->rt_ref);
-
- if (dir)
- pn->right = ln;
- else
- pn->left = ln;
-
- rt6_stats.fib_nodes++;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return(0);
- }
-
- if (addr_match(&fn->leaf->rt_dst, addr, fn->fn_bit))
- {
- if (pbit == fn->fn_bit && pbit &&
- addr_bit_equal(addr, &fn->leaf->rt_dst,
- rt->rt_prefixlen))
- {
- /* clean up an intermediate node */
- if ((fn->fn_flags & RTN_BACKTRACK) == 0)
- {
- rt_release(fn->leaf);
- fn->leaf = NULL;
- fn->fn_flags |= RTN_BACKTRACK;
- }
-
- fib6_add_rt2node(fn, rt);
- fn->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return 0;
- }
-
- if (pbit > fn->fn_bit || pbit == 0)
- {
- /* walk down on tree */
-
- fn->fn_sernum = sernum;
-
- dir = addr_bit_set(addr, fn->fn_bit);
- pn = fn;
- fn = dir ? fn->right: fn->left;
-
- continue;
- }
- }
-
- /*
- * split since we don't have a common prefix anymore or
- * we have a less significant route.
- * we've to insert an intermediate node on the list
- * this new node will point to the one we need to create
- * and the current
- */
-
- pn = fn->parent;
-
- /* find 1st bit in difference between the 2 addrs */
- bit = addr_diff(addr, &fn->leaf->rt_dst);
-
-
- /*
- * (intermediate)
- * / \
- * (new leaf node) (old node)
- */
- if (rt->rt_prefixlen > bit)
- {
- in = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (in == NULL)
- return (-ENOMEM);
-
- memset(in, 0, sizeof(struct fib6_node));
-
- /*
- * new intermediate node.
- * RTN_BACKTRACK will
- * be off since that an address that chooses one of
- * the branches would not match less specific routes
- * int the other branch
- */
-
- in->fn_bit = bit;
- in->parent = pn;
- in->leaf = rt;
- in->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- /* leaf node */
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- {
- kfree(in);
- return (-ENOMEM);
- }
-
- /* update parent pointer */
- if (dir)
- pn->right = in;
- else
- pn->left = in;
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
- ln->parent = in;
- fn->parent = in;
-
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt->fib_node = ln;
-
- if (addr_bit_set(addr, bit))
- {
- in->right = ln;
- in->left = fn;
- }
- else
- {
- in->left = ln;
- in->right = fn;
- }
-
- rt6_stats.fib_nodes += 2;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return 0;
- }
-
- /*
- * (new leaf node)
- * / \
- * (old node) NULL
- */
-
- ln = kmalloc(sizeof(struct fib6_node), GFP_ATOMIC);
-
- if (ln == NULL)
- return (-ENOMEM);
-
- memset(ln, 0, sizeof(struct fib6_node));
- ln->fn_bit = pbit;
- ln->fn_flags = RTN_BACKTRACK;
-
-
- ln->parent = pn;
- ln->leaf = rt;
- ln->fn_sernum = sernum;
- atomic_inc(&rt->rt_ref);
-
- rt->fib_node = ln;
-
- if (dir)
- pn->right = ln;
- else
- pn->left = ln;
-
-
- if (addr_bit_set(&fn->leaf->rt_dst, pbit))
- ln->right = fn;
- else
- ln->left = fn;
-
- fn->parent = ln;
-
- rt6_stats.fib_nodes++;
- rt6_stats.fib_route_nodes++;
- rt6_stats.fib_rt_entries++;
-
- return(0);
- }
-
- return (-1);
-}
-
-static struct rt6_info * fib6_lookup_1(struct in6_addr *addr, int flags)
-{
- struct fib6_node *fn, *next;
- int dir;
-
- fn = &routing_table;
-
- for (;;)
- {
- dir = addr_bit_set(addr, fn->fn_bit);
-
- next = dir ? fn->right: fn->left;
-
- if (next)
- {
- fn = next;
- continue;
- }
-
- break;
- }
-
-
- while ((fn->fn_flags & RTN_ROOT) == 0)
- {
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- if (addr_match(&fn->leaf->rt_dst, addr,
- fn->leaf->rt_prefixlen))
- {
- struct rt6_info *rt;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- if ((rt->rt_flags & flags) == 0)
- return rt;
- }
- }
- }
-
- fn = fn->parent;
- }
-
- return NULL;
-}
-
-
-
-/*
- * called to trim the tree of intermediate nodes when possible
- */
-
-static void fib6_del_3(struct fib6_node *fn)
-{
- int children = 0;
- int dir = 0;
- int bit;
-
- /*
- * 0 or one children:
- * delete the node
- *
- * 2 children:
- * move the bit down
- */
-
- if (fn->left)
- {
- children++;
- dir = 0;
- }
-
- if (fn->right)
- {
- children++;
- dir = 1;
- }
-
- if (children < 2)
- {
- struct fib6_node *child;
- struct fib6_node *pn;
-
- child = dir ? fn->right : fn->left;
-
- if (fn->parent->left == fn)
- {
- fn->parent->left = child;
- }
- else
- {
- fn->parent->right = child;
- }
-
- if (child)
- {
- child->parent = fn->parent;
- }
-
- /*
- * try to collapse on top
- */
- pn = fn->parent;
- fn->parent = NULL;
-
- if ((pn->fn_flags & (RTN_BACKTRACK | RTN_ROOT)) == 0)
- {
- if (pn->leaf)
- {
- rt_release(pn->leaf);
- pn->leaf = NULL;
- }
- fib6_del_3(pn);
- }
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- rt6_stats.fib_route_nodes--;
- }
- rt6_stats.fib_nodes--;
- kfree(fn);
- return;
- }
-
- bit = addr_diff(&fn->left->leaf->rt_dst, &fn->right->leaf->rt_dst);
-
- fn->fn_bit = bit;
- fn->fn_flags &= ~RTN_BACKTRACK;
-
- fn->leaf = fn->left->leaf;
- atomic_inc(&fn->leaf->rt_ref);
-
- rt6_stats.fib_route_nodes--;
-}
-
-static struct fib6_node * fib6_del_2(struct in6_addr *addr, __u32 prefixlen,
- struct in6_addr *gw, struct device *dev)
-{
- struct fib6_node *fn;
-
- for (fn = &routing_table; fn;)
- {
- int dir;
-
- if ((fn->fn_flags & RTN_BACKTRACK) &&
- prefixlen == fn->leaf->rt_prefixlen &&
- addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
- )
- {
- break;
- }
-
- dir = addr_bit_set(addr, fn->fn_bit);
-
- fn = dir ? fn->right: fn->left;
- }
-
- /*
- * if route tree node found
- * search among it's entries
- */
-
- if (fn)
- {
- struct rt6_info *back = NULL;
- struct rt6_info *lf;
-
- for(lf = fn->leaf; lf; lf=lf->next)
- {
- if ((gw && (ipv6_addr_cmp(addr, &lf->rt_dst) == 0)) ||
- (dev && dev == lf->rt_dev))
- {
- /* delete this entry */
- if (back == NULL)
- fn->leaf = lf->next;
- else
- back->next = lf->next;
-
- lf->fib_node = NULL;
- rt_release(lf);
- return fn;
- }
- back = lf;
- }
- }
-
- return NULL;
-}
-
-static struct fib6_node * fib6_del_rt_2(struct rt6_info *rt)
-{
- struct fib6_node *fn;
- struct in6_addr *addr = &rt->rt_dst;
- int prefixlen = rt->rt_prefixlen;
-
- for (fn = &routing_table; fn;)
- {
- int dir;
-
- if ((fn->fn_flags & RTN_BACKTRACK) &&
- prefixlen == fn->leaf->rt_prefixlen &&
- addr_match(&fn->leaf->rt_dst, addr, fn->leaf->rt_prefixlen)
- )
- {
- break;
- }
-
- dir = addr_bit_set(addr, fn->fn_bit);
-
- fn = dir ? fn->right: fn->left;
- }
-
- /*
- * if route tree node found
- * search among its entries
- */
-
- if (fn)
- {
- struct rt6_info **back;
- struct rt6_info *lf;
-
- back = &fn->leaf;
-
- for(lf = fn->leaf; lf; lf=lf->next)
- {
- if (rt == lf)
- {
- /*
- * delete this entry
- */
-
- *back = lf->next;
- rt_release(lf);
- return fn;
- }
- back = &lf->next;
- }
- }
-
- return NULL;
-}
-
-int fib6_del_1(struct in6_addr *addr, __u32 prefixlen, struct in6_addr *gw,
- struct device *dev)
-{
- struct fib6_node *fn;
-
- fn = fib6_del_2(addr, prefixlen, gw, dev);
-
- if (fn == NULL)
- return -ENOENT;
-
- if (fn->leaf == NULL)
- {
- fib6_del_3(fn);
- }
-
- return 0;
-}
-
-int fib6_del_rt(struct rt6_info *rt)
-{
- struct fib6_node *fn;
-
- fn = fib6_del_rt_2(rt);
-
- if (fn == NULL)
- return -ENOENT;
-
- if (fn->leaf == NULL)
- {
- fib6_del_3(fn);
- }
-
- return 0;
-}
-
-static void fib6_flush_1(struct fib6_node *fn, void *p_arg)
-{
- struct rt6_info *rt;
-
- for (rt = fn->leaf; rt;)
- {
- struct rt6_info *itr;
-
- itr = rt;
- rt = rt->next;
- itr->fib_node = NULL;
- rt_release(itr);
- }
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- rt6_stats.fib_route_nodes--;
- }
- rt6_stats.fib_nodes--;
- kfree(fn);
-}
-
-void fib6_flush(void)
-{
- rt6_walk_tree(fib6_flush_1, NULL, RT6_FILTER_NONE);
-}
-
-int ipv6_route_add(struct in6_rtmsg *rtmsg)
-{
- struct rt6_info *rt;
- struct device * dev = NULL;
- struct inet6_dev *idev;
- struct rt6_req *request;
- int flags = rtmsg->rtmsg_flags;
-
- idev = ipv6_dev_by_index(rtmsg->rtmsg_ifindex);
- if (idev)
- {
- dev = idev->dev;
- }
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
-
- rt6_stats.fib_rt_alloc++;
-
- memset(rt, 0, sizeof(struct rt6_info));
-
- memcpy(&rt->rt_dst, &rtmsg->rtmsg_dst, sizeof(struct in6_addr));
- rt->rt_prefixlen = rtmsg->rtmsg_prefixlen;
-
- if (rt->rt_prefixlen == 0)
- {
- printk(KERN_DEBUG "ip6_fib: zero length route not allowed\n");
- return -EINVAL;
- }
-
- if (flags & (RTF_GATEWAY | RTF_NEXTHOP))
- {
- /* check to see if its an acceptable gateway */
- if (flags & RTF_GATEWAY)
- {
- struct rt6_info *gw_rt;
-
- gw_rt = fibv6_lookup(&rtmsg->rtmsg_gateway, dev,
- RTI_GATEWAY);
-
- if (gw_rt == NULL)
- {
- return -EHOSTUNREACH;
- }
-
- dev = gw_rt->rt_dev;
- }
-
- rt->rt_nexthop = ndisc_get_neigh(dev, &rtmsg->rtmsg_gateway);
-
- if (rt->rt_nexthop == NULL)
- {
- printk(KERN_DEBUG "ipv6_route_add: no nexthop\n");
- kfree(rt);
- return -EINVAL;
- }
-
- rt->rt_dev = dev;
-
- if (loopback_rt == NULL && (dev->flags & IFF_LOOPBACK))
- {
- loopback_rt = rt;
- }
-
- }
- else
- {
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ipv6_route_add: NULL dev\n");
- kfree(rt);
- return -EINVAL;
- }
-
- rt->rt_dev = dev;
- rt->rt_nexthop = NULL;
- }
-
- rt->rt_metric = rtmsg->rtmsg_metric;
- rt->rt_flags = rtmsg->rtmsg_flags;
-
- if (rt->rt_flags & RTF_ADDRCONF)
- {
- rt->rt_expires = rtmsg->rtmsg_info;
- }
-
- request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
- if (request == NULL)
- {
- printk(KERN_WARNING "ipv6_route_add: kmalloc failed\n");
- return -ENOMEM;
- }
-
- request->operation = RT_OPER_ADD;
- request->ptr = rt;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- rt6_bh_mask |= RT_BH_REQUEST;
-
- rt6_run_bh();
-
- return 0;
-}
-
-int ipv6_route_del(struct in6_rtmsg *rtmsg)
-{
- struct rt6_info * rt;
- int res = -ENOENT;
-
- atomic_inc(&rt6_lock);
-
- rt = fib6_lookup_1(&rtmsg->rtmsg_dst, 0);
-
- if (rt && (rt->rt_prefixlen == rtmsg->rtmsg_prefixlen))
- {
- int test;
-
- start_bh_atomic();
-
- test = (rt6_lock == 1);
-
- if (test)
- {
- res = fib6_del_rt(rt);
- }
- end_bh_atomic();
-
- if (!test)
- {
- struct rt6_req *request;
-
- request = kmalloc(sizeof(struct rt6_req), GFP_KERNEL);
-
- if (!request)
- {
- res = -ENOMEM;
- goto out;
- }
- request->operation = RT_OPER_DEL;
- request->ptr = rt;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- rt6_bh_mask |= RT_BH_REQUEST;
- res = 0;
- }
- }
- out:
- atomic_dec(&rt6_lock);
- rt6_run_bh();
- return res;
-}
-
-/*
- * search the routing table
- * the flags parameter restricts the search to entries where
- * the flag is *not* set
- */
-struct rt6_info * fibv6_lookup(struct in6_addr *addr, struct device *src_dev,
- int flags)
-{
- struct rt6_info *rt;
-
- atomic_inc(&rt6_lock);
-
- if ((rt = fib6_lookup_1(addr, flags)))
- {
- if (src_dev)
- {
- struct rt6_info *sprt;
-
- for (sprt=rt; sprt; sprt=sprt->next)
- {
- if (sprt->rt_dev == src_dev)
- {
- rt = sprt;
- goto out;
- }
- }
-
- if (flags & RTI_DEVRT)
- {
- rt = NULL;
- }
- }
-
- goto out;
- }
-
- if (!(flags & RTI_GATEWAY))
- {
- if ((rt = dflt_rt_lookup()))
- {
- goto out;
- }
-
- rt = last_resort_rt;
- }
- out:
- atomic_dec(&rt6_lock);
- return rt;
-}
-
-/*
- * Destination Cache
- */
-
-struct dest_entry * ipv6_dst_route(struct in6_addr * daddr,
- struct device *src_dev,
- int flags)
-{
- struct dest_entry * dc = NULL;
- struct rt6_info * rt;
-
- atomic_inc(&rt6_lock);
-
- rt = fibv6_lookup(daddr, src_dev, flags);
-
- if (rt == NULL)
- {
- goto exit;
- }
-
- if (rt->rt_nexthop)
- {
- /*
- * We can use the generic route
- * (warning: the pmtu value maybe invalid)
- */
-
- dc = (struct dest_entry *) rt;
- atomic_inc(&rt->rt_use);
- }
- else
- {
- struct rt6_req *request;
-
- if (ipv6_chk_addr(daddr) && !(rt->rt_dev->flags & IFF_LOOPBACK))
- {
- rt = loopback_rt;
-
- if (rt == NULL)
- {
- goto exit;
- }
- }
-
- /*
- * dynamicly allocate a new route
- */
-
- dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
- GFP_ATOMIC);
-
- if (dc == NULL)
- {
- printk(KERN_WARNING "dst_route: kmalloc failed\n");
- goto exit;
- }
-
- rt6_stats.fib_rt_alloc++;
- rt6_stats.fib_dc_alloc++;
-
- memset(dc, 0, sizeof(struct dest_entry));
-
- memcpy(&dc->dc_addr, daddr, sizeof(struct in6_addr));
- dc->rt.rt_prefixlen = 128;
- dc->dc_usecnt = 1;
- dc->rt.rt_metric = rt->rt_metric;
-
- dc->dc_flags = (rt->rt_flags | RTF_HOST | RTI_DYNAMIC |
- RTI_DCACHE | DCF_PMTU);
-
- dc->dc_pmtu = rt->rt_dev->mtu;
- dc->rt.rt_dev = rt->rt_dev;
- dc->rt.rt_output_method = rt->rt_output_method;
- dc->dc_tstamp = jiffies;
- /* add it to the request queue */
-
- request = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
- if (request == NULL)
- {
- printk(KERN_WARNING "dst_route: kmalloc failed\n");
- dc = NULL;
- goto exit;
- }
-
- dc->dc_nexthop = ndisc_get_neigh(rt->rt_dev, daddr);
-
- rt6_bh_mask |= RT_BH_REQUEST;
-
- request->operation = RT_OPER_ADD;
- request->ptr = (struct rt6_info *) dc;
- request->next = request->prev = NULL;
- rtreq_queue(request);
- }
-
- atomic_inc(&rt_clients);
-
- exit:
-
- atomic_dec(&rt6_lock);
- rt6_run_bh();
-
- return dc;
-}
-
-/*
- * check cache entry for vality...
- * this needs to be done as a inline func that calls
- * ipv6_slow_dst_check if entry is invalid
- */
-
-struct dest_entry * ipv6_dst_check(struct dest_entry *dc,
- struct in6_addr *daddr,
- __u32 sernum, int flags)
-{
- int uptodate = 0;
-
- /*
- * destination cache becomes invalid when routing
- * changes or a more specific dynamic entry is
- * created.
- * if route is removed from table fib_node will
- * become NULL
- */
-
- if (dc->rt.fib_node && (dc->rt.fib_node->fn_sernum == sernum))
- uptodate = 1;
-
- if (uptodate && ((dc->dc_flags & DCF_INVALID) == 0))
- {
- if (dc->dc_nexthop && !(dc->dc_nexthop->flags & NCF_NOARP))
- {
- ndisc_event_send(dc->dc_nexthop, NULL);
- }
- return dc;
- }
-
- /* route for destination may have changed */
-
- ipv6_dst_unlock(dc);
-
- return ipv6_dst_route(daddr, NULL, flags);
-}
-
-void ipv6_dst_unlock(struct dest_entry *dc)
-{
- /*
- * decrement counter and mark entry for deletion
- * if counter reaches 0. we delay deletions in hope
- * we can reuse cache entries.
- */
-
- atomic_dec(&dc->dc_usecnt);
-
- if (dc->dc_usecnt == 0)
- {
-
- if (dc->dc_flags & RTI_DCACHE)
- {
- /*
- * update last usage tstamp
- */
-
- dc->dc_tstamp = jiffies;
- rt6_bh_mask |= RT_BH_GC;
- }
-
- if (dc->rt.rt_ref == 0)
- {
- /*
- * entry out of the routing table
- * pending to be released on last deref
- */
-
- if (dc->dc_nexthop)
- {
- ndisc_dec_neigh(dc->dc_nexthop);
- }
-
- if (dc->dc_flags & RTI_DCACHE)
- {
- rt6_stats.fib_dc_alloc--;
- }
-
- rt6_stats.fib_rt_alloc--;
- kfree(dc);
- }
-
- }
-
- atomic_dec(&rt_clients);
-}
-
-/*
- * Received a packet too big icmp that lowers the mtu for this
- * address. If the route for the destination is genric we create
- * a new route with the apropriate MTU info. The route_add
- * procedure will update the serial number on the generic routes
- * belonging to the afected tree forcing clients to request a route
- * lookup.
- */
-void rt6_handle_pmtu(struct in6_addr *addr, int pmtu)
-{
- struct rt6_info *rt;
- struct rt6_req *req;
- struct dest_entry *dc;
-
- printk(KERN_DEBUG "rt6_handle_pmtu\n");
-
- if (pmtu < 0 || pmtu > 65536)
- {
- printk(KERN_DEBUG "invalid MTU value\n");
- return;
- }
-
- rt = fibv6_lookup(addr, NULL, 0);
-
- if (rt == NULL)
- {
- printk(KERN_DEBUG "rt6_handle_pmtu: route not found\n");
- return;
- }
-
- if (rt->rt_flags & RTI_DCACHE)
- {
- /*
- * we do have a destination cache entry for this
- * address.
- */
-
- dc = (struct dest_entry *) rt;
-
- /*
- * fixme: some sanity checks are likely to be needed
- * here
- */
-
- dc->dc_pmtu = pmtu;
- dc->dc_flags |= DCF_PMTU;
- return;
- }
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
-
- /* now add the new destination cache entry */
-
- dc = (struct dest_entry *) kmalloc(sizeof(struct dest_entry),
- GFP_ATOMIC);
-
- rt6_stats.fib_rt_alloc++;
- rt6_stats.fib_dc_alloc++;
-
- memset(dc, 0, sizeof(struct dest_entry));
-
- memcpy(&dc->dc_addr, addr, sizeof(struct in6_addr));
- dc->rt.rt_prefixlen = 128;
- dc->rt.rt_metric = rt->rt_metric;
-
- dc->dc_flags = (rt->rt_flags | RTI_DYNAMIC | RTI_DCACHE | DCF_PMTU |
- RTF_HOST);
-
- dc->dc_pmtu = pmtu;
- dc->dc_tstamp = jiffies;
-
- dc->dc_nexthop = rt->rt_nexthop;
- atomic_inc(&dc->dc_nexthop->refcnt);
-
- dc->rt.rt_dev = rt->rt_dev;
- dc->rt.rt_output_method = rt->rt_output_method;
-
- req->operation = RT_OPER_ADD;
- req->ptr = (struct rt6_info *) dc;
- req->next = req->prev = NULL;
-
- rtreq_queue(req);
-
- rt6_bh_mask |= RT_BH_REQUEST;
-
- rt6_run_bh();
-}
-
-/*
- * Redirect received: target is nexthop for dest
- */
-struct rt6_info * ipv6_rt_redirect(struct device *dev, struct in6_addr *dest,
- struct in6_addr *target, int on_link)
-
-{
- struct rt6_info *rt;
- struct rt6_req *req;
- int metric;
-
- rt = fibv6_lookup(dest, dev, 0);
-
- if (rt == NULL)
- {
- printk(KERN_WARNING "rt_redirect: unable to locate route\n");
- return NULL;
- }
-
- metric = rt->rt_metric;
-
- if ((rt->rt_flags & RTF_HOST) == 0)
- {
- /* Need to create an host route for this address */
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
- memset(rt, 0, sizeof(struct rt6_info));
- ipv6_addr_copy(&rt->rt_dst, dest);
- rt->rt_prefixlen = 128;
- rt->rt_flags = RTF_HOST | RTF_UP;
- rt->rt_dev = dev;
-
- /*
- * clone rt->rt_output_method ?
- */
-
- rt->rt_metric = metric;
-
- rt6_stats.fib_rt_alloc++;
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
- GFP_ATOMIC);
- req->operation = RT_OPER_ADD;
- req->ptr = rt;
- req->next = req->prev = NULL;
-
- rtreq_queue(req);
- rt6_bh_mask |= RT_BH_REQUEST;
- }
- else
- {
- rt->rt_flags |= RTF_MODIFIED;
- }
-
- rt->rt_flags |= RTF_DYNAMIC;
-
- if (on_link)
- {
- rt->rt_flags &= ~RTF_GATEWAY;
- }
- else
- {
- rt->rt_flags |= RTF_GATEWAY;
- }
-
- if (rt->rt_nexthop)
- {
- if (ipv6_addr_cmp(&rt->rt_nexthop->addr, target) == 0)
- {
- atomic_inc(&rt->rt_nexthop->refcnt);
- goto exit;
- }
- else
- {
- ndisc_dec_neigh(rt->rt_nexthop);
- }
- }
-
- rt->rt_nexthop = ndisc_get_neigh(dev, target);
-
- exit:
- rt6_run_bh();
- return rt;
-}
-
-static int dcache_gc_node(struct fib6_node *fn, int timeout)
-{
- struct rt6_info *rt, *back;
- int more = 0;
- unsigned long now = jiffies;
-
- back = NULL;
-
- for (rt = fn->leaf; rt;)
- {
- if ((rt->rt_flags & RTI_DCACHE) && rt->rt_use == 0)
- {
- struct dest_entry *dc;
-
- dc = (struct dest_entry *) rt;
-
- if (now - dc->dc_tstamp > timeout)
- {
- struct rt6_info *old;
-
- old = rt;
-
- rt = rt->next;
-
- if (back == NULL)
- {
- fn->leaf = rt;
- }
- else
- {
- back->next = rt;
- }
-
- old->fib_node = NULL;
- rt_release(old);
- rt6_stats.fib_rt_entries--;
- continue;
- }
- else
- {
- more++;
- }
- }
-
- back = rt;
- rt = rt->next;
- }
-
- if (fn->leaf == NULL)
- {
- return -1;
- }
- return more;
-}
-
-struct dc_gc_args {
- unsigned long timeout;
- int more;
-};
-
-static void dc_garbage_collect(struct fib6_node *fn, void *p_arg)
-{
- struct dc_gc_args * args = (struct dc_gc_args *) p_arg;
-
- if (fn->fn_flags & RTN_BACKTRACK)
- {
- if (fn->fn_bit == 127)
- {
- int more;
-
- more = dcache_gc_node(fn, args->timeout);
-
- if (more == -1)
- {
- if (fn->parent->left == fn)
- fn->parent->left = NULL;
- else
- fn->parent->right = NULL;
-
- kfree(fn);
-
- rt6_stats.fib_nodes--;
- rt6_stats.fib_route_nodes--;
-
- return;
- }
- args->more += more;
- }
- }
- else if (!(fn->fn_flags & RTN_ROOT))
- {
- int children = 0;
- struct fib6_node *chld = NULL;
-
- if (fn->left)
- {
- children++;
- chld = fn->left;
- }
-
- if (fn->right)
- {
- children++;
- chld = fn->right;
- }
-
- if (children <= 1)
- {
- struct fib6_node *pn = fn->parent;
-
- if (pn->left == fn)
- {
- pn->left = chld;
- }
- else
- {
- pn->right = chld;
- }
-
- if (chld)
- {
- chld->parent = pn;
- }
-
- rt_release(fn->leaf);
-
- rt6_stats.fib_nodes--;
- kfree(fn);
- }
- }
-}
-
-/*
- * called with ints off
- */
-
-static void __rt6_run_bh(void)
-{
- static last_gc_run = 0;
-
- if (rt6_bh_mask & RT_BH_REQUEST)
- {
- struct rt6_req *request;
-
- while ((request = rtreq_dequeue()))
- {
- struct rt6_info *rt;
-
- rt = request->ptr;
-
- switch (request->operation) {
- case RT_OPER_ADD:
- fib6_add_1(rt);
- break;
-
- case RT_OPER_DEL:
- fib6_del_rt(rt);
- break;
-
- default:
- printk(KERN_WARNING
- "rt6_run_bh: bad request in queue\n");
- }
-
- kfree(request);
- }
-
- rt6_bh_mask &= ~RT_BH_REQUEST;
- }
-
- if (rt6_bh_mask & RT_BH_GC)
- {
- if (jiffies - last_gc_run > DC_TIME_RUN)
- {
- struct dc_gc_args args;
-
- if (rt6_stats.fib_dc_alloc >= DC_WATER_MARK)
- args.timeout = DC_SHORT_TIMEOUT;
- else
- args.timeout = DC_LONG_TIMEOUT;
-
- args.more = 0;
- rt6_walk_tree(dc_garbage_collect, &args, RT6_FILTER_NONE);
-
- last_gc_run = jiffies;
-
- if (!args.more)
- {
- rt6_bh_mask &= ~RT_BH_GC;
- }
- }
- }
-}
-
-/*
- * Timer for expiring routes learned via addrconf and stale DC
- * entries when there is no network actuvity
- */
-
-void rt6_timer_handler(unsigned long data)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (rt6_lock == 0)
- {
- if (rt_clients == 0 && rt6_bh_mask)
- {
- __rt6_run_bh();
- }
-
- /*
- * route expiry
- */
-
- rt6_walk_tree(rt6_rt_timeout, NULL, RT6_FILTER_RTNODES);
- }
-
- restore_flags(flags);
-
- rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
- add_timer(&rt6_gc_timer);
-}
-
-/*
- * Check if routes should be timed out.
- * Called from rt6_walk_tree for every node.
- */
-
-static void rt6_rt_timeout(struct fib6_node *fn, void *arg)
-{
- struct rt6_info *rt;
- unsigned long now = jiffies;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- if ((rt->rt_flags & RTF_ADDRCONF) && now > rt->rt_expires)
- {
- struct rt6_req *req;
-
- /*
- * request route deletion. routes will only
- * be deleted after walk_tree completes
- */
-
- req = (struct rt6_req *) kmalloc(sizeof(struct rt6_req),
- GFP_ATOMIC);
- req->operation = RT_OPER_DEL;
- req->ptr = rt;
- req->next = req->prev = NULL;
- }
- }
-}
-
-static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- skb->free = 1;
-
- memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
- sizeof(struct in6_rtmsg));
-
- if (netlink_post(NETLINK_ROUTE6, skb))
- {
- kfree_skb(skb, FREE_WRITE);
- }
-}
-
-int ipv6_route_ioctl(unsigned int cmd, void *arg)
-{
- struct in6_rtmsg rtmsg;
- int err;
-
- switch(cmd)
- {
- case SIOCADDRT: /* Add a route */
- case SIOCDELRT: /* Delete a route */
- if (!suser())
- return -EPERM;
- err = copy_from_user(&rtmsg, arg,
- sizeof(struct in6_rtmsg));
- if (err)
- return -EFAULT;
-
- err = (cmd == SIOCDELRT) ? ipv6_route_del(&rtmsg) :
- ipv6_route_add(&rtmsg);
-
- if (err == 0)
- {
- rt6_sndrtmsg(&rtmsg);
- }
- return err;
- }
-
- return -EINVAL;
-}
-
-static void rt6_ifdown_scan(struct fib6_node *fn, void *arg)
-{
- struct rt6_info *rt;
- struct device *dev = (struct device *) arg;
-
- for (rt = fn->leaf; rt; rt=rt->next)
- {
- if (((rt->rt_flags & RTI_DCACHE) == 0) && rt->rt_dev == dev)
- {
- struct rt6_req *req;
-
- req = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
- req->operation = RT_OPER_DEL;
- req->ptr = rt;
- req->next = req->prev = NULL;
- rt6_bh_mask |= RT_BH_REQUEST;
- }
- }
-}
-
-void rt6_ifdown(struct device *dev)
-{
- rt6_walk_tree(rt6_ifdown_scan, (void *) dev, RT6_FILTER_RTNODES);
-}
-
-static void rt6_walk_tree(f_pnode func, void * arg, int filter)
-{
- struct fib6_node *fn;
- /*
- * adquire lock
- * this warranties that the operation will be atomic with
- * respect to the garbage collect routine that also does
- * a tree transversal and tags nodes with the RTN_TAG flag
- */
- atomic_inc(&rt6_lock);
-
- fn = &routing_table;
-
- do {
- if (!(fn->fn_flags & RTN_TAG))
- {
- fn->fn_flags |= RTN_TAG;
-
- if (fn->left)
- {
- fn = fn->left;
- continue;
- }
- }
-
- fn->fn_flags &= ~RTN_TAG;
-
- if (fn->right)
- {
- fn = fn->right;
- continue;
- }
-
- do {
- struct fib6_node *node;
-
- if (fn->fn_flags & RTN_ROOT)
- break;
- node = fn;
- fn = fn->parent;
-
- if (!(node->fn_flags & RTN_TAG) &&
- (!filter || (node->fn_flags & RTN_BACKTRACK)))
- {
- (*func)(node, arg);
- }
-
- } while (!(fn->fn_flags & RTN_TAG));
-
- } while (!(fn->fn_flags & RTN_ROOT) || (fn->fn_flags & RTN_TAG));
-
- atomic_dec(&rt6_lock);
-}
-
-#ifdef CONFIG_PROC_FS
-#define RT6_INFO_LEN (32 + 2 + 32 + 2 + 2 + 2 + 4 + 8 + 7 + 1)
-
-struct rt6_proc_arg {
- char *buffer;
- int offset;
- int length;
- int skip;
- int len;
-};
-
-static void rt6_info_node(struct fib6_node *fn, void *p_arg)
-{
- struct rt6_info *rt;
- struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
-
- for (rt = fn->leaf; rt; rt = rt->next)
- {
- int i;
-
- if (arg->skip < arg->offset / RT6_INFO_LEN)
- {
- arg->skip++;
- continue;
- }
-
- if (arg->len >= arg->length)
- return;
-
- for (i=0; i<16; i++)
- {
- sprintf(arg->buffer + arg->len, "%02x",
- rt->rt_dst.s6_addr[i]);
- arg->len += 2;
- }
- arg->len += sprintf(arg->buffer + arg->len, " %02x ",
- rt->rt_prefixlen);
- if (rt->rt_nexthop)
- {
- for (i=0; i<16; i++)
- {
- sprintf(arg->buffer + arg->len, "%02x",
- rt->rt_nexthop->addr.s6_addr[i]);
- arg->len += 2;
- }
- }
- else
- {
- sprintf(arg->buffer + arg->len,
- "00000000000000000000000000000000");
- arg->len += 32;
- }
- arg->len += sprintf(arg->buffer + arg->len,
- " %02x %02x %02x %04x %8s\n",
- rt->rt_metric, rt->rt_use,
- rt->rt_ref, rt->rt_flags,
- rt->rt_dev ? rt->rt_dev->name : "");
- }
-}
-
-static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
- int dummy)
-{
- struct rt6_proc_arg arg;
- struct fib6_node sfn;
- arg.buffer = buffer;
- arg.offset = offset;
- arg.length = length;
- arg.skip = 0;
- arg.len = 0;
-
- rt6_walk_tree(rt6_info_node, &arg, RT6_FILTER_RTNODES);
-
- sfn.leaf = default_rt_list;
- rt6_info_node(&sfn, &arg);
-
- sfn.leaf = last_resort_rt;
- rt6_info_node(&sfn, &arg);
-
- *start = buffer;
-
- if (offset)
- *start += offset % RT6_INFO_LEN;
-
- arg.len -= offset % RT6_INFO_LEN;
-
- if (arg.len > length)
- arg.len = length;
-
- return arg.len;
-}
-
-
-static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
- int dummy)
-{
- int len;
-
- len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
- rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
- rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
- rt6_stats.fib_dc_alloc);
-
- len -= offset;
-
- if (len > length)
- len = length;
-
- *start = buffer + offset;
-
- return len;
-}
-
-#endif /* CONFIG_PROC_FS */
-
-/*
- * init/cleanup code
- *
- */
-
-void ipv6_route_init(void)
-{
-#ifdef CONFIG_PROC_FS
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RT6, 10, "ipv6_route",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt6_proc_info
- });
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RT6_STATS, 9, "rt6_stats",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rt6_proc_stats
- });
-
-#endif
- rt6_gc_timer.expires = jiffies + 4 * DC_LONG_TIMEOUT;
- add_timer(&rt6_gc_timer);
- netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
-}
-
-#ifdef MODULE
-void ipv6_route_cleanup(void)
-{
- proc_net_unregister(PROC_NET_RT6);
- proc_net_unregister(PROC_NET_RT6_STATS);
- netlink_detach(NETLINK_ROUTE6);
- del_timer(&rt6_gc_timer);
- fib6_flush();
-}
-#endif
-
-/*
- * NETLINK interface
- * routing socket moral equivalent
- */
-
-static int rt6_msgrcv(int unit, struct sk_buff *skb)
-{
- int count = 0;
- struct in6_rtmsg *rtmsg;
-
- while (skb->len)
- {
- if (skb->len < sizeof(struct in6_rtmsg))
- {
- count = -EINVAL;
- goto out;
- }
-
- rtmsg = (struct in6_rtmsg *) skb->data;
- skb_pull(skb, sizeof(struct in6_rtmsg));
- count += sizeof(struct in6_rtmsg);
-
- switch (rtmsg->rtmsg_type) {
- case RTMSG_NEWROUTE:
- ipv6_route_add(rtmsg);
- break;
- case RTMSG_DELROUTE:
- ipv6_route_del(rtmsg);
- break;
- default:
- count = -EINVAL;
- goto out;
- }
- }
-
- out:
- kfree_skb(skb, FREE_READ);
- return count;
-}
-
-void rt6_sndmsg(__u32 type, struct in6_addr *dst, struct in6_addr *gw,
- __u16 plen, struct device *dev, __u16 metric, __u16 flags)
-{
- struct sk_buff *skb;
- struct in6_rtmsg *msg;
- int ifindex = 0;
-
- skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
- if (skb == NULL)
- return;
-
- skb->free = 1;
-
- msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
-
- msg->rtmsg_type = type;
-
- if (dst)
- {
- ipv6_addr_copy(&msg->rtmsg_dst, dst);
- }
- else
- memset(&msg->rtmsg_dst, 0, sizeof(struct in6_addr));
-
- if (gw)
- {
- ipv6_addr_copy(&msg->rtmsg_gateway, gw);
- }
- else
- memset(&msg->rtmsg_gateway, 0, sizeof(struct in6_addr));
-
- msg->rtmsg_prefixlen = plen;
- msg->rtmsg_metric = metric;
-
- if (dev)
- {
- struct inet6_dev *idev;
-
- idev = ipv6_get_idev(dev);
- if (idev)
- {
- ifindex = idev->if_index;
- }
- }
-
- msg->rtmsg_ifindex = ifindex;
-
- msg->rtmsg_flags = flags;
-
- if (netlink_post(NETLINK_ROUTE6, skb))
- {
- kfree_skb(skb, FREE_WRITE);
- }
-}
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 7ae830876..88920bb73 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -7,14 +7,21 @@
*
* Based on linux/net/ipv4/ip_sockglue.c
*
- * $Id: ipv6_sockglue.c,v 1.12 1996/10/29 22:45:53 roque Exp $
+ * $Id: ipv6_sockglue.c,v 1.11 1997/04/20 09:44:33 davem Exp $
*
* This program 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 of the License, or (at your option) any later version.
+ *
+ * FIXME: Make the setsockopt code POSIX compliant: That is
+ *
+ * o Return -EINVAL for setsockopt of short lengths
+ * o Truncate getsockopt returns
+ * o Return an optlen of the truncated length if need be
*/
+#include <linux/config.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -34,7 +41,7 @@
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/inet_common.h>
#include <net/sit.h>
@@ -46,7 +53,7 @@
struct ipv6_mib ipv6_statistics={0, };
struct packet_type ipv6_packet_type =
{
- 0,
+ __constant_htons(ETH_P_IPV6),
NULL, /* All devices */
ipv6_rcv,
NULL,
@@ -67,17 +74,14 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int val, err;
- int retv = -EOPNOTSUPP;
+ int retv = -ENOPROTOOPT;
if(level!=SOL_IPV6)
goto out;
- if (optval == NULL)
- {
+ if (optval == NULL) {
val=0;
- }
- else
- {
+ } else {
err = get_user(val, (int *) optval);
if(err)
return err;
@@ -87,42 +91,33 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
switch (optname) {
case IPV6_ADDRFORM:
- if (val == PF_INET)
- {
+ if (val == PF_INET) {
if (sk->protocol != IPPROTO_UDP &&
sk->protocol != IPPROTO_TCP)
- {
goto out;
- }
- if (sk->state != TCP_ESTABLISHED)
- {
+ if (sk->state != TCP_ESTABLISHED) {
retv = ENOTCONN;
goto out;
}
- if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED))
- {
+ if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
retv = -EADDRNOTAVAIL;
goto out;
}
- if (sk->protocol == IPPROTO_TCP)
- {
+ if (sk->protocol == IPPROTO_TCP) {
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
sk->prot = &tcp_prot;
tp->af_specific = &ipv4_specific;
- }
- else
- {
+ sk->socket->ops = &inet_stream_ops;
+ } else {
sk->prot = &udp_prot;
+ sk->socket->ops = &inet_dgram_ops;
}
- sk->socket->ops = &inet_proto_ops;
retv = 0;
- }
- else
- {
+ } else {
retv = -EINVAL;
}
break;
@@ -132,13 +127,15 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
retv = 0;
break;
+ case IPV6_HOPLIMIT:
+ np->rxhlim = val;
+ retv = 0;
+ break;
+
case IPV6_UNICAST_HOPS:
if (val > 255)
- {
retv = -EINVAL;
- }
- else
- {
+ else {
np->hop_limit = val;
retv = 0;
}
@@ -146,11 +143,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
case IPV6_MULTICAST_HOPS:
if (val > 255)
- {
retv = -EINVAL;
- }
- else
- {
+ else {
np->mcast_hops = val;
retv = 0;
}
@@ -168,23 +162,19 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
if(err)
return -EFAULT;
- if (ipv6_addr_any(&addr))
- {
- np->mc_if = NULL;
- }
- else
- {
+ if (ipv6_addr_any(&addr)) {
+ np->oif = NULL;
+ } else {
struct inet6_ifaddr *ifp;
ifp = ipv6_chk_addr(&addr);
- if (ifp == NULL)
- {
+ if (ifp == NULL) {
retv = -EADDRNOTAVAIL;
break;
}
- np->mc_if = ifp->idev->dev;
+ np->oif = ifp->idev->dev;
}
retv = 0;
break;
@@ -200,8 +190,8 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
if(err)
return -EFAULT;
- if (mreq.ipv6mr_ifindex == 0)
- {
+ if (mreq.ipv6mr_ifindex == 0) {
+#if 0
struct in6_addr mcast;
struct dest_entry *dc;
@@ -214,34 +204,22 @@ int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
dev = dc->rt.rt_dev;
ipv6_dst_unlock(dc);
}
- }
- else
- {
- struct inet6_dev *idev;
-
- if ((idev = ipv6_dev_by_index(mreq.ipv6mr_ifindex)))
- {
- dev = idev->dev;
- }
+#endif
+ } else {
+ dev = dev_get_by_index(mreq.ipv6mr_ifindex);
}
if (dev == NULL)
- {
return -ENODEV;
- }
if (optname == IPV6_ADD_MEMBERSHIP)
- {
retv = ipv6_sock_mc_join(sk, dev, &mreq.ipv6mr_multiaddr);
- }
else
- {
retv = ipv6_sock_mc_drop(sk, dev, &mreq.ipv6mr_multiaddr);
- }
- }
}
+ };
- out:
+out:
return retv;
}
@@ -251,7 +229,7 @@ int ipv6_getsockopt(struct sock *sk, int level, int optname, char *optval,
return 0;
}
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
/*
* sysctl registration functions defined in sysctl_net_ipv6.c
@@ -263,17 +241,15 @@ extern void ipv6_sysctl_unregister(void);
void ipv6_init(void)
{
- ipv6_packet_type.type = ntohs(ETH_P_IPV6);
-
dev_add_pack(&ipv6_packet_type);
-#ifdef MODULE
+#if defined(MODULE) && defined(CONFIG_SYSCTL)
ipv6_sysctl_register();
#endif
register_netdevice_notifier(&ipv6_dev_notf);
- ipv6_route_init();
+ ip6_route_init();
}
#ifdef MODULE
@@ -281,15 +257,13 @@ void ipv6_cleanup(void)
{
unregister_netdevice_notifier(&ipv6_dev_notf);
dev_remove_pack(&ipv6_packet_type);
+#ifdef CONFIG_SYSCTL
ipv6_sysctl_unregister();
- ipv6_route_cleanup();
+#endif
+ ip6_route_cleanup();
ndisc_cleanup();
addrconf_cleanup();
}
#endif
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O6 -m486 -c ipv6_sockglue.c"
- * End:
- */
+
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 14ba9ef5f..573f1f611 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: mcast.c,v 1.8 1997/04/12 04:32:48 davem Exp $
+ *
* Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c
*
* This program is free software; you can redistribute it and/or
@@ -13,6 +15,8 @@
* 2 of the License, or (at your option) any later version.
*/
+#define __NO_VERSION__
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -22,6 +26,7 @@
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/route.h>
#include <net/sock.h>
#include <net/snmp.h>
@@ -30,13 +35,32 @@
#include <net/protocol.h>
#include <net/if_inet6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
+#include <net/checksum.h>
+
+/* Set to 3 to get tracing... */
+#define MCAST_DEBUG 2
+
+#if MCAST_DEBUG >= 3
+#define MDBG(x) printk x
+#else
+#define MDBG(x)
+#endif
+
+static struct inode igmp6_inode;
+static struct socket *igmp6_socket=&igmp6_inode.u.socket_i;
+
+static void igmp6_join_group(struct ifmcaddr6 *ma);
+static void igmp6_leave_group(struct ifmcaddr6 *ma);
+void igmp6_timer_handler(unsigned long data);
+
+#define IGMP6_UNSOLICITED_IVAL (10*HZ)
/*
* socket join on multicast group
*/
+
int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
struct in6_addr *addr)
{
@@ -44,21 +68,25 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int err;
+ MDBG(("ipv6_sock_mc_join(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
if (!(ipv6_addr_type(addr) & IPV6_ADDR_MULTICAST))
return -EINVAL;
if(!(dev->flags & IFF_MULTICAST))
return -EADDRNOTAVAIL;
- mc_lst = (struct ipv6_mc_socklist *)
- kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
+ mc_lst = kmalloc(sizeof(struct ipv6_mc_socklist), GFP_KERNEL);
if (mc_lst == NULL)
return -ENOMEM;
mc_lst->next = NULL;
memcpy(&mc_lst->addr, addr, sizeof(struct in6_addr));
- mc_lst->dev = dev;
+ mc_lst->dev = dev;
/*
* now add/increase the group membership on the device
@@ -66,8 +94,7 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
err = ipv6_dev_mc_inc(dev, addr);
- if (err)
- {
+ if (err) {
kfree(mc_lst);
return err;
}
@@ -84,7 +111,30 @@ int ipv6_sock_mc_join(struct sock *sk, struct device *dev,
int ipv6_sock_mc_drop(struct sock *sk, struct device *dev,
struct in6_addr *addr)
{
- return 0;
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ struct ipv6_mc_socklist *mc_lst, **lnk;
+
+ lnk = &np->ipv6_mc_list;
+
+ MDBG(("ipv6_sock_mc_drop(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+
+ for (mc_lst = *lnk ; mc_lst; mc_lst = mc_lst->next) {
+ if (mc_lst->dev == dev &&
+ ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
+ *lnk = mc_lst->next;
+ ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+ kfree(mc_lst);
+
+ return 0;
+ }
+ lnk = &mc_lst->next;
+ }
+
+ return -ENOENT;
}
void ipv6_sock_mc_close(struct sock *sk)
@@ -92,14 +142,15 @@ void ipv6_sock_mc_close(struct sock *sk)
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct ipv6_mc_socklist *mc_lst;
- for (mc_lst = np->ipv6_mc_list; mc_lst; )
- {
+ for (mc_lst = np->ipv6_mc_list; mc_lst; ) {
struct ipv6_mc_socklist *back;
/*
* leave group
*/
+ ipv6_dev_mc_dec(mc_lst->dev, &mc_lst->addr);
+
back = mc_lst;
mc_lst = mc_lst->next;
kfree(back);
@@ -111,53 +162,59 @@ void ipv6_sock_mc_close(struct sock *sk)
*/
int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
{
- struct ipv6_mc_list *mc;
- struct inet6_dev *i6dev;
+ struct ifmcaddr6 *mc;
+ struct inet6_dev *idev;
char buf[6];
- u8 hash;
-
- for (i6dev = inet6_dev_lst; i6dev; i6dev=i6dev->next)
- if (i6dev->dev == dev)
+ int hash;
+
+ MDBG(("ipv6_dev_mc_inc(%s) addr[", dev ? dev->name : "[NULL]"));
+ MDBG(("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+ hash = ipv6_devindex_hash(dev->ifindex);
+
+ for (idev = inet6_dev_lst[hash]; idev; idev=idev->next)
+ if (idev->dev == dev)
break;
-
- if (i6dev == NULL)
- {
+
+ if (idev == NULL) {
printk(KERN_DEBUG "ipv6_dev_mc_inc: device not found\n");
return -EINVAL;
}
- for (mc = i6dev->mc_list; mc; mc = mc->if_next)
- if (ipv6_addr_cmp(&mc->addr, addr) == 0)
- {
- atomic_inc(&mc->users);
+ hash = ipv6_addr_hash(addr);
+
+ for (mc = inet6_mcast_lst[hash]; mc; mc = mc->next) {
+ if (ipv6_addr_cmp(&mc->mca_addr, addr) == 0) {
+ atomic_inc(&mc->mca_users);
return 0;
}
+ }
/*
* not found: create a new one.
*/
- mc = (struct ipv6_mc_list *) kmalloc(sizeof(struct ipv6_mc_list),
- GFP_ATOMIC);
+ mc = kmalloc(sizeof(struct ifmcaddr6), GFP_ATOMIC);
if (mc == NULL)
- {
return -ENOMEM;
- }
- memset(mc, 0, sizeof(struct ipv6_mc_list));
+ MDBG(("create new ipv6 MC entry, "));
+ memset(mc, 0, sizeof(struct ifmcaddr6));
+ mc->mca_timer.function = igmp6_timer_handler;
+ mc->mca_timer.data = (unsigned long) mc;
- memcpy(&mc->addr, addr, sizeof(struct in6_addr));
+ memcpy(&mc->mca_addr, addr, sizeof(struct in6_addr));
mc->dev = dev;
- mc->users = 1;
-
- hash = ipv6_addr_hash(addr);
+ atomic_set(&mc->mca_users, 1);
mc->next = inet6_mcast_lst[hash];
inet6_mcast_lst[hash] = mc;
-
- mc->if_next = i6dev->mc_list;
- i6dev->mc_list = mc;
+
+ mc->if_next = idev->mc_list;
+ idev->mc_list = mc;
/*
* multicast mapping is defined in IPv6-over-foo documents
@@ -166,27 +223,67 @@ int ipv6_dev_mc_inc(struct device *dev, struct in6_addr *addr)
switch (dev->type) {
case ARPHRD_ETHER:
ipv6_mc_map(addr, buf);
+ MDBG(("ARPHRD_ETHER[%02x:%02x:%02x:%02x:%02x:%02x] dev_mc_add()\n",
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]));
dev_mc_add(dev, buf, ETH_ALEN, 0);
break;
-
+
default:
printk(KERN_DEBUG "dev_mc_inc: unkown device type\n");
- }
-
+ };
- /*
- * FIXME: ICMP report handling
- */
+ igmp6_join_group(mc);
return 0;
}
+static void ipv6_mca_remove(struct device *dev, struct ifmcaddr6 *ma)
+{
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(dev);
+
+ if (idev) {
+ struct ifmcaddr6 *iter, **lnk;
+
+ lnk = &idev->mc_list;
+
+ for (iter = *lnk; iter; iter = iter->if_next) {
+ if (iter == ma) {
+ *lnk = iter->if_next;
+ break;
+ }
+ lnk = &iter->if_next;
+ }
+ }
+}
+
/*
* device multicast group del
*/
int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
{
- return 0;
+ struct ifmcaddr6 *ma, **lnk;
+ int hash;
+
+ hash = ipv6_addr_hash(addr);
+
+ lnk = &inet6_mcast_lst[hash];
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma = ma->next) {
+ if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0) {
+ if (atomic_dec_and_test(&ma->mca_users)) {
+ igmp6_leave_group(ma);
+ *lnk = ma->next;
+ ipv6_mca_remove(ma->dev, ma);
+ kfree(ma);
+ }
+ return 0;
+ }
+ lnk = &ma->next;
+ }
+
+ return -ENOENT;
}
/*
@@ -194,17 +291,15 @@ int ipv6_dev_mc_dec(struct device *dev, struct in6_addr *addr)
*/
int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
{
- struct ipv6_mc_list *mc;
- u8 hash;
+ struct ifmcaddr6 *mc;
+ int hash;
hash = ipv6_addr_hash(addr);
- for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next)
- if ((mc->dev == dev) &&
- ipv6_addr_cmp(&mc->addr, addr) == 0)
- {
+ for (mc = inet6_mcast_lst[hash]; mc; mc=mc->next) {
+ if ((mc->dev == dev) && ipv6_addr_cmp(&mc->mca_addr, addr) == 0)
return 1;
- }
+ }
return 0;
}
@@ -213,8 +308,216 @@ int ipv6_chk_mcast_addr(struct device *dev, struct in6_addr *addr)
* IGMP handling (alias multicast ICMPv6 messages)
*/
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o mcast.o mcast.c"
- * End:
- */
+static void igmp6_group_queried(struct ifmcaddr6 *ma, unsigned long resptime)
+{
+ unsigned long delay;
+
+ ma->mca_flags |= MAF_TIMER_RUNNING;
+
+ delay = ipv6_random() % resptime;
+ ma->mca_timer.expires = jiffies + delay;
+ add_timer(&ma->mca_timer);
+}
+
+int igmp6_event_query(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ unsigned long resptime;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+ return -EINVAL;
+
+ resptime = hdr->icmp6_maxdelay;
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ if (ipv6_addr_any(addrp)) {
+ struct inet6_dev *idev;
+
+ idev = ipv6_get_idev(skb->dev);
+
+ if (idev == NULL)
+ return 0;
+
+ for (ma = idev->mc_list; ma; ma=ma->if_next)
+ igmp6_group_queried(ma, resptime);
+ } else {
+ int hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if (ma->dev == skb->dev &&
+ ipv6_addr_cmp(addrp, &ma->mca_addr) == 0) {
+ igmp6_group_queried(ma, resptime);
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int igmp6_event_report(struct sk_buff *skb, struct icmp6hdr *hdr, int len)
+{
+ struct ifmcaddr6 *ma;
+ struct in6_addr *addrp;
+ struct device *dev;
+ int hash;
+
+ if (len < sizeof(struct icmp6hdr) + sizeof(struct ipv6hdr))
+ return -EINVAL;
+
+ addrp = (struct in6_addr *) (hdr + 1);
+
+ dev = skb->dev;
+
+ /*
+ * Cancel the timer for this group
+ */
+
+ hash = ipv6_addr_hash(addrp);
+
+ for (ma = inet6_mcast_lst[hash]; ma; ma=ma->next) {
+ if ((ma->dev == dev) && ipv6_addr_cmp(&ma->mca_addr, addrp) == 0) {
+ if (ma->mca_flags & MAF_TIMER_RUNNING) {
+ del_timer(&ma->mca_timer);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+ }
+
+ ma->mca_flags &= ~MAF_LAST_REPORTER;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void igmp6_send(struct in6_addr *addr, struct device *dev, int type)
+{
+ struct sock *sk = igmp6_socket->sk;
+ struct sk_buff *skb;
+ struct icmp6hdr *hdr;
+ struct inet6_ifaddr *ifp;
+ struct in6_addr *addrp;
+ int err, len, plen;
+
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+
+ plen = sizeof(struct ipv6hdr) + len;
+
+ skb = sock_alloc_send_skb(sk, dev->hard_header_len + plen, 0, 0, &err);
+
+ if (skb == NULL)
+ return;
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
+ ipv6_mc_map(addr, ha);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, plen);
+ skb->arp = 1;
+ }
+ }
+
+ ifp = ipv6_get_lladdr(dev);
+
+ if (ifp == NULL) {
+#if MCAST_DEBUG >= 1
+ printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
+ dev->name);
+#endif
+ return;
+ }
+
+ ip6_nd_hdr(sk, skb, dev, &ifp->addr, addr, IPPROTO_ICMPV6, len);
+
+ /*
+ * need hop-by-hop router alert option.
+ */
+
+ hdr = (struct icmp6hdr *) skb_put(skb, sizeof(struct icmp6hdr));
+ memset(hdr, 0, sizeof(struct icmp6hdr));
+ hdr->icmp6_type = type;
+
+ addrp = (struct in6_addr *) skb_put(skb, sizeof(struct in6_addr));
+ ipv6_addr_copy(addrp, addr);
+
+ hdr->icmp6_cksum = csum_ipv6_magic(&ifp->addr, addr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
+
+ dev_queue_xmit(skb);
+}
+
+static void igmp6_join_group(struct ifmcaddr6 *ma)
+{
+ unsigned long delay;
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & IPV6_ADDR_LINKLOCAL))
+ return;
+
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+
+ delay = ipv6_random() % IGMP6_UNSOLICITED_IVAL;
+ ma->mca_timer.expires = jiffies + delay;
+
+ add_timer(&ma->mca_timer);
+ ma->mca_flags |= MAF_TIMER_RUNNING | MAF_LAST_REPORTER;
+}
+
+static void igmp6_leave_group(struct ifmcaddr6 *ma)
+{
+ int addr_type;
+
+ addr_type = ipv6_addr_type(&ma->mca_addr);
+
+ if ((addr_type & IPV6_ADDR_LINKLOCAL))
+ return;
+
+ if (ma->mca_flags & MAF_LAST_REPORTER)
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REDUCTION);
+
+ if (ma->mca_flags & MAF_TIMER_RUNNING)
+ del_timer(&ma->mca_timer);
+}
+
+void igmp6_timer_handler(unsigned long data)
+{
+ struct ifmcaddr6 *ma = (struct ifmcaddr6 *) data;
+
+ ma->mca_flags |= MAF_LAST_REPORTER;
+ igmp6_send(&ma->mca_addr, ma->dev, ICMPV6_MGM_REPORT);
+ ma->mca_flags &= ~MAF_TIMER_RUNNING;
+}
+
+void igmp6_init(struct net_proto_family *ops)
+{
+ struct sock *sk;
+ int err;
+
+ igmp6_inode.i_mode = S_IFSOCK;
+ igmp6_inode.i_sock = 1;
+ igmp6_inode.i_uid = 0;
+ igmp6_inode.i_gid = 0;
+
+ igmp6_socket->inode = &igmp6_inode;
+ igmp6_socket->state = SS_UNCONNECTED;
+ igmp6_socket->type = SOCK_RAW;
+
+ if((err=ops->create(igmp6_socket, IPPROTO_ICMPV6))<0)
+ printk(KERN_DEBUG
+ "Failed to create the IGMP6 control socket.\n");
+
+ MOD_DEC_USE_COUNT;
+
+ sk = igmp6_socket->sk;
+ sk->allocation = GFP_ATOMIC;
+ sk->num = 256; /* Don't receive any data */
+
+ sk->net_pinfo.af_inet6.hop_limit = 1;
+}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 31d50a5b5..3a1704f37 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -6,7 +6,7 @@
* Pedro Roque <roque@di.fc.ul.pt>
* Mike Shaver <shaver@ingenia.com>
*
- * $Id: ndisc.c,v 1.28 1996/10/11 16:03:06 roque Exp $
+ * $Id: ndisc.c,v 1.14 1997/04/12 04:32:51 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -23,18 +23,14 @@
* Janos Farkas : kmalloc failure checks
*/
-/*
- * Interface:
- *
- * ndisc_lookup will be called from eth.c on dev->(re)build_header
- *
- * ndisc_rcv
- * ndisc_validate is called by higher layers when they know a neighbour
- * is reachable.
- *
- * Manages neighbour cache
- *
- */
+/* Set to 3 to get tracing... */
+#define ND_DEBUG 2
+
+#if ND_DEBUG >= 3
+#define NDBG(x) printk x
+#else
+#define NDBG(x)
+#endif
#define __NO_VERSION__
#include <linux/module.h>
@@ -46,7 +42,8 @@
#include <linux/sched.h>
#include <linux/net.h>
#include <linux/in6.h>
-#include <linux/netdevice.h>
+#include <linux/route.h>
+
#include <linux/if_arp.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
@@ -57,38 +54,44 @@
#include <net/ipv6.h>
#include <net/protocol.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
+
#include <net/checksum.h>
#include <linux/proc_fs.h>
#define NCACHE_NUM_BUCKETS 32
-static struct socket ndisc_socket;
+static struct inode ndisc_inode;
+static struct socket *ndisc_socket=&ndisc_inode.u.socket_i;
unsigned long nd_rand_seed = 152L;
struct ndisc_statistics nd_stats;
-static struct neighbour *neighbours[NCACHE_NUM_BUCKETS];
+static struct neigh_table nd_tbl;
+
+unsigned int ndisc_hash(void *primary_key);
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb);
+
+static struct neigh_ops nd_neigh_ops = {
+ ETH_P_IPV6,
+ ndisc_hash,
+ ndisc_eth_resolv,
+ NULL
+};
+
static struct timer_list ndisc_timer;
static struct timer_list ndisc_gc_timer;
-static atomic_t ndisc_lock = 0;
-
/*
* Protocol variables
*/
-int nd_max_multicast_solicit = 3;
-int nd_max_unicast_solicit = 3;
-int nd_retrans_timer = RETRANS_TIMER;
-int nd_reachable_time = RECHABLE_TIME;
-int nd_base_reachable_time = RECHABLE_TIME;
-int nd_delay_first_probe = 5 * HZ;
-int nd_gc_interval = 5 * HZ;
+unsigned long nd_reachable_time = RECHABLE_TIME;
+int nd_gc_interval = 5 * HZ;
/*
* garbage collection timeout must be greater than reachable time
@@ -99,20 +102,10 @@ int nd_gc_interval = 5 * HZ;
int nd_gc_staletime = 3 * RECHABLE_TIME;
-static struct neighbour ndisc_insert_queue = {
- {{{0,}}}, 0, 0, NULL, 0,
- {0,}, NULL, {0,}, 0, 0, 0, 0, 0,
- &ndisc_insert_queue,
- &ndisc_insert_queue
-};
-
-static int ndisc_ins_queue_len = 0;
-
-int ndisc_event_timer(struct neighbour *neigh);
-static void ndisc_bh_insert(void);
+static int ndisc_event_timer(struct nd_neigh *ndn);
-int ipv6_random(void)
+unsigned long ipv6_random(void)
{
nd_rand_seed=nd_rand_seed*69069L+1;
return nd_rand_seed^jiffies;
@@ -122,26 +115,22 @@ static __inline__ unsigned long rand_reach_time(void)
{
unsigned long val;
- val = ipv6_random() % (MAX_RANDOM_FACTOR * nd_base_reachable_time);
- if (val < (MIN_RANDOM_FACTOR * nd_base_reachable_time))
- {
- val += (MIN_RANDOM_FACTOR * nd_base_reachable_time);
- }
+ val = ipv6_random() % (MAX_RANDOM_FACTOR *
+ ipv6_config.nd_base_reachable_time);
+
+ if (val < (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time))
+ val+= (MIN_RANDOM_FACTOR * ipv6_config.nd_base_reachable_time);
return val;
}
-void ndisc_verify_reachability(struct neighbour * neigh);
-
-/*
- * (inline) support functions
- */
-
-static __inline__ __u32 ndisc_hash(struct in6_addr *addr)
+unsigned int ndisc_hash(void *primary_key)
{
-
+ struct in6_addr *addr = (struct in6_addr *) primary_key;
__u32 hash_val;
+ addr = (struct in6_addr *) primary_key;
+
hash_val = addr->s6_addr32[2] ^ addr->s6_addr32[3];
hash_val ^= hash_val >> 16;
@@ -149,239 +138,117 @@ static __inline__ __u32 ndisc_hash(struct in6_addr *addr)
return (hash_val & (NCACHE_NUM_BUCKETS - 1));
}
+static int ndisc_gc_func(struct neighbour *neigh, void *arg);
-static __inline__ void ndisc_neigh_queue(struct neighbour *neigh)
-{
- struct neighbour *next = &ndisc_insert_queue;
-
- ndisc_ins_queue_len++;
-
- neigh->prev = next->prev;
- neigh->prev->next = neigh;
- next->prev = neigh;
- neigh->next = next;
-}
-
-static __inline__ struct neighbour * ndisc_dequeue(void)
-{
- struct neighbour *next = &ndisc_insert_queue;
- struct neighbour *head;
-
- ndisc_ins_queue_len--;
-
- head = next->next;
-
- if (head == next)
- {
- return NULL;
- }
-
- head->next->prev = head->prev;
- next->next = head->next;
-
- head->next = NULL;
- head->prev = NULL;
-
- return head;
-}
-
-static __inline__ void ndisc_release_lock(void)
+static void ndisc_periodic_timer(unsigned long arg)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- ndisc_lock--;
-
- if (ndisc_lock == 0 && ndisc_ins_queue_len)
- {
- ndisc_bh_insert();
- }
-
- restore_flags(flags);
-}
-
-static void ndisc_insert_neigh(struct neighbour *neigh)
-{
-
- struct neighbour * bucket;
- __u32 hash_val = ndisc_hash(&neigh->addr);
-
- bucket = neighbours[hash_val];
-
- if (!bucket)
- {
- neighbours[hash_val] = neigh;
- return;
- }
-
- for (; bucket->next; bucket = bucket->next)
- ;
-
- bucket->next = neigh;
- neigh->prev = bucket;
-}
-
-static __inline__ struct neighbour *
-ndisc_retrieve_neigh(struct device *dev, struct in6_addr *addr)
-{
-
- struct neighbour * iter;
- iter = neighbours[ndisc_hash(addr)];
-
- for (; iter; iter = iter->next)
- {
- if (dev == iter->dev && ipv6_addr_cmp(addr, &iter->addr) == 0)
- return iter;
- }
- return NULL;
-}
-
-static void ndisc_unlink_neigh(struct neighbour * neigh)
-{
- if (neigh->prev)
- neigh->prev->next = neigh->next;
- else
- {
- int hash = ndisc_hash(&neigh->addr);
- neighbours[hash] = neigh->next;
- }
-
- if (neigh->next)
- neigh->next->prev = neigh->prev;
-}
-
-static void ndisc_release_neigh(struct neighbour * neigh)
-{
- struct sk_buff *skb;
-
- while((skb=skb_dequeue(&neigh->arp_queue)))
- {
- dev_kfree_skb(skb, FREE_WRITE);
- }
-
- if (neigh->refcnt == 0)
- {
- ndisc_unlink_neigh(neigh);
- kfree(neigh);
- }
-}
-
-static void ndisc_bh_insert(void)
-{
- struct neighbour *neigh;
-
- while((neigh = ndisc_dequeue()))
- {
- ndisc_insert_neigh(neigh);
- }
-}
-
-
-static void ndisc_garbage_collect(unsigned long arg)
-{
- struct neighbour * neigh;
static unsigned long last_rand = 0;
- unsigned long now = jiffies;
- unsigned long flags;
- int i = 0;
-
-
+ unsigned long now = jiffies;
+
/*
* periodicly compute ReachableTime from random function
*/
- if (now - last_rand > REACH_RANDOM_INTERVAL)
- {
+
+ if ((now - last_rand) > REACH_RANDOM_INTERVAL) {
last_rand = now;
nd_reachable_time = rand_reach_time();
}
- save_flags(flags);
- cli();
+ neigh_table_lock(&nd_tbl);
- if (ndisc_lock)
- {
- restore_flags(flags);
+ start_bh_atomic();
+ if (atomic_read(&nd_tbl.tbl_lock) == 1) {
+ ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
+ ndisc_gc_timer.expires = now + nd_gc_interval;
+ } else {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_gc delayed: table locked\n");
+#endif
ndisc_gc_timer.expires = now + HZ;
- add_timer(&ndisc_gc_timer);
- return;
}
-
- for (; i < NCACHE_NUM_BUCKETS; i++)
- for (neigh = neighbours[i]; neigh;)
- {
- /*
- * Release unused entries
- */
- if (neigh->refcnt == 0 &&
- ((neigh->nud_state == NUD_FAILED) ||
- ((neigh->nud_state == NUD_REACHABLE) &&
- (neigh->tstamp <= (now - nd_gc_staletime))
- )
- )
- )
- {
- struct neighbour *prev;
-
- prev = neigh;
- neigh = neigh->next;
- ndisc_release_neigh(prev);
- continue;
- }
- neigh = neigh->next;
- }
+ end_bh_atomic();
+
+ neigh_table_unlock(&nd_tbl);
+
+ add_timer(&ndisc_gc_timer);
+}
- restore_flags(flags);
+static int ndisc_gc_func(struct neighbour *neigh, void *arg)
+{
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+ unsigned long now = jiffies;
- ndisc_gc_timer.expires = now + nd_gc_interval;
- add_timer(&ndisc_gc_timer);
+ if (atomic_read(&ndn->ndn_refcnt) == 0) {
+ switch (ndn->ndn_nud_state) {
+
+ case NUD_REACHABLE:
+ case NUD_STALE:
+ if (now - ndn->ndn_tstamp < nd_gc_staletime)
+ break;
+ case NUD_FAILED:
+ return 1;
+ default:
+ };
+ }
+ return 0;
}
-static __inline__ void ndisc_add_timer(struct neighbour *neigh, int timer)
+static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer)
{
unsigned long now = jiffies;
- unsigned long tval;
+ unsigned long tval = ~0UL;
- neigh->expires = now + timer;
- tval = del_timer(&ndisc_timer);
+ ndn->ndn_expires = now + timer;
+
+ if (del_timer(&ndisc_timer))
+ tval = ndisc_timer.expires;
- if (tval)
- {
- tval = min(tval, neigh->expires);
- }
- else
- tval = neigh->expires;
+ tval = min(tval, ndn->ndn_expires);
ndisc_timer.expires = tval;
add_timer(&ndisc_timer);
}
-static void ndisc_del_timer(struct neighbour *neigh)
+static void ndisc_del_timer(struct nd_neigh *ndn)
{
- unsigned long tval;
+ unsigned long tval = ~0UL;
+ unsigned long neigh_val;
- if (!(neigh->nud_state & NUD_IN_TIMER))
- return;
+ if (del_timer(&ndisc_timer))
+ tval = ndisc_timer.expires;
- tval = del_timer(&ndisc_timer);
-
- if (tval == neigh->expires)
- {
+ neigh_val = ndn->ndn_expires;
+ ndn->ndn_expires = 0;
+
+ if (tval == neigh_val) {
int i;
tval = ~0UL;
+ neigh_table_lock(&nd_tbl);
+
/* need to search the entire neighbour cache */
- for (i=0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for (neigh = neighbours[i]; neigh; neigh=neigh->next)
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- tval = min(tval, neigh->expires);
- }
- }
+ for (i=0; i < nd_tbl.tbl_size; i++) {
+ struct neighbour *neigh, *head;
+ head = nd_tbl.hash_buckets[i];
+
+ if ((neigh = head) == NULL)
+ continue;
+
+ do {
+ struct nd_neigh *n;
+
+ n = (struct nd_neigh *) neigh;
+ if ((n->ndn_nud_state & NUD_IN_TIMER) &&
+ n->ndn_expires)
+ tval = min(tval, n->ndn_expires);
+
+ neigh = neigh->next;
+
+ } while (neigh != head);
+ }
+ neigh_table_unlock(&nd_tbl);
}
if (tval == ~(0UL))
@@ -391,53 +258,79 @@ static void ndisc_del_timer(struct neighbour *neigh)
add_timer(&ndisc_timer);
}
-static struct neighbour * ndisc_new_neigh(struct device *dev,
- struct in6_addr *addr)
+static int ndisc_forced_gc(struct neighbour *neigh, void *arg)
+{
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+
+ if (atomic_read(&ndn->ndn_refcnt) == 0) {
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
+
+ return 1;
+ }
+ return 0;
+}
+
+static struct nd_neigh * ndisc_new_neigh(struct device *dev,
+ struct in6_addr *addr)
{
- struct neighbour *neigh;
- unsigned long flags;
+ struct nd_neigh *ndn;
- neigh = (struct neighbour *) kmalloc(sizeof(struct neighbour),
- GFP_ATOMIC);
+ NDBG(("ndisc_new_neigh("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL],"));
+ NDBG(("[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]): ",
+ addr->s6_addr16[0], addr->s6_addr16[1], addr->s6_addr16[2],
+ addr->s6_addr16[3], addr->s6_addr16[4], addr->s6_addr16[5],
+ addr->s6_addr16[6], addr->s6_addr16[7]));
+
+ ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
+ &nd_neigh_ops);
+ if (ndn == NULL) {
+
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "neigh_alloc: out of memory\n");
+#endif
- if (neigh == NULL)
- {
- printk(KERN_DEBUG "ndisc: kmalloc failure\n");
+ start_bh_atomic();
+ if (atomic_read(&nd_tbl.tbl_lock) == 1) {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
+#endif
+ ntbl_walk_table(&nd_tbl, ndisc_forced_gc, 0, 0, NULL);
+ }
+
+ end_bh_atomic();
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_alloc failed\n");
+#endif
return NULL;
}
nd_stats.allocs++;
- memset(neigh, 0, sizeof (struct neighbour));
- skb_queue_head_init(&neigh->arp_queue);
-
- ipv6_addr_copy(&neigh->addr, addr);
- neigh->len = 128;
- neigh->type = ipv6_addr_type(addr);
- neigh->dev = dev;
- neigh->tstamp = jiffies;
+ ipv6_addr_copy(&ndn->ndn_addr, addr);
+ ndn->ndn_plen = 128;
+ ndn->ndn_type = ipv6_addr_type(addr);
+ ndn->ndn_dev = dev;
+ ndn->ndn_tstamp = jiffies;
- if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
- {
- neigh->flags |= NCF_NOARP;
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ NDBG(("MULTICAST(NCF_NOARP) "));
+ ndn->ndn_flags |= NCF_NOARP;
}
- save_flags(flags);
- cli();
-
- if (ndisc_lock == 0)
- {
- /* Add to the cache. */
- ndisc_insert_neigh(neigh);
- }
- else
- {
- ndisc_neigh_queue(neigh);
+ if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT) {
+ NDBG(("%s(NCF_NOARP) ",
+ (dev->type==ARPHRD_LOOPBACK) ? "LOOPBACK" : "SIT"));
+ ndn->ndn_flags |= NCF_NOARP;
}
- restore_flags(flags);
-
- return neigh;
+ neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+ NDBG(("returning ndn(%p)\n", ndn));
+ return ndn;
}
/*
@@ -448,33 +341,34 @@ static struct neighbour * ndisc_new_neigh(struct device *dev,
struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
{
- struct neighbour *neigh;
+ struct nd_neigh *neigh;
/*
* neighbour cache:
* cached information about nexthop and addr resolution
*/
- if (dev == NULL)
- {
- printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+ if (dev == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_get_neigh: NULL device\n");
+#endif
return NULL;
}
- atomic_inc(&ndisc_lock);
-
- neigh = ndisc_retrieve_neigh(dev, addr);
-
- ndisc_release_lock();
+ neigh_table_lock(&nd_tbl);
- if (neigh == NULL)
- {
+ neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
+ sizeof(struct in6_addr), dev);
+ if (neigh == NULL) {
neigh = ndisc_new_neigh(dev, addr);
+
+ if (neigh == NULL)
+ return NULL;
}
- atomic_inc(&neigh->refcnt);
-
- return neigh;
+ neigh_table_unlock(&nd_tbl);
+
+ return neighbour_clone((struct neighbour *) neigh);
}
/*
@@ -483,69 +377,54 @@ struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
* 1 - Address Resolution unfinished / packet queued
*/
-int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
- struct sk_buff *skb)
+int ndisc_eth_resolv(unsigned char *h_dest, struct sk_buff *skb)
{
- struct neighbour *neigh;
-
- neigh = skb->nexthop;
+ struct nd_neigh *ndn = NULL;
- if (neigh == NULL)
- {
- int addr_type;
-
- addr_type = ipv6_addr_type(&skb->ipv6_hdr->daddr);
-
- if (addr_type & IPV6_ADDR_MULTICAST)
- {
- ipv6_mc_map(&skb->ipv6_hdr->daddr, h_dest);
- return 0;
- }
+ if (skb->dst)
+ ndn = (struct nd_neigh *) skb->dst->neighbour;
+ if (ndn == NULL) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
+#endif
goto discard;
}
- if (skb->pkt_type == PACKET_NDISC)
- goto ndisc_pkt;
-
- switch (neigh->nud_state) {
+ if ((ndn->ndn_type & IPV6_ADDR_MULTICAST)) {
+ struct in6_addr *daddr;
+
+ daddr = &skb->nh.ipv6h->daddr;
+ ipv6_mc_map(daddr, h_dest);
+ return 0;
+ }
+
+ switch (ndn->ndn_nud_state) {
case NUD_FAILED:
case NUD_NONE:
- ndisc_event_send(neigh, skb);
+ ndisc_event_send((struct neighbour *)ndn, skb);
case NUD_INCOMPLETE:
- if (skb_queue_len(&neigh->arp_queue) >= NDISC_QUEUE_LEN)
- {
+ if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN) {
struct sk_buff *buff;
- buff = neigh->arp_queue.prev;
+ buff = ndn->neigh.arp_queue.prev;
skb_unlink(buff);
dev_kfree_skb(buff, FREE_WRITE);
}
- skb_queue_head(&neigh->arp_queue, skb);
+ skb_queue_head(&ndn->neigh.arp_queue, skb);
return 1;
default:
- ndisc_event_send(neigh, skb);
- }
-
- ndisc_pkt:
+ ndisc_event_send((struct neighbour *)ndn, skb);
+ };
- if (neigh->h_dest == NULL)
- {
- printk(KERN_DEBUG "neigh->h_dest is NULL\n");
- goto discard;
- }
-
- memcpy(h_dest, neigh->h_dest, dev->addr_len);
-
- if ((neigh->flags & NCF_HHVALID) == 0)
- {
- /*
- * copy header to hh_data and move h_dest pointer
- * this is strictly media dependent.
- */
+ if ((ndn->ndn_flags & NTF_COMPLETE) == 0) {
+#if ND_DEBUG >=1
+ /* This shouldn't happen */
+ printk(KERN_DEBUG "ND: using incomplete entry\n");
+#endif
}
+ memcpy(h_dest, ndn->ndn_ha, skb->dev->addr_len);
return 0;
discard:
@@ -554,54 +433,78 @@ int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
return 1;
}
+/*
+ * Send a Neighbour Advertisement
+ */
-/* Send the actual Neighbour Advertisement */
-
-void ndisc_send_na(struct device *dev, struct neighbour *neigh,
- struct in6_addr *daddr,
- struct in6_addr *solicited_addr,
+void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
+ struct in6_addr *daddr, struct in6_addr *solicited_addr,
int router, int solicited, int override, int inc_opt)
{
- struct sock *sk = (struct sock *)ndisc_socket.data;
+ struct sock *sk = ndisc_socket->sk;
struct nd_msg *msg;
int len, opt_len;
struct sk_buff *skb;
int err;
+ NDBG(("ndisc_send_na("));
+ if(dev)
+ NDBG(("%s,", dev->name));
+ else
+ NDBG(("[NULL]"));
+ NDBG(("%p): ", ndn));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(solicited_addr)
+ NDBG(("solicit_addr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicited_addr->s6_addr16[0], solicited_addr->s6_addr16[1],
+ solicited_addr->s6_addr16[2], solicited_addr->s6_addr16[3],
+ solicited_addr->s6_addr16[4], solicited_addr->s6_addr16[5],
+ solicited_addr->s6_addr16[6], solicited_addr->s6_addr16[7]));
+ NDBG(("rtr(%d)sol(%d)ovr(%d)iopt(%d)\n", router, solicited, override, inc_opt));
+
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
+#if ND_DEBUG >=1
+ if (dev == NULL) {
+ printk(KERN_DEBUG "send_na: null device\n");
+ return;
+ }
+#endif
if (inc_opt)
- {
len += opt_len << 3;
- }
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
- if (skb == NULL)
- {
+ if (skb == NULL) {
printk(KERN_DEBUG "send_na: alloc skb failed\n");
return;
}
-
- skb->free=1;
-
- if (ipv6_bld_hdr_2(sk, skb, dev, neigh, solicited_addr, daddr,
- IPPROTO_ICMPV6, len) < 0)
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_na: ipv6_build_header returned < 0\n");
- return;
+ /*
+ * build the MAC header
+ */
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ skb->arp = 1;
+ }
}
- skb->pkt_type = PACKET_NDISC;
-
+ ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+
msg = (struct nd_msg *) skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_ADVERTISEMENT;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
msg->icmph.icmp6_unused = 0;
msg->icmph.icmp6_router = router;
@@ -611,152 +514,192 @@ void ndisc_send_na(struct device *dev, struct neighbour *neigh,
/* Set the target address. */
ipv6_addr_copy(&msg->target, solicited_addr);
- if (inc_opt)
- {
+ if (inc_opt) {
/* Set the source link-layer address option. */
msg->opt.opt_type = ND_OPT_TARGET_LL_ADDR;
msg->opt.opt_len = opt_len;
memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(solicited_addr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(solicited_addr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_ns(struct device *dev, struct neighbour *neigh,
struct in6_addr *solicit,
struct in6_addr *daddr, struct in6_addr *saddr)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
+ unsigned char ha[MAX_ADDR_LEN];
+ struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
struct nd_msg *msg;
- int len, opt_len;
+ int len, opt_len;
+ void *h_dest;
int err;
+ NDBG(("ndisc_send_ns(%s,%p): ", (dev ? dev->name : "[NULL]"), neigh));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ if(solicit)
+ NDBG(("solicit[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ solicit->s6_addr16[0], solicit->s6_addr16[1],
+ solicit->s6_addr16[2], solicit->s6_addr16[3],
+ solicit->s6_addr16[4], solicit->s6_addr16[5],
+ solicit->s6_addr16[6], solicit->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
+ len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr) +
(opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
+#endif
return;
}
- skb->free=1;
skb->pkt_type = PACKET_NDISC;
- if (saddr == NULL)
- {
+ if (saddr == NULL) {
struct inet6_ifaddr *ifa;
/* use link local address */
ifa = ipv6_get_lladdr(dev);
if (ifa)
- {
saddr = &ifa->addr;
- }
}
- if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
- {
- nd_stats.snt_probes_mcast++;
+ if ((ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)) {
+ nd_stats.snt_probes_mcast++;
+ ipv6_mc_map(daddr, ha);
+ h_dest = ha;
+ } else {
+ if (neigh == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "send_ns: ucast destination "
+ "with null neighbour\n");
+#endif
+ return;
+ }
+ h_dest = neigh->ha;
+ nd_stats.snt_probes_ucast++;
}
- else
- {
- nd_stats.snt_probes_ucast++;
+
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(skb, dev, ETH_P_IPV6, h_dest, NULL,
+ len);
+ skb->arp = 1;
+ }
}
- if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
- }
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- msg = (struct nd_msg *)skb_put(skb, len);
- msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
- msg->icmph.code = 0;
- msg->icmph.checksum = 0;
- msg->icmph.icmp6_unused = 0;
+ msg = (struct nd_msg *)skb_put(skb, len);
+ msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
+ msg->icmph.icmp6_code = 0;
+ msg->icmph.icmp6_cksum = 0;
+ msg->icmph.icmp6_unused = 0;
- /* Set the target address. */
- ipv6_addr_copy(&msg->target, solicit);
+ /* Set the target address. */
+ ipv6_addr_copy(&msg->target, solicit);
- /* Set the source link-layer address option. */
- msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
- msg->opt.opt_len = opt_len;
+ /* Set the source link-layer address option. */
+ msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
+ msg->opt.opt_len = opt_len;
- memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
+ memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(msg->opt.link_addr + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- msg->icmph.checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr,
- daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) msg,
- len, 0));
+ msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
+ daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) msg,
+ len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
struct in6_addr *daddr)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
+ struct sock *sk = ndisc_socket->sk;
struct sk_buff *skb;
- struct icmpv6hdr *hdr;
+ struct icmp6hdr *hdr;
__u8 * opt;
int len, opt_len;
int err;
+ NDBG(("ndisc_send_rs(%s): ", (dev ? dev->name : "[NULL]")));
+ if(daddr)
+ NDBG(("daddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ daddr->s6_addr16[0], daddr->s6_addr16[1], daddr->s6_addr16[2],
+ daddr->s6_addr16[3], daddr->s6_addr16[4], daddr->s6_addr16[5],
+ daddr->s6_addr16[6], daddr->s6_addr16[7]));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
/* length of addr in 8 octet groups.*/
opt_len = ((dev->addr_len + 1) >> 3) + 1;
- len = sizeof(struct icmpv6hdr) + (opt_len << 3);
+ len = sizeof(struct icmp6hdr) + (opt_len << 3);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
- if (skb == NULL)
- {
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (skb == NULL) {
printk(KERN_DEBUG "send_ns: alloc skb failed\n");
return;
}
- skb->free=1;
+ if (dev->hard_header_len) {
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ unsigned char ha[MAX_ADDR_LEN];
- if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
- len) < 0 )
- {
- kfree_skb(skb, FREE_WRITE);
- printk(KERN_DEBUG
- "ndisc_send_ns: ipv6_build_header returned < 0\n");
- return;
- }
+ ipv6_mc_map(daddr, ha);
+ dev->hard_header(skb, dev, ETH_P_IPV6, ha, NULL, len);
+ skb->arp = 1;
+ }
+ }
+
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
- hdr = (struct icmpv6hdr *) skb_put(skb, len);
- hdr->type = NDISC_ROUTER_SOLICITATION;
- hdr->code = 0;
- hdr->checksum = 0;
+ hdr = (struct icmp6hdr *) skb_put(skb, len);
+ hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
+ hdr->icmp6_code = 0;
+ hdr->icmp6_cksum = 0;
hdr->icmp6_unused = 0;
opt = (u8*) (hdr + 1);
@@ -767,27 +710,25 @@ void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
memcpy(opt + 2, dev->dev_addr, dev->addr_len);
- if ((opt_len << 3) - (2 + dev->addr_len))
- {
+ if ((opt_len << 3) - (2 + dev->addr_len)) {
memset(opt + 2 + dev->addr_len, 0,
(opt_len << 3) - (2 + dev->addr_len));
}
/* checksum */
- hdr->checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr, daddr, len,
- IPPROTO_ICMPV6,
- csum_partial((__u8 *) hdr, len, 0));
+ hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
+ IPPROTO_ICMPV6,
+ csum_partial((__u8 *) hdr, len, 0));
/* send it! */
- ipv6_queue_xmit(sk, skb->dev, skb, 1);
+ dev_queue_xmit(skb);
}
-static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
- __u8 *h_addr, int option)
+static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
+ int option)
{
- while (*opt != option && opt_len)
- {
+ while (*opt != option && opt_len) {
int len;
len = opt[1] << 3;
@@ -802,9 +743,8 @@ static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
opt_len -= len;
}
- if (*opt == option)
- {
- memcpy(h_addr, opt + 2, dev->addr_len);
+ if (*opt == option) {
+ memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len);
return 0;
}
@@ -816,63 +756,46 @@ static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
static void ndisc_timer_handler(unsigned long arg)
{
unsigned long now = jiffies;
- struct neighbour * neigh;
unsigned long ntimer = ~0UL;
int i;
- atomic_inc(&ndisc_lock);
-
- for (i=0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for (neigh = neighbours[i]; neigh;)
- {
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- int time;
-
- if (neigh->expires <= now)
- {
- time = ndisc_event_timer(neigh);
- }
- else
- time = neigh->expires - now;
-
- if (time == 0)
- {
- unsigned long flags;
+ neigh_table_lock(&nd_tbl);
+
+ for (i=0; i < nd_tbl.tbl_size; i++) {
+ struct nd_neigh *ndn, *head;
- save_flags(flags);
- cli();
+ head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
- if (ndisc_lock == 1)
- {
- struct neighbour *old = neigh;
+ if ((ndn = head) == NULL)
+ continue;
- neigh = neigh->next;
- ndisc_release_neigh(old);
- restore_flags(flags);
- continue;
- }
+ do {
+ if (ndn->ndn_nud_state & NUD_IN_TIMER) {
+ unsigned long time;
- restore_flags(flags);
- }
+ time = ndn->ndn_expires - now;
- ntimer = min(ntimer, time);
+ if ((long) time <= 0)
+ time = ndisc_event_timer(ndn);
+
+ if (time)
+ ntimer = min(ntimer, time);
}
- neigh = neigh->next;
- }
+ ndn = (struct nd_neigh *) ndn->neigh.next;
+
+ } while (ndn != head);
}
- if (ntimer != (~0UL))
- {
- ndisc_timer.expires = jiffies + ntimer;
+ if (ntimer != (~0UL)) {
+ ndisc_timer.expires = now + ntimer;
add_timer(&ndisc_timer);
}
- ndisc_release_lock();
+
+ neigh_table_unlock(&nd_tbl);
}
-int ndisc_event_timer(struct neighbour *neigh)
+static int ndisc_event_timer(struct nd_neigh *ndn)
{
struct in6_addr *daddr;
struct in6_addr *target;
@@ -880,375 +803,248 @@ int ndisc_event_timer(struct neighbour *neigh)
struct device *dev;
int max_probes;
- if (neigh->nud_state == NUD_DELAY)
- {
- neigh->nud_state = NUD_PROBE;
- }
+ if (ndn->ndn_nud_state == NUD_DELAY)
+ ndn->ndn_nud_state = NUD_PROBE;
- max_probes = (neigh->nud_state == NUD_PROBE ? nd_max_unicast_solicit:
- nd_max_multicast_solicit);
+ max_probes = (ndn->ndn_nud_state == NUD_PROBE ?
+ ipv6_config.nd_max_ucast_solicit:
+ ipv6_config.nd_max_mcast_solicit);
- if (neigh->probes == max_probes)
- {
+ if (ndn->ndn_probes == max_probes) {
struct sk_buff *skb;
- neigh->nud_state = NUD_FAILED;
- neigh->flags |= NCF_INVALID;
+ ndn->ndn_nud_state = NUD_FAILED;
+ ndn->ndn_flags &= ~NTF_COMPLETE;
nd_stats.res_failed++;
- while((skb=skb_dequeue(&neigh->arp_queue)))
- {
+ while((skb=skb_dequeue(&ndn->neigh.arp_queue))) {
/*
* "The sender MUST return an ICMP
* destination unreachable"
*/
icmpv6_send(skb, ICMPV6_DEST_UNREACH,
- ICMPV6_ADDR_UNREACH, 0, neigh->dev);
+ ICMPV6_ADDR_UNREACH, 0, ndn->ndn_dev);
dev_kfree_skb(skb, FREE_WRITE);
}
return 0;
}
-
- neigh->probes++;
- dev = neigh->dev;
- target = &neigh->addr;
+ ndn->ndn_probes++;
- if (neigh->nud_state == NUD_INCOMPLETE)
- {
- addrconf_addr_solict_mult(&neigh->addr, &mcaddr);
- daddr = &mcaddr;
- neigh = NULL;
- }
- else
- {
- daddr = &neigh->addr;
+ dev = ndn->ndn_dev;
+ target = &ndn->ndn_addr;
+
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE) {
+ addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
+ daddr = &mcaddr;
+ ndn = NULL;
+ } else {
+ daddr = &ndn->ndn_addr;
}
- ndisc_send_ns(dev, neigh, target, daddr, NULL);
+ ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
- return nd_retrans_timer;
+ return ipv6_config.nd_retrans_time;
}
void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
{
- unsigned long now = jiffies;
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
struct in6_addr daddr;
+ unsigned long now = jiffies;
struct in6_addr *saddr = NULL;
- switch (neigh->nud_state) {
+ if ((ndn->ndn_flags & NCF_NOARP))
+ return;
+
+ switch (ndn->ndn_nud_state) {
case NUD_FAILED:
- neigh->probes = 0;
+ ndn->ndn_probes = 0;
case NUD_NONE:
-
- if (skb && !skb->stamp.tv_sec)
- {
+ if (skb && !skb->stamp.tv_sec) {
/*
* skb->stamp allows us to know if we are
* originating the skb or forwarding it.
* (it is set on netif_rx)
*/
- saddr = &skb->ipv6_hdr->saddr;
+ saddr = &skb->nh.ipv6h->saddr;
}
- neigh->nud_state = NUD_INCOMPLETE;
- addrconf_addr_solict_mult(&neigh->addr, &daddr);
- ndisc_send_ns(neigh->dev, NULL, &neigh->addr, &daddr, saddr);
- ndisc_add_timer(neigh, nd_retrans_timer);
+ ndn->ndn_nud_state = NUD_INCOMPLETE;
+ addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
+ ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
+ saddr);
+ ndisc_add_timer(ndn, ipv6_config.nd_retrans_time);
break;
case NUD_REACHABLE:
- if (now - neigh->tstamp < nd_reachable_time)
+ if ((now - ndn->ndn_tstamp) < nd_reachable_time)
break;
case NUD_STALE:
- neigh->nud_state = NUD_DELAY;
- ndisc_add_timer(neigh, nd_delay_first_probe);
+ ndn->ndn_nud_state = NUD_DELAY;
+ ndisc_add_timer(ndn, ipv6_config.nd_delay_probe_time);
}
}
/*
* Received a neighbour announce
*/
-void ndisc_event_na(struct neighbour *neigh, unsigned char * opt, int opt_len,
+void ndisc_event_na(struct nd_neigh *ndn, unsigned char *opt, int opt_len,
int solicited, int override)
{
struct sk_buff *skb;
- if (neigh->nud_state == NUD_NONE)
- {
- neigh->nud_state = NUD_INCOMPLETE;
- }
+ NDBG(("ndisc_event_na(%p,%p,%d,%d,%d)\n", ndn, opt, opt_len,
+ solicited, override));
- if (neigh->nud_state == NUD_INCOMPLETE || override)
- {
+ if (ndn->ndn_nud_state == NUD_NONE)
+ ndn->ndn_nud_state = NUD_INCOMPLETE;
- if (opt_len == 0)
- {
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE || override) {
+ if (opt_len == 0) {
printk(KERN_DEBUG "no opt on NA\n");
- }
- else
- {
- /* record hardware address */
-
- neigh->h_dest = neigh->hh_data;
- neigh->flags &= ~NCF_HHVALID;
+ } else {
+ /* Record hardware address. */
+ ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(neigh->dev, opt, opt_len,
- neigh->h_dest,
- ND_OPT_TARGET_LL_ADDR))
- {
+ if (ndisc_store_hwaddr(ndn, opt, opt_len,
+ ND_OPT_TARGET_LL_ADDR)) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG
"event_na: invalid TARGET_LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
+#endif
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
return;
}
}
}
+ if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE) {
+ ndn->ndn_probes = 0;
+ ndn->ndn_tstamp = jiffies;
- if (solicited || override || neigh->nud_state == NUD_INCOMPLETE)
- {
-
- neigh->probes = 0;
- neigh->tstamp = jiffies;
-
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
if (solicited)
- {
- neigh->nud_state = NUD_REACHABLE;
- }
+ ndn->ndn_nud_state = NUD_REACHABLE;
else
- {
- neigh->nud_state = NUD_STALE;
- }
+ ndn->ndn_nud_state = NUD_STALE;
}
- while ((skb=skb_dequeue(&neigh->arp_queue)))
- {
- int priority = SOPRI_NORMAL;
-
- if (skb->sk)
- priority = skb->sk->priority;
-
- dev_queue_xmit(skb, neigh->dev, priority);
- }
+ while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
+ dev_queue_xmit(skb);
}
-static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
+static struct nd_neigh * ndisc_event_ns(struct in6_addr *saddr,
+ struct sk_buff *skb)
{
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
u8 *opt;
int len;
+ NDBG(("ndisc_event_ns: "));
+ if(saddr)
+ NDBG(("saddr[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x] ",
+ saddr->s6_addr16[0], saddr->s6_addr16[1], saddr->s6_addr16[2],
+ saddr->s6_addr16[3], saddr->s6_addr16[4], saddr->s6_addr16[5],
+ saddr->s6_addr16[6], saddr->s6_addr16[7]));
+ NDBG(("\n"));
+
opt = skb->h.raw;
- opt += sizeof(struct icmpv6hdr) + sizeof(struct in6_addr);
+ opt += sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
len = skb->tail - opt;
- neigh = ndisc_retrieve_neigh(skb->dev, saddr);
-
- if (neigh == NULL)
- {
- neigh = ndisc_new_neigh(skb->dev, saddr);
- }
-
- switch(neigh->nud_state) {
- case NUD_REACHABLE:
- case NUD_STALE:
- case NUD_DELAY:
- if (*opt != ND_OPT_SOURCE_LL_ADDR ||
- len != neigh->dev->addr_len ||
- memcmp(neigh->h_dest, opt + 2, len))
- {
- break;
- }
-
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
- default:
- neigh->flags &= ~NCF_HHVALID;
- neigh->h_dest = neigh->hh_data;
-
- if (ndisc_store_hwaddr(neigh->dev, opt, len,
- neigh->h_dest,
- ND_OPT_SOURCE_LL_ADDR))
- {
- printk(KERN_DEBUG
- "event_ns: invalid SOURCE_LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
- return;
- }
-
- neigh->nud_state = NUD_STALE;
- neigh->tstamp = jiffies;
- neigh->probes = 0;
- }
-
-}
-
-static struct rt6_info *ndisc_get_dflt_router(struct device *dev,
- struct in6_addr *addr)
-{
- struct rt6_info *iter;
-
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (dev == iter->rt_dev &&
- ipv6_addr_cmp(&iter->rt_dst, addr) == 0)
- {
- return iter;
- }
- }
- return NULL;
-}
+ neigh_table_lock(&nd_tbl);
+
+ ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
+ sizeof(struct in6_addr),
+ skb->dev);
-static void ndisc_add_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter;
+ if (ndn == NULL)
+ ndn = ndisc_new_neigh(skb->dev, saddr);
- rt->rt_ref++;
- rt->fib_node = &routing_table;
- rt6_stats.fib_rt_alloc++;
+ neigh_table_unlock(&nd_tbl);
- if (default_rt_list == NULL)
- {
- default_rt_list = rt;
- return;
- }
+ if (ndn == NULL)
+ return NULL;
- for (iter = default_rt_list; iter->next; iter=iter->next)
- ;
+ switch(ndn->ndn_nud_state) {
+ case NUD_REACHABLE:
+ case NUD_STALE:
+ case NUD_DELAY:
+ if (*opt != ND_OPT_SOURCE_LL_ADDR ||
+ len != ndn->ndn_dev->addr_len ||
+ memcmp(ndn->neigh.ha, opt + 2, len))
+ break;
- iter->next = rt;
-}
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
-static void ndisc_del_dflt_router(struct rt6_info *rt)
-{
- struct rt6_info *iter, *back;
+ /* FALLTHROUGH */
+ default:
+ ndn->ndn_flags |= NTF_COMPLETE;
+
+ if (ndisc_store_hwaddr(ndn, opt, len, ND_OPT_SOURCE_LL_ADDR)) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG
+ "event_ns: invalid SOURCE_LL_ADDR\n");
+#endif
- if (rt == default_rt_list)
- {
- default_rt_list = rt->next;
- }
- else
- {
- back = NULL;
- for (iter = default_rt_list; iter; iter=iter->next)
- {
- if (iter == rt)
- {
- back->next = rt->next;
- break;
- }
- back = iter;
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
+ return ndn;
}
- }
- rt->fib_node = NULL;
- rt_release(rt);
-}
+ ndn->ndn_nud_state = NUD_STALE;
+ ndn->ndn_tstamp = jiffies;
+ ndn->ndn_probes = 0;
+ };
-static void ndisc_purge_dflt_routers(void)
-{
- struct rt6_info *iter, *rt;
-
- for (iter = default_rt_list; iter; )
- {
- rt = iter;
- iter=iter->next;
- rt_release(rt);
- }
- default_rt_list = NULL;
+ return ndn;
}
-static void ndisc_ll_addr_update(struct neighbour *neigh, u8* opt, int len,
+
+static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
int type)
{
- switch(neigh->nud_state) {
+ switch(ndn->ndn_nud_state) {
case NUD_REACHABLE:
case NUD_STALE:
case NUD_DELAY:
- if (len == neigh->dev->addr_len &&
- memcmp(neigh->h_dest, opt + 2, len) == 0)
- {
+ if (len == ndn->ndn_dev->addr_len &&
+ memcmp(ndn->neigh.ha, opt + 2, len) == 0)
break;
- }
- if (neigh->nud_state & NUD_IN_TIMER)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state & NUD_IN_TIMER)
+ ndisc_del_timer(ndn);
default:
- neigh->flags &= ~NCF_HHVALID;
- neigh->h_dest = neigh->hh_data;
+ ndn->ndn_flags |= NTF_COMPLETE;
- if (ndisc_store_hwaddr(neigh->dev, opt, len, neigh->h_dest,
- type))
- {
+ if (ndisc_store_hwaddr(ndn, opt, len, type)) {
+#if ND_DEBUG >=1
printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
- neigh->h_dest = NULL;
- neigh->nud_state = NUD_NONE;
+#endif
+ ndn->ndn_flags &= ~NTF_COMPLETE;
+ ndn->ndn_nud_state = NUD_NONE;
break;
}
- neigh->nud_state = NUD_STALE;
- neigh->tstamp = jiffies;
- neigh->probes = 0;
- }
-
-}
-
-struct rt6_info * dflt_rt_lookup(void)
-{
- struct rt6_info *match = NULL;
- struct rt6_info *rt;
- int score = -1;
- unsigned long now = jiffies;
-
- for (rt = default_rt_list; rt; rt=rt->next)
- {
- struct neighbour *neigh = rt->rt_nexthop;
-
- if (score < 0)
- {
- score = 0;
- match = rt;
- }
-
- if (neigh->nud_state == NUD_REACHABLE)
- {
- if (score < 1)
- {
- score = 1;
- match = rt;
- }
-
- if (now - neigh->tstamp < nd_reachable_time)
- {
- return rt;
- }
- }
-
- }
-
- return match;
+ ndn->ndn_nud_state = NUD_STALE;
+ ndn->ndn_tstamp = jiffies;
+ ndn->ndn_probes = 0;
+ };
}
static void ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct inet6_dev *in6_dev;
struct rt6_info *rt;
int lifetime;
@@ -1256,11 +1052,12 @@ static void ndisc_router_discovery(struct sk_buff *skb)
__u8 * opt = (__u8 *)(ra_msg + 1);
+ NDBG(("ndisc_router_discovery(%p)\n", skb));
+
optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
- if (skb->ipv6_hdr->hop_limit != 255)
- {
- printk(KERN_WARNING
+ if (skb->nh.ipv6h->hop_limit != 255) {
+ printk(KERN_INFO
"NDISC: fake router advertisment received\n");
return;
}
@@ -1270,14 +1067,12 @@ static void ndisc_router_discovery(struct sk_buff *skb)
*/
in6_dev = ipv6_get_idev(skb->dev);
- if (in6_dev == NULL)
- {
+ if (in6_dev == NULL) {
printk(KERN_DEBUG "RA: can't find in6 device\n");
return;
}
- if (in6_dev->if_flags & IF_RS_SENT)
- {
+ if (in6_dev->if_flags & IF_RS_SENT) {
/*
* flag that an RA was received after an RS was sent
* out on this interface.
@@ -1287,81 +1082,57 @@ static void ndisc_router_discovery(struct sk_buff *skb)
lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
- rt = ndisc_get_dflt_router(skb->dev, &skb->ipv6_hdr->saddr);
+ rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- if (rt && lifetime == 0)
- {
- ndisc_del_dflt_router(rt);
+ if (rt && lifetime == 0) {
+ ip6_del_rt(rt);
rt = NULL;
}
- if (rt == NULL && lifetime)
- {
- printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
-
- rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
- GFP_ATOMIC);
- if (rt)
- {
- neigh = ndisc_retrieve_neigh(skb->dev,
- &skb->ipv6_hdr->saddr);
-
- if (neigh == NULL)
- {
- neigh = ndisc_new_neigh(skb->dev,
- &skb->ipv6_hdr->saddr);
- }
+ if (rt == NULL && lifetime) {
+#if ND_DEBUG >= 2
+ printk(KERN_DEBUG "ndisc_rdisc: adding default router\n");
+#endif
- if (neigh)
- {
- atomic_inc(&neigh->refcnt);
- neigh->flags |= NCF_ROUTER;
-
- memset(rt, 0, sizeof(struct rt6_info));
+ rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
- ipv6_addr_copy(&rt->rt_dst,
- &skb->ipv6_hdr->saddr);
- rt->rt_metric = 1;
- rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
- rt->rt_dev = skb->dev;
- rt->rt_nexthop = neigh;
+ if (rt == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "route_add failed\n");
+#endif
+ return;
+ }
- ndisc_add_dflt_router(rt);
- }
- else
- {
- kfree(rt);
- }
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+ if (ndn == NULL) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "nd: add default router: null "
+ "neighbour\n");
+#endif
+ return;
}
+ ndn->ndn_flags |= NCF_ROUTER;
}
if (rt)
- {
- rt->rt_expires = jiffies + (HZ * lifetime);
- }
+ rt->rt6i_expires = jiffies + (HZ * lifetime);
if (ra_msg->icmph.icmp6_hop_limit)
- {
- ipv6_hop_limit = ra_msg->icmph.icmp6_hop_limit;
- }
+ ipv6_config.hop_limit = ra_msg->icmph.icmp6_hop_limit;
/*
* Update Reachable Time and Retrans Timer
*/
if (ra_msg->retrans_timer)
- {
- nd_retrans_timer = ntohl(ra_msg->retrans_timer);
- }
+ ipv6_config.nd_retrans_time = ntohl(ra_msg->retrans_timer);
- if (ra_msg->reachable_time)
- {
+ if (ra_msg->reachable_time) {
__u32 rtime = ntohl(ra_msg->reachable_time);
- if (rtime != nd_base_reachable_time)
- {
- nd_base_reachable_time = rtime;
- nd_gc_staletime = 3 * nd_base_reachable_time;
+ if (rtime != ipv6_config.nd_base_reachable_time) {
+ ipv6_config.nd_base_reachable_time = rtime;
+ nd_gc_staletime = 3 * rtime;
nd_reachable_time = rand_reach_time();
}
@@ -1376,22 +1147,22 @@ static void ndisc_router_discovery(struct sk_buff *skb)
len = (opt[1] << 3);
- if (len == 0)
- {
+ if (len == 0) {
printk(KERN_DEBUG "RA: opt has 0 len\n");
break;
}
switch(*opt) {
case ND_OPT_SOURCE_LL_ADDR:
-
+
if (rt == NULL)
break;
- neigh = rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
- ndisc_ll_addr_update(neigh, opt, len,
- ND_OPT_SOURCE_LL_ADDR);
+ if (ndn)
+ ndisc_ll_addr_update(ndn, opt, len,
+ ND_OPT_SOURCE_LL_ADDR);
break;
case ND_OPT_PREFIX_INFO:
@@ -1399,17 +1170,17 @@ static void ndisc_router_discovery(struct sk_buff *skb)
break;
case ND_OPT_MTU:
-
- if (rt)
- {
+ if (rt) {
int mtu;
struct device *dev;
mtu = htonl(*(__u32 *)(opt+4));
- dev = rt->rt_nexthop->dev;
+ dev = rt->rt6i_dev;
+
+ if (dev == NULL)
+ break;
- if (mtu < 576)
- {
+ if (mtu < 576) {
printk(KERN_DEBUG "NDISC: router "
"announcement with mtu = %d\n",
mtu);
@@ -1417,13 +1188,9 @@ static void ndisc_router_discovery(struct sk_buff *skb)
}
if (dev->change_mtu)
- {
dev->change_mtu(dev, mtu);
- }
else
- {
dev->mtu = mtu;
- }
}
break;
@@ -1433,109 +1200,100 @@ static void ndisc_router_discovery(struct sk_buff *skb)
break;
default:
printk(KERN_DEBUG "unkown option in RA\n");
- }
+ };
optlen -= len;
opt += len;
}
-
}
void ndisc_forwarding_on(void)
{
+
/*
- * forwarding was turned on
+ * Forwarding was turned on.
*/
- ndisc_purge_dflt_routers();
+ rt6_purge_dflt_routers(0);
}
void ndisc_forwarding_off(void)
{
/*
- * forwarding was turned off
+ * Forwarding was turned off.
*/
}
static void ndisc_redirect_rcv(struct sk_buff *skb)
{
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *dest;
struct in6_addr *target; /* new first hop to destination */
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct rt6_info *rt;
int on_link = 0;
int optlen;
u8 * opt;
- if (skb->ipv6_hdr->hop_limit != 255)
- {
+ NDBG(("ndisc_redirect_rcv(%p)\n", skb));
+
+ if (skb->nh.ipv6h->hop_limit != 255) {
printk(KERN_WARNING
"NDISC: fake ICMP redirect received\n");
return;
}
- if (!(ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL))
- {
+ if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: source address is not linklocal\n");
return;
}
optlen = skb->tail - skb->h.raw;
- optlen -= sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
- if (optlen < 0)
- {
+ if (optlen < 0) {
printk(KERN_WARNING "ICMP redirect: packet too small\n");
return;
}
- icmph = (struct icmpv6hdr *) skb->h.raw;
+ icmph = (struct icmp6hdr *) skb->h.raw;
target = (struct in6_addr *) (icmph + 1);
dest = target + 1;
- if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST)
- {
+ if (ipv6_addr_type(dest) & IPV6_ADDR_MULTICAST) {
printk(KERN_WARNING "ICMP redirect for multicast addr\n");
return;
}
- if (ipv6_addr_cmp(dest, target) == 0)
- {
+ if (ipv6_addr_cmp(dest, target) == 0) {
on_link = 1;
- }
- else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL))
- {
+ } else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {
printk(KERN_WARNING
"ICMP redirect: target address is not linklocal\n");
return;
}
/* passed validation tests */
+ rt = rt6_redirect(dest, &skb->nh.ipv6h->saddr, target, skb->dev,
+ on_link);
- rt = ipv6_rt_redirect(skb->dev, dest, target, on_link);
-
- if (rt == NULL)
- {
+ if (rt == NULL) {
printk(KERN_WARNING "ICMP redirect: no route to host\n");
return;
}
- neigh = rt->rt_nexthop;
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
opt = (u8 *) (dest + 1);
- while (optlen > 0)
- {
+ while (optlen > 0) {
int len;
len = (opt[1] << 3);
if (*opt == ND_OPT_TARGET_LL_ADDR)
- {
- ndisc_ll_addr_update(neigh, opt, len,
+ ndisc_ll_addr_update(ndn, opt, len,
ND_OPT_TARGET_LL_ADDR);
- }
opt += len;
optlen -= len;
@@ -1545,12 +1303,14 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
struct in6_addr *target)
{
- struct sock *sk = (struct sock *) ndisc_socket.data;
- int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
+ struct sock *sk = ndisc_socket->sk;
+ int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
struct sk_buff *buff;
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
struct inet6_ifaddr *ifp;
- struct icmpv6hdr *icmph;
+ struct icmp6hdr *icmph;
struct in6_addr *addrp;
+ struct device *dev;
struct rt6_info *rt;
int ta_len = 0;
u8 *opt;
@@ -1558,53 +1318,68 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
int err;
int hlen;
- rt = fibv6_lookup(&skb->ipv6_hdr->saddr, skb->dev, 0);
-
- if (rt->rt_flags & RTF_GATEWAY)
- {
+ dev = skb->dev;
+ rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+#if ND_DEBUG >= 1
+ printk(KERN_DEBUG "ndisc_send_redirect: hostunreach\n");
+#endif
+ return;
+ }
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
+#endif
return;
}
- if (neigh->nud_state == NUD_REACHABLE)
- {
- ta_len = ((neigh->dev->addr_len + 1) >> 3) + 1;
+ if (ndn->ndn_nud_state == NUD_REACHABLE) {
+ ta_len = ((dev->addr_len + 1) >> 3) + 1;
len += (ta_len << 3);
}
- rd_len = min(536 - len, ntohs(skb->ipv6_hdr->payload_len) + 8);
+ rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
rd_len &= ~0x7;
len += rd_len;
- ifp = ipv6_get_lladdr(skb->dev);
+ ifp = ipv6_get_lladdr(dev);
- if (ifp == NULL)
- {
+ if (ifp == NULL) {
+#if ND_DEBUG >= 1
printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
+#endif
return;
}
- buff = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
-
- if (buff == NULL)
- {
+ buff = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ 0, 0, &err);
+ if (buff == NULL) {
+#if ND_DEBUG >= 2
printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
+#endif
return;
}
-
hlen = 0;
- if (skb->dev->hard_header_len)
- {
- hlen = (skb->dev->hard_header_len + 15) & ~15;
- }
- skb_reserve(buff, hlen + sizeof(struct ipv6hdr));
+ if (dev->hard_header_len) {
+ skb_reserve(buff, (dev->hard_header_len + 15) & ~15);
+ if (dev->hard_header) {
+ dev->hard_header(buff, dev, ETH_P_IPV6, ndn->ndn_ha,
+ NULL, len);
+ buff->arp = 1;
+ }
+ }
- icmph = (struct icmpv6hdr *) skb_put(buff, len);
+ ip6_nd_hdr(sk, buff, dev, &ifp->addr, &skb->nh.ipv6h->saddr,
+ IPPROTO_ICMPV6, len);
- memset(icmph, 0, sizeof(struct icmpv6hdr));
- icmph->type = NDISC_REDIRECT;
+ icmph = (struct icmp6hdr *) skb_put(buff, len);
+
+ memset(icmph, 0, sizeof(struct icmp6hdr));
+ icmph->icmp6_type = NDISC_REDIRECT;
/*
* copy target and destination addresses
@@ -1613,7 +1388,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
addrp = (struct in6_addr *)(icmph + 1);
ipv6_addr_copy(addrp, target);
addrp++;
- ipv6_addr_copy(addrp, &skb->ipv6_hdr->daddr);
+ ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
opt = (u8*) (addrp + 1);
@@ -1621,14 +1396,13 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
* include target_address option
*/
- if (ta_len)
- {
+ if (ta_len) {
int zb;
*(opt++) = ND_OPT_TARGET_LL_ADDR;
*(opt++) = ta_len;
- memcpy(opt, neigh->h_dest, neigh->dev->addr_len);
+ memcpy(opt, neigh->ha, neigh->dev->addr_len);
opt += neigh->dev->addr_len;
/*
@@ -1637,8 +1411,7 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
*/
zb = (neigh->dev->addr_len + 2) & 0x7;
- if (zb)
- {
+ if (zb) {
int comp;
comp = 8 - zb;
@@ -1656,30 +1429,33 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
*(opt++) = (rd_len >> 3);
opt += 6;
- memcpy(opt, &skb->ipv6_hdr, rd_len - 8);
+ memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
- icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->ipv6_hdr->saddr,
- len, IPPROTO_ICMPV6,
- csum_partial((u8 *) icmph, len, 0));
+ icmph->icmp6_cksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
+ len, IPPROTO_ICMPV6,
+ csum_partial((u8 *) icmph, len, 0));
- ipv6_xmit(sk, buff, &ifp->addr, &skb->ipv6_hdr->saddr, NULL, IPPROTO_ICMPV6);
+ dev_queue_xmit(buff);
}
/* Called by upper layers to validate neighbour cache entries. */
void ndisc_validate(struct neighbour *neigh)
{
- if (neigh->nud_state == NUD_INCOMPLETE)
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+
+ if (neigh == NULL)
+ return;
+
+ if (ndn->ndn_nud_state == NUD_INCOMPLETE)
return;
- if (neigh->nud_state == NUD_DELAY)
- {
- ndisc_del_timer(neigh);
- }
+ if (ndn->ndn_nud_state == NUD_DELAY)
+ ndisc_del_timer(ndn);
nd_stats.rcv_upper_conf++;
- neigh->nud_state = NUD_REACHABLE;
- neigh->tstamp = jiffies;
+ ndn->ndn_nud_state = NUD_REACHABLE;
+ ndn->ndn_tstamp = jiffies;
}
int ndisc_rcv(struct sk_buff *skb, struct device *dev,
@@ -1687,29 +1463,33 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
struct ipv6_options *opt, unsigned short len)
{
struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
- struct neighbour *neigh;
+ struct nd_neigh *ndn;
struct inet6_ifaddr *ifp;
- switch (msg->icmph.type) {
+ NDBG(("ndisc_rcv(type=%d) ", msg->icmph.icmp6_type));
+ switch (msg->icmph.icmp6_type) {
case NDISC_NEIGHBOUR_SOLICITATION:
- if ((ifp = ipv6_chk_addr(&msg->target)))
- {
+ NDBG(("NS "));
+ if ((ifp = ipv6_chk_addr(&msg->target))) {
int addr_type;
- if (ifp->flags & DAD_INCOMPLETE)
- {
+ if (ifp->flags & DAD_INCOMPLETE) {
/*
- * DAD failed
+ * DAD failed
+ */
+
+ /* XXX Check if this came in over same interface
+ * XXX we just sent an NS from! That is valid! -DaveM
*/
- printk(KERN_DEBUG "duplicate address\n");
+ printk(KERN_DEBUG "%s: duplicate address\n",
+ ifp->idev->dev->name);
del_timer(&ifp->timer);
return 0;
}
addr_type = ipv6_addr_type(saddr);
- if (addr_type & IPV6_ADDR_UNICAST)
- {
+ if (addr_type & IPV6_ADDR_UNICAST) {
int inc;
/*
@@ -1718,129 +1498,137 @@ int ndisc_rcv(struct sk_buff *skb, struct device *dev,
*/
nd_stats.rcv_probes_ucast++;
- ndisc_event_ns(saddr, skb);
- /* answer solicitation */
- neigh = ndisc_retrieve_neigh(dev, saddr);
+ ndn = ndisc_event_ns(saddr, skb);
+
+ if (ndn == NULL)
+ return 0;
inc = ipv6_addr_type(daddr);
inc &= IPV6_ADDR_MULTICAST;
- ndisc_send_na(dev, neigh, saddr, &ifp->addr,
+ ndisc_send_na(dev, ndn, saddr, &ifp->addr,
ifp->idev->router, 1, inc, inc);
- }
- else
- {
+ } else {
+#if ND_DEBUG >= 1
/* FIXME */
printk(KERN_DEBUG "ns: non unicast saddr\n");
+#endif
}
}
break;
case NDISC_NEIGHBOUR_ADVERTISEMENT:
-
- neigh = ndisc_retrieve_neigh(skb->dev, &msg->target);
- if (neigh)
- {
- if (neigh->flags & NCF_ROUTER)
- {
- if (msg->icmph.icmp6_router == 0)
- {
+ NDBG(("NA "));
+ neigh_table_lock(&nd_tbl);
+ ndn = (struct nd_neigh *)
+ neigh_lookup(&nd_tbl, (void *) &msg->target,
+ sizeof(struct in6_addr), skb->dev);
+ neigh_table_unlock(&nd_tbl);
+
+ if (ndn) {
+ if (ndn->ndn_flags & NCF_ROUTER) {
+ if (msg->icmph.icmp6_router == 0) {
/*
* Change: router to host
*/
-
+#if 0
struct rt6_info *rt;
rt = ndisc_get_dflt_router(skb->dev,
saddr);
if (rt)
- {
ndisc_del_dflt_router(rt);
- }
+#endif
}
- }
- else
- {
+ } else {
if (msg->icmph.icmp6_router)
- {
- neigh->flags |= NCF_ROUTER;
- }
+ ndn->ndn_flags |= NCF_ROUTER;
}
- ndisc_event_na(neigh, (unsigned char *) &msg->opt,
+ ndisc_event_na(ndn, (unsigned char *) &msg->opt,
skb->tail - (u8 *)&msg->opt /*opt_len*/,
msg->icmph.icmp6_solicited,
msg->icmph.icmp6_override);
}
break;
- }
+ };
- if (ipv6_forwarding == 0)
- {
- switch (msg->icmph.type) {
+ if (ipv6_config.forwarding == 0) {
+ switch (msg->icmph.icmp6_type) {
case NDISC_ROUTER_ADVERTISEMENT:
- ndisc_router_discovery(skb);
+ NDBG(("RA "));
+ if (ipv6_config.accept_ra)
+ ndisc_router_discovery(skb);
break;
case NDISC_REDIRECT:
- ndisc_redirect_rcv(skb);
+ NDBG(("REDIR "));
+ if (ipv6_config.accept_redirects)
+ ndisc_redirect_rcv(skb);
break;
- }
+ };
}
return 0;
}
+#ifdef CONFIG_PROC_FS
int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
int dummy)
{
- struct neighbour *neigh;
unsigned long now = jiffies;
int len = 0;
int i;
- atomic_inc(&ndisc_lock);
+ neigh_table_lock(&nd_tbl);
- for (i = 0; i < NCACHE_NUM_BUCKETS; i++)
- {
- for(neigh = neighbours[i]; neigh; neigh=neigh->next)
- {
+ for (i = 0; i < nd_tbl.tbl_size; i++) {
+ struct neighbour *neigh, *head;
+ head = nd_tbl.hash_buckets[i];
+
+ if ((neigh = head) == NULL)
+ continue;
+
+ do {
+ struct nd_neigh *ndn = (struct nd_neigh *) neigh;
int j;
- for (j=0; j<16; j++)
- {
+ for (j=0; j<16; j++) {
sprintf(buffer + len, "%02x",
- neigh->addr.s6_addr[j]);
+ ndn->ndn_addr.s6_addr[j]);
len += 2;
}
len += sprintf(buffer + len,
- " %02x %02x %08lx %08lx %04x %04x ",
- i,
- neigh->nud_state,
- neigh->expires - now,
- now - neigh->tstamp,
- neigh->refcnt,
- neigh->flags);
-
- if (neigh->h_dest)
- {
- for (j=0; j< neigh->dev->addr_len; j++)
- {
+ " %02x %02x %02x %02x %08lx %08lx %08lx %04x %04x %04lx %8s ", i,
+ ndn->ndn_plen,
+ ndn->ndn_type,
+ ndn->ndn_nud_state,
+ ndn->ndn_expires ? ndn->ndn_expires - now : 0,
+ now - ndn->ndn_tstamp,
+ nd_reachable_time,
+ nd_gc_staletime,
+ atomic_read(&ndn->ndn_refcnt),
+ ndn->ndn_flags,
+ ndn->ndn_dev ? ndn->ndn_dev->name : "NULLDEV");
+
+ if ((ndn->ndn_flags & NTF_COMPLETE)) {
+ for (j=0; j< neigh->dev->addr_len; j++) {
sprintf(buffer + len, "%02x",
- neigh->h_dest[j]);
+ neigh->ha[j]);
len += 2;
}
- }
- else
+ } else {
len += sprintf(buffer + len, "000000000000");
+ }
len += sprintf(buffer + len, "\n");
-
- }
+
+ neigh = neigh->next;
+ } while (neigh != head);
}
- ndisc_release_lock();
-
+ neigh_table_unlock(&nd_tbl);
+
*start = buffer + offset;
len -= offset;
@@ -1852,42 +1640,44 @@ int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
struct proc_dir_entry ndisc_proc_entry =
{
- 0, 11, "ndisc_cache",
+ PROC_NET_NDISC, 5, "ndisc",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL,
&ndisc_get_info
};
+#endif /* CONFIG_PROC_FS */
-void ndisc_init(struct proto_ops *ops)
+void ndisc_init(struct net_proto_family *ops)
{
struct sock *sk;
- int i = 0;
int err;
- /*
- * Init ndisc_socket
- */
- ndisc_socket.type=SOCK_RAW;
- ndisc_socket.ops=ops;
+ ndisc_inode.i_mode = S_IFSOCK;
+ ndisc_inode.i_sock = 1;
+ ndisc_inode.i_uid = 0;
+ ndisc_inode.i_gid = 0;
+
+ ndisc_socket->inode = &ndisc_inode;
+ ndisc_socket->state = SS_UNCONNECTED;
+ ndisc_socket->type=SOCK_RAW;
- if((err=ops->create(&ndisc_socket, IPPROTO_ICMPV6))<0)
+ if((err=ops->create(ndisc_socket, IPPROTO_ICMPV6))<0)
printk(KERN_DEBUG
"Failed to create the NDISC control socket.\n");
MOD_DEC_USE_COUNT;
- sk = ndisc_socket.data;
+ sk = ndisc_socket->sk;
sk->allocation = GFP_ATOMIC;
sk->net_pinfo.af_inet6.hop_limit = 255;
sk->net_pinfo.af_inet6.priority = 15;
- sk->num = 256; /* Don't receive any data */
+ sk->num = 256;
/*
- * Initialize the neighbours hash buckets.
+ * Initialize the neighbour table
*/
-
- for (; i < NCACHE_NUM_BUCKETS; i++)
- neighbours[i] = NULL;
+
+ neigh_table_init(&nd_tbl, &nd_neigh_ops, NCACHE_NUM_BUCKETS);
/* General ND state machine timer. */
init_timer(&ndisc_timer);
@@ -1897,31 +1687,24 @@ void ndisc_init(struct proto_ops *ops)
/* ND GC timer */
init_timer(&ndisc_gc_timer);
- ndisc_gc_timer.function = ndisc_garbage_collect;
+ ndisc_gc_timer.function = ndisc_periodic_timer;
ndisc_gc_timer.data = 0L;
ndisc_gc_timer.expires = jiffies + nd_gc_interval;
add_timer(&ndisc_gc_timer);
-#ifdef CONFIG_IPV6_MODULE
- ndisc_eth_hook = ndisc_eth_resolv;
- proc_register_dynamic(&proc_net, &ndisc_proc_entry);
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&ndisc_proc_entry);
#endif
}
-#ifdef CONFIG_IPV6_MODULE
+#ifdef MODULE
void ndisc_cleanup(void)
{
- ndisc_eth_hook = NULL;
- proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(ndisc_proc_entry.low_ino);
+#endif
del_timer(&ndisc_gc_timer);
del_timer(&ndisc_timer);
}
#endif
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o ndisc.o ndisc.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
new file mode 100644
index 000000000..b9b811e35
--- /dev/null
+++ b/net/ipv6/proc.c
@@ -0,0 +1,143 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * This file implements the various access functions for the
+ * PROC file system. This is very similar to the IPv4 version,
+ * except it reports the sockets in the INET6 address family.
+ *
+ * Version: $Id: proc.c,v 1.4 1997/04/20 22:50:44 schenk Exp $
+ *
+ * Authors: David S. Miller (davem@caip.rutgers.edu)
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/net.h>
+#include <linux/in6.h>
+#include <net/sock.h>
+#include <net/transp_v6.h>
+
+/* This is the main implementation workhorse of all these routines. */
+static int get__netinfo6(struct proto *pro, char *buffer, int format, char **start,
+ off_t offset, int length)
+{
+ struct sock *sp;
+ struct tcp_opt *tp;
+ int timer_active, timer_active1, timer_active2;
+ unsigned long timer_expires;
+ struct in6_addr *dest, *src;
+ unsigned short destp, srcp;
+ int len = 0, i = 0;
+ off_t pos = 0;
+ off_t begin;
+ char tmpbuf[150];
+
+ if(offset < 149)
+ len += sprintf(buffer, "%-148s\n",
+ " sl " /* 6 */
+ "local_address " /* 38 */
+ "remote_address " /* 38 */
+ "st tx_queue rx_queue tr tm->when retrnsmt" /* 41 */
+ " uid timeout inode"); /* 21 */
+ /*----*/
+ /*144 */
+
+ pos = 149;
+ SOCKHASH_LOCK();
+ sp = pro->sklist_next;
+ while(sp != (struct sock *)pro) {
+ pos += 149;
+ if(pos < offset)
+ goto next;
+ tp = &(sp->tp_pinfo.af_tcp);
+ dest = &sp->net_pinfo.af_inet6.daddr;
+ src = &sp->net_pinfo.af_inet6.rcv_saddr;
+ destp = ntohs(sp->dummy_th.dest);
+ srcp = ntohs(sp->dummy_th.source);
+
+ timer_active1 = del_timer(&tp->retransmit_timer);
+ timer_active2 = del_timer(&sp->timer);
+ if(!timer_active1) tp->retransmit_timer.expires = 0;
+ if(!timer_active2) sp->timer.expires = 0;
+ timer_active = 0;
+ timer_expires = (unsigned) -1;
+ if(timer_active1 && tp->retransmit_timer.expires < timer_expires) {
+ timer_active = timer_active1;
+ timer_expires = tp->retransmit_timer.expires;
+ }
+ if(timer_active2 && sp->timer.expires < timer_expires) {
+ timer_active = timer_active2;
+ timer_expires = sp->timer.expires;
+ }
+ sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
+ "%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+ i,
+ src->s6_addr32[0], src->s6_addr32[1],
+ src->s6_addr32[2], src->s6_addr32[3], srcp,
+ dest->s6_addr32[0], dest->s6_addr32[1],
+ dest->s6_addr32[2], dest->s6_addr32[3], destp,
+ sp->state,
+ format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc),
+ format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+ timer_active, timer_expires-jiffies,
+ tp->retransmits,
+ sp->socket ? sp->socket->inode->i_uid:0,
+ timer_active?sp->timeout:0,
+ sp->socket ? sp->socket->inode->i_ino:0);
+
+ if(timer_active1) add_timer(&tp->retransmit_timer);
+ if(timer_active2) add_timer(&sp->timer);
+ len += sprintf(buffer+len, "%-148s\n", tmpbuf);
+ if(len >= length)
+ break;
+ next:
+ sp = sp->sklist_next;
+ i++;
+ }
+ SOCKHASH_UNLOCK();
+
+ begin = len - (pos - offset);
+ *start = buffer + begin;
+ len -= begin;
+ if(len > length)
+ len = length;
+ return len;
+}
+
+/* These get exported and registered with procfs in af_inet6.c at init time. */
+int tcp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&tcpv6_prot, buffer, 0, start, offset, length);
+}
+
+int udp6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&udpv6_prot, buffer, 1, start, offset, length);
+}
+
+int raw6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return get__netinfo6(&rawv6_prot, buffer, 1, start, offset, length);
+}
+
+int afinet6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len = 0;
+ len += sprintf(buffer+len, "TCP6: inuse %d highest %d\n",
+ tcpv6_prot.inuse, tcpv6_prot.highestinuse);
+ len += sprintf(buffer+len, "UDP6: inuse %d highest %d\n",
+ udpv6_prot.inuse, udpv6_prot.highestinuse);
+ len += sprintf(buffer+len, "RAW6: inuse %d highest %d\n",
+ rawv6_prot.inuse, rawv6_prot.highestinuse);
+ *start = buffer + offset;
+ len -= offset;
+ if(len > length)
+ len = length;
+ return len;
+}
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 7ba6f5be1..3ec242adb 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -1,3 +1,20 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * AF_INET6 protocol dispatch tables.
+ *
+ * Version: $Id: protocol.c,v 1.5 1997/03/18 18:24:44 davem Exp $
+ *
+ * Authors: Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -27,8 +44,7 @@ struct inet6_protocol *inet6_get_protocol(unsigned char prot)
struct inet6_protocol *p;
hash = prot & (MAX_INET_PROTOS - 1);
- for (p = inet6_protos[hash] ; p != NULL; p=p->next)
- {
+ for (p = inet6_protos[hash] ; p != NULL; p=p->next) {
if (p->protocol == prot)
return((struct inet6_protocol *) p);
}
@@ -41,7 +57,7 @@ void inet6_add_protocol(struct inet6_protocol *prot)
struct inet6_protocol *p2;
hash = prot->protocol & (MAX_INET_PROTOS - 1);
- prot ->next = inet6_protos[hash];
+ prot->next = inet6_protos[hash];
inet6_protos[hash] = prot;
prot->copy = 0;
@@ -50,10 +66,8 @@ void inet6_add_protocol(struct inet6_protocol *prot)
*/
p2 = (struct inet6_protocol *) prot->next;
- while(p2 != NULL)
- {
- if (p2->protocol == prot->protocol)
- {
+ while(p2 != NULL) {
+ if (p2->protocol == prot->protocol) {
prot->copy = 1;
break;
}
@@ -72,22 +86,19 @@ int inet6_del_protocol(struct inet6_protocol *prot)
unsigned char hash;
hash = prot->protocol & (MAX_INET_PROTOS - 1);
- if (prot == inet6_protos[hash])
- {
+ if (prot == inet6_protos[hash]) {
inet6_protos[hash] = (struct inet6_protocol *) inet6_protos[hash]->next;
return(0);
}
p = (struct inet6_protocol *) inet6_protos[hash];
- while(p != NULL)
- {
+ while(p != NULL) {
/*
* We have to worry if the protocol being deleted is
* the last one on the list, then we may need to reset
* someone's copied bit.
*/
- if (p->next != NULL && p->next == prot)
- {
+ if (p->next != NULL && p->next == prot) {
/*
* if we are the last one with this protocol and
* there is a previous one, reset its copy bit.
@@ -104,9 +115,3 @@ int inet6_del_protocol(struct inet6_protocol *prot)
}
return(-1);
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o protocol.o protocol.c"
- * End:
- */
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index bab6514d6..303649705 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -7,7 +7,7 @@
*
* Adapted from linux/net/ipv4/raw.c
*
- * $Id: raw.c,v 1.5 1996/10/29 22:45:53 roque Exp $
+ * $Id: raw.c,v 1.12 1997/04/01 02:23:34 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -29,34 +29,170 @@
#include <net/sock.h>
#include <net/snmp.h>
-#include <net/ip.h>
-#include <net/udp.h>
-
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/protocol.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/transp_v6.h>
+#include <net/rawv6.h>
+
#include <asm/uaccess.h>
+struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+static void raw_v6_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[num];
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (RAWV6_HTABLE_SIZE - 1);
+ skp = &raw_v6_htable[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = raw_v6_htable[num];
+ raw_v6_htable[num] = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+ struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+{
+ struct sock *s = sk;
+ int addr_type = ipv6_addr_type(loc_addr);
+
+ for(s = sk; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+
+ if (!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if (!ipv6_addr_any(&np->rcv_saddr)) {
+ if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return(s);
+ if ((addr_type & IPV6_ADDR_MULTICAST) &&
+ inet6_mc_check(s, loc_addr))
+ return (s);
+ continue;
+ }
+ return(s);
+ }
+ }
+ return NULL;
+}
+
+/* This cleans up af_inet6 a bit. -DaveM */
+static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+ struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+ __u32 v4addr = 0;
+ int addr_type;
+
+ /* Check these errors. */
+ if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+ return -EINVAL;
+
+ addr_type = ipv6_addr_type(&addr->sin6_addr);
+
+ /* Check if the address belongs to the host. */
+ if (addr_type == IPV6_ADDR_MAPPED) {
+ v4addr = addr->sin6_addr.s6_addr32[3];
+ if (__ip_chk_addr(v4addr) != IS_MYADDR)
+ return(-EADDRNOTAVAIL);
+ } else {
+ if (addr_type != IPV6_ADDR_ANY) {
+ /* ipv4 addr of the socket is invalid. Only the
+ * unpecified and mapped address have a v4 equivalent.
+ */
+ v4addr = LOOPBACK4_IPV6;
+ if (!(addr_type & IPV6_ADDR_MULTICAST)) {
+ if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+ return(-EADDRNOTAVAIL);
+ }
+ }
+ }
+
+ sk->rcv_saddr = v4addr;
+ sk->saddr = v4addr;
+ memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ if (!(addr_type & IPV6_ADDR_MULTICAST))
+ memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr,
+ sizeof(struct in6_addr));
+ return 0;
+}
+
void rawv6_err(struct sock *sk, int type, int code, unsigned char *buff,
struct in6_addr *saddr, struct in6_addr *daddr)
{
if (sk == NULL)
return;
-
}
static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
{
/* Charge it to the socket. */
-
- if (sock_queue_rcv_skb(sk,skb)<0)
- {
+ if (sock_queue_rcv_skb(sk,skb)<0) {
/* ip_statistics.IpInDiscards++; */
- skb->sk=NULL;
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -69,7 +205,7 @@ static inline int rawv6_rcv_skb(struct sock * sk, struct sk_buff * skb)
* This is next to useless...
* if we demultiplex in network layer we don't need the extra call
* just to queue the skb...
- * maybe we could have the network decide uppon an hint if it
+ * maybe we could have the network decide uppon a hint if it
* should call raw_rcv for demultiplexing
*/
int rawv6_rcv(struct sk_buff *skb, struct device *dev,
@@ -81,11 +217,9 @@ int rawv6_rcv(struct sk_buff *skb, struct device *dev,
sk = skb->sk;
if (sk->ip_hdrincl)
- {
- skb->h.raw = (unsigned char *) skb->ipv6_hdr;
- }
+ skb->h.raw = skb->nh.raw;
- if (sk->users) {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return 0;
}
@@ -131,26 +265,16 @@ int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
return err;
/* Copy the address. */
- if (sin6)
- {
+ if (sin6) {
sin6->sin6_family = AF_INET6;
- memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr,
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
*addr_len = sizeof(struct sockaddr_in6);
}
- if (msg->msg_control)
- {
- int err;
-
- err = datagram_recv_ctl(sk, msg, skb);
-
- if (err < 0)
- {
- copied = err;
- }
- }
+ if (msg->msg_controllen)
+ datagram_recv_ctl(sk, msg, skb);
skb_free_datagram(sk, skb);
return (copied);
@@ -186,8 +310,7 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
hdr->cksum = csum_partial_copy_fromiovecend(buff, hdr->iov, offset,
len, hdr->cksum);
- if (offset == 0)
- {
+ if (offset == 0) {
struct sock *sk;
struct raw6_opt *opt;
struct in6_addr *daddr;
@@ -196,26 +319,19 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
opt = &sk->tp_pinfo.tp_raw;
if (hdr->daddr)
- {
daddr = hdr->daddr;
- }
else
- {
daddr = addr + 1;
- }
hdr->cksum = csum_ipv6_magic(addr, daddr, hdr->len,
hdr->proto, hdr->cksum);
- if (opt->offset < len)
- {
+ if (opt->offset < len) {
__u16 *csum;
csum = (__u16 *) (buff + opt->offset);
*csum = hdr->cksum;
- }
- else
- {
+ } else {
/*
* FIXME
* signal an error to user via sk->err
@@ -227,8 +343,7 @@ static int rawv6_frag_cksum(const void *data, struct in6_addr *addr,
}
-static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
- int noblock, int flags)
+static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
struct ipv6_options opt_space;
struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -236,25 +351,26 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
struct ipv6_options *opt = NULL;
struct device *dev = NULL;
struct in6_addr *saddr = NULL;
+ struct flowi fl;
int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
struct raw6_opt *raw_opt;
+ int hlimit = -1;
u16 proto;
int err;
/* Mirror BSD error message compatibility */
- if (flags & MSG_OOB)
+ if (msg->msg_flags & MSG_OOB)
return -EOPNOTSUPP;
- if (flags & ~MSG_DONTROUTE)
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
/*
* Get and verify the address.
*/
- if (sin6)
- {
+ if (sin6) {
if (addr_len < sizeof(struct sockaddr_in6))
return(-EINVAL);
@@ -272,14 +388,11 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
daddr = &sin6->sin6_addr;
- if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
- {
- ipv6_dst_unlock(np->dest);
- np->dest = NULL;
+ if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+ dst_release(np->dst);
+ np->dst = NULL;
}
- }
- else
- {
+ } else {
if (sk->state != TCP_ESTABLISHED)
return(-EINVAL);
@@ -287,8 +400,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
daddr = &(sk->net_pinfo.af_inet6.daddr);
}
- if (ipv6_addr_any(daddr))
- {
+ if (ipv6_addr_any(daddr)) {
/*
* unspecfied destination address
* treated as error... is this correct ?
@@ -302,14 +414,12 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
if (len + (sk->ip_hdrincl ? 0 : sizeof(struct ipv6hdr)) > 65535)
return -EMSGSIZE;
- if (msg->msg_control)
- {
+ if (msg->msg_controllen) {
opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_options));
- err = datagram_send_ctl(msg, &dev, &saddr, opt);
- if (err < 0)
- {
+ err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
+ if (err < 0) {
printk(KERN_DEBUG "invalid msg_control\n");
return err;
}
@@ -317,9 +427,14 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
raw_opt = &sk->tp_pinfo.tp_raw;
+ fl.proto = proto;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.icmpt.type = 0;
+ fl.uli_u.icmpt.code = 0;
- if (raw_opt->checksum)
- {
+ if (raw_opt->checksum) {
struct rawv6_fakehdr hdr;
hdr.iov = msg->msg_iov;
@@ -329,22 +444,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len,
hdr.proto = proto;
if (opt && opt->srcrt)
- {
hdr.daddr = daddr;
- }
else
- {
hdr.daddr = NULL;
- }
- err = ipv6_build_xmit(sk, rawv6_frag_cksum, &hdr, daddr, len,
- saddr, dev, opt, proto, noblock);
- }
- else
- {
- err = ipv6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, daddr,
- len, saddr, dev, opt, proto,
- noblock);
+ err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len,
+ opt, hlimit, msg->msg_flags);
+ } else {
+ err = ip6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, &fl, len,
+ opt, hlimit, msg->msg_flags);
}
return err<0?err:len;
@@ -376,8 +484,7 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
struct raw6_opt *opt = &sk->tp_pinfo.tp_raw;
int val, err;
- switch(level)
- {
+ switch(level) {
case SOL_RAW:
break;
@@ -392,7 +499,7 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
default:
return ipv6_setsockopt(sk, level, optname, optval,
optlen);
- }
+ };
if (optval == NULL)
return(-EINVAL);
@@ -401,15 +508,11 @@ static int rawv6_setsockopt(struct sock *sk, int level, int optname,
if(err)
return err;
- switch (optname)
- {
+ switch (optname) {
case IPV6_CHECKSUM:
- if (val < 0)
- {
+ if (val < 0) {
opt->checksum = 0;
- }
- else
- {
+ } else {
opt->checksum = 1;
opt->offset = val;
}
@@ -428,11 +531,10 @@ static void rawv6_close(struct sock *sk, unsigned long timeout)
sk->state = TCP_CLOSE;
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
+ ipv6_sock_mc_close(sk);
destroy_sock(sk);
}
@@ -442,33 +544,33 @@ static int rawv6_init_sk(struct sock *sk)
}
struct proto rawv6_prot = {
- rawv6_close,
- udpv6_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
- NULL,
- rawv6_init_sk,
- NULL,
- NULL,
- rawv6_setsockopt,
- ipv6_getsockopt, /* FIXME */
- rawv6_sendmsg,
- rawv6_recvmsg,
- NULL, /* No special bind */
- rawv6_rcv_skb,
- 128,
- 0,
- "RAW",
- 0, 0,
- NULL
+ (struct sock *)&rawv6_prot, /* sklist_next */
+ (struct sock *)&rawv6_prot, /* sklist_prev */
+ rawv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ NULL, /* ioctl */
+ rawv6_init_sk, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ rawv6_setsockopt, /* setsockopt */
+ ipv6_getsockopt, /* getsockopt - FIXME */
+ rawv6_sendmsg, /* sendmsg */
+ rawv6_recvmsg, /* recvmsg */
+ rawv6_bind, /* bind */
+ rawv6_rcv_skb, /* backlog_rcv */
+ raw_v6_hash, /* hash */
+ raw_v6_unhash, /* unhash */
+ raw_v6_rehash, /* rehash */
+ NULL, /* good_socknum */
+ NULL, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "RAW", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o rawv6.o rawv6.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index e76dcc17c..35aa41b95 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -5,6 +5,8 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: reassembly.c,v 1.7 1997/03/18 18:24:47 davem Exp $
+ *
* Based on: net/ipv4/ip_fragment.c
*
* This program is free software; you can redistribute it and/or
@@ -32,7 +34,6 @@
#include <net/transp_v6.h>
#include <net/rawv6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
@@ -57,10 +58,11 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
__u8 *nhptr,
struct frag_hdr *fhdr)
{
- __u32 expires;
+ __u32 expires = jiffies + IPV6_FRAG_TIMEOUT;
int nh;
- expires = del_timer(&fq->timer);
+ if (del_timer(&fq->timer))
+ expires = fq->timer.expires;
/*
* We queue the packet even if it's the last.
@@ -72,18 +74,16 @@ static int reasm_frag(struct frag_queue *fq, struct sk_buff **skb,
*/
reasm_queue(fq, *skb, fhdr);
- if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
- {
+ if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
fq->last_in = 1;
fq->nhptr = nhptr;
}
- if (fq->last_in)
- {
+ if (fq->last_in) {
if ((nh = reasm_frag_1(fq, skb)))
return nh;
}
-
+
fq->timer.expires = expires;
add_timer(&fq->timer);
@@ -96,17 +96,13 @@ int ipv6_reassembly(struct sk_buff **skb, struct device *dev, __u8 *nhptr,
struct frag_hdr *fhdr = (struct frag_hdr *) ((*skb)->h.raw);
struct frag_queue *fq;
- for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next)
- {
+ for (fq = ipv6_frag_queue.next; fq != &ipv6_frag_queue; fq = fq->next) {
if (fq->id == fhdr->identification)
- {
return reasm_frag(fq, skb, nhptr,fhdr);
- }
}
create_frag_entry(*skb, dev, nhptr, fhdr);
-
return 0;
}
@@ -115,8 +111,7 @@ static void fq_free(struct frag_queue *fq)
{
struct ipv6_frag *fp, *back;
- for(fp = fq->fragments; fp; )
- {
+ for(fp = fq->fragments; fp; ) {
kfree_skb(fp->skb, FREE_READ);
back = fp;
fp=fp->next;
@@ -129,7 +124,6 @@ static void fq_free(struct frag_queue *fq)
fq->prev = fq->next = NULL;
kfree(fq);
-
}
static void frag_expire(unsigned long data)
@@ -143,13 +137,12 @@ static void frag_expire(unsigned long data)
frag = fq->fragments;
- if (frag == NULL)
- {
+ if (frag == NULL) {
printk(KERN_DEBUG "invalid fragment queue\n");
return;
}
- icmpv6_send(frag->skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_FRAGTIME, 0,
+ icmpv6_send(frag->skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
frag->skb->dev);
fq_free(fq);
@@ -165,8 +158,7 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
fq = (struct frag_queue *) kmalloc(sizeof(struct frag_queue),
GFP_ATOMIC);
- if (fq == NULL)
- {
+ if (fq == NULL) {
kfree_skb(skb, FREE_READ);
return;
}
@@ -185,8 +177,7 @@ static void create_frag_entry(struct sk_buff *skb, struct device *dev,
fq->nexthdr = fhdr->nexthdr;
- if ((fhdr->frag_off & __constant_htons(0x0001)) == 0)
- {
+ if ((fhdr->frag_off & __constant_htons(0x0001)) == 0) {
fq->last_in = 1;
fq->nhptr = nhptr;
}
@@ -209,16 +200,14 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
nfp = (struct ipv6_frag *) kmalloc(sizeof(struct ipv6_frag),
GFP_ATOMIC);
- if (nfp == NULL)
- {
+ if (nfp == NULL) {
kfree_skb(skb, FREE_READ);
return;
}
-
nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
- nfp->len = (ntohs(skb->ipv6_hdr->payload_len) -
- ((u8 *) (fhdr + 1) - (u8 *) (skb->ipv6_hdr + 1)));
+ nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
+ ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
nfp->skb = skb;
@@ -228,18 +217,14 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
bptr = &fq->fragments;
-
- for (fp = fq->fragments; fp; fp=fp->next)
- {
+ for (fp = fq->fragments; fp; fp=fp->next) {
if (nfp->offset <= fp->offset)
break;
bptr = &fp->next;
}
- if (fp && fp->offset == nfp->offset)
- {
- if (fp->len != nfp->len)
- {
+ if (fp && fp->offset == nfp->offset) {
+ if (fp->len != nfp->len) {
/* this cannot happen */
printk(KERN_DEBUG "reasm_queue: dup with wrong len\n");
}
@@ -250,7 +235,6 @@ static void reasm_queue(struct frag_queue *fq, struct sk_buff *skb,
return;
}
-
*bptr = nfp;
nfp->next = fp;
}
@@ -270,9 +254,7 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
__u16 copy;
int nh;
-
- for(fp = fq->fragments; fp; fp=fp->next)
- {
+ for(fp = fq->fragments; fp; fp=fp->next) {
if (offset != fp->offset)
return 0;
@@ -286,15 +268,14 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
* this means we have all fragments.
*/
- unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->ipv6_hdr + 1);
+ unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->nh.ipv6h + 1);
payload_len = (unfrag_len + tail->offset +
(tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
printk(KERN_DEBUG "reasm: payload len = %d\n", payload_len);
- if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL)
- {
+ if ((skb = dev_alloc_skb(sizeof(struct ipv6hdr) + payload_len))==NULL) {
printk(KERN_DEBUG "reasm_frag: no memory for reassembly\n");
fq_free(fq);
return 1;
@@ -302,20 +283,18 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
copy = unfrag_len + sizeof(struct ipv6hdr);
- skb->ipv6_hdr = (struct ipv6hdr *) skb->data;
+ skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
- skb->free = 1;
skb->dev = fq->dev;
-
nh = fq->nexthdr;
*(fq->nhptr) = nh;
- memcpy(skb_put(skb, copy), tail->skb->ipv6_hdr, copy);
+ memcpy(skb_put(skb, copy), tail->skb->nh.ipv6h, copy);
skb->h.raw = skb->tail;
- skb->ipv6_hdr->payload_len = ntohs(payload_len);
+ skb->nh.ipv6h->payload_len = ntohs(payload_len);
*skb_in = skb;
@@ -323,8 +302,7 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
* FIXME: If we don't have a checksum we ought to be able
* to defragment and checksum in this pass. [AC]
*/
- for(fp = fq->fragments; fp; )
- {
+ for(fp = fq->fragments; fp; ) {
struct ipv6_frag *back;
memcpy(skb_put(skb, fp->len), (__u8*)(fp->fhdr + 1), fp->len);
@@ -343,12 +321,3 @@ static int reasm_frag_1(struct frag_queue *fq, struct sk_buff **skb_in)
return nh;
}
-
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o reassembly.o reassembly.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
new file mode 100644
index 000000000..d04464e26
--- /dev/null
+++ b/net/ipv6/route.c
@@ -0,0 +1,1599 @@
+/*
+ * Linux INET6 implementation
+ * FIB front-end.
+ *
+ * Authors:
+ * Pedro Roque <roque@di.fc.ul.pt>
+ *
+ * $Id: route.c,v 1.11 1997/04/16 05:58:05 davem Exp $
+ *
+ * This program 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 of the License, or (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/route.h>
+#include <linux/netdevice.h>
+#include <linux/in6.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#include <net/snmp.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+#include <net/ip6_route.h>
+#include <net/ndisc.h>
+#include <net/addrconf.h>
+#include <net/netlink.h>
+
+#include <asm/uaccess.h>
+
+#undef CONFIG_RT6_POLICY
+
+/* Set to 3 to get tracing. */
+#define RT6_DEBUG 2
+
+#if RT6_DEBUG >= 3
+#define RDBG(x) printk x
+#else
+#define RDBG(x)
+#endif
+
+static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie);
+static struct dst_entry *ip6_dst_reroute(struct dst_entry *dst,
+ struct sk_buff *skb);
+
+static int ip6_pkt_discard(struct sk_buff *skb);
+
+struct dst_ops ip6_dst_ops = {
+ AF_INET6,
+ ip6_dst_check,
+ ip6_dst_reroute,
+ NULL
+};
+
+struct rt6_info ip6_null_entry = {
+ {{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
+ 0, 0, 0, 0, 0, 0, 0, 0, -ENETUNREACH, NULL, NULL,
+ ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
+ NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0UL,
+ 0, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+};
+
+struct fib6_node ip6_routing_table = {
+ NULL, NULL, NULL, NULL,
+ &ip6_null_entry,
+ 0, RTN_ROOT|RTN_TL_ROOT, 0
+};
+
+#ifdef CONFIG_RT6_POLICY
+int ip6_rt_policy = 0;
+
+struct pol_chain *rt6_pol_list = NULL;
+
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb);
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk);
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args);
+
+#else
+#define ip6_rt_policy (0)
+#endif
+
+static atomic_t rt6_tbl_lock = ATOMIC_INIT(0);
+static int rt6_bh_mask = 0;
+
+#define RT_BH_REQUEST 1
+#define RT_BH_GC 2
+
+static void __rt6_run_bh(void);
+
+/*
+ * request queue operations
+ * FIFO queue/dequeue
+ */
+
+static struct rt6_req request_queue = {
+ 0, NULL, &request_queue, &request_queue
+};
+
+static __inline__ void rtreq_queue(struct rt6_req * req)
+{
+ unsigned long flags;
+ struct rt6_req *next = &request_queue;
+
+ save_flags(flags);
+ cli();
+
+ req->prev = next->prev;
+ req->prev->next = req;
+ next->prev = req;
+ req->next = next;
+ restore_flags(flags);
+}
+
+static __inline__ struct rt6_req * rtreq_dequeue(void)
+{
+ struct rt6_req *next = &request_queue;
+ struct rt6_req *head;
+
+ head = next->next;
+
+ if (head == next)
+ return NULL;
+
+ head->next->prev = head->prev;
+ next->next = head->next;
+
+ head->next = NULL;
+ head->prev = NULL;
+
+ return head;
+}
+
+void rtreq_add(struct rt6_info *rt, int operation)
+{
+ struct rt6_req *rtreq;
+
+ rtreq = kmalloc(sizeof(struct rt6_req), GFP_ATOMIC);
+
+ if (rtreq == NULL)
+ return;
+
+ memset(rtreq, 0, sizeof(struct rt6_req));
+
+ rtreq->operation = operation;
+ rtreq->ptr = rt;
+ rtreq_queue(rtreq);
+
+ rt6_bh_mask |= RT_BH_REQUEST;
+}
+
+static __inline__ void rt6_lock(void)
+{
+ atomic_inc(&rt6_tbl_lock);
+}
+
+static __inline__ void rt6_unlock(void)
+{
+ if (atomic_dec_and_test(&rt6_tbl_lock) && rt6_bh_mask) {
+ start_bh_atomic();
+ __rt6_run_bh();
+ end_bh_atomic();
+ }
+}
+
+/*
+ * Route lookup
+ */
+
+static __inline__ struct rt6_info *rt6_device_match(struct rt6_info *rt,
+ struct device *dev,
+ int strict)
+{
+ struct rt6_info *sprt;
+
+ RDBG(("rt6_device_match: (%p,%p,%d) ", rt, dev, strict));
+ if (dev) {
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (sprt->rt6i_dev == dev) {
+ RDBG(("match --> %p\n", sprt));
+ return sprt;
+ }
+ }
+
+ if (strict) {
+ RDBG(("nomatch & STRICT --> ip6_null_entry\n"));
+ return &ip6_null_entry;
+ }
+ }
+ RDBG(("!dev or (no match and !strict) --> rt(%p)\n", rt));
+ return rt;
+}
+
+/*
+ * pointer to the last default router chosen
+ */
+static struct rt6_info *rt6_dflt_pointer = NULL;
+
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct device *dev)
+{
+ struct rt6_info *match = NULL;
+ struct rt6_info *sprt;
+ int mpri = 0;
+
+ RDBG(("rt6_best_dflt(%p,%p): ", rt, dev));
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ struct nd_neigh *ndn;
+
+ RDBG(("sprt(%p): ", sprt));
+ if ((ndn = (struct nd_neigh *) sprt->rt6i_nexthop)) {
+ int m = -1;
+
+ RDBG(("nxthop(%p,%d) ", ndn, ndn->ndn_nud_state));
+ switch (ndn->ndn_nud_state) {
+ case NUD_REACHABLE:
+ RDBG(("NUD_REACHABLE "));
+ if (sprt != rt6_dflt_pointer) {
+ rt = sprt;
+ RDBG(("sprt!=dflt_ptr -> %p\n",
+ sprt));
+ goto out;
+ }
+ RDBG(("m=2, "));
+ m = 2;
+ break;
+
+ case NUD_DELAY:
+ RDBG(("NUD_DELAY, m=1, "));
+ m = 1;
+ break;
+
+ case NUD_STALE:
+ RDBG(("NUD_STALE, m=1, "));
+ m = 1;
+ break;
+ };
+
+ if (dev && sprt->rt6i_dev == dev) {
+ RDBG(("dev&&sprt->rt6i_dev==dev(%p), m+=2, ", dev));
+ m += 2;
+ }
+
+ if (m >= mpri) {
+ RDBG(("m>=mpri setmatch, "));
+ mpri = m;
+ match = sprt;
+ }
+ }
+ }
+
+ if (match) {
+ RDBG(("match, set rt, "));
+ rt = match;
+ } else {
+ /*
+ * No default routers are known to be reachable.
+ * SHOULD round robin
+ */
+ RDBG(("!match, trying rt6_dflt_pointer, "));
+ if (rt6_dflt_pointer) {
+ struct rt6_info *next;
+
+ if ((next = rt6_dflt_pointer->u.next) &&
+ next->u.dst.error == 0)
+ rt = next;
+ }
+ }
+
+out:
+ rt6_dflt_pointer = rt;
+ RDBG(("returning %p, dflt_ptr set\n", rt));
+ return rt;
+}
+
+struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
+ struct device *dev, int flags)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+
+ RDBG(("rt6_lookup(%p,%p,%p,%x) from %p\n",
+ daddr, saddr, dev, flags, __builtin_return_address(0)));
+ rt6_lock();
+ fn = fib6_lookup(&ip6_routing_table, daddr, saddr);
+
+ rt = rt6_device_match(fn->leaf, dev, 0);
+ rt6_unlock();
+ return rt;
+}
+
+static struct rt6_info *rt6_cow(struct rt6_info *rt, struct in6_addr *daddr,
+ struct in6_addr *saddr)
+{
+ /*
+ * Clone the route.
+ */
+
+ rt = ip6_rt_copy(rt);
+
+ if (rt) {
+ ipv6_addr_copy(&rt->rt6i_dst.addr, daddr);
+
+ rt->rt6i_dst.plen = 128;
+ rt->rt6i_flags |= RTF_CACHE;
+
+ if (rt->rt6i_src.plen) {
+ ipv6_addr_copy(&rt->rt6i_src.addr, saddr);
+ rt->rt6i_src.plen = 128;
+ }
+
+ rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, daddr);
+
+ rtreq_add(rt, RT_OPER_ADD);
+ } else {
+ rt = &ip6_null_entry;
+ }
+ return rt;
+}
+
+#ifdef CONFIG_RT6_POLICY
+static __inline__ struct rt6_info *rt6_flow_lookup_in(struct rt6_info *rt,
+ struct sk_buff *skb)
+{
+ struct in6_addr *daddr, *saddr;
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_FORWARD;
+ arg.fl_u.skb = skb;
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+
+ return rt6_flow_lookup(rt, daddr, saddr, &arg);
+}
+
+static __inline__ struct rt6_info *rt6_flow_lookup_out(struct rt6_info *rt,
+ struct sock *sk,
+ struct flowi *fl)
+{
+ struct fl_acc_args arg;
+
+ arg.type = FL_ARG_ORIGIN;
+ arg.fl_u.fl_o.sk = sk;
+ arg.fl_u.fl_o.flow = fl;
+
+ return rt6_flow_lookup(rt, fl->nl_u.ip6_u.daddr, fl->nl_u.ip6_u.saddr,
+ &arg);
+}
+
+#endif
+
+void ip6_route_input(struct sk_buff *skb)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ struct dst_entry *dst;
+
+ RDBG(("ip6_route_input(%p) from %p\n", skb, __builtin_return_address(0)));
+ rt6_lock();
+ fn = fib6_lookup(&ip6_routing_table, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, skb->dev, 0);
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_in(sprt, skb)) {
+ rt = sprt;
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+
+ rt = rt6_device_match(rt, skb->dev, 0);
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+ ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+ rt = rt6_cow(rt, &skb->nh.ipv6h->daddr,
+ &skb->nh.ipv6h->saddr);
+ }
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_in(rt, skb);
+#endif
+ }
+
+out:
+ dst = dst_clone((struct dst_entry *) rt);
+ rt6_unlock();
+
+ skb->dst = dst;
+ dst->input(skb);
+}
+
+struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl)
+{
+ struct fib6_node *fn;
+ struct rt6_info *rt;
+ struct dst_entry *dst;
+ int strict;
+
+ RDBG(("ip6_route_output(%p,%p) from(%p)", sk, fl,
+ __builtin_return_address(0)));
+ strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & IPV6_ADDR_MULTICAST;
+
+ rt6_lock();
+#if RT6_DEBUG >= 3
+ RDBG(("lkup("));
+ if(fl->nl_u.ip6_u.daddr) {
+ struct in6_addr *addr = fl->nl_u.ip6_u.daddr;
+ int i;
+ RDBG(("daddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+ if(fl->nl_u.ip6_u.saddr) {
+ struct in6_addr *addr = fl->nl_u.ip6_u.saddr;
+ int i;
+ RDBG(("saddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+
+ RDBG(("-->(%p[%s])) ", fn, fn == &ip6_routing_table ? "ROOT" : "!ROOT"));
+
+ rt = fn->leaf;
+
+ if ((rt->rt6i_flags & RTF_CACHE)) {
+ RDBG(("RTF_CACHE "));
+ if (ip6_rt_policy == 0) {
+ rt = rt6_device_match(rt, fl->dev, strict);
+ RDBG(("devmatch(%p) ", rt));
+ goto out;
+ }
+
+#ifdef CONFIG_RT6_POLICY
+ if ((rt->rt6i_flags & RTF_FLOW)) {
+ struct rt6_info *sprt;
+
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
+ if (rt6_flow_match_out(sprt, sk)) {
+ rt = sprt;
+ goto out;
+ }
+ }
+ }
+#endif
+ }
+ RDBG(("!RTF_CACHE "));
+ if (rt->rt6i_flags & RTF_DEFAULT) {
+ RDBG(("RTF_DEFAULT "));
+ if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF) {
+ rt = rt6_best_dflt(rt, fl->dev);
+ RDBG(("best_dflt(%p) ", rt));
+ }
+ } else {
+ rt = rt6_device_match(rt, fl->dev, strict);
+ RDBG(("!RTF_DEFAULT devmatch(%p) ", rt));
+ }
+
+ if (ip6_rt_policy == 0) {
+ if (!rt->rt6i_nexthop && rt->rt6i_dev &&
+ ((rt->rt6i_flags & RTF_NONEXTHOP) == 0)) {
+ rt = rt6_cow(rt, fl->nl_u.ip6_u.daddr,
+ fl->nl_u.ip6_u.saddr);
+ RDBG(("(!nhop&&rt6i_dev&&!RTF_NONEXTHOP) cow(%p) ", rt));
+ }
+ } else {
+#ifdef CONFIG_RT6_POLICY
+ rt = rt6_flow_lookup_out(rt, sk, fl);
+#endif
+ }
+
+out:
+ dst = dst_clone((struct dst_entry *) rt);
+ rt6_unlock();
+ RDBG(("dclone/ret(%p)\n", dst));
+ return dst;
+}
+
+
+void rt6_ins(struct rt6_info *rt)
+{
+ start_bh_atomic();
+ if (atomic_read(&rt6_tbl_lock) == 1)
+ fib6_add(&ip6_routing_table, rt);
+ else
+ rtreq_add(rt, RT_OPER_ADD);
+ end_bh_atomic();
+}
+
+/*
+ * Destination cache support functions
+ */
+
+struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
+{
+ struct rt6_info *rt;
+
+ RDBG(("ip6dstchk(%p,%08x)[%p]\n", dst, cookie,
+ __builtin_return_address(0)));
+
+ rt = (struct rt6_info *) dst;
+
+ if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
+ if (rt->rt6i_nexthop)
+ ndisc_event_send(rt->rt6i_nexthop, NULL);
+
+ return dst;
+ }
+
+ dst_release(dst);
+ return NULL;
+}
+
+struct dst_entry *ip6_dst_reroute(struct dst_entry *dst, struct sk_buff *skb)
+{
+ /*
+ * FIXME
+ */
+ RDBG(("ip6_dst_reroute(%p,%p)[%p] (AIEEE)\n", dst, skb,
+ __builtin_return_address(0)));
+ return NULL;
+}
+
+/*
+ *
+ */
+
+struct rt6_info *ip6_route_add(struct in6_rtmsg *rtmsg, int *err)
+{
+ struct rt6_info *rt;
+ struct device *dev = NULL;
+ int addr_type;
+
+ RDBG(("ip6_route_add(%p)[%p] ", rtmsg, __builtin_return_address(0)));
+ *err = 0;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt == NULL) {
+ RDBG(("dalloc fails, "));
+ *err = -ENOMEM;
+ goto out;
+ }
+
+ /*
+ * default... this should be chosen according to route flags
+ */
+
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = &rtmsg->rtmsg_dst;
+ int i;
+
+ RDBG(("daddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ addr = &rtmsg->rtmsg_src;
+ RDBG(("saddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+
+ addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
+
+ if (addr_type & IPV6_ADDR_MULTICAST) {
+ RDBG(("MCAST, "));
+ rt->u.dst.input = ip6_mc_input;
+ } else {
+ RDBG(("!MCAST "));
+ rt->u.dst.input = ip6_forward;
+ }
+
+ rt->u.dst.output = dev_queue_xmit;
+
+ if (rtmsg->rtmsg_ifindex)
+ dev = dev_get_by_index(rtmsg->rtmsg_ifindex);
+ if(dev)
+ RDBG(("d[%s] ", dev->name));
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst);
+ rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len;
+
+ /* XXX Figure out what really is supposed to be happening here -DaveM */
+ ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+ rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
+
+ if ((rt->rt6i_src.plen = rtmsg->rtmsg_src_len)) {
+ RDBG(("splen, "));
+ ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src);
+ } else {
+ RDBG(("!splen, "));
+ }
+ /* XXX */
+
+ if (rtmsg->rtmsg_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) {
+ struct rt6_info *grt;
+ struct in6_addr *gw_addr;
+ u32 flags = 0;
+
+ RDBG(("RTF_GATEWAY, "));
+ /*
+ * 1. gateway route lookup
+ * 2. ndisc_get_neigh
+ */
+
+ gw_addr = &rtmsg->rtmsg_gateway;
+
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = gw_addr;
+ int i;
+
+ RDBG(("gwaddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+
+ if ((rtmsg->rtmsg_flags & RTF_GATEWAY) &&
+ (rtmsg->rtmsg_flags & RTF_ADDRCONF) == 0) {
+ RDBG(("RTF_GATEWAY && !RTF_ADDRCONF, "));
+ if (dev)
+ flags |= RTF_LINKRT;
+
+ grt = rt6_lookup(gw_addr, NULL, dev, flags);
+
+ if (grt == NULL)
+ {
+ RDBG(("!grt, "));
+ *err = -EHOSTUNREACH;
+ goto out;
+ }
+ dev = grt->rt6i_dev;
+ RDBG(("grt(d=%s), ", dev ? dev->name : "NULL"));
+ }
+
+ rt->rt6i_nexthop = ndisc_get_neigh(dev, gw_addr);
+
+ if (rt->rt6i_nexthop == NULL) {
+ RDBG(("!nxthop, "));
+ *err = -ENOMEM;
+ goto out;
+ }
+ RDBG(("nxthop, "));
+ }
+
+ if (dev == NULL) {
+ RDBG(("!dev, "));
+ *err = -ENODEV;
+ goto out;
+ }
+
+ rt->rt6i_metric = rtmsg->rtmsg_metric;
+
+ rt->rt6i_dev = dev;
+ rt->u.dst.pmtu = dev->mtu;
+ rt->rt6i_flags = rtmsg->rtmsg_flags;
+
+ RDBG(("rt6ins(%p) ", rt));
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+
+out:
+ if (*err) {
+ RDBG(("dfree(%p) ", rt));
+ dst_free((struct dst_entry *) rt);
+ rt = NULL;
+ }
+ RDBG(("ret(%p)\n", rt));
+ return rt;
+}
+
+int ip6_del_rt(struct rt6_info *rt)
+{
+ rt6_lock();
+
+ start_bh_atomic();
+
+ rt6_dflt_pointer = NULL;
+
+ if (atomic_read(&rt6_tbl_lock) == 1)
+ fib6_del(rt);
+ else
+ rtreq_add(rt, RT_OPER_DEL);
+ end_bh_atomic();
+ rt6_unlock();
+ return 0;
+}
+
+int ip6_route_del(struct in6_rtmsg *rtmsg)
+{
+ return 0;
+}
+
+
+/*
+ * bottom handler, runs with atomic_bh protection
+ */
+void __rt6_run_bh(void)
+{
+ struct rt6_req *rtreq;
+
+ while ((rtreq = rtreq_dequeue())) {
+ switch (rtreq->operation) {
+ case RT_OPER_ADD:
+ fib6_add(&ip6_routing_table, rtreq->ptr);
+ break;
+ case RT_OPER_DEL:
+ fib6_del(rtreq->ptr);
+ break;
+ };
+ kfree(rtreq);
+ }
+ rt6_bh_mask = 0;
+}
+
+/*
+ * NETLINK interface
+ * routing socket moral equivalent
+ */
+
+static int rt6_msgrcv(int unit, struct sk_buff *skb)
+{
+ int count = 0;
+ struct in6_rtmsg *rtmsg;
+ int err;
+
+ while (skb->len) {
+ if (skb->len < sizeof(struct in6_rtmsg)) {
+ count = -EINVAL;
+ goto out;
+ }
+
+ rtmsg = (struct in6_rtmsg *) skb->data;
+ skb_pull(skb, sizeof(struct in6_rtmsg));
+ count += sizeof(struct in6_rtmsg);
+
+ switch (rtmsg->rtmsg_type) {
+ case RTMSG_NEWROUTE:
+ ip6_route_add(rtmsg, &err);
+ break;
+ case RTMSG_DELROUTE:
+ ip6_route_del(rtmsg);
+ break;
+ default:
+ count = -EINVAL;
+ goto out;
+ };
+ }
+
+out:
+ kfree_skb(skb, FREE_READ);
+ return count;
+}
+
+static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
+ sizeof(struct in6_rtmsg));
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb, FREE_WRITE);
+}
+
+void rt6_sndmsg(int type, struct in6_addr *dst, struct in6_addr *src,
+ struct in6_addr *gw, struct device *dev,
+ int dstlen, int srclen, int metric, __u32 flags)
+{
+ struct sk_buff *skb;
+ struct in6_rtmsg *msg;
+
+ skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC);
+ if (skb == NULL)
+ return;
+
+ msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
+
+ memset(msg, 0, sizeof(struct in6_rtmsg));
+
+ msg->rtmsg_type = type;
+
+ if (dst)
+ ipv6_addr_copy(&msg->rtmsg_dst, dst);
+
+ if (src) {
+ ipv6_addr_copy(&msg->rtmsg_src, src);
+ msg->rtmsg_src_len = srclen;
+ }
+
+ if (gw)
+ ipv6_addr_copy(&msg->rtmsg_gateway, gw);
+
+ msg->rtmsg_dst_len = dstlen;
+ msg->rtmsg_metric = metric;
+
+ if (dev)
+ msg->rtmsg_ifindex = dev->ifindex;
+
+ msg->rtmsg_flags = flags;
+
+ if (netlink_post(NETLINK_ROUTE6, skb))
+ kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * Handle redirects
+ */
+struct rt6_info *rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr,
+ struct in6_addr *target, struct device *dev,
+ int on_link)
+{
+ struct rt6_info *rt, *tgtr, *nrt;
+
+ RDBG(("rt6_redirect(%s)[%p]: ",
+ dev ? dev->name : "NULL",
+ __builtin_return_address(0)));
+ rt = rt6_lookup(dest, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+ RDBG(("!rt\n"));
+ printk(KERN_DEBUG "rt6_redirect: no route to destination\n");
+ return NULL;
+ }
+
+ if (rt->rt6i_flags & RTF_GATEWAY) {
+ /*
+ * This can happen due to misconfiguration
+ * if we are dealing with an "on link" redirect.
+ */
+ RDBG(("RTF_GATEWAY\n"));
+ printk(KERN_DEBUG "rt6_redirect: destination not directly "
+ "connected\n");
+ return NULL;
+ }
+ RDBG(("tgt_lkup, "));
+ tgtr = rt6_lookup(target, NULL, dev, 0);
+
+ if (tgtr == NULL || tgtr->u.dst.error) {
+ /*
+ * duh?! no route to redirect target.
+ * How where we talking to it in the first place ?
+ */
+ RDBG(("!tgtr||dsterr\n"));
+ printk(KERN_DEBUG "rt6_redirect: no route to target\n");
+ return NULL;
+ }
+
+ if ((tgtr->rt6i_flags & RTF_GATEWAY) &&
+ ipv6_addr_cmp(dest, &tgtr->rt6i_gateway) == 0) {
+ RDBG(("tgt RTF_GATEWAY && dstmatch, dup\n"));
+ /*
+ * Check if we already have the right route.
+ */
+#if RT6_DEBUG >= 1
+ printk(KERN_DEBUG "rt6_redirect: duplicate\n");
+#endif
+ return NULL;
+ }
+
+ /*
+ * RFC 1970 specifies that redirects should only be
+ * accepted if they come from the nexthop to the target.
+ * Due to the way default routers are chosen, this notion
+ * is a bit fuzzy and one might need to check all default
+ * routers.
+ */
+
+ if (ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+ RDBG(("saddr/tgt->gway match, "));
+ if (tgtr->rt6i_flags & RTF_DEFAULT) {
+ tgtr = ip6_routing_table.leaf;
+
+ for (; tgtr; tgtr = tgtr->u.next) {
+ if (!ipv6_addr_cmp(saddr, &tgtr->rt6i_gateway)) {
+ RDBG(("found srcok, "));
+ goto source_ok;
+ }
+ }
+ }
+ RDBG(("!dflt||!srcok, "));
+ printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop "
+ "for redirect target\n");
+ }
+
+source_ok:
+
+ /*
+ * We have finally decided to accept it.
+ */
+ RDBG(("srcok: "));
+ if ((tgtr->rt6i_flags & RTF_HOST)) {
+ /*
+ * Already a host route.
+ *
+ */
+ RDBG(("hralready, "));
+ if (tgtr->rt6i_nexthop) {
+ RDBG(("nrel(nxthop) "));
+ neigh_release(tgtr->rt6i_nexthop);
+ }
+ /*
+ * purge hh_cache
+ */
+ tgtr->rt6i_flags |= RTF_MODIFIED | RTF_CACHE;
+ ipv6_addr_copy(&tgtr->rt6i_gateway, dest);
+ tgtr->rt6i_nexthop = ndisc_get_neigh(tgtr->rt6i_dev, dest);
+ RDBG(("hhpurge, getnewneigh, ret(%p)\n", tgtr));
+ return tgtr;
+ }
+
+ nrt = ip6_rt_copy(tgtr);
+ nrt->rt6i_flags = RTF_GATEWAY|RTF_HOST|RTF_UP|RTF_DYNAMIC|RTF_CACHE;
+
+ ipv6_addr_copy(&nrt->rt6i_dst.addr, target);
+ nrt->rt6i_dst.plen = 128;
+
+ ipv6_addr_copy(&nrt->rt6i_gateway, dest);
+ nrt->rt6i_nexthop = ndisc_get_neigh(nrt->rt6i_dev, dest);
+ nrt->rt6i_dev = dev;
+ nrt->u.dst.pmtu = dev->mtu;
+
+ RDBG(("rt6_ins(%p)\n", nrt));
+
+ rt6_lock();
+ rt6_ins(nrt);
+ rt6_unlock();
+
+ return nrt;
+}
+
+/*
+ * Handle ICMP "packet too big" messages
+ * i.e. Path MTU discovery
+ */
+
+void rt6_pmtu_discovery(struct in6_addr *addr, struct device *dev, int pmtu)
+{
+ struct rt6_info *rt;
+
+ if (pmtu < 576 || pmtu > 65536) {
+#if RT6_DEBUG >= 1
+ printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
+ pmtu);
+#endif
+ return;
+ }
+
+ rt = rt6_lookup(addr, NULL, dev, 0);
+
+ if (rt == NULL || rt->u.dst.error) {
+#if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "rt6_pmtu_discovery: no route to host\n");
+#endif
+ return;
+ }
+
+ if (rt->rt6i_flags & RTF_HOST) {
+ /*
+ * host route
+ */
+ rt->u.dst.pmtu = pmtu;
+ rt->rt6i_flags |= RTF_MODIFIED;
+
+ return;
+ }
+
+ rt = ip6_rt_copy(rt);
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+ rt->rt6i_dst.plen = 128;
+
+ rt->rt6i_flags |= (RTF_HOST | RTF_DYNAMIC | RTF_CACHE);
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+}
+
+/*
+ * Misc support functions
+ */
+
+struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
+{
+ struct rt6_info *rt;
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+
+ if (rt) {
+ rt->u.dst.input = ort->u.dst.input;
+ rt->u.dst.output = ort->u.dst.output;
+
+ rt->u.dst.pmtu = ort->u.dst.pmtu;
+ rt->rt6i_dev = ort->rt6i_dev;
+
+ ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway);
+ rt->rt6i_keylen = ort->rt6i_keylen;
+ rt->rt6i_flags = ort->rt6i_flags;
+ rt->rt6i_metric = ort->rt6i_metric;
+
+ memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));
+ memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));
+ }
+ return rt;
+}
+
+struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+ struct fib6_node *fn;
+
+ RDBG(("rt6_get_dflt_router(%p,%p)[%p]", addr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ int i;
+
+ RDBG(("addr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+ rt6_lock();
+
+ fn = &ip6_routing_table;
+
+ for (rt = fn->leaf; rt; rt=rt->u.next) {
+ if (dev == rt->rt6i_dev &&
+ ipv6_addr_cmp(&rt->rt6i_dst.addr, addr) == 0)
+ break;
+ }
+
+ rt6_unlock();
+ return rt;
+}
+
+struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
+ struct device *dev)
+{
+ struct in6_rtmsg rtmsg;
+ struct rt6_info *rt;
+ int err;
+
+ RDBG(("rt6_add_dflt_router(%p,%p)[%p] ", gwaddr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ struct in6_addr *addr = gwaddr;
+ int i;
+
+ RDBG(("gwaddr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+
+ memset(&rtmsg, 0, sizeof(struct in6_rtmsg));
+ rtmsg.rtmsg_type = RTMSG_NEWROUTE;
+ ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
+ rtmsg.rtmsg_metric = 1024;
+ rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP;
+
+ rtmsg.rtmsg_ifindex = dev->ifindex;
+
+ rt = ip6_route_add(&rtmsg, &err);
+
+ if (err) {
+ printk(KERN_DEBUG "rt6_add_dflt: ip6_route_add error %d\n",
+ err);
+ }
+ return rt;
+}
+
+void rt6_purge_dflt_routers(int last_resort)
+{
+ struct rt6_info *rt;
+ struct fib6_node *fn;
+ u32 flags;
+
+ RDBG(("rt6_purge_dflt_routers(%d)[%p]\n", last_resort,
+ __builtin_return_address(0)));
+ fn = &ip6_routing_table;
+
+ rt6_dflt_pointer = NULL;
+
+ if (last_resort)
+ flags = RTF_ALLONLINK;
+ else
+ flags = RTF_DEFAULT | RTF_ADDRCONF;
+
+ for (rt = fn->leaf; rt; ) {
+ if ((rt->rt6i_flags & flags)) {
+ struct rt6_info *drt;
+#if RT6_DEBUG >= 2
+ printk(KERN_DEBUG "rt6_purge_dflt: deleting entry\n");
+#endif
+ drt = rt;
+ rt = rt->u.next;
+ ip6_del_rt(drt);
+ continue;
+ }
+ rt = rt->u.next;
+ }
+}
+
+int ipv6_route_ioctl(unsigned int cmd, void *arg)
+{
+ struct in6_rtmsg rtmsg;
+ int err;
+
+ RDBG(("ipv6_route_ioctl(%d,%p)\n", cmd, arg));
+ switch(cmd) {
+ case SIOCADDRT: /* Add a route */
+ case SIOCDELRT: /* Delete a route */
+ if (!suser())
+ return -EPERM;
+ err = copy_from_user(&rtmsg, arg,
+ sizeof(struct in6_rtmsg));
+ if (err)
+ return -EFAULT;
+
+ switch (cmd) {
+ case SIOCADDRT:
+ ip6_route_add(&rtmsg, &err);
+ break;
+ case SIOCDELRT:
+ err = ip6_route_del(&rtmsg);
+ break;
+ default:
+ err = -EINVAL;
+ };
+
+ if (err == 0)
+ rt6_sndrtmsg(&rtmsg);
+ return err;
+ };
+
+ return -EINVAL;
+}
+
+/*
+ * Drop the packet on the floor
+ */
+
+int ip6_pkt_discard(struct sk_buff *skb)
+{
+ ipv6_statistics.Ip6OutNoRoutes++;
+ kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/*
+ * Add address
+ */
+
+int ip6_rt_addr_add(struct in6_addr *addr, struct device *dev)
+{
+ struct rt6_info *rt;
+
+ RDBG(("ip6_rt_addr_add(%p,%p)[%p]\n", addr, dev,
+ __builtin_return_address(0)));
+#if RT6_DEBUG >= 3
+ {
+ int i;
+
+ RDBG(("addr["));
+ for(i = 0; i < 8; i++) {
+ RDBG(("%04x%c", addr->s6_addr16[i],
+ i == 7 ? ']' : ':'));
+ }
+ }
+#endif
+ RDBG(("\n"));
+
+ rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops);
+ if (rt == NULL)
+ return -ENOMEM;
+
+ memset(rt, 0, sizeof(struct rt6_info));
+
+ rt->u.dst.input = ip6_input;
+ rt->u.dst.output = dev_queue_xmit;
+ rt->rt6i_dev = dev_get("lo");
+ rt->u.dst.pmtu = rt->rt6i_dev->mtu;
+
+ rt->rt6i_flags = RTF_HOST | RTF_LOCAL | RTF_UP | RTF_NONEXTHOP;
+
+ ipv6_addr_copy(&rt->rt6i_dst.addr, addr);
+ rt->rt6i_dst.plen = 128;
+
+ rt6_lock();
+ rt6_ins(rt);
+ rt6_unlock();
+
+ return 0;
+}
+
+#ifdef CONFIG_RT6_POLICY
+
+static int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb)
+{
+ struct flow_filter *frule;
+ struct pkt_filter *filter;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ for (filter = frule->u.filter; filter; filter = filter->next) {
+ __u32 *word;
+
+ word = (__u32 *) skb->h.raw;
+ word += filter->offset;
+
+ if ((*word ^ filter->value) & filter->mask) {
+ res = 0;
+ break;
+ }
+ }
+
+out:
+ return res;
+}
+
+static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk)
+{
+ struct flow_filter *frule;
+ int res = 1;
+
+ if ((frule = rt->rt6i_filter) == NULL)
+ goto out;
+
+ if (frule->type != FLR_INPUT) {
+ res = 0;
+ goto out;
+ }
+
+ if (frule->u.sk != sk)
+ res = 0;
+out:
+ return res;
+}
+
+static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt,
+ struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct fl_acc_args *args)
+{
+ struct flow_rule *frule;
+ struct rt6_info *nrt = NULL;
+ struct pol_chain *pol;
+
+ for (pol = rt6_pol_list; pol; pol = pol->next) {
+ struct fib6_node *fn;
+ struct rt6_info *sprt;
+
+ fn = fib6_lookup(pol->rules, daddr, saddr);
+
+ do {
+ for (sprt = fn->leaf; sprt; sprt=sprt->u.next) {
+ int res;
+
+ frule = sprt->rt6i_flowr;
+#if RT6_DEBUG >= 2
+ if (frule == NULL) {
+ printk(KERN_DEBUG "NULL flowr\n");
+ goto error;
+ }
+#endif
+ res = frule->ops->accept(rt, sprt, args, &nrt);
+
+ switch (res) {
+ case FLOWR_SELECT:
+ goto found;
+ case FLOWR_CLEAR:
+ goto next_policy;
+ case FLOWR_NODECISION:
+ break;
+ default:
+ goto error;
+ };
+ }
+
+ fn = fn->parent;
+
+ } while ((fn->fn_flags & RTN_TL_ROOT) == 0);
+
+ next_policy:
+ }
+
+error:
+ return &ip6_null_entry;
+
+found:
+
+ if (nrt == NULL)
+ goto error;
+
+ nrt->rt6i_flags |= RTF_CACHE;
+ rt6_ins(nrt);
+
+ return nrt;
+}
+#endif
+
+/*
+ * /proc
+ */
+
+#ifdef CONFIG_PROC_FS
+
+#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)
+
+struct rt6_proc_arg {
+ char *buffer;
+ int offset;
+ int length;
+ int skip;
+ int len;
+};
+
+static void rt6_info_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_info *rt;
+ struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+
+ for (rt = fn->leaf; rt; rt = rt->u.next) {
+ int i;
+
+ if (arg->skip < arg->offset / RT6_INFO_LEN) {
+ arg->skip++;
+ continue;
+ }
+
+ if (arg->len >= arg->length)
+ return;
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_dst.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_dst.plen);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_src.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_src.plen);
+
+ if (rt->rt6i_nexthop) {
+ for (i=0; i<16; i++) {
+ struct nd_neigh *ndn;
+
+ ndn = (struct nd_neigh *) rt->rt6i_nexthop;
+ sprintf(arg->buffer + arg->len, "%02x",
+ ndn->ndn_addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ } else {
+ sprintf(arg->buffer + arg->len,
+ "00000000000000000000000000000000");
+ arg->len += 32;
+ }
+ arg->len += sprintf(arg->buffer + arg->len,
+ " %08lx %08x %08x %08lx %8s\n",
+ rt->rt6i_metric, rt->rt6i_use,
+ rt->rt6i_ref, rt->rt6i_flags,
+ rt->rt6i_dev ? rt->rt6i_dev->name : "");
+ }
+}
+
+static int rt6_proc_info(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ struct rt6_proc_arg arg;
+ arg.buffer = buffer;
+ arg.offset = offset;
+ arg.length = length;
+ arg.skip = 0;
+ arg.len = 0;
+
+ fib6_walk_tree(&ip6_routing_table, rt6_info_node, &arg,
+ RT6_FILTER_RTNODES);
+
+ rt6_info_node(&ip6_routing_table, &arg);
+
+ *start = buffer;
+ if (offset)
+ *start += offset % RT6_INFO_LEN;
+
+ arg.len -= offset % RT6_INFO_LEN;
+
+ if(arg.len > length)
+ arg.len = length;
+ if(arg.len < 0)
+ arg.len = 0;
+
+ return arg.len;
+}
+
+#define PTR_SZ (sizeof(void *) * 2)
+#define FI_LINE_SZ (2 * (PTR_SZ) + 7 + 32 + 4 + 32 + 4)
+
+static void rt6_tree_node(struct fib6_node *fn, void *p_arg)
+{
+ struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
+ struct rt6_info *rt;
+ char f;
+ int i;
+
+ rt = fn->leaf;
+
+ if (arg->skip < arg->offset / FI_LINE_SZ) {
+ arg->skip++;
+ return;
+ }
+
+ if (arg->len + FI_LINE_SZ >= arg->length)
+ return;
+
+ f = (fn->fn_flags & RTN_RTINFO) ? 'r' : 'n';
+ arg->len += sprintf(arg->buffer + arg->len, "%p %p %02x %c ",
+ fn, fn->parent, fn->fn_bit, f);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_dst.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x ",
+ rt->rt6i_dst.plen);
+
+ for (i=0; i<16; i++) {
+ sprintf(arg->buffer + arg->len, "%02x",
+ rt->rt6i_src.addr.s6_addr[i]);
+ arg->len += 2;
+ }
+ arg->len += sprintf(arg->buffer + arg->len, " %02x\n",
+ rt->rt6i_src.plen);
+
+}
+
+static int rt6_proc_tree(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ struct rt6_proc_arg arg;
+ arg.buffer = buffer;
+ arg.offset = offset;
+ arg.length = length;
+ arg.skip = 0;
+ arg.len = 0;
+
+ fib6_walk_tree(&ip6_routing_table, rt6_tree_node, &arg, 0);
+
+ *start = buffer;
+ if (offset)
+ *start += offset % RT6_INFO_LEN;
+
+ arg.len -= offset % RT6_INFO_LEN;
+
+ if(arg.len > length)
+ arg.len = length;
+ if(arg.len < 0)
+ arg.len = 0;
+
+ return arg.len;
+}
+
+extern struct rt6_statistics rt6_stats;
+
+static int rt6_proc_stats(char *buffer, char **start, off_t offset, int length,
+ int dummy)
+{
+ int len;
+
+ len = sprintf(buffer, "%04x %04x %04x %04x %04x\n",
+ rt6_stats.fib_nodes, rt6_stats.fib_route_nodes,
+ rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries,
+ rt6_stats.fib_rt_cache);
+
+ len -= offset;
+
+ if (len > length)
+ len = length;
+ if(len < 0)
+ len = 0;
+
+ *start = buffer + offset;
+
+ return len;
+}
+
+static struct proc_dir_entry proc_rt6_info = {
+ PROC_NET_RT6, 10, "ipv6_route",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_info
+};
+static struct proc_dir_entry proc_rt6_stats = {
+ PROC_NET_RT6_STATS, 9, "rt6_stats",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_stats
+};
+static struct proc_dir_entry proc_rt6_tree = {
+ PROC_NET_RT6_TREE, 7, "ip6_fib",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rt6_proc_tree
+};
+#endif /* CONFIG_PROC_FS */
+
+void ip6_route_init(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_rt6_info);
+ proc_net_register(&proc_rt6_stats);
+ proc_net_register(&proc_rt6_tree);
+#endif
+ netlink_attach(NETLINK_ROUTE6, rt6_msgrcv);
+}
+
+#ifdef MODULE
+void ip6_route_cleanup(void)
+{
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_RT6);
+ proc_net_unregister(PROC_NET_RT6_TREE);
+ proc_net_unregister(PROC_NET_RT6_STATS);
+#endif
+ netlink_detach(NETLINK_ROUTE6);
+#if 0
+ fib6_flush();
+#endif
+}
+#endif /* MODULE */
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index f96b62229..4b072889c 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -5,6 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
+ * $Id: sit.c,v 1.13 1997/03/18 18:24:50 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,7 +31,6 @@
#include <net/protocol.h>
#include <net/transp_v6.h>
#include <net/ndisc.h>
-#include <net/ipv6_route.h>
#include <net/addrconf.h>
#include <net/ip.h>
#include <net/udp.h>
@@ -51,23 +51,15 @@ static void sit_mtu_cache_gc(void);
static int sit_xmit(struct sk_buff *skb,
struct device *dev);
-static int sit_rcv(struct sk_buff *skb,
- struct device *dev,
- struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo,
- struct inet_protocol * protocol);
+static int sit_rcv(struct sk_buff *skb, unsigned short len);
+static void sit_err(struct sk_buff *skb, unsigned char *dp);
static int sit_open(struct device *dev);
static int sit_close(struct device *dev);
-static struct enet_statistics * sit_get_stats(struct device *dev);
+static struct net_device_stats *sit_get_stats(struct device *dev);
-static void sit_err(int type, int code,
- unsigned char *buff, __u32 info,
- __u32 daddr, __u32 saddr,
- struct inet_protocol *protocol,
- int len);
+extern void udp_err(struct sk_buff *, unsigned char *);
static struct inet_protocol sit_protocol = {
sit_rcv,
@@ -126,10 +118,8 @@ static struct sit_mtu_info * sit_mtu_lookup(__u32 addr)
hash = sit_addr_hash(addr);
- for(iter = sit_mtu_cache[hash]; iter; iter=iter->next)
- {
- if (iter->addr == addr)
- {
+ for(iter = sit_mtu_cache[hash]; iter; iter=iter->next) {
+ if (iter->addr == addr) {
iter->tstamp = jiffies;
break;
}
@@ -139,8 +129,7 @@ static struct sit_mtu_info * sit_mtu_lookup(__u32 addr)
* run garbage collector
*/
- if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY)
- {
+ if (jiffies - sit_gc_last_run > SIT_GC_FREQUENCY) {
sit_mtu_cache_gc();
sit_gc_last_run = jiffies;
}
@@ -154,26 +143,19 @@ static void sit_mtu_cache_gc(void)
unsigned long now = jiffies;
int i;
- for (i=0; i < SIT_NUM_BUCKETS; i++)
- {
+ for (i=0; i < SIT_NUM_BUCKETS; i++) {
back = NULL;
- for (iter = sit_mtu_cache[i]; iter;)
- {
- if (now - iter->tstamp > SIT_GC_TIMEOUT)
- {
+ for (iter = sit_mtu_cache[i]; iter;) {
+ if (now - iter->tstamp > SIT_GC_TIMEOUT) {
struct sit_mtu_info *old;
old = iter;
iter = iter->next;
if (back)
- {
back->next = iter;
- }
else
- {
sit_mtu_cache[i] = iter;
- }
kfree(old);
continue;
@@ -194,12 +176,12 @@ static int sit_init_dev(struct device *dev)
dev->hard_start_xmit = sit_xmit;
dev->get_stats = sit_get_stats;
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
for (i = 0; i < DEV_NUMBUFFS; i++)
@@ -208,7 +190,7 @@ static int sit_init_dev(struct device *dev)
dev->hard_header = NULL;
dev->rebuild_header = NULL;
dev->set_mac_address = NULL;
- dev->header_cache_bind = NULL;
+ dev->hard_header_cache = NULL;
dev->header_cache_update= NULL;
dev->type = ARPHRD_SIT;
@@ -216,7 +198,7 @@ static int sit_init_dev(struct device *dev)
dev->hard_header_len = MAX_HEADER;
dev->mtu = 1500 - sizeof(struct iphdr);
dev->addr_len = 0;
- dev->tx_queue_len = 2;
+ dev->tx_queue_len = 0;
memset(dev->broadcast, 0, MAX_ADDR_LEN);
memset(dev->dev_addr, 0, MAX_ADDR_LEN);
@@ -238,12 +220,12 @@ static int sit_init_vif(struct device *dev)
int i;
dev->flags = IFF_NOARP|IFF_POINTOPOINT|IFF_MULTICAST;
- dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+ dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
for (i = 0; i < DEV_NUMBUFFS; i++)
skb_queue_head_init(&dev->buffs[i]);
@@ -261,7 +243,6 @@ static int sit_close(struct device *dev)
return 0;
}
-
int sit_init(void)
{
int i;
@@ -269,9 +250,7 @@ int sit_init(void)
/* register device */
if (register_netdev(&sit_device) != 0)
- {
return -EIO;
- }
inet_add_protocol(&sit_protocol);
@@ -322,9 +301,8 @@ struct device *sit_add_tunnel(__u32 dstaddr)
void sit_cleanup(void)
{
struct sit_vif *vif;
-
- for (vif = vif_list; vif;)
- {
+
+ for (vif = vif_list; vif;) {
struct device *dev = vif->dev;
struct sit_vif *cur;
@@ -343,29 +321,26 @@ void sit_cleanup(void)
}
-
-
/*
* receive IPv4 ICMP messages
*/
-static void sit_err(int type, int code, unsigned char *buff, __u32 info,
- __u32 daddr, __u32 saddr, struct inet_protocol *protocol,
- int len)
-
+static void sit_err(struct sk_buff *skb, unsigned char *dp)
{
- if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
- {
- struct sit_mtu_info *minfo;
+ struct iphdr *iph = (struct iphdr*)dp;
+ int type = skb->h.icmph->type;
+ int code = skb->h.icmph->code;
- info -= sizeof(struct iphdr);
+ if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+ struct sit_mtu_info *minfo;
+ unsigned short info = skb->h.icmph->un.frag.mtu - sizeof(struct iphdr);
- minfo = sit_mtu_lookup(daddr);
+ minfo = sit_mtu_lookup(iph->daddr);
- printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(saddr),
+ printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(iph->saddr),
info);
- if (minfo == NULL)
- {
+
+ if (minfo == NULL) {
minfo = kmalloc(sizeof(struct sit_mtu_info),
GFP_ATOMIC);
@@ -373,46 +348,40 @@ static void sit_err(int type, int code, unsigned char *buff, __u32 info,
return;
start_bh_atomic();
- sit_cache_insert(daddr, info);
+ sit_cache_insert(iph->daddr, info);
end_bh_atomic();
- }
- else
- {
+ } else {
minfo->mtu = info;
}
}
}
-static int sit_rcv(struct sk_buff *skb, struct device *idev,
- struct options *opt,
- __u32 daddr, unsigned short len,
- __u32 saddr, int redo, struct inet_protocol * protocol)
+static int sit_rcv(struct sk_buff *skb, unsigned short len)
{
- struct enet_statistics *stats;
+ struct net_device_stats *stats;
struct device *dev = NULL;
struct sit_vif *vif;
+ __u32 saddr = skb->nh.iph->saddr;
- skb->h.raw = skb_pull(skb, skb->h.raw - skb->data);
+ skb->h.raw = skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+
skb->protocol = __constant_htons(ETH_P_IPV6);
- for (vif = vif_list; vif; vif = vif->next)
- {
- if (saddr == vif->dev->pa_dstaddr)
- {
+ for (vif = vif_list; vif; vif = vif->next) {
+ if (saddr == vif->dev->pa_dstaddr) {
dev = vif->dev;
break;
}
}
if (dev == NULL)
- {
dev = &sit_device;
- }
skb->dev = dev;
skb->ip_summed = CHECKSUM_NONE;
- stats = (struct enet_statistics *)dev->priv;
+ stats = (struct net_device_stats *)dev->priv;
+ stats->rx_bytes += len;
stats->rx_packets++;
ipv6_rcv(skb, dev, NULL);
@@ -421,145 +390,100 @@ static int sit_rcv(struct sk_buff *skb, struct device *idev,
static int sit_xmit(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats;
+ struct net_device_stats *stats;
struct sit_mtu_info *minfo;
struct in6_addr *addr6;
- unsigned long flags;
struct rtable *rt;
struct iphdr *iph;
__u32 saddr;
__u32 daddr;
- __u32 raddr;
int addr_type;
int mtu;
- int len;
+ int headroom;
/*
* Make sure we are not busy (check lock variable)
*/
- stats = (struct enet_statistics *)dev->priv;
- save_flags(flags);
- cli();
- if (dev->tbusy != 0)
- {
- restore_flags(flags);
- printk(KERN_DEBUG "sit_xmit: busy\n");
- return(1);
- }
- dev->tbusy = 1;
- restore_flags(flags);
+ stats = (struct net_device_stats *)dev->priv;
daddr = dev->pa_dstaddr;
- if (daddr == 0)
- {
- struct neighbour *neigh;
+ if (daddr == 0) {
+ struct nd_neigh *neigh = NULL;
- neigh = skb->nexthop;
- if (neigh == NULL)
- {
+ if (skb->dst)
+ neigh = (struct nd_neigh *) skb->dst->neighbour;
+
+ if (neigh == NULL) {
printk(KERN_DEBUG "sit: nexthop == NULL\n");
goto on_error;
}
- addr6 = &neigh->addr;
+ addr6 = &neigh->ndn_addr;
addr_type = ipv6_addr_type(addr6);
- if (addr_type == IPV6_ADDR_ANY)
- {
- addr6 = &skb->ipv6_hdr->daddr;
+ if (addr_type == IPV6_ADDR_ANY) {
+ addr6 = &skb->nh.ipv6h->daddr;
addr_type = ipv6_addr_type(addr6);
}
- if ((addr_type & IPV6_ADDR_COMPATv4) == 0)
- {
+ if ((addr_type & IPV6_ADDR_COMPATv4) == 0) {
printk(KERN_DEBUG "sit_xmit: non v4 address\n");
goto on_error;
}
daddr = addr6->s6_addr32[3];
}
- len = skb->tail - (skb->data + sizeof(struct ipv6hdr));
-
- if (skb->sk)
- {
- atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
- }
-
- skb->sk = NULL;
-
- iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr));
-
- skb->protocol = htons(ETH_P_IP);
-
- /* get route */
-
- rt = ip_rt_route(daddr, skb->localroute);
-
- if (rt == NULL)
- {
+ if (ip_route_output(&rt, daddr, 0, 0, NULL)) {
printk(KERN_DEBUG "sit: no route to host\n");
goto on_error;
}
minfo = sit_mtu_lookup(daddr);
+ /* IP should calculate pmtu correctly,
+ * let's check it...
+ */
+#if 0
if (minfo)
mtu = minfo->mtu;
else
- mtu = rt->rt_dev->mtu;
+#endif
+ mtu = rt->u.dst.pmtu;
- if (mtu > 576 && len > mtu)
- {
+ if (mtu > 576 && skb->tail - (skb->data + sizeof(struct ipv6hdr)) > mtu) {
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev);
+ ip_rt_put(rt);
goto on_error;
}
- saddr = rt->rt_src;
- skb->dev = rt->rt_dev;
- raddr = rt->rt_gateway;
-
- if (raddr == 0)
- raddr = daddr;
-
- /* now for the device header */
+ headroom = ((rt->u.dst.dev->hard_header_len+15)&~15)+sizeof(struct iphdr);
- skb->arp = 1;
-
- if (skb->dev->hard_header_len)
- {
- int mac;
-
- if (skb->data - skb->head < skb->dev->hard_header_len)
- {
- printk(KERN_DEBUG "sit: space at head < dev header\n");
+ if (skb_headroom(skb) < headroom || skb_shared(skb)) {
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, headroom);
+ if (!new_skb) {
+ ip_rt_put(rt);
goto on_error;
}
-
- if (skb->dev->hard_header)
- {
- mac = skb->dev->hard_header(skb, skb->dev, ETH_P_IP,
- NULL, NULL, len);
-
- if (mac < 0)
- skb->arp = 0;
-
- skb->raddr = raddr;
- }
-
+ dev_kfree_skb(skb, FREE_WRITE);
+ skb = new_skb;
}
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- ip_rt_put(rt);
+ iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr));
+ skb->nh.iph = iph;
+ saddr = rt->rt_src;
+ dst_release(skb->dst);
+ skb->dst = &rt->u.dst;
iph->version = 4;
iph->ihl = 5;
iph->tos = 0; /* tos set to 0... */
if (mtu > 576)
- {
iph->frag_off = htons(IP_DF);
- }
else
iph->frag_off = 0;
@@ -567,33 +491,24 @@ static int sit_xmit(struct sk_buff *skb, struct device *dev)
iph->saddr = saddr;
iph->daddr = daddr;
iph->protocol = IPPROTO_IPV6;
- skb->ip_hdr = iph;
-
+ iph->tot_len = htons(skb->len);
+ iph->id = htons(ip_id_count++);
ip_send_check(iph);
- ip_queue_xmit(NULL, skb->dev, skb, 1);
+ ip_send(skb);
+ stats->tx_bytes += skb->len;
stats->tx_packets++;
- dev->tbusy=0;
return 0;
- on_error:
- kfree_skb(skb, FREE_WRITE);
- dev->tbusy=0;
+on_error:
+ dev_kfree_skb(skb, FREE_WRITE);
stats->tx_errors++;
return 0;
}
-static struct enet_statistics *sit_get_stats(struct device *dev)
+static struct net_device_stats *sit_get_stats(struct device *dev)
{
- return((struct enet_statistics*) dev->priv);
+ return((struct net_device_stats *) dev->priv);
}
-
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o sit.o sit.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index ce7bb4681..212bcbc3e 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -4,45 +4,114 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
#include <linux/in6.h>
#include <linux/ipv6.h>
#include <net/ndisc.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
+struct ipv6_config ipv6_config =
+{
+ 0, /* forwarding */
+ IPV6_DEFAULT_HOPLIMIT, /* hop limit */
+ 1, /* accept RAs */
+ 1, /* accept redirects */
+
+ 3, /* nd_max_mcast_solicit */
+ 3, /* nd_max_ucast_solicit */
+ RETRANS_TIMER, /* nd_retrans_time */
+ RECHABLE_TIME, /* nd_base_reach_time */
+ (5 * HZ), /* nd_delay_probe_time */
+
+ 1, /* autoconfiguration */
+ 1, /* dad transmits */
+ MAX_RTR_SOLICITATIONS, /* router solicits */
+ RTR_SOLICITATION_INTERVAL, /* rtr solicit interval */
+ MAX_RTR_SOLICITATION_DELAY, /* rtr solicit delay */
+
+ 60*HZ, /* rt cache timeout */
+ 30*HZ, /* rt gc period */
+};
-int ipv6_hop_limit = IPV6_DEFAULT_HOPLIMIT;
+#ifdef CONFIG_SYSCTL
int ipv6_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
- int val = ipv6_forwarding;
+ int val = ipv6_config.forwarding;
int retv;
retv = proc_dointvec(ctl, write, filp, buffer, lenp);
- if (write)
- {
- if (ipv6_forwarding && val == 0) {
+ if (write) {
+ if (ipv6_config.forwarding && val == 0) {
printk(KERN_DEBUG "sysctl: IPv6 forwarding enabled\n");
ndisc_forwarding_on();
addrconf_forwarding_on();
}
- if (ipv6_forwarding == 0 && val) {
+ if (ipv6_config.forwarding == 0 && val)
ndisc_forwarding_off();
- }
}
return retv;
}
ctl_table ipv6_table[] = {
- {NET_IPV6_FORWARDING, "ipv6_forwarding",
- &ipv6_forwarding, sizeof(int), 0644, NULL,
+ {NET_IPV6_FORWARDING, "forwarding",
+ &ipv6_config.forwarding, sizeof(int), 0644, NULL,
&ipv6_sysctl_forwarding},
- {NET_IPV6_HOPLIMIT, "ipv6_hop_limit",
- &ipv6_hop_limit, sizeof(int), 0644, NULL,
+ {NET_IPV6_HOPLIMIT, "hop_limit",
+ &ipv6_config.hop_limit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_RA, "accept_ra",
+ &ipv6_config.accept_ra, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects",
+ &ipv6_config.accept_redirects, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_MAX_MCAST_SOLICIT, "nd_max_mcast_solicit",
+ &ipv6_config.nd_max_mcast_solicit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_MAX_UCAST_SOLICIT, "nd_max_ucast_solicit",
+ &ipv6_config.nd_max_ucast_solicit, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_RETRANS_TIME, "nd_retrans_time",
+ &ipv6_config.nd_retrans_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_REACHABLE_TIME, "nd_base_reachble_time",
+ &ipv6_config.nd_base_reachable_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_ND_DELAY_PROBE_TIME, "nd_delay_first_probe_time",
+ &ipv6_config.nd_delay_probe_time, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_AUTOCONF, "autoconf",
+ &ipv6_config.autoconf, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_DAD_TRANSMITS, "dad_transmits",
+ &ipv6_config.dad_transmits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICITS, "router_solicitations",
+ &ipv6_config.rtr_solicits, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval",
+ &ipv6_config.rtr_solicit_interval, sizeof(int), 0644, NULL,
+ &proc_dointvec},
+
+ {NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay",
+ &ipv6_config.rtr_solicit_delay, sizeof(int), 0644, NULL,
&proc_dointvec},
{0}
@@ -73,6 +142,9 @@ void ipv6_sysctl_unregister(void)
{
unregister_sysctl_table(ipv6_sysctl_header);
}
+#endif /* MODULE */
+
+#endif /* CONFIG_SYSCTL */
+
-#endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index bb03b34dd..5151013a7 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: tcp_ipv6.c,v 1.15 1996/10/29 22:45:53 roque Exp $
+ * $Id: tcp_ipv6.c,v 1.27 1997/04/22 02:53:20 davem Exp $
*
* Based on:
* linux/net/ipv4/tcp.c
@@ -37,24 +37,216 @@
#include <net/ipv6.h>
#include <net/transp_v6.h>
#include <net/addrconf.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <asm/uaccess.h>
-static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
- struct tcphdr *th, struct proto *prot,
- struct ipv6_options *opt,
- struct device *dev, int pri, int hop_limit);
+extern int sysctl_tcp_sack;
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
-static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
- struct sk_buff *skb);
+static void tcp_v6_send_reset(struct in6_addr *saddr,
+ struct in6_addr *daddr,
+ struct tcphdr *th, struct proto *prot,
+ struct ipv6_options *opt,
+ struct device *dev, int pri, int hop_limit);
-static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
-static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
+ struct sk_buff *skb);
+
+static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb);
+static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb);
+static void tcp_v6_xmit(struct sk_buff *skb);
static struct tcp_func ipv6_mapped;
static struct tcp_func ipv6_specific;
+/* I have no idea if this is a good hash for v6 or not. -DaveM */
+static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
+ struct in6_addr *faddr, u16 fport)
+{
+ int hashent = (lport ^ fport);
+
+ hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
+ hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
+ hashent ^= (faddr->s6_addr32[2] ^ faddr->s6_addr32[3]);
+ return (hashent & ((TCP_HTABLE_SIZE/2) - 1));
+}
+
+static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
+{
+ struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
+ struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
+ __u16 lport = sk->num;
+ __u16 fport = sk->dummy_th.dest;
+ return tcp_v6_hashfn(laddr, lport, faddr, fport);
+}
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
+ for(; sk2 != NULL; sk2 = sk2->bind_next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+ if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+
+ return retval;
+}
+
+static void tcp_v6_hash(struct sock *sk)
+{
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(state != TCP_CLOSE) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN)
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ else
+ skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_unhash(struct sock *sk)
+{
+ SOCKHASH_LOCK();
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_rehash(struct sock *sk)
+{
+ unsigned char state;
+
+ SOCKHASH_LOCK();
+ state = sk->state;
+ if(sk->pprev) {
+ if(sk->next)
+ sk->next->pprev = sk->pprev;
+ *sk->pprev = sk->next;
+ sk->pprev = NULL;
+ tcp_sk_unbindify(sk);
+ }
+ if(state != TCP_CLOSE) {
+ struct sock **skp;
+
+ if(state == TCP_LISTEN) {
+ skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+ } else {
+ int hash = tcp_v6_sk_hashfn(sk);
+ if(state == TCP_TIME_WAIT)
+ hash += (TCP_HTABLE_SIZE/2);
+ skp = &tcp_established_hash[hash];
+ }
+ if((sk->next = *skp) != NULL)
+ (*skp)->pprev = &sk->next;
+ *skp = sk;
+ sk->pprev = skp;
+ tcp_sk_bindify(sk);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *tcp_v6_lookup_listener(struct in6_addr *daddr, unsigned short hnum)
+{
+ struct sock *sk;
+ struct sock *result = NULL;
+
+ sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+ for(; sk; sk = sk->next) {
+ if((sk->num == hnum) && (sk->family == AF_INET6)) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(!ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ return sk; /* Best possible match. */
+ } else if(!result)
+ result = sk;
+ }
+ }
+ return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
+ struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport)
+{
+ unsigned short hnum = ntohs(dport);
+ struct sock *sk;
+ int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+
+ /* Optimize here for direct hit, only listening connections can
+ * have wildcards anyways. It is assumed that this code only
+ * gets called from within NET_BH.
+ */
+ for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+ /* For IPV6 do the cheaper port and family tests first. */
+ if(sk->num == hnum && /* local port */
+ sk->family == AF_INET6 && /* address family */
+ sk->dummy_th.dest == sport && /* remote port */
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ goto hit; /* You sunk my battleship! */
+
+ /* Must check for a TIME_WAIT'er before going to listener hash. */
+ for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+ if(sk->num == hnum && /* local port */
+ sk->family == AF_INET6 && /* address family */
+ sk->dummy_th.dest == sport && /* remote port */
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr) &&
+ !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+ goto hit;
+
+ sk = tcp_v6_lookup_listener(daddr, hnum);
+hit:
+ return sk;
+}
+
+#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp))
+
static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
struct in6_addr *saddr,
struct in6_addr *daddr,
@@ -68,15 +260,12 @@ static __u32 tcp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
__u32 si;
__u32 di;
- if (skb->protocol == __constant_htons(ETH_P_IPV6))
- {
- si = skb->ipv6_hdr->saddr.s6_addr32[3];
- di = skb->ipv6_hdr->daddr.s6_addr32[3];
- }
- else
- {
- si = skb->saddr;
- di = skb->daddr;
+ if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+ si = skb->nh.ipv6h->saddr.s6_addr32[3];
+ di = skb->nh.ipv6h->daddr.s6_addr32[3];
+ } else {
+ si = skb->nh.iph->saddr;
+ di = skb->nh.iph->daddr;
}
return secure_tcp_sequence_number(di, si,
@@ -90,14 +279,15 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
- struct dest_entry *dc;
struct inet6_ifaddr *ifa;
+ struct in6_addr *saddr = NULL;
+ struct flowi fl;
+ struct dst_entry *dst;
struct tcphdr *th;
- __u8 *ptr;
struct sk_buff *buff;
struct sk_buff *skb1;
- int addr_type;
int tmp;
+ int addr_type;
if (sk->state != TCP_CLOSE)
return(-EISCONN);
@@ -125,9 +315,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
addr_type = ipv6_addr_type(&usin->sin6_addr);
if(addr_type & IPV6_ADDR_MULTICAST)
- {
return -ENETUNREACH;
- }
/*
* connect to self not allowed
@@ -135,9 +323,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (ipv6_addr_cmp(&usin->sin6_addr, &np->saddr) == 0 &&
usin->sin6_port == sk->dummy_th.source)
- {
return (-EINVAL);
- }
memcpy(&np->daddr, &usin->sin6_addr, sizeof(struct in6_addr));
@@ -145,24 +331,22 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
* TCP over IPv4
*/
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
int err;
- printk(KERN_DEBUG "connect: ipv4 mapped\n");
+ SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
sin.sin_family = AF_INET;
sin.sin_port = usin->sin6_port;
sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
sk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
- sk->backlog_rcv = tcp_v4_backlog_rcv;
+ sk->backlog_rcv = tcp_v4_do_rcv;
err = tcp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
- if (err)
- {
+ if (err) {
sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
sk->backlog_rcv = tcp_v6_backlog_rcv;
}
@@ -170,31 +354,49 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
return err;
}
- dc = ipv6_dst_route(&np->daddr, NULL, (sk->localroute ? RTI_GATEWAY : 0));
+ if (!ipv6_addr_any(&np->rcv_saddr))
+ saddr = &np->rcv_saddr;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = NULL;
+ fl.uli_u.ports.dport = usin->sin6_port;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
- if (dc == NULL)
- {
- return -ENETUNREACH;
+ if (dst->error) {
+ dst_release(dst);
+ return dst->error;
}
- np->dest = dc;
- np->dc_sernum = (dc->rt.fib_node ? dc->rt.fib_node->fn_sernum : 0);
+ ip6_dst_store(sk, dst);
- ifa = ipv6_get_saddr((struct rt6_info *)dc, &np->daddr);
+ np->oif = dst->dev;
- if (ifa == NULL)
- {
- return -ENETUNREACH;
+ if (saddr == NULL) {
+ ifa = ipv6_get_saddr(dst, &np->daddr);
+
+ if (ifa == NULL)
+ return -ENETUNREACH;
+
+ saddr = &ifa->addr;
+
+ /* set the source address */
+ ipv6_addr_copy(&np->rcv_saddr, saddr);
+ ipv6_addr_copy(&np->saddr, saddr);
}
-
+ /* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */
+
/*
* Init variables
*/
lock_sock(sk);
- sk->dummy_th.dest = usin->sin6_port;
+ sk->dummy_th.dest = usin->sin6_port;
sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
np->daddr.s6_addr32[3],
sk->dummy_th.source,
@@ -214,20 +416,11 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
if (buff == NULL)
- {
return(-ENOMEM);
- }
+
lock_sock(sk);
- buff->sk = sk;
- buff->free = 0;
- buff->localroute = sk->localroute;
-
- tmp = tcp_v6_build_header(sk, buff);
- /* set the source address */
-
- memcpy(&np->saddr, &ifa->addr, sizeof(struct in6_addr));
- memcpy(&np->rcv_saddr, &ifa->addr, sizeof(struct in6_addr));
+ tcp_v6_build_header(sk, buff);
/* build the tcp header */
th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
@@ -241,60 +434,57 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
th->ack = 0;
th->window = 2;
th->syn = 1;
- th->doff = 6;
-
- sk->window_clamp=0;
- if ((dc->dc_flags & DCF_PMTU))
- sk->mtu = dc->dc_pmtu;
- else
- sk->mtu = dc->rt.rt_dev->mtu;
+ tp->window_clamp = 0;
+ sk->mtu = dst->pmtu;
sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
/*
* Put in the TCP options to say MTU.
*/
- ptr = skb_put(buff,4);
- ptr[0] = 2;
- ptr[1] = 4;
- ptr[2] = (sk->mss) >> 8;
- ptr[3] = (sk->mss) & 0xff;
- buff->csum = csum_partial(ptr, 4, 0);
+ tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
+ sysctl_tcp_timestamps,
+ sysctl_tcp_window_scaling?tp->rcv_wscale:0);
+ th->doff = sizeof(*th)/4 + (tmp>>2);
+ buff->csum = 0;
+ tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + tmp, buff);
- tcp_v6_send_check(sk, th, sizeof(struct tcphdr) + 4, buff);
-
tcp_set_state(sk, TCP_SYN_SENT);
-
+
+ /* Socket identity change complete, no longer
+ * in TCP_CLOSE, so rehash.
+ */
+ sk->prot->rehash(sk);
+
/* FIXME: should use dcache->rtt if availiable */
tp->rto = TCP_TIMEOUT_INIT;
tcp_init_xmit_timers(sk);
- sk->retransmits = 0;
+ tp->retransmits = 0;
skb_queue_tail(&sk->write_queue, buff);
- sk->packets_out++;
+ tp->packets_out++;
buff->when = jiffies;
skb1 = skb_clone(buff, GFP_KERNEL);
- sk->wmem_alloc += skb1->truesize;
+ skb_set_owner_w(skb1, sk);
- tmp = ipv6_xmit(sk, skb1, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
+ tcp_v6_xmit(skb1);
/* Timer for repeating the SYN until an answer */
tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
tcp_statistics.TcpActiveOpens++;
tcp_statistics.TcpOutSegs++;
-
+
release_sock(sk);
-
- return(tmp);
+
+ return(0);
}
-static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
- int len, int nonblock, int flags)
+static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
int retval = -EINVAL;
@@ -303,7 +493,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
* Do sanity checking for sendmsg/sendto/send
*/
- if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+ if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
goto out;
if (msg->msg_name) {
struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name;
@@ -326,7 +516,7 @@ static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
lock_sock(sk);
retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
- len, nonblock, flags);
+ msg->msg_flags);
release_sock(sk);
@@ -344,123 +534,117 @@ void tcp_v6_err(int type, int code, unsigned char *header, __u32 info,
int err;
int opening;
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+ sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
if (sk == NULL)
- {
return;
- }
np = &sk->net_pinfo.af_inet6;
- if (type == ICMPV6_PKT_TOOBIG)
- {
+ if (type == ICMPV6_PKT_TOOBIG) {
/* icmp should have updated the destination cache entry */
- np->dest = ipv6_dst_check(np->dest, &np->daddr, np->dc_sernum,
- 0);
+ dst_check(&np->dst, np->dst_cookie);
- np->dc_sernum = (np->dest->rt.fib_node ?
- np->dest->rt.fib_node->fn_sernum : 0);
+ if (np->dst == NULL) {
+ struct flowi fl;
+ struct dst_entry *dst;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
- if (np->dest->dc_flags & DCF_PMTU)
- sk->mtu = np->dest->dc_pmtu;
+ dst = ip6_route_output(sk, &fl);
- sk->mtu = (sk->mtu - sizeof(struct ipv6hdr) -
- sizeof(struct tcphdr));
+ ip6_dst_store(sk, dst);
+ }
+
+ if (np->dst->error)
+ sk->err_soft = np->dst->error;
+ else
+ sk->mtu = np->dst->pmtu;
return;
}
opening = (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV);
- if (icmpv6_err_convert(type, code, &err) || opening)
- {
+ if (icmpv6_err_convert(type, code, &err) || opening) {
sk->err = err;
- if (opening)
- {
+
+ if (opening) {
tcp_statistics.TcpAttemptFails++;
tcp_set_state(sk,TCP_CLOSE);
sk->error_report(sk);
}
- }
- else
+ } else {
sk->err_soft = err;
+ }
}
static void tcp_v6_send_synack(struct sock *sk, struct open_request *req)
{
- struct tcp_v6_open_req *af_req = (struct tcp_v6_open_req *) req;
struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct sk_buff * skb;
struct tcphdr *th;
- unsigned char *ptr;
- struct dest_entry *dc;
- int mss;
+ struct dst_entry *dst;
+ struct flowi fl;
+ int tmp;
skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
-
if (skb == NULL)
- {
return;
- }
- skb_reserve(skb, (MAX_HEADER + 15) & ~15);
- skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &req->af.v6_req.rmt_addr;
+ fl.nl_u.ip6_u.saddr = &req->af.v6_req.loc_addr;
+ fl.dev = req->af.v6_req.dev;
+ fl.uli_u.ports.dport = req->rmt_port;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
- dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
+ dst = ip6_route_output(sk, &fl);
+ if (dst->error) {
+ kfree_skb(skb, FREE_WRITE);
+ dst_release(dst);
+ return;
+ }
- skb->dev = af_req->dev;
-
- if (dc)
- {
- if (dc->dc_flags & DCF_PMTU)
- mss = dc->dc_pmtu;
- else
- mss = dc->dc_nexthop->dev->mtu;
- mss -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr);
+ skb->dev = dst->dev;
+ skb_reserve(skb, (skb->dev->hard_header_len + 15) & ~15);
+ skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb,sizeof(struct ipv6hdr));
- ipv6_dst_unlock(dc);
- }
- else
- mss = 516;
+ skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
- th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
- skb->h.th = th;
+ /* Yuck, make this header setup more efficient... -DaveM */
memset(th, 0, sizeof(struct tcphdr));
-
th->syn = 1;
th->ack = 1;
-
th->source = sk->dummy_th.source;
th->dest = req->rmt_port;
-
skb->seq = req->snt_isn;
skb->end_seq = skb->seq + 1;
-
th->seq = ntohl(skb->seq);
th->ack_seq = htonl(req->rcv_isn + 1);
th->doff = sizeof(*th)/4 + 1;
th->window = ntohs(tp->rcv_wnd);
- ptr = skb_put(skb, TCPOLEN_MSS);
- ptr[0] = TCPOPT_MSS;
- ptr[1] = TCPOLEN_MSS;
- ptr[2] = (mss >> 8) & 0xff;
- ptr[3] = mss & 0xff;
- skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
+ tmp = tcp_syn_build_options(skb, sk->mss, req->sack_ok, req->tstamp_ok,
+ (req->snd_wscale)?tp->rcv_wscale:0);
+ th->doff = sizeof(*th)/4 + (tmp>>2);
+ th->check = tcp_v6_check(th, sizeof(*th) + tmp,
+ &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
+ csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
- th->check = tcp_v6_check(th, sizeof(*th) + TCPOLEN_MSS, &af_req->loc_addr,
- &af_req->rmt_addr,
- csum_partial((char *)th, sizeof(*th), skb->csum));
+ ip6_dst_store(sk, dst);
+ ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
+ dst_release(dst);
- ipv6_xmit(sk, skb, &af_req->loc_addr, &af_req->rmt_addr, af_req->opt,
- IPPROTO_TCP);
-
tcp_statistics.TcpOutSegs++;
-
}
static void tcp_v6_or_free(struct open_request *req)
@@ -475,71 +659,55 @@ static struct or_calltable or_ipv6 = {
static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
__u32 isn)
{
- struct tcp_v6_open_req *af_req;
+ struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
struct open_request *req;
+ __u16 req_mss;
/* If the socket is dead, don't accept the connection. */
- if (sk->dead)
- {
- if(sk->debug)
- {
- printk("Reset on %p: Connect on dead socket.\n",sk);
- }
+ if (sk->dead) {
+ SOCK_DEBUG(sk, "Reset on %p: Connect on dead socket.\n", sk);
tcp_statistics.TcpAttemptFails++;
- return -ENOTCONN;
+ return -ENOTCONN;
}
if (skb->protocol == __constant_htons(ETH_P_IP))
- {
return tcp_v4_conn_request(sk, skb, ptr, isn);
- }
/*
* There are no SYN attacks on IPv6, yet...
*/
- if (sk->ack_backlog >= sk->max_ack_backlog)
- {
+ if (sk->ack_backlog >= sk->max_ack_backlog) {
printk(KERN_DEBUG "droping syn ack:%d max:%d\n",
sk->ack_backlog, sk->max_ack_backlog);
tcp_statistics.TcpAttemptFails++;
goto exit;
}
- af_req = kmalloc(sizeof(struct tcp_v6_open_req), GFP_ATOMIC);
-
- if (af_req == NULL)
- {
+ req = tcp_openreq_alloc();
+ if (req == NULL) {
tcp_statistics.TcpAttemptFails++;
goto exit;
}
sk->ack_backlog++;
- req = (struct open_request *) af_req;
-
- memset(af_req, 0, sizeof(struct tcp_v6_open_req));
req->rcv_isn = skb->seq;
req->snt_isn = isn;
- /* mss */
- req->mss = tcp_parse_options(skb->h.th);
-
- if (!req->mss)
- {
- req->mss = 536;
- }
-
+ tcp_parse_options(skb->h.th,tp);
+ req_mss = tp->in_mss;
+ if (!req_mss)
+ req_mss = 536;
+ req->mss = req_mss;
req->rmt_port = skb->h.th->source;
-
- ipv6_addr_copy(&af_req->rmt_addr, &skb->ipv6_hdr->saddr);
- ipv6_addr_copy(&af_req->loc_addr, &skb->ipv6_hdr->daddr);
-
- /* FIXME: options */
-
- /* keep incoming device so that link locals have meaning */
- af_req->dev = skb->dev;
+ ipv6_addr_copy(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr);
+ ipv6_addr_copy(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr);
+ req->af.v6_req.opt = NULL; /* FIXME: options */
+ req->af.v6_req.dev = skb->dev; /* So that link locals have meaning */
req->class = &or_ipv6;
+ req->retrans = 0;
+ req->sk = NULL;
tcp_v6_send_synack(sk, req);
@@ -549,7 +717,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr,
sk->data_ready(sk, 0);
- exit:
+exit:
kfree_skb(skb, FREE_READ);
return 0;
}
@@ -561,31 +729,29 @@ static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
th->check = 0;
th->check = csum_ipv6_magic(&np->saddr, &np->daddr, len, IPPROTO_TCP,
- csum_partial((char *)th, sizeof(*th),
+ csum_partial((char *)th, th->doff<<2,
skb->csum));
}
static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
struct open_request *req)
{
- struct tcp_v6_open_req *af_req = (struct tcp_v6_open_req *) req;
struct ipv6_pinfo *np;
- struct dest_entry *dc;
+ struct dst_entry *dst;
+ struct flowi fl;
struct tcp_opt *newtp;
struct sock *newsk;
-
- if (skb->protocol == __constant_htons(ETH_P_IP))
- {
- /*
- * v6 mapped
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
+ /*
+ * v6 mapped
*/
-
+
newsk = tcp_v4_syn_recv_sock(sk, skb, req);
if (newsk == NULL)
return NULL;
-
+
np = &newsk->net_pinfo.af_inet6;
ipv6_addr_set(&np->daddr, 0, 0, __constant_htonl(0x0000FFFF),
@@ -597,31 +763,31 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
ipv6_addr_copy(&np->rcv_saddr, &np->saddr);
newsk->tp_pinfo.af_tcp.af_specific = &ipv6_mapped;
- newsk->backlog_rcv = tcp_v4_backlog_rcv;
+ newsk->backlog_rcv = tcp_v4_do_rcv;
return newsk;
}
- newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+ newsk = sk_alloc(GFP_ATOMIC);
if (newsk == NULL)
- {
return NULL;
- }
memcpy(newsk, sk, sizeof(*newsk));
+
+ /* Or else we die! -DaveM */
+ newsk->sklist_next = NULL;
+
newsk->opt = NULL;
- newsk->ip_route_cache = NULL;
+ newsk->dst_cache = NULL;
skb_queue_head_init(&newsk->write_queue);
skb_queue_head_init(&newsk->receive_queue);
skb_queue_head_init(&newsk->out_of_order_queue);
-
+ skb_queue_head_init(&newsk->error_queue);
+
/*
* Unused
*/
- newsk->send_head = NULL;
- newsk->send_tail = NULL;
-
newtp = &(newsk->tp_pinfo.af_tcp);
np = &newsk->net_pinfo.af_inet6;
@@ -634,17 +800,16 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->prot->init(newsk);
- newsk->cong_count = 0;
- newsk->ssthresh = 0;
+ newtp->snd_cwnd_cnt = 0;
+#if 0 /* Don't mess up the initialization we did in the init routine! */
+ newtp->snd_ssthresh = 0;
+#endif
newtp->backoff = 0;
- newsk->blog = 0;
- newsk->intr = 0;
newsk->proc = 0;
newsk->done = 0;
- newsk->partial = NULL;
newsk->pair = NULL;
- newsk->wmem_alloc = 0;
- newsk->rmem_alloc = 0;
+ atomic_set(&newsk->wmem_alloc, 0);
+ atomic_set(&newsk->rmem_alloc, 0);
newsk->localroute = sk->localroute;
newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
@@ -653,24 +818,23 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->shutdown = 0;
newsk->ack_backlog = 0;
- newsk->fin_seq = req->rcv_isn;
+ newtp->fin_seq = req->rcv_isn;
newsk->syn_seq = req->rcv_isn;
newsk->state = TCP_SYN_RECV;
newsk->timeout = 0;
- newsk->ip_xmit_timeout = 0;
newsk->write_seq = req->snt_isn;
newtp->snd_wnd = ntohs(skb->h.th->window);
- newsk->max_window = newtp->snd_wnd;
+ newtp->max_window = newtp->snd_wnd;
newtp->snd_wl1 = req->rcv_isn;
newtp->snd_wl2 = newsk->write_seq;
newtp->snd_una = newsk->write_seq++;
newtp->snd_nxt = newsk->write_seq;
newsk->urg_data = 0;
- newsk->packets_out = 0;
- newsk->retransmits = 0;
+ newtp->packets_out = 0;
+ newtp->retransmits = 0;
newsk->linger=0;
newsk->destroy = 0;
init_timer(&newsk->timer);
@@ -681,40 +845,65 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newsk->dummy_th.source = sk->dummy_th.source;
newsk->dummy_th.dest = req->rmt_port;
-
+ newsk->sock_readers=0;
+
newtp->rcv_nxt = req->rcv_isn + 1;
newtp->rcv_wup = req->rcv_isn + 1;
newsk->copied_seq = req->rcv_isn + 1;
newsk->socket = NULL;
- ipv6_addr_copy(&np->daddr, &af_req->rmt_addr);
- ipv6_addr_copy(&np->saddr, &af_req->loc_addr);
- ipv6_addr_copy(&np->rcv_saddr, &af_req->loc_addr);
-
+ ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
+ ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
+ ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
+ np->oif = req->af.v6_req.dev;
+
/*
- * options / mss
+ * options / mss / route cache
*/
-
- dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
- np->dest = dc;
- if (np->dest && (np->dest->dc_flags & DCF_PMTU))
- newsk->mtu = np->dest->dc_pmtu;
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = newsk->dummy_th.dest;
+ fl.uli_u.ports.sport = newsk->dummy_th.source;
+
+ dst = ip6_route_output(newsk, &fl);
+
+ ip6_dst_store(newsk, dst);
+
+ newtp->sack_ok = req->sack_ok;
+ newtp->tstamp_ok = req->tstamp_ok;
+ newtp->snd_wscale = req->snd_wscale;
+ newtp->ts_recent = req->ts_recent;
+ if (newtp->tstamp_ok) {
+ newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */
+ newsk->dummy_th.doff += 3;
+ } else {
+ newtp->tcp_header_len = sizeof(struct tcphdr);
+ }
+
+ if (dst->error)
+ newsk->mtu = req->af.v6_req.dev->mtu;
else
- newsk->mtu = af_req->dev->mtu;
+ newsk->mtu = dst->pmtu;
+
+ newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
+ (newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
+ /* XXX tp->window_clamp??? -DaveM */
- newsk->mss = min(req->mss, (newsk->mtu - sizeof(struct ipv6hdr) -
- sizeof(struct tcphdr)));
-
newsk->daddr = LOOPBACK4_IPV6;
newsk->saddr = LOOPBACK4_IPV6;
newsk->rcv_saddr= LOOPBACK4_IPV6;
-
- inet_put_sock(newsk->num, newsk);
+ newsk->prot->hash(newsk);
+ add_to_prot_sklist(newsk);
return newsk;
+}
+static void tcp_v6_reply_reset(struct sk_buff *skb)
+{
}
static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
@@ -724,6 +913,7 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
{
struct sk_buff *buff;
struct tcphdr *t1;
+ struct flowi fl;
if(th->rst)
return;
@@ -737,9 +927,7 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
if (buff == NULL)
return;
- buff->sk = NULL;
buff->dev = dev;
- buff->localroute = 0;
tcp_v6_build_header(NULL, buff);
@@ -755,12 +943,9 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
t1->doff = sizeof(*t1)/4;
t1->rst = 1;
- if(th->ack)
- {
+ if(th->ack) {
t1->seq = th->ack_seq;
- }
- else
- {
+ } else {
t1->ack = 1;
if(!th->syn)
t1->ack_seq = th->seq;
@@ -773,91 +958,69 @@ static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr,
t1->check = csum_ipv6_magic(saddr, daddr, sizeof(*t1), IPPROTO_TCP,
buff->csum);
-
- ipv6_xmit(NULL, buff, saddr, daddr, NULL, IPPROTO_TCP);
-
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.ports.dport = th->dest;
+ fl.uli_u.ports.sport = th->source;
+
+ ip6_xmit(NULL, buff, &fl, NULL);
tcp_statistics.TcpOutSegs++;
}
struct sock *tcp_v6_check_req(struct sock *sk, struct sk_buff *skb)
{
struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
- struct open_request *req;
-
+ struct open_request *req = tp->syn_wait_queue;
- /*
- * assumption: the socket is not in use.
+ /* assumption: the socket is not in use.
* as we checked the user count on tcp_rcv and we're
* running from a soft interrupt.
*/
-
- req = tp->syn_wait_queue;
-
-
if (!req)
- {
return sk;
- }
-
- do {
- struct tcp_v6_open_req *af_req;
-
- af_req = (struct tcp_v6_open_req *) req;
- if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->ipv6_hdr->saddr) &&
- !ipv6_addr_cmp(&af_req->loc_addr, &skb->ipv6_hdr->daddr) &&
- req->rmt_port == skb->h.th->source)
- {
+ while(req) {
+ if (!ipv6_addr_cmp(&req->af.v6_req.rmt_addr, &skb->nh.ipv6h->saddr) &&
+ !ipv6_addr_cmp(&req->af.v6_req.loc_addr, &skb->nh.ipv6h->daddr) &&
+ req->rmt_port == skb->h.th->source) {
u32 flg;
-
- if (req->sk)
- {
+
+ if (req->sk) {
printk(KERN_DEBUG "BUG: syn_recv:"
"socket exists\n");
break;
}
- /* match */
-
- /*
- * Check for syn retransmission
- */
+ /* Check for syn retransmission */
flg = *(((u32 *)skb->h.th) + 3);
flg &= __constant_htonl(0x002f0000);
-
+
if ((flg == __constant_htonl(0x00020000)) &&
- (!after(skb->seq, req->rcv_isn)))
- {
- /*
- * retransmited syn
+ (!after(skb->seq, req->rcv_isn))) {
+ /* retransmited syn
* FIXME: must send an ack
*/
return NULL;
}
- atomic_sub(skb->truesize, &sk->rmem_alloc);
+ skb_orphan(skb);
sk = tp->af_specific->syn_recv_sock(sk, skb, req);
tcp_dec_slow_timer(TCP_SLT_SYNACK);
if (sk == NULL)
- {
return NULL;
- }
- atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb_set_owner_r(skb, sk);
req->expires = 0UL;
req->sk = sk;
- skb->sk = sk;
break;
}
-
req = req->dl_next;
- } while (req != tp->syn_wait_queue);
-
-
+ }
return sk;
-
}
int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
@@ -879,41 +1042,37 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
sk = skb->sk;
- if (!redo)
- {
-
+ if (!redo) {
if (skb->pkt_type != PACKET_HOST)
goto discard_it;
/*
* Pull up the IP header.
*/
-
+
skb_pull(skb, skb->h.raw - skb->data);
/*
* Try to use the device checksum if provided.
*/
-
- switch (skb->ip_summed)
- {
- case CHECKSUM_NONE:
- skb->csum = csum_partial((char *)th, len, 0);
- case CHECKSUM_HW:
- if (tcp_v6_check(th,len,saddr,daddr,skb->csum))
- {
- printk(KERN_DEBUG "tcp csum failed\n");
- goto discard_it;
- }
- default:
- /* CHECKSUM_UNNECESSARY */
- }
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr,
- th->dest, th->source);
+ switch (skb->ip_summed) {
+ case CHECKSUM_NONE:
+ skb->csum = csum_partial((char *)th, len, 0);
+ case CHECKSUM_HW:
+ if (tcp_v6_check(th,len,saddr,daddr,skb->csum)) {
+ printk(KERN_DEBUG "tcp csum failed\n");
+ goto discard_it;
+ }
+ default:
+ /* CHECKSUM_UNNECESSARY */
+ };
+
+ tcp_statistics.TcpInSegs++;
+
+ sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
- if (!sk)
- {
+ if (!sk) {
printk(KERN_DEBUG "socket not found\n");
goto no_tcp_socket;
}
@@ -923,17 +1082,14 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
skb->ack_seq = ntohl(th->ack_seq);
- skb->acked = 0;
skb->used = 0;
- skb->free = 1;
- }
+ }
/*
- * We may need to add it to the backlog here.
+ * We may need to add it to the backlog here.
*/
- if (sk->users)
- {
+ if (sk->sock_readers) {
__skb_queue_tail(&sk->back_log, skb);
return(0);
}
@@ -942,45 +1098,38 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
* Signal NDISC that the connection is making
* "forward progress"
*/
- if (sk->state != TCP_LISTEN)
- {
+ if (sk->state != TCP_LISTEN) {
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
if (after(skb->seq, tp->rcv_nxt) ||
- after(skb->ack_seq, tp->snd_una))
- {
- if (np->dest)
- ndisc_validate(np->dest->dc_nexthop);
+ after(skb->ack_seq, tp->snd_una)) {
+ if (np->dst)
+ ndisc_validate(np->dst->neighbour);
}
}
- if (!sk->prot)
- {
+ if (!sk->prot) {
printk(KERN_DEBUG "tcp_rcv: sk->prot == NULL\n");
return(0);
}
- atomic_add(skb->truesize, &sk->rmem_alloc);
+ skb_set_owner_r(skb, sk);
- if (sk->state == TCP_ESTABLISHED)
- {
- tcp_rcv_established(sk, skb, th, len);
+ if (sk->state == TCP_ESTABLISHED) {
+ if (tcp_rcv_established(sk, skb, th, len))
+ goto no_tcp_socket;
return 0;
}
-
- if (sk->state == TCP_LISTEN)
- {
+
+ if (sk->state == TCP_LISTEN) {
/*
* find possible connection requests
*/
sk = tcp_v6_check_req(sk, skb);
if (sk == NULL)
- {
goto discard_it;
- }
-
}
if (tcp_rcv_state_process(sk, skb, th, opt, len) == 0)
@@ -989,12 +1138,12 @@ int tcp_v6_rcv(struct sk_buff *skb, struct device *dev,
no_tcp_socket:
/*
- * No such TCB. If th->rst is 0 send a reset
+ * No such TCB. If th->rst is 0 send a reset
* (checked in tcp_send_reset)
*/
- tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev,
- skb->ipv6_hdr->priority, 255);
+ tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev,
+ skb->nh.ipv6h->priority, 255);
discard_it:
@@ -1004,37 +1153,38 @@ discard_it:
kfree_skb(skb, FREE_READ);
return 0;
-
}
static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-
- if (np->dest)
- {
- np->dest = ipv6_dst_check(np->dest, &np->daddr,
- np->dc_sernum, 0);
-
- }
- else
- {
- np->dest = ipv6_dst_route(&np->daddr, NULL, 0);
+
+ if (np->dst)
+ dst_check(&np->dst, np->dst_cookie);
+
+ if (np->dst == NULL) {
+ struct flowi fl;
+ struct dst_entry *dst;
+
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
+ ip6_dst_store(sk, dst);
}
- if (!np->dest)
- {
+ if (np->dst->error) {
/*
* lost route to destination
*/
- return -1;
+ return -EHOSTUNREACH;
}
-
- np->dc_sernum = (np->dest->rt.fib_node ?
- np->dest->rt.fib_node->fn_sernum : 0);
- ipv6_redo_mac_hdr(skb, np->dest->dc_nexthop,
- skb->tail - (u8*) skb->ipv6_hdr);
+ skb_pull(skb, skb->nh.raw - skb->data);
return 0;
}
@@ -1043,9 +1193,9 @@ static int tcp_v6_backlog_rcv(struct sock *sk, struct sk_buff *skb)
int res;
res = tcp_v6_rcv(skb, skb->dev,
- &skb->ipv6_hdr->saddr, &skb->ipv6_hdr->daddr,
- (struct ipv6_options *) skb->proto_priv,
- skb->len, 1,
+ &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+ (struct ipv6_options *) skb->cb,
+ skb->len, 1,
(struct inet6_protocol *) sk->pair);
return res;
}
@@ -1054,20 +1204,16 @@ static struct sock * tcp_v6_get_sock(struct sk_buff *skb, struct tcphdr *th)
{
struct in6_addr *saddr;
struct in6_addr *daddr;
- struct sock *sk;
- saddr = &skb->ipv6_hdr->saddr;
- daddr = &skb->ipv6_hdr->daddr;
-
- sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
-
- return sk;
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+ return tcp_v6_lookup(saddr, th->source, daddr, th->dest);
}
-
+
static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
{
skb_reserve(skb, (MAX_HEADER + 15) & ~15);
- skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+ skb->nh.raw = skb_put(skb, sizeof(struct ipv6hdr));
/*
* FIXME: reserve space for option headers
@@ -1077,32 +1223,37 @@ static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static void tcp_v6_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
- int free)
+static void tcp_v6_xmit(struct sk_buff *skb)
{
+ struct sock *sk = skb->sk;
struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
+ struct flowi fl;
int err;
- err = ipv6_xmit(sk, skb, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
-
+ fl.proto = IPPROTO_TCP;
+ fl.nl_u.ip6_u.daddr = &np->daddr;
+ fl.nl_u.ip6_u.saddr = &np->saddr;
+ fl.dev = np->oif;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+
+ err = ip6_xmit(sk, skb, &fl, np->opt);
+
/*
* FIXME: check error handling.
*/
sk->err_soft = err;
}
-
-
static void v6_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
{
struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr;
-
+
sin6->sin6_family = AF_INET6;
memcpy(&sin6->sin6_addr, &np->daddr, sizeof(struct in6_addr));
sin6->sin6_port = sk->dummy_th.dest;
-
}
static struct tcp_func ipv6_specific = {
@@ -1117,6 +1268,7 @@ static struct tcp_func ipv6_specific = {
ipv6_setsockopt,
ipv6_getsockopt,
v6_addr2sockaddr,
+ tcp_v6_reply_reset,
sizeof(struct sockaddr_in6)
};
@@ -1136,6 +1288,7 @@ static struct tcp_func ipv6_mapped = {
ipv6_setsockopt,
ipv6_getsockopt,
v6_addr2sockaddr,
+ tcp_v6_reply_reset,
sizeof(struct sockaddr_in6)
};
@@ -1156,8 +1309,8 @@ static int tcp_v6_init_sock(struct sock *sk)
tp->rcv_wnd = 8192;
/* start with only sending one packet at a time. */
- sk->cong_window = 1;
- sk->ssthresh = 0x7fffffff;
+ tp->snd_cwnd = 1;
+ tp->snd_ssthresh = 0x7fffffff;
sk->priority = 1;
sk->state = TCP_CLOSE;
@@ -1165,21 +1318,22 @@ static int tcp_v6_init_sock(struct sock *sk)
/* this is how many unacked bytes we will accept for this socket. */
sk->max_unacked = 2048; /* needs to be at most 2 full packets. */
sk->max_ack_backlog = SOMAXCONN;
-
+
sk->mtu = 576;
sk->mss = 516;
sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
-
/*
- * Speed up by setting some standard state for the dummy_th
- * if TCP uses it (maybe move to tcp_init later)
+ * Speed up by setting some standard state for the dummy_th.
*/
-
- sk->dummy_th.ack=1;
+ sk->dummy_th.ack=1;
sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
+ /* Init SYN queue. */
+ tp->syn_wait_queue = NULL;
+ tp->syn_wait_last = &tp->syn_wait_queue;
+
sk->tp_pinfo.af_tcp.af_specific = &ipv6_specific;
return 0;
@@ -1191,70 +1345,67 @@ static int tcp_v6_destroy_sock(struct sock *sk)
struct sk_buff *skb;
tcp_clear_xmit_timers(sk);
-
+
if (sk->keepopen)
- {
tcp_dec_slow_timer(TCP_SLT_KEEPALIVE);
- }
/*
- * Cleanup up the write buffer.
+ * Cleanup up the write buffer.
*/
-
- while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
- IS_SKB(skb);
- skb->free = 1;
+
+ while((skb = skb_dequeue(&sk->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
/*
* Cleans up our, hopefuly empty, out_of_order_queue
*/
- while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL) {
- IS_SKB(skb);
+ while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
/*
* Release destination entry
*/
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
return 0;
}
-
struct proto tcpv6_prot = {
- tcp_close,
- tcp_v6_connect,
- tcp_accept,
- NULL,
- tcp_write_wakeup,
- tcp_read_wakeup,
- tcp_select,
- tcp_ioctl,
- tcp_v6_init_sock,
- tcp_v6_destroy_sock,
- tcp_shutdown,
- tcp_setsockopt,
- tcp_getsockopt,
- tcp_v6_sendmsg,
- tcp_recvmsg,
- NULL, /* No special bind() */
- tcp_v6_backlog_rcv,
- 128,
- 0,
- "TCPv6",
- 0, 0,
- NULL
+ (struct sock *)&tcpv6_prot, /* sklist_next */
+ (struct sock *)&tcpv6_prot, /* sklist_prev */
+ tcp_close, /* close */
+ tcp_v6_connect, /* connect */
+ tcp_accept, /* accept */
+ NULL, /* retransmit */
+ tcp_write_wakeup, /* write_wakeup */
+ tcp_read_wakeup, /* read_wakeup */
+ tcp_poll, /* poll */
+ tcp_ioctl, /* ioctl */
+ tcp_v6_init_sock, /* init */
+ tcp_v6_destroy_sock, /* destroy */
+ tcp_shutdown, /* shutdown */
+ tcp_setsockopt, /* setsockopt */
+ tcp_getsockopt, /* getsockopt */
+ tcp_v6_sendmsg, /* sendmsg */
+ tcp_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ tcp_v6_backlog_rcv, /* backlog_rcv */
+ tcp_v6_hash, /* hash */
+ tcp_v6_unhash, /* unhash */
+ tcp_v6_rehash, /* rehash */
+ tcp_good_socknum, /* good_socknum */
+ tcp_v6_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "TCPv6", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
-static struct inet6_protocol tcpv6_protocol =
+static struct inet6_protocol tcpv6_protocol =
{
tcp_v6_rcv, /* TCP handler */
tcp_v6_err, /* TCP error control */
@@ -1265,16 +1416,8 @@ static struct inet6_protocol tcpv6_protocol =
"TCPv6" /* name */
};
-
void tcpv6_init(void)
{
/* register inet6 protocol */
inet6_add_protocol(&tcpv6_protocol);
}
-
-/*
- * Local variables:
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strength-reduce -pipe -m486 -DCPU=486 -DMODULE -DMODVERSIONS -include /usr/src/linux/include/linux/modversions.h -c -o tcp_ipv6.o tcp_ipv6.c"
- * c-file-style: "Linux"
- * End:
- */
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 380122210..1f0fb8ce5 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -7,7 +7,7 @@
*
* Based on linux/ipv4/udp.c
*
- * $Id: udp.c,v 1.6 1996/10/16 18:34:16 roque Exp $
+ * $Id: udp.c,v 1.16 1997/04/11 22:22:57 davem Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -34,7 +34,7 @@
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/transp_v6.h>
-#include <net/ipv6_route.h>
+#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/ip.h>
#include <net/udp.h>
@@ -43,6 +43,139 @@
struct udp_mib udp_stats_in6;
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+ struct sock *sk2;
+ int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+ int retval = 0, sk_reuse = sk->reuse;
+
+ SOCKHASH_LOCK();
+ for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+ if((sk2->num == snum) && (sk2 != sk)) {
+ unsigned char state = sk2->state;
+ int sk2_reuse = sk2->reuse;
+ if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+ if((!sk2_reuse) ||
+ (!sk_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ } else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+ &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+ if((!sk_reuse) ||
+ (!sk2_reuse) ||
+ (state == TCP_LISTEN)) {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+ SOCKHASH_UNLOCK();
+ return retval;
+}
+
+static void udp_v6_hash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ sk->next = *skp;
+ *skp = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_unhash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[num];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_rehash(struct sock *sk)
+{
+ struct sock **skp;
+ int num = sk->num;
+ int oldnum = sk->hashent;
+
+ num &= (UDP_HTABLE_SIZE - 1);
+ skp = &udp_hash[oldnum];
+
+ SOCKHASH_LOCK();
+ while(*skp != NULL) {
+ if(*skp == sk) {
+ *skp = sk->next;
+ break;
+ }
+ skp = &((*skp)->next);
+ }
+ sk->next = udp_hash[num];
+ udp_hash[num] = sk;
+ sk->hashent = num;
+ SOCKHASH_UNLOCK();
+}
+
+static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
+ struct in6_addr *daddr, u16 dport)
+{
+ struct sock *sk, *result = NULL;
+ unsigned short hnum = ntohs(dport);
+ int badness = -1;
+
+ for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+ if((sk->num == hnum) &&
+ (sk->family == AF_INET6) &&
+ !(sk->dead && (sk->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+ int score = 0;
+ if(sk->dummy_th.dest) {
+ if(sk->dummy_th.dest != sport)
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+ continue;
+ score++;
+ }
+ if(!ipv6_addr_any(&np->daddr)) {
+ if(ipv6_addr_cmp(&np->daddr, saddr))
+ continue;
+ score++;
+ }
+ if(score == 3) {
+ result = sk;
+ break;
+ } else if(score > badness) {
+ result = sk;
+ badness = score;
+ }
+ }
+ }
+ return result;
+}
+
/*
*
*/
@@ -51,9 +184,10 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
struct in6_addr *daddr;
- struct dest_entry *dest;
+ struct dst_entry *dst;
struct ipv6_pinfo *np;
struct inet6_ifaddr *ifa;
+ struct flowi fl;
int addr_type;
if (addr_len < sizeof(*usin))
@@ -65,8 +199,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
addr_type = ipv6_addr_type(&usin->sin6_addr);
np = &sk->net_pinfo.af_inet6;
- if (addr_type == IPV6_ADDR_ANY)
- {
+ if (addr_type == IPV6_ADDR_ANY) {
/*
* connect to self
*/
@@ -75,8 +208,7 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
daddr = &usin->sin6_addr;
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
int err;
@@ -86,22 +218,18 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
err = udp_connect(sk, (struct sockaddr*) &sin, sizeof(sin));
if (err < 0)
- {
return err;
- }
ipv6_addr_copy(&np->daddr, daddr);
- if(ipv6_addr_any(&np->saddr))
- {
+ if(ipv6_addr_any(&np->saddr)) {
ipv6_addr_set(&np->saddr, 0, 0,
__constant_htonl(0x0000ffff),
sk->saddr);
}
- if(ipv6_addr_any(&np->rcv_saddr))
- {
+ if(ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_set(&np->rcv_saddr, 0, 0,
__constant_htonl(0x0000ffff),
sk->rcv_saddr);
@@ -111,35 +239,41 @@ int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
ipv6_addr_copy(&np->daddr, daddr);
+ sk->dummy_th.dest = usin->sin6_port;
+
/*
* Check for a route to destination an obtain the
* destination cache for it.
*/
- dest = ipv6_dst_route(daddr, NULL, sk->localroute ? RTI_GATEWAY : 0);
-
- np->dest = dest;
+ fl.proto = IPPROTO_UDP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = NULL;
+ fl.dev = NULL;
+ fl.uli_u.ports.dport = sk->dummy_th.dest;
+ fl.uli_u.ports.sport = sk->dummy_th.source;
+
+ dst = ip6_route_output(sk, &fl);
+
+ if (dst->error) {
+ dst_release(dst);
+ return dst->error;
+ }
- if (dest == NULL)
- return -ENETUNREACH;
+ ip6_dst_store(sk, dst);
/* get the source adddress used in the apropriate device */
- ifa = ipv6_get_saddr((struct rt6_info *) dest, daddr);
+ ifa = ipv6_get_saddr(dst, daddr);
if(ipv6_addr_any(&np->saddr))
- {
ipv6_addr_copy(&np->saddr, &ifa->addr);
- }
- if(ipv6_addr_any(&np->rcv_saddr))
- {
+ if(ipv6_addr_any(&np->rcv_saddr)) {
ipv6_addr_copy(&np->rcv_saddr, &ifa->addr);
sk->rcv_saddr = 0xffffffff;
}
- sk->dummy_th.dest = usin->sin6_port;
-
sk->state = TCP_ESTABLISHED;
return(0);
@@ -152,11 +286,11 @@ static void udpv6_close(struct sock *sk, unsigned long timeout)
lock_sock(sk);
sk->state = TCP_CLOSE;
- if (np->dest)
- {
- ipv6_dst_unlock(np->dest);
- }
+ if (np->dst)
+ dst_release(np->dst);
+ ipv6_sock_mc_close(sk);
+ udp_v6_unhash(sk);
release_sock(sk);
destroy_sock(sk);
}
@@ -173,7 +307,6 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
int truesize;
struct sk_buff *skb;
int err;
-
/*
* Check any passed addresses
@@ -191,11 +324,11 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
if(skb==NULL)
return err;
- truesize = skb->tail - skb->h.raw - sizeof(struct udphdr);
+ truesize=ntohs(((struct udphdr *)skb->h.raw)->len) - sizeof(struct udphdr);
copied=truesize;
- if(copied>len)
- {
+
+ if(copied>len) {
copied=len;
msg->msg_flags|=MSG_TRUNC;
}
@@ -212,8 +345,7 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sk->stamp=skb->stamp;
/* Copy the address. */
- if (msg->msg_name)
- {
+ if (msg->msg_name) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -221,27 +353,15 @@ int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
sin6->sin6_family = AF_INET6;
sin6->sin6_port = skb->h.uh->source;
- if (skb->protocol == __constant_htons(ETH_P_IP))
- {
+ if (skb->protocol == __constant_htons(ETH_P_IP)) {
ipv6_addr_set(&sin6->sin6_addr, 0, 0,
- __constant_htonl(0xffff), skb->daddr);
- }
- else
- {
- memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr,
+ __constant_htonl(0xffff), skb->nh.iph->saddr);
+ } else {
+ memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
sizeof(struct in6_addr));
- if (msg->msg_control)
- {
- int err;
-
- err = datagram_recv_ctl(sk, msg, skb);
-
- if (err < 0)
- {
- copied = err;
- }
- }
+ if (msg->msg_controllen)
+ datagram_recv_ctl(sk, msg, skb);
}
}
@@ -259,24 +379,22 @@ void udpv6_err(int type, int code, unsigned char *buff, __u32 info,
uh = (struct udphdr *) buff;
- sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->source, uh->dest);
+ sk = udp_v6_lookup(daddr, uh->dest, saddr, uh->source);
- if (sk == NULL)
- {
- printk(KERN_DEBUG "icmp for unkown sock\n");
+ if (sk == NULL) {
+ printk(KERN_DEBUG "icmp for unknown sock\n");
return;
}
- if (icmpv6_err_convert(type, code, &err))
- {
+ if (icmpv6_err_convert(type, code, &err)) {
if(sk->bsdism && sk->state!=TCP_ESTABLISHED)
return;
sk->err = err;
sk->error_report(sk);
- }
- else
+ } else {
sk->err_soft = err;
+ }
}
static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
@@ -294,6 +412,74 @@ static inline int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
return 0;
}
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+ struct ipv6_mc_socklist *mc;
+
+ for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+ if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static struct sock *udp_v6_mcast_next(struct sock *sk,
+ u16 loc_port, struct in6_addr *loc_addr,
+ u16 rmt_port, struct in6_addr *rmt_addr)
+{
+ struct sock *s = sk;
+ unsigned short num = ntohs(loc_port);
+ for(; s; s = s->next) {
+ if((s->num == num) &&
+ !(s->dead && (s->state == TCP_CLOSE))) {
+ struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+ if(s->dummy_th.dest) {
+ if(s->dummy_th.dest != rmt_port)
+ continue;
+ }
+ if(!ipv6_addr_any(&np->daddr) &&
+ ipv6_addr_cmp(&np->daddr, rmt_addr))
+ continue;
+
+ if(!ipv6_addr_any(&np->rcv_saddr)) {
+ if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+ return s;
+ }
+ if(!inet6_mc_check(s, loc_addr))
+ continue;
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static void udpv6_mcast_deliver(struct udphdr *uh,
+ struct in6_addr *saddr, struct in6_addr *daddr,
+ struct sk_buff *skb)
+{
+ struct sock *sk, *sk2;
+
+ sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+ sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+ if(sk) {
+ sk2 = sk;
+ while((sk2 = udp_v6_mcast_next(sk2->next,
+ uh->dest, saddr,
+ uh->source, daddr))) {
+ struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
+ if(sock_queue_rcv_skb(sk, buff) < 0) {
+ buff->sk = NULL;
+ kfree_skb(buff, FREE_READ);
+ }
+ }
+ }
+ if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ }
+}
+
int udpv6_rcv(struct sk_buff *skb, struct device *dev,
struct in6_addr *saddr, struct in6_addr *daddr,
struct ipv6_options *opt, unsigned short len,
@@ -314,16 +500,14 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
ulen = ntohs(uh->len);
- if (ulen > len || len < sizeof(*uh))
- {
+ if (ulen > len || len < sizeof(*uh)) {
printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len);
udp_stats_in6.UdpInErrors++;
kfree_skb(skb, FREE_READ);
return(0);
}
- if (uh->check == 0)
- {
+ if (uh->check == 0) {
printk(KERN_DEBUG "IPv6: udp checksum is 0\n");
goto discard;
}
@@ -332,53 +516,19 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
case CHECKSUM_NONE:
skb->csum = csum_partial((char*)uh, len, 0);
case CHECKSUM_HW:
- if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum))
- {
+ if (csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, skb->csum)) {
printk(KERN_DEBUG "IPv6: udp checksum error\n");
goto discard;
}
- }
+ };
len = ulen;
/*
* Multicast receive code
*/
- if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)
- {
- struct sock *sk2;
- int lport;
-
- lport = ntohs(uh->dest);
- sk = udpv6_prot.sock_array[lport & (SOCK_ARRAY_SIZE-1)];
-
- sk = inet6_get_sock_mcast(sk, lport, uh->source,
- daddr, saddr);
-
- if (sk)
- {
- sk2 = sk;
-
- while ((sk2 = inet6_get_sock_mcast(sk2->next, lport,
- uh->source,
- daddr, saddr)))
- {
- struct sk_buff *buff;
-
- buff = skb_clone(skb, GFP_ATOMIC);
-
- if (sock_queue_rcv_skb(sk, buff) < 0)
- {
- buff->sk = NULL;
- kfree_skb(buff, FREE_READ);
- }
- }
- }
- if (!sk || sock_queue_rcv_skb(sk, skb) < 0)
- {
- skb->sk = NULL;
- kfree_skb(skb, FREE_READ);
- }
+ if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+ udpv6_mcast_deliver(uh, saddr, daddr, skb);
return 0;
}
@@ -389,14 +539,12 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
* for sock caches... i'll skip this for now.
*/
- sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->dest, uh->source);
+ sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
- if (sk == NULL)
- {
+ if (sk == NULL) {
udp_stats_in6.UdpNoPorts++;
- icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH,
- 0, dev);
+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_PORT_UNREACH, 0, dev);
kfree_skb(skb, FREE_READ);
return(0);
@@ -404,18 +552,14 @@ int udpv6_rcv(struct sk_buff *skb, struct device *dev,
/* deliver */
- if (sk->users)
- {
+ if (sk->sock_readers)
__skb_queue_tail(&sk->back_log, skb);
- }
else
- {
udpv6_queue_rcv_skb(sk, skb);
- }
return(0);
- discard:
+discard:
udp_stats_in6.UdpInErrors++;
kfree_skb(skb, FREE_READ);
return(0);
@@ -448,12 +592,9 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
dst = buff;
- if (offset)
- {
+ if (offset) {
offset -= sizeof(struct udphdr);
- }
- else
- {
+ } else {
dst += sizeof(struct udphdr);
final = 1;
clen -= sizeof(struct udphdr);
@@ -462,19 +603,15 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
udh->wcheck = csum_partial_copy_fromiovecend(dst, udh->iov, offset,
clen, udh->wcheck);
- if (final)
- {
+ if (final) {
struct in6_addr *daddr;
udh->wcheck = csum_partial((char *)udh, sizeof(struct udphdr),
udh->wcheck);
- if (udh->daddr)
- {
+ if (udh->daddr) {
daddr = udh->daddr;
- }
- else
- {
+ } else {
/*
* use packet destination address
* this should improve cache locality
@@ -492,29 +629,28 @@ static int udpv6_getfrag(const void *data, struct in6_addr *addr,
return 0;
}
-static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
- int noblock, int flags)
+static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
{
-
struct ipv6_options opt_space;
struct udpv6fakehdr udh;
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) msg->msg_name;
struct ipv6_options *opt = NULL;
struct device *dev = NULL;
+ struct flowi fl;
int addr_len = msg->msg_namelen;
struct in6_addr *daddr;
struct in6_addr *saddr = NULL;
int len = ulen + sizeof(struct udphdr);
int addr_type;
- int err;
+ int hlimit = -1;
+ int err;
- if (flags & ~MSG_DONTROUTE)
+ if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
return(-EINVAL);
- if (sin6)
- {
+ if (sin6) {
if (addr_len < sizeof(*sin6))
return(-EINVAL);
@@ -527,14 +663,11 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
udh.uh.dest = sin6->sin6_port;
daddr = &sin6->sin6_addr;
- if (np->dest && ipv6_addr_cmp(daddr, &np->daddr))
- {
- ipv6_dst_unlock(np->dest);
- np->dest = NULL;
+ if (np->dst && ipv6_addr_cmp(daddr, &np->daddr)) {
+ dst_release(np->dst);
+ np->dst = NULL;
}
- }
- else
- {
+ } else {
if (sk->state != TCP_ESTABLISHED)
return(-EINVAL);
@@ -544,34 +677,29 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
addr_type = ipv6_addr_type(daddr);
- if (addr_type == IPV6_ADDR_MAPPED)
- {
+ if (addr_type == IPV6_ADDR_MAPPED) {
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = daddr->s6_addr32[3];
- return udp_sendmsg(sk, msg, len, noblock, flags);
+ return udp_sendmsg(sk, msg, len);
}
udh.daddr = NULL;
- if (msg->msg_control)
- {
+ if (msg->msg_controllen) {
opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_options));
- err = datagram_send_ctl(msg, &dev, &saddr, opt);
- if (err < 0)
- {
+ err = datagram_send_ctl(msg, &dev, &saddr, opt, &hlimit);
+ if (err < 0) {
printk(KERN_DEBUG "invalid msg_control\n");
return err;
}
if (opt->srcrt)
- {
udh.daddr = daddr;
- }
}
udh.uh.source = sk->dummy_th.source;
@@ -580,10 +708,17 @@ static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen,
udh.iov = msg->msg_iov;
udh.wcheck = 0;
udh.pl_len = len;
-
- err = ipv6_build_xmit(sk, udpv6_getfrag, &udh, daddr, len,
- saddr, dev, opt, IPPROTO_UDP, noblock);
-
+
+ fl.proto = IPPROTO_UDP;
+ fl.nl_u.ip6_u.daddr = daddr;
+ fl.nl_u.ip6_u.saddr = saddr;
+ fl.dev = dev;
+ fl.uli_u.ports.dport = udh.uh.dest;
+ fl.uli_u.ports.sport = udh.uh.source;
+
+ err = ip6_build_xmit(sk, udpv6_getfrag, &udh, &fl, len, opt, hlimit,
+ msg->msg_flags);
+
if (err < 0)
return err;
@@ -604,28 +739,35 @@ static struct inet6_protocol udpv6_protocol =
struct proto udpv6_prot = {
- udpv6_close,
- udpv6_connect,
- NULL,
- NULL,
- NULL,
- NULL,
- datagram_select,
- udp_ioctl,
- NULL,
- NULL,
- NULL,
- ipv6_setsockopt,
- ipv6_getsockopt,
- udpv6_sendmsg,
- udpv6_recvmsg,
- NULL, /* No special bind function */
- udpv6_queue_rcv_skb,
- 128,
- 0,
- "UDP",
- 0, 0,
- NULL
+ (struct sock *)&udpv6_prot, /* sklist_next */
+ (struct sock *)&udpv6_prot, /* sklist_prev */
+ udpv6_close, /* close */
+ udpv6_connect, /* connect */
+ NULL, /* accept */
+ NULL, /* retransmit */
+ NULL, /* write_wakeup */
+ NULL, /* read_wakeup */
+ datagram_poll, /* poll */
+ udp_ioctl, /* ioctl */
+ NULL, /* init */
+ NULL, /* destroy */
+ NULL, /* shutdown */
+ ipv6_setsockopt, /* setsockopt */
+ ipv6_getsockopt, /* getsockopt */
+ udpv6_sendmsg, /* sendmsg */
+ udpv6_recvmsg, /* recvmsg */
+ NULL, /* bind */
+ udpv6_queue_rcv_skb, /* backlog_rcv */
+ udp_v6_hash, /* hash */
+ udp_v6_unhash, /* unhash */
+ udp_v6_rehash, /* rehash */
+ udp_good_socknum, /* good_socknum */
+ udp_v6_verify_bind, /* verify_bind */
+ 128, /* max_header */
+ 0, /* retransmits */
+ "UDP", /* name */
+ 0, /* inuse */
+ 0 /* highestinuse */
};
void udpv6_init(void)
diff --git a/net/ipx/Makefile b/net/ipx/Makefile
index c54f9436f..0c29dc5d3 100644
--- a/net/ipx/Makefile
+++ b/net/ipx/Makefile
@@ -8,9 +8,13 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := ipx.o
-O_OBJS := af_ipx.o sysctl_net_ipx.o
+O_OBJS := af_ipx.o
M_OBJS := $(O_TARGET)
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_ipx.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 82a85f685..5b131e4a9 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -12,17 +12,17 @@
*
* All the material in this file is subject to the Gnu license version 2.
* Neither Alan Cox nor the Swansea University Computer Society admit liability
- * nor provide warranty for any of this software. This material is provided
- * as is and at no charge.
+ * nor provide warranty for any of this software. This material is provided
+ * as is and at no charge.
*
* Revision 0.21: Uses the new generic socket option code.
* Revision 0.22: Gcc clean ups and drop out device registration. Use the
- * new multi-protocol edition of hard_header
+ * new multi-protocol edition of hard_header
* Revision 0.23: IPX /proc by Mark Evans.
* Adding a route will overwrite any existing route to the same
* network.
* Revision 0.24: Supports new /proc with no 4K limit
- * Revision 0.25: Add ephemeral sockets, passive local network
+ * Revision 0.25: Add ephemeral sockets, passive local network
* identification, support for local net 0 and
* multiple datalinks <Greg Page>
* Revision 0.26: Device drop kills IPX routes via it. (needed for modules)
@@ -46,6 +46,7 @@
* Handles WIN95 discovery packets <Volker Lendecke>
* Revision 0.36: Internal bump up for 2.1
* Revision 0.37: Began adding POSIXisms.
+ * Revision 0.38: Asynchronous socket stuff made current.
*
* Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
* pair. Also, now usage count is managed this way
@@ -56,14 +57,13 @@
*
*
* Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
- * Neither Greg Page nor Caldera, Inc. admit liability nor provide
- * warranty for any of this software. This material is provided
- * "AS-IS" and at no charge.
+ * Neither Greg Page nor Caldera, Inc. admit liability nor provide
+ * warranty for any of this software. This material is provided
+ * "AS-IS" and at no charge.
*/
-#include <linux/module.h>
-
#include <linux/config.h>
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -108,27 +108,31 @@ static struct datalink_proto *pEII_datalink = NULL;
static struct datalink_proto *p8023_datalink = NULL;
static struct datalink_proto *pSNAP_datalink = NULL;
+static struct proto_ops ipx_dgram_ops;
+
static ipx_route *ipx_routes = NULL;
static ipx_interface *ipx_interfaces = NULL;
static ipx_interface *ipx_primary_net = NULL;
static ipx_interface *ipx_internal_net = NULL;
-static int
-ipxcfg_set_auto_create(char val)
+static int ipxcfg_set_auto_create(char val)
{
- if (ipxcfg_auto_create_interfaces != val){
- if (val){
+ if (ipxcfg_auto_create_interfaces != val)
+ {
+ if (val)
+ {
MOD_INC_USE_COUNT;
- }else{
+ }
+ else
+ {
MOD_DEC_USE_COUNT;
}
ipxcfg_auto_create_interfaces = val;
}
return 0;
}
-
-static int
-ipxcfg_set_auto_select(char val)
+
+static int ipxcfg_set_auto_select(char val)
{
ipxcfg_auto_select_primary = val;
if (val && (ipx_primary_net == NULL))
@@ -136,11 +140,10 @@ ipxcfg_set_auto_select(char val)
return 0;
}
-static int
-ipxcfg_get_config_data(ipx_config_data *arg)
+static int ipxcfg_get_config_data(ipx_config_data *arg)
{
ipx_config_data vals;
-
+
vals.ipxcfg_auto_create_interfaces = ipxcfg_auto_create_interfaces;
vals.ipxcfg_auto_select_primary = ipxcfg_auto_select_primary;
return copy_to_user(arg, &vals, sizeof(vals)) ? -EFAULT : 0;
@@ -158,17 +161,16 @@ ipxcfg_get_config_data(ipx_config_data *arg)
* handler using this technique. They can be added although we do not
* use this facility.
*/
-
-static void
-ipx_remove_socket(ipx_socket *sk)
+
+static void ipx_remove_socket(struct sock *sk)
{
- ipx_socket *s;
+ struct sock *s;
ipx_interface *intrfc;
unsigned long flags;
save_flags(flags);
cli();
-
+
/* Determine interface with which socket is associated */
intrfc = sk->protinfo.af_ipx.intrfc;
if (intrfc == NULL) {
@@ -181,7 +183,7 @@ ipx_remove_socket(ipx_socket *sk)
intrfc->if_sklist=s->next;
restore_flags(flags);
return;
- }
+ }
while(s && s->next) {
if(s->next==sk) {
@@ -200,9 +202,8 @@ ipx_remove_socket(ipx_socket *sk)
* Once it is removed from the queue no interrupt or bottom half will
* touch it and we are (fairly 8-) ) safe.
*/
-
-static void
-ipx_destroy_socket(ipx_socket *sk)
+
+static void ipx_destroy_socket(struct sock *sk)
{
struct sk_buff *skb;
@@ -210,19 +211,18 @@ ipx_destroy_socket(ipx_socket *sk)
while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
kfree_skb(skb,FREE_READ);
}
-
+
sk_free(sk);
MOD_DEC_USE_COUNT;
}
-
+
/* The following code is used to support IPX Interfaces (IPXITF). An
* IPX interface is defined by a physical device and a frame type.
*/
static ipx_route * ipxrtr_lookup(unsigned long);
-static void
-ipxitf_clear_primary_net(void)
+static void ipxitf_clear_primary_net(void)
{
if (ipxcfg_auto_select_primary && (ipx_interfaces != NULL))
ipx_primary_net = ipx_interfaces;
@@ -230,20 +230,18 @@ ipxitf_clear_primary_net(void)
ipx_primary_net = NULL;
}
-static ipx_interface *
-ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
+static ipx_interface *ipxitf_find_using_phys(struct device *dev, unsigned short datalink)
{
ipx_interface *i;
- for (i=ipx_interfaces;
- i && ((i->if_dev!=dev) || (i->if_dlink_type!=datalink));
+ for (i=ipx_interfaces;
+ i && ((i->if_dev!=dev) || (i->if_dlink_type!=datalink));
i=i->if_next)
;
return i;
}
-static ipx_interface *
-ipxitf_find_using_net(unsigned long net)
+static ipx_interface *ipxitf_find_using_net(unsigned long net)
{
ipx_interface *i;
@@ -257,10 +255,9 @@ ipxitf_find_using_net(unsigned long net)
}
/* Sockets are bound to a particular IPX interface. */
-static void
-ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
+static void ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk)
{
- ipx_socket *s;
+ struct sock *s;
sk->protinfo.af_ipx.intrfc = intrfc;
sk->next = NULL;
@@ -273,13 +270,12 @@ ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
}
}
-static ipx_socket *
-ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
+static struct sock *ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
{
- ipx_socket *s;
+ struct sock *s;
- for (s=intrfc->if_sklist;
- (s != NULL) && (s->protinfo.af_ipx.port != port);
+ for (s=intrfc->if_sklist;
+ (s != NULL) && (s->protinfo.af_ipx.port != port);
s=s->next)
;
@@ -288,11 +284,10 @@ ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
#ifdef CONFIG_IPX_INTERN
-static ipx_socket *
-ipxitf_find_internal_socket(ipx_interface *intrfc,
+static struct sock *ipxitf_find_internal_socket(ipx_interface *intrfc,
unsigned char *node, unsigned short port)
{
- ipx_socket *s = intrfc->if_sklist;
+ struct sock *s = intrfc->if_sklist;
while (s != NULL)
{
@@ -304,16 +299,15 @@ ipxitf_find_internal_socket(ipx_interface *intrfc,
s = s->next;
}
return s;
-}
+}
#endif
static void ipxrtr_del_routes(ipx_interface *);
-static void
-ipxitf_down(ipx_interface *intrfc)
+static void ipxitf_down(ipx_interface *intrfc)
{
ipx_interface *i;
- ipx_socket *s, *t;
+ struct sock *s, *t;
/* Delete all routes associated with this interface */
ipxrtr_del_routes(intrfc);
@@ -335,11 +329,11 @@ ipxitf_down(ipx_interface *intrfc)
if (intrfc == ipx_interfaces) {
ipx_interfaces = intrfc->if_next;
} else {
- for (i = ipx_interfaces;
+ for (i = ipx_interfaces;
(i != NULL) && (i->if_next != intrfc);
i = i->if_next)
;
- if ((i != NULL) && (i->if_next == intrfc))
+ if ((i != NULL) && (i->if_next == intrfc))
i->if_next = intrfc->if_next;
}
@@ -357,8 +351,7 @@ ipxitf_down(ipx_interface *intrfc)
return;
}
-static int
-ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
+static int ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *ptr)
{
struct device *dev = ptr;
ipx_interface *i, *tmp;
@@ -367,9 +360,9 @@ ipxitf_device_event(struct notifier_block *notifier, unsigned long event, void *
return NOTIFY_DONE;
for (i = ipx_interfaces; i != NULL; ) {
-
+
tmp = i->if_next;
- if (i->if_dev == dev)
+ if (i->if_dev == dev)
ipxitf_down(i);
i = tmp;
@@ -382,7 +375,7 @@ static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
{
int retval;
- if((retval = sock_queue_rcv_skb(sock, skb))<0)
+ if((retval = sock_queue_rcv_skb(sock, skb))<0)
{
/*
* skb->sk is NULL here, so FREE_WRITE does not hurt
@@ -398,11 +391,10 @@ static int ipxitf_def_skb_handler(struct sock *sock, struct sk_buff *skb)
*/
#ifdef CONFIG_IPX_INTERN
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
{
- ipx_packet *ipx = (ipx_packet *)(skb->h.raw);
- ipx_socket *s;
+ struct ipxhdr *ipx = skb->nh.ipxh;
+ struct sock *s;
int is_broadcast = (memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
IPX_NODE_LEN) == 0);
@@ -424,7 +416,7 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1 != NULL)
{
- skb1->arp = skb1->free = 1;
+ skb1->arp = 1;
}
else
{
@@ -462,11 +454,10 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
#else
-static int
-ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
+static int ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
{
- ipx_packet *ipx = (ipx_packet *)(skb->h.raw);
- ipx_socket *sock1 = NULL, *sock2 = NULL;
+ struct ipxhdr *ipx = skb->nh.ipxh;
+ struct sock *sock1 = NULL, *sock2 = NULL;
struct sk_buff *skb1 = NULL, *skb2 = NULL;
sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
@@ -478,10 +469,10 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
* The *SPECIAL* socket list contains: 0x452(SAP), 0x453(RIP) and
* 0x456(Diagnostic).
*/
-
- if (ipx_primary_net && (intrfc != ipx_primary_net))
+
+ if (ipx_primary_net && (intrfc != ipx_primary_net))
{
- switch (ntohs(ipx->ipx_dest.sock))
+ switch (ntohs(ipx->ipx_dest.sock))
{
case 0x452:
case 0x453:
@@ -492,7 +483,7 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
* interface via ipxitf_send; however, we'll cheat
* and just demux it here.
*/
- sock2 = ipxitf_find_socket(ipx_primary_net,
+ sock2 = ipxitf_find_socket(ipx_primary_net,
ipx->ipx_dest.sock);
break;
default:
@@ -500,57 +491,57 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
}
}
- /*
+ /*
* if there is nothing to do, return. The kfree will
* cancel any charging.
*/
-
- if (sock1 == NULL && sock2 == NULL)
+
+ if (sock1 == NULL && sock2 == NULL)
{
- if (!copy)
+ if (!copy)
kfree_skb(skb,FREE_WRITE);
return 0;
}
/*
* This next segment of code is a little awkward, but it sets it up
- * so that the appropriate number of copies of the SKB are made and
- * that skb1 and skb2 point to it (them) so that it (they) can be
+ * so that the appropriate number of copies of the SKB are made and
+ * that skb1 and skb2 point to it (them) so that it (they) can be
* demuxed to sock1 and/or sock2. If we are unable to make enough
* copies, we do as much as is possible.
*/
-
- if (copy)
+
+ if (copy)
{
skb1 = skb_clone(skb, GFP_ATOMIC);
- if (skb1 != NULL)
- skb1->arp = skb1->free = 1;
- }
- else
+ if (skb1)
+ skb1->arp=1;
+ }
+ else
{
skb1 = skb;
}
-
- if (skb1 == NULL)
- return -ENOMEM;
+
+ if (skb1 == NULL)
+ return -ENOMEM;
/*
- * Do we need 2 SKBs?
+ * Do we need 2 SKBs?
*/
-
- if (sock1 && sock2)
+
+ if (sock1 && sock2)
{
skb2 = skb_clone(skb1, GFP_ATOMIC);
- if (skb2 != NULL)
- skb2->arp = skb2->free = 1;
+ if (skb2 != NULL)
+ skb2->arp = 1;
}
- else
+ else
skb2 = skb1;
-
+
if (sock1)
(void) ipxitf_def_skb_handler(sock1, skb1);
- if (skb2 == NULL)
+ if (skb2 == NULL)
return -ENOMEM;
if (sock2)
@@ -560,8 +551,7 @@ ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy)
}
#endif
-static struct sk_buff *
-ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
+static struct sk_buff *ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
{
struct sk_buff *skb2;
int in_offset = skb->h.raw - skb->head;
@@ -570,7 +560,7 @@ ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
/* Hopefully, most cases */
if (in_offset >= out_offset) {
- skb->arp = skb->free = 1;
+ skb->arp = 1;
return skb;
}
@@ -579,8 +569,8 @@ ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
skb2 = alloc_skb(len, GFP_ATOMIC);
if (skb2 != NULL) {
skb_reserve(skb2,out_offset);
+ skb2->nh.raw=
skb2->h.raw=skb_put(skb2,skb->len);
- skb2->free=1;
skb2->arp=1;
memcpy(skb2->h.raw, skb->h.raw, skb->len);
}
@@ -590,7 +580,7 @@ ipxitf_adjust_skbuff(ipx_interface *intrfc, struct sk_buff *skb)
static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
{
- ipx_packet *ipx = (ipx_packet *)(skb->h.raw);
+ struct ipxhdr *ipx = skb->nh.ipxh;
struct device *dev = intrfc->if_dev;
struct datalink_proto *dl = intrfc->if_dlink;
char dest_node[IPX_NODE_LEN];
@@ -616,17 +606,15 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
{
/*
* To our own node, loop and free the original.
+ * The internal net will receive on all node address.
*/
- if (memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0)
+ if ((intrfc == ipx_internal_net)
+ || memcmp(intrfc->if_node, node, IPX_NODE_LEN) == 0)
{
/*
* Don't charge sender
*/
- if(skb->sk)
- {
- atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
- skb->sk=NULL;
- }
+ skb_orphan(skb);
/*
* Will charge receiver
*/
@@ -635,38 +623,35 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
/*
* Broadcast, loop and possibly keep to send on.
*/
- if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0)
+ if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0)
{
- if (!send_to_wire && skb->sk)
- {
- atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
- skb->sk=NULL;
- }
+ if (!send_to_wire)
+ skb_orphan(skb);
ipxitf_demux_socket(intrfc, skb, send_to_wire);
- if (!send_to_wire)
+ if (!send_to_wire)
return 0;
}
}
/*
- * If the originating net is not equal to our net; this is routed
+ * If the originating net is not equal to our net; this is routed
* We are still charging the sender. Which is right - the driver
* free will handle this fairly.
*/
-
- if (ipx->ipx_source.net != intrfc->if_netnum)
+
+ if (ipx->ipx_source.net != intrfc->if_netnum)
{
- if (++(ipx->ipx_tctrl) > ipxcfg_max_hops)
+ if (++(ipx->ipx_tctrl) > ipxcfg_max_hops)
send_to_wire = 0;
}
- if (!send_to_wire)
+ if (!send_to_wire)
{
/*
* We do a FREE_WRITE here because this indicates how
- * to treat the socket with which the packet is
+ * to treat the socket with which the packet is
* associated. If this packet is associated with a
- * socket at all, it must be the originator of the
+ * socket at all, it must be the originator of the
* packet. Routed packets will have no socket associated
* with them.
*/
@@ -675,21 +660,21 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
}
/*
- * Determine the appropriate hardware address
+ * Determine the appropriate hardware address
*/
-
+
addr_len = dev->addr_len;
- if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0)
+ if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0)
memcpy(dest_node, dev->broadcast, addr_len);
else
memcpy(dest_node, &(node[IPX_NODE_LEN-addr_len]), addr_len);
/*
- * Make any compensation for differing physical/data link size
+ * Make any compensation for differing physical/data link size
*/
-
+
skb = ipxitf_adjust_skbuff(intrfc, skb);
- if (skb == NULL)
+ if (skb == NULL)
return 0;
/* set up data link and physical headers */
@@ -701,15 +686,15 @@ static int ipxitf_send(ipx_interface *intrfc, struct sk_buff *skb, char *node)
* Now log the packet just before transmission
*/
- dump_pkt("IPX snd:", (ipx_packet *)skb->h.raw);
- dump_data("ETH hdr:", skb->data, skb->h.raw - skb->data);
+ dump_pkt("IPX snd:", skb->nh.ipxh);
+ dump_data("ETH hdr:", skb->mac.raw, skb->nh.raw - skb->mac.raw);
#endif
/*
* Send it out
*/
-
- dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ skb->priority = SOPRI_NORMAL;
+ dev_queue_xmit(skb);
return 0;
}
@@ -726,40 +711,37 @@ static int ipxrtr_route_skb(struct sk_buff *);
static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
{
- ipx_packet *ipx = (ipx_packet *) (skb->h.raw);
+ struct ipxhdr *ipx = skb->nh.ipxh;
ipx_interface *i;
-#ifdef CONFIG_FIREWALL
/*
* We firewall first, ask questions later.
*/
-
- if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+
+ if (call_in_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_READ);
return 0;
}
-
-#endif
/* See if we should update our network number */
- if ((intrfc->if_netnum == 0L) &&
+ if ((intrfc->if_netnum == 0L) &&
(ipx->ipx_source.net == ipx->ipx_dest.net) &&
- (ipx->ipx_source.net != 0L))
+ (ipx->ipx_source.net != 0L))
{
/* NB: NetWare servers lie about their hop count so we
* dropped the test based on it. This is the best way
* to determine this is a 0 hop count packet.
*/
- if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL)
+ if ((i=ipxitf_find_using_net(ipx->ipx_source.net))==NULL)
{
intrfc->if_netnum = ipx->ipx_source.net;
(void) ipxitf_add_local_route(intrfc);
- }
- else
+ }
+ else
{
printk(KERN_WARNING "IPX: Network number collision %lx\n %s %s and %s %s\n",
- htonl(ipx->ipx_source.net),
+ htonl(ipx->ipx_source.net),
ipx_device_name(i),
ipx_frame_name(i->if_dlink_type),
ipx_device_name(intrfc),
@@ -767,26 +749,115 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
}
}
+#ifdef CONFIG_IPX_PPROP_ROUTING
+ if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 )
+ {
+ int i;
+ ipx_interface *ifcs;
+ struct sk_buff *skb2;
+ long *l;
+ char *c;
+
+ if(skb->pkt_type!=PACKET_HOST)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+#ifdef DEBUG_IPX_PPROP_ROUTING
+ printk(KERN_INFO "IPX: PPROP packet received\n"
+ " Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n",
+ htonl(ipx->ipx_source.net),
+ ipx->ipx_source.node[0], ipx->ipx_source.node[1],
+ ipx->ipx_source.node[2], ipx->ipx_source.node[3],
+ ipx->ipx_source.node[4], ipx->ipx_source.node[5],
+ htons(ipx->ipx_source.sock),
+ htons(ipx->ipx_dest.sock)
+ );
+#endif
+
+ c = (char *) skb->data;
+ c += sizeof( struct ipxhdr );
+
+ l = (long *) c;
+
+#ifdef DEBUG_IPX_PPROP_ROUTING
+ printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) );
+ for( i = 0 ; i < ipx->ipx_tctrl ; i++ )
+ printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) );
+ l = (long *) c;
+#endif
+ i = 0;
+ /*
+ * Dump packet if too many hops or already seen this net
+ */
+ if( ipx->ipx_tctrl < 8 )
+ for( ; i < ipx->ipx_tctrl ; i++ )
+ if( *l++ == intrfc->if_netnum )
+ break;
+
+ if( i == ipx->ipx_tctrl )
+ {
+ /* < 8 hops && input itfc not in list */
+ *l = intrfc->if_netnum; /* insert recvd netnum into list */
+ /* xmit on all other interfaces... */
+ for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next)
+ {
+ /* That aren't in the list */
+ l = (long *) c;
+ for( i = 0 ; i <= ipx->ipx_tctrl ; i++ )
+ if( ifcs->if_netnum == *l++ )
+ break;
+ if( i - 1 == ipx->ipx_tctrl )
+ {
+ ipx->ipx_dest.net = ifcs->if_netnum;
+#ifdef DEBUG_IPX_PPROP_ROUTING
+ printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+#endif
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+
+ /*
+ * See if we are allowed to firewall forward
+ */
+ if (call_fw_firewall(PF_IPX, skb2->dev, ipx, NULL, &skb)!=FW_ACCEPT)
+ {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+ ipxrtr_route_skb(skb2);
+ }
+#ifdef DEBUG_IPX_PPROP_ROUTING
+ else
+ printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+#endif
+ }
+ /*
+ * Reset network number in packet
+ */
+ ipx->ipx_dest.net = intrfc->if_netnum;
+ }
+
+ }
+#endif
+
if (ipx->ipx_dest.net == 0L)
ipx->ipx_dest.net = intrfc->if_netnum;
if (ipx->ipx_source.net == 0L)
ipx->ipx_source.net = intrfc->if_netnum;
- if (intrfc->if_netnum != ipx->ipx_dest.net)
+ if (intrfc->if_netnum != ipx->ipx_dest.net)
{
-#ifdef CONFIG_FIREWALL
/*
* See if we are allowed to firewall forward
*/
- if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+ if (call_fw_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_READ);
return 0;
}
-#endif
+
/* We only route point-to-point packets. */
- if ((skb->pkt_type != PACKET_BROADCAST) &&
- (skb->pkt_type != PACKET_MULTICAST))
+ if (skb->pkt_type == PACKET_HOST)
{
skb=skb_unshare(skb, GFP_ATOMIC, FREE_READ);
if(skb)
@@ -799,8 +870,8 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
}
/* see if we should keep it */
- if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)
- || (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0))
+ if ((memcmp(ipx_broadcast_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0)
+ || (memcmp(intrfc->if_node, ipx->ipx_dest.node, IPX_NODE_LEN) == 0))
{
return ipxitf_demux_socket(intrfc, skb, 0);
}
@@ -810,8 +881,7 @@ static int ipxitf_rcv(ipx_interface *intrfc, struct sk_buff *skb)
return 0;
}
-static void
-ipxitf_insert(ipx_interface *intrfc)
+static void ipxitf_insert(ipx_interface *intrfc)
{
ipx_interface *i;
@@ -830,16 +900,17 @@ ipxitf_insert(ipx_interface *intrfc)
return;
}
-static int
-ipxitf_create_internal(ipx_interface_definition *idef)
+static int ipxitf_create_internal(ipx_interface_definition *idef)
{
ipx_interface *intrfc;
/* Only one primary network allowed */
- if (ipx_primary_net != NULL) return -EEXIST;
+ if (ipx_primary_net != NULL)
+ return -EEXIST;
/* Must have a valid network number */
- if (idef->ipx_network == 0L) return -EADDRNOTAVAIL;
+ if (idef->ipx_network == 0L)
+ return -EADDRNOTAVAIL;
if (ipxitf_find_using_net(idef->ipx_network) != NULL)
return -EADDRINUSE;
@@ -861,28 +932,32 @@ ipxitf_create_internal(ipx_interface_definition *idef)
return ipxitf_add_local_route(intrfc);
}
-static int
-ipx_map_frame_type(unsigned char type)
+static int ipx_map_frame_type(unsigned char type)
{
- switch (type) {
- case IPX_FRAME_ETHERII: return htons(ETH_P_IPX);
- case IPX_FRAME_8022: return htons(ETH_P_802_2);
- case IPX_FRAME_TR_8022: return htons(ETH_P_TR_802_2);
- case IPX_FRAME_SNAP: return htons(ETH_P_SNAP);
- case IPX_FRAME_8023: return htons(ETH_P_802_3);
+ switch (type)
+ {
+ case IPX_FRAME_ETHERII:
+ return htons(ETH_P_IPX);
+ case IPX_FRAME_8022:
+ return htons(ETH_P_802_2);
+ case IPX_FRAME_TR_8022:
+ return htons(ETH_P_TR_802_2);
+ case IPX_FRAME_SNAP:
+ return htons(ETH_P_SNAP);
+ case IPX_FRAME_8023:
+ return htons(ETH_P_802_3);
}
return 0;
}
-static int
-ipxitf_create(ipx_interface_definition *idef)
+static int ipxitf_create(ipx_interface_definition *idef)
{
struct device *dev;
unsigned short dlink_type = 0;
struct datalink_proto *datalink = NULL;
ipx_interface *intrfc;
- if (idef->ipx_special == IPX_INTERNAL)
+ if (idef->ipx_special == IPX_INTERNAL)
return ipxitf_create_internal(idef);
if ((idef->ipx_special == IPX_PRIMARY) && (ipx_primary_net != NULL))
@@ -892,37 +967,38 @@ ipxitf_create(ipx_interface_definition *idef)
(ipxitf_find_using_net(idef->ipx_network) != NULL))
return -EADDRINUSE;
- switch (idef->ipx_dlink_type) {
- case IPX_FRAME_ETHERII:
- dlink_type = htons(ETH_P_IPX);
- datalink = pEII_datalink;
- break;
- case IPX_FRAME_TR_8022:
- dlink_type = htons(ETH_P_TR_802_2);
- datalink = p8022tr_datalink;
- break;
- case IPX_FRAME_8022:
- dlink_type = htons(ETH_P_802_2);
- datalink = p8022_datalink;
- break;
- case IPX_FRAME_SNAP:
- dlink_type = htons(ETH_P_SNAP);
- datalink = pSNAP_datalink;
- break;
- case IPX_FRAME_8023:
- dlink_type = htons(ETH_P_802_3);
- datalink = p8023_datalink;
- break;
- case IPX_FRAME_NONE:
- default:
- break;
- }
-
- if (datalink == NULL)
+ switch (idef->ipx_dlink_type)
+ {
+ case IPX_FRAME_ETHERII:
+ dlink_type = htons(ETH_P_IPX);
+ datalink = pEII_datalink;
+ break;
+ case IPX_FRAME_TR_8022:
+ dlink_type = htons(ETH_P_TR_802_2);
+ datalink = p8022tr_datalink;
+ break;
+ case IPX_FRAME_8022:
+ dlink_type = htons(ETH_P_802_2);
+ datalink = p8022_datalink;
+ break;
+ case IPX_FRAME_SNAP:
+ dlink_type = htons(ETH_P_SNAP);
+ datalink = pSNAP_datalink;
+ break;
+ case IPX_FRAME_8023:
+ dlink_type = htons(ETH_P_802_3);
+ datalink = p8023_datalink;
+ break;
+ case IPX_FRAME_NONE:
+ default:
+ break;
+ }
+
+ if (datalink == NULL)
return -EPROTONOSUPPORT;
dev=dev_get(idef->ipx_device);
- if (dev==NULL)
+ if (dev==NULL)
return -ENODEV;
if (!(dev->flags & IFF_UP))
@@ -932,7 +1008,7 @@ ipxitf_create(ipx_interface_definition *idef)
if(dev->addr_len>IPX_NODE_LEN)
return -EINVAL;
- if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL)
+ if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL)
{
/* Ok now create */
intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
@@ -945,14 +1021,14 @@ ipxitf_create(ipx_interface_definition *idef)
intrfc->if_sklist = NULL;
intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
/* Setup primary if necessary */
- if ((idef->ipx_special == IPX_PRIMARY))
+ if ((idef->ipx_special == IPX_PRIMARY))
ipx_primary_net = intrfc;
intrfc->if_internal = 0;
intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length;
if(memcmp(idef->ipx_node, "\000\000\000\000\000\000", IPX_NODE_LEN)==0)
{
memset(intrfc->if_node, 0, IPX_NODE_LEN);
- memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
+ memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
dev->dev_addr, dev->addr_len);
}
else
@@ -961,21 +1037,22 @@ ipxitf_create(ipx_interface_definition *idef)
}
/* If the network number is known, add a route */
- if (intrfc->if_netnum == 0L)
+ if (intrfc->if_netnum == 0L)
return 0;
return ipxitf_add_local_route(intrfc);
}
-static int
-ipxitf_delete(ipx_interface_definition *idef)
+static int ipxitf_delete(ipx_interface_definition *idef)
{
struct device *dev = NULL;
unsigned short dlink_type = 0;
ipx_interface *intrfc;
- if (idef->ipx_special == IPX_INTERNAL) {
- if (ipx_internal_net != NULL) {
+ if (idef->ipx_special == IPX_INTERNAL)
+ {
+ if (ipx_internal_net != NULL)
+ {
ipxitf_down(ipx_internal_net);
return 0;
}
@@ -997,21 +1074,33 @@ ipxitf_delete(ipx_interface_definition *idef)
return -EINVAL;
}
-static ipx_interface *
-ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
+static ipx_interface *ipxitf_auto_create(struct device *dev,
+ unsigned short dlink_type)
{
struct datalink_proto *datalink = NULL;
ipx_interface *intrfc;
- switch (htons(dlink_type)) {
- case ETH_P_IPX: datalink = pEII_datalink; break;
- case ETH_P_802_2: datalink = p8022_datalink; break;
- case ETH_P_TR_802_2: datalink = p8022tr_datalink; break;
- case ETH_P_SNAP: datalink = pSNAP_datalink; break;
- case ETH_P_802_3: datalink = p8023_datalink; break;
- default: return NULL;
+ switch (htons(dlink_type))
+ {
+ case ETH_P_IPX:
+ datalink = pEII_datalink;
+ break;
+ case ETH_P_802_2:
+ datalink = p8022_datalink;
+ break;
+ case ETH_P_TR_802_2:
+ datalink = p8022tr_datalink;
+ break;
+ case ETH_P_SNAP:
+ datalink = pSNAP_datalink;
+ break;
+ case ETH_P_802_3:
+ datalink = p8023_datalink;
+ break;
+ default:
+ return NULL;
}
-
+
if (dev == NULL)
return NULL;
@@ -1019,7 +1108,8 @@ ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
if(dev->addr_len>IPX_NODE_LEN) return NULL;
intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
- if (intrfc!=NULL) {
+ if (intrfc!=NULL)
+ {
intrfc->if_dev=dev;
intrfc->if_netnum=0L;
intrfc->if_dlink_type = dlink_type;
@@ -1027,10 +1117,10 @@ ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
intrfc->if_sklist = NULL;
intrfc->if_internal = 0;
intrfc->if_sknum = IPX_MIN_EPHEMERAL_SOCKET;
- intrfc->if_ipx_offset = dev->hard_header_len +
+ intrfc->if_ipx_offset = dev->hard_header_len +
datalink->header_length;
memset(intrfc->if_node, 0, IPX_NODE_LEN);
- memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
+ memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]),
dev->dev_addr, dev->addr_len);
ipxitf_insert(intrfc);
}
@@ -1038,12 +1128,12 @@ ipxitf_auto_create(struct device *dev, unsigned short dlink_type)
return intrfc;
}
-static int
-ipxitf_ioctl_real(unsigned int cmd, void *arg)
+static int ipxitf_ioctl_real(unsigned int cmd, void *arg)
{
switch(cmd)
{
- case SIOCSIFADDR: {
+ case SIOCSIFADDR:
+ {
struct ifreq ifr;
struct sockaddr_ipx *sipx;
ipx_interface_definition f;
@@ -1063,13 +1153,14 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg)
else
return ipxitf_create(&f);
}
- case SIOCGIFADDR: {
+ case SIOCGIFADDR:
+ {
struct ifreq ifr;
struct sockaddr_ipx *sipx;
ipx_interface *ipxif;
struct device *dev;
int err;
-
+
if (copy_from_user(&ifr,arg,sizeof(ifr)))
return -EFAULT;
sipx=(struct sockaddr_ipx *)&ifr.ifr_addr;
@@ -1087,14 +1178,16 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg)
return -EFAULT;
return err;
}
- case SIOCAIPXITFCRT: {
+ case SIOCAIPXITFCRT:
+ {
int err, val;
err = get_user(val, (unsigned char *) arg);
if (err)
return err;
return ipxcfg_set_auto_create(val);
}
- case SIOCAIPXPRISLT: {
+ case SIOCAIPXPRISLT:
+ {
int err, val;
err = get_user(val, (unsigned char *) arg);
if (err)
@@ -1106,8 +1199,7 @@ ipxitf_ioctl_real(unsigned int cmd, void *arg)
}
}
-static int
-ipxitf_ioctl(unsigned int cmd, void *arg)
+static int ipxitf_ioctl(unsigned int cmd, void *arg)
{
int ret;
MOD_INC_USE_COUNT;
@@ -1115,14 +1207,14 @@ ipxitf_ioctl(unsigned int cmd, void *arg)
MOD_DEC_USE_COUNT;
return ret;
}
+
/*******************************************************************************************************************\
* *
* Routing tables for the IPX socket layer *
* *
\*******************************************************************************************************************/
-static ipx_route *
-ipxrtr_lookup(unsigned long net)
+static ipx_route *ipxrtr_lookup(unsigned long net)
{
ipx_route *r;
@@ -1132,14 +1224,14 @@ ipxrtr_lookup(unsigned long net)
return r;
}
-static int
-ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
+static int ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *node)
{
ipx_route *rt;
/* Get a route structure; either existing or create */
rt = ipxrtr_lookup(network);
- if (rt==NULL) {
+ if (rt==NULL)
+ {
rt=(ipx_route *)kmalloc(sizeof(ipx_route),GFP_ATOMIC);
if(rt==NULL)
return -EAGAIN;
@@ -1161,8 +1253,7 @@ ipxrtr_add_route(unsigned long network, ipx_interface *intrfc, unsigned char *no
return 0;
}
-static void
-ipxrtr_del_routes(ipx_interface *intrfc)
+static void ipxrtr_del_routes(ipx_interface *intrfc)
{
ipx_route **r, *tmp;
@@ -1176,8 +1267,7 @@ ipxrtr_del_routes(ipx_interface *intrfc)
}
}
-static int
-ipxrtr_create(ipx_route_definition *rd)
+static int ipxrtr_create(ipx_route_definition *rd)
{
ipx_interface *intrfc;
@@ -1190,22 +1280,22 @@ ipxrtr_create(ipx_route_definition *rd)
}
-static int
-ipxrtr_delete(long net)
+static int ipxrtr_delete(long net)
{
ipx_route **r;
ipx_route *tmp;
- for (r = &ipx_routes; (tmp = *r) != NULL; ) {
- if (tmp->ir_net == net) {
- if (!(tmp->ir_routed)) {
+ for (r = &ipx_routes; (tmp = *r) != NULL; )
+ {
+ if (tmp->ir_net == net)
+ {
+ if (!(tmp->ir_routed))
/* Directly connected; can't lose route */
return -EPERM;
- }
*r = tmp->ir_next;
kfree_s(tmp, sizeof(ipx_route));
return 0;
- }
+ }
r = &(tmp->ir_next);
}
@@ -1218,7 +1308,7 @@ ipxrtr_delete(long net)
/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
-static __u16 ipx_set_checksum(ipx_packet *packet,int length)
+static __u16 ipx_set_checksum(struct ipxhdr *packet,int length)
{
/*
* NOTE: sum is a net byte order quantity, which optimizes the
@@ -1235,73 +1325,73 @@ static __u16 ipx_set_checksum(ipx_packet *packet,int length)
__u16 *p=(__u16 *)&packet->ipx_pktsize;
/*
- * Number of complete words
+ * Number of complete words
*/
__u32 i=length>>1;
/*
- * Loop through all complete words except the checksum field
+ * Loop through all complete words except the checksum field
*/
while(--i)
sum+=*p++;
/*
- * Add on the last part word if it exists
+ * Add on the last part word if it exists
*/
if(packet->ipx_pktsize&htons(1))
sum+=ntohs(0xff00)&*p;
/*
- * Do final fixup
+ * Do final fixup
*/
-
+
sum=(sum&0xffff)+(sum>>16);
/*
- * It's a pity there's no concept of carry in C
+ * It's a pity there's no concept of carry in C
*/
if(sum>=0x10000)
sum++;
-
+
return ~sum;
};
-
+
/*
* Route an outgoing frame from a socket.
*/
-static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock)
+static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock)
{
struct sk_buff *skb;
ipx_interface *intrfc;
- ipx_packet *ipx;
+ struct ipxhdr *ipx;
int size;
int ipx_offset;
ipx_route *rt = NULL;
int err;
-
+
/* Find the appropriate interface on which to send packet */
- if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL))
+ if ((usipx->sipx_network == 0L) && (ipx_primary_net != NULL))
{
usipx->sipx_network = ipx_primary_net->if_netnum;
intrfc = ipx_primary_net;
- }
- else
+ }
+ else
{
rt = ipxrtr_lookup(usipx->sipx_network);
- if (rt==NULL) {
+ if (rt==NULL)
return -ENETUNREACH;
- }
+
intrfc = rt->ir_intrfc;
}
-
+
ipx_offset = intrfc->if_ipx_offset;
- size=sizeof(ipx_packet)+len;
+ size=sizeof(struct ipxhdr)+len;
size += ipx_offset;
skb=sock_alloc_send_skb(sk, size, 0, noblock, &err);
@@ -1309,22 +1399,21 @@ static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struc
return err;
skb_reserve(skb,ipx_offset);
- skb->free=1;
skb->arp=1;
skb->sk=sk;
/* Fill in IPX header */
- ipx=(ipx_packet *)skb_put(skb,sizeof(ipx_packet));
- ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
+ ipx=(struct ipxhdr *)skb_put(skb,sizeof(struct ipxhdr));
+ ipx->ipx_pktsize=htons(len+sizeof(struct ipxhdr));
ipx->ipx_tctrl=0;
ipx->ipx_type=usipx->sipx_type;
- skb->h.raw = (unsigned char *)ipx;
+ skb->h.raw = (void *)skb->nh.ipxh = ipx;
ipx->ipx_source.net = sk->protinfo.af_ipx.intrfc->if_netnum;
#ifdef CONFIG_IPX_INTERN
memcpy(ipx->ipx_source.node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
#else
- if ((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452)
+ if ((err = ntohs(sk->protinfo.af_ipx.port)) == 0x453 || err == 0x452)
{
/* RIP/SAP special handling for mars_nwe */
ipx->ipx_source.net = intrfc->if_netnum;
@@ -1355,35 +1444,33 @@ static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struc
if(sk->no_check || intrfc->if_dlink_type==IPX_FRAME_8023)
ipx->ipx_checksum=0xFFFF;
else
- ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(ipx_packet));
+ ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(struct ipxhdr));
-#ifdef CONFIG_FIREWALL
- if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL)!=FW_ACCEPT)
+ if(call_out_firewall(PF_IPX, skb->dev, ipx, NULL, &skb)!=FW_ACCEPT)
{
kfree_skb(skb, FREE_WRITE);
return -EPERM;
}
-#endif
return ipxitf_send(intrfc, skb, (rt && rt->ir_routed) ?
rt->ir_router_node : ipx->ipx_dest.node);
}
-static int
-ipxrtr_route_skb(struct sk_buff *skb)
+static int ipxrtr_route_skb(struct sk_buff *skb)
{
- ipx_packet *ipx = (ipx_packet *) (skb->h.raw);
+ struct ipxhdr *ipx = skb->nh.ipxh;
ipx_route *r;
ipx_interface *i;
r = ipxrtr_lookup(ipx->ipx_dest.net);
- if (r == NULL) {
+ if (r == NULL)
+ {
/* no known route */
kfree_skb(skb,FREE_READ);
return 0;
}
i = r->ir_intrfc;
- (void)ipxitf_send(i, skb, (r->ir_routed) ?
+ (void)ipxitf_send(i, skb, (r->ir_routed) ?
r->ir_router_node : ipx->ipx_dest.node);
return 0;
}
@@ -1397,21 +1484,21 @@ static int ipxrtr_ioctl(unsigned int cmd, void *arg)
int err;
struct rtentry rt; /* Use these to behave like 'other' stacks */
struct sockaddr_ipx *sg,*st;
-
+
err = copy_from_user(&rt,arg,sizeof(rt));
if (err)
- return -EFAULT;
+ return -EFAULT;
sg=(struct sockaddr_ipx *)&rt.rt_gateway;
st=(struct sockaddr_ipx *)&rt.rt_dst;
-
+
if(!(rt.rt_flags&RTF_GATEWAY))
return -EINVAL; /* Direct routes are fixed */
if(sg->sipx_family!=AF_IPX)
return -EINVAL;
if(st->sipx_family!=AF_IPX)
return -EINVAL;
-
+
switch(cmd)
{
case SIOCDELRT:
@@ -1429,21 +1516,26 @@ static int ipxrtr_ioctl(unsigned int cmd, void *arg)
}
}
-static const char *
-ipx_frame_name(unsigned short frame)
+static const char *ipx_frame_name(unsigned short frame)
{
- switch (ntohs(frame)) {
- case ETH_P_IPX: return "EtherII";
- case ETH_P_802_2: return "802.2";
- case ETH_P_SNAP: return "SNAP";
- case ETH_P_802_3: return "802.3";
- case ETH_P_TR_802_2: return "802.2TR";
- default: return "None";
+ switch (ntohs(frame))
+ {
+ case ETH_P_IPX:
+ return "EtherII";
+ case ETH_P_802_2:
+ return "802.2";
+ case ETH_P_SNAP:
+ return "SNAP";
+ case ETH_P_802_3:
+ return "802.3";
+ case ETH_P_TR_802_2:
+ return "802.2TR";
+ default:
+ return "None";
}
}
-static const char *
-ipx_device_name(ipx_interface *intrfc)
+static const char *ipx_device_name(ipx_interface *intrfc)
{
return (intrfc->if_internal ? "Internal" :
(intrfc->if_dev ? intrfc->if_dev->name : "Unknown"));
@@ -1460,22 +1552,22 @@ static int ipx_interface_get_info(char *buffer, char **start, off_t offset,
/* Theory.. Keep printing in the same place until we pass offset */
- len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network",
+ len += sprintf (buffer,"%-11s%-15s%-9s%-11s%s\n", "Network",
"Node_Address", "Primary", "Device", "Frame_Type");
for (i = ipx_interfaces; i != NULL; i = i->if_next) {
len += sprintf(buffer+len, "%08lX ", ntohl(i->if_netnum));
- len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X ",
+ len += sprintf (buffer+len,"%02X%02X%02X%02X%02X%02X ",
i->if_node[0], i->if_node[1], i->if_node[2],
i->if_node[3], i->if_node[4], i->if_node[5]);
len += sprintf(buffer+len, "%-9s", (i == ipx_primary_net) ?
"Yes" : "No");
len += sprintf (buffer+len, "%-11s", ipx_device_name(i));
- len += sprintf (buffer+len, "%s\n",
+ len += sprintf (buffer+len, "%s\n",
ipx_frame_name(i->if_dlink_type));
/* Are we still dumping unwanted data then discard the record */
pos=begin+len;
-
+
if(pos<offset) {
len=0; /* Keep dumping into the buffer start */
begin=pos;
@@ -1483,20 +1575,20 @@ static int ipx_interface_get_info(char *buffer, char **start, off_t offset,
if(pos>offset+length) /* We have dumped enough */
break;
}
-
+
/* The data in question runs from begin to begin+len */
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Remove unwanted header data from length */
if(len>length)
len=length; /* Remove unwanted tail data from length */
-
+
return len;
}
static int ipx_get_info(char *buffer, char **start, off_t offset,
int length, int dummy)
{
- ipx_socket *s;
+ struct sock *s;
ipx_interface *i;
int len=0;
off_t pos=0;
@@ -1504,28 +1596,28 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
/* Theory.. Keep printing in the same place until we pass offset */
-#ifdef CONFIG_IPX_INTERN
- len += sprintf (buffer,"%-28s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
+#ifdef CONFIG_IPX_INTERN
+ len += sprintf (buffer,"%-28s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
#else
- len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
+ len += sprintf (buffer,"%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
#endif
- "Remote_Address", "Tx_Queue", "Rx_Queue",
+ "Remote_Address", "Tx_Queue", "Rx_Queue",
"State", "Uid");
for (i = ipx_interfaces; i != NULL; i = i->if_next) {
for (s = i->if_sklist; s != NULL; s = s->next) {
#ifdef CONFIG_IPX_INTERN
len += sprintf(buffer+len,
- "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
+ "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
htonl(s->protinfo.af_ipx.intrfc->if_netnum),
s->protinfo.af_ipx.node[0],
- s->protinfo.af_ipx.node[1],
- s->protinfo.af_ipx.node[2],
- s->protinfo.af_ipx.node[3],
- s->protinfo.af_ipx.node[4],
+ s->protinfo.af_ipx.node[1],
+ s->protinfo.af_ipx.node[2],
+ s->protinfo.af_ipx.node[3],
+ s->protinfo.af_ipx.node[4],
s->protinfo.af_ipx.node[5],
htons(s->protinfo.af_ipx.port));
#else
- len += sprintf(buffer+len,"%08lX:%04X ",
+ len += sprintf(buffer+len,"%08lX:%04X ",
htonl(i->if_netnum),
htons(s->protinfo.af_ipx.port));
#endif
@@ -1533,24 +1625,25 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
len += sprintf(buffer+len, "%-28s", "Not_Connected");
} else {
len += sprintf (buffer+len,
- "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
+ "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
htonl(s->protinfo.af_ipx.dest_addr.net),
s->protinfo.af_ipx.dest_addr.node[0],
- s->protinfo.af_ipx.dest_addr.node[1],
+ s->protinfo.af_ipx.dest_addr.node[1],
s->protinfo.af_ipx.dest_addr.node[2],
- s->protinfo.af_ipx.dest_addr.node[3],
+ s->protinfo.af_ipx.dest_addr.node[3],
s->protinfo.af_ipx.dest_addr.node[4],
s->protinfo.af_ipx.dest_addr.node[5],
htons(s->protinfo.af_ipx.dest_addr.sock));
}
- len += sprintf (buffer+len,"%08X %08X ",
- s->wmem_alloc, s->rmem_alloc);
- len += sprintf (buffer+len,"%02X %03d\n",
+ len += sprintf (buffer+len,"%08X %08X ",
+ atomic_read(&s->wmem_alloc),
+ atomic_read(&s->rmem_alloc));
+ len += sprintf (buffer+len,"%02X %03d\n",
s->state, SOCK_INODE(s->socket)->i_uid);
-
+
/* Are we still dumping unwanted data then discard the record */
pos=begin+len;
-
+
if(pos<offset)
{
len=0; /* Keep dumping into the buffer start */
@@ -1560,13 +1653,13 @@ static int ipx_get_info(char *buffer, char **start, off_t offset,
break;
}
}
-
+
/* The data in question runs from begin to begin+len */
*start=buffer+(offset-begin); /* Start of wanted data */
len-=(offset-begin); /* Remove unwanted header data from length */
if(len>length)
len=length; /* Remove unwanted tail data from length */
-
+
return len;
}
@@ -1578,16 +1671,16 @@ static int ipx_rt_get_info(char *buffer, char **start, off_t offset,
off_t pos=0;
off_t begin=0;
- len += sprintf (buffer,"%-11s%-13s%s\n",
+ len += sprintf (buffer,"%-11s%-13s%s\n",
"Network", "Router_Net", "Router_Node");
for (rt = ipx_routes; rt != NULL; rt = rt->ir_next)
{
len += sprintf (buffer+len,"%08lX ", ntohl(rt->ir_net));
if (rt->ir_routed) {
- len += sprintf (buffer+len,"%08lX %02X%02X%02X%02X%02X%02X\n",
- ntohl(rt->ir_intrfc->if_netnum),
- rt->ir_router_node[0], rt->ir_router_node[1],
- rt->ir_router_node[2], rt->ir_router_node[3],
+ len += sprintf (buffer+len,"%08lX %02X%02X%02X%02X%02X%02X\n",
+ ntohl(rt->ir_intrfc->if_netnum),
+ rt->ir_router_node[0], rt->ir_router_node[1],
+ rt->ir_router_node[2], rt->ir_router_node[3],
rt->ir_router_node[4], rt->ir_router_node[5]);
} else {
len += sprintf (buffer+len, "%-13s%s\n",
@@ -1615,29 +1708,20 @@ static int ipx_rt_get_info(char *buffer, char **start, off_t offset,
* *
\*******************************************************************************************************************/
-static int ipx_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- switch(cmd)
- {
- default:
- return(-EINVAL);
- }
-}
-
static int ipx_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
- ipx_socket *sk;
+ struct sock *sk;
int err,opt;
-
- sk=(ipx_socket *)sock->data;
-
- if (optval==NULL)
+
+ sk=sock->sk;
+
+ if (optlen!=sizeof(int))
return(-EINVAL);
err = get_user(opt, (unsigned int *)optval);
if (err)
return err;
-
+
switch(level)
{
case SOL_IPX:
@@ -1647,26 +1731,23 @@ static int ipx_setsockopt(struct socket *sock, int level, int optname, char *opt
sk->protinfo.af_ipx.type=opt;
return 0;
default:
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
}
break;
-
- case SOL_SOCKET:
- return sock_setsockopt(sk,level,optname,optval,optlen);
default:
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
}
}
static int ipx_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
- ipx_socket *sk;
+ struct sock *sk;
int val=0;
- int err;
-
- sk=(ipx_socket *)sock->data;
+ int len;
+
+ sk=sock->sk;
switch(level)
{
@@ -1681,90 +1762,51 @@ static int ipx_getsockopt(struct socket *sock, int level, int optname,
return -ENOPROTOOPT;
}
break;
-
- case SOL_SOCKET:
- return sock_getsockopt(sk,level,optname,optval,optlen);
-
- default:
- return -EOPNOTSUPP;
- }
- err = put_user(sizeof(int), optlen);
- if (!err)
- err = put_user(val, (int *)optval);
- return err;
-}
-static int ipx_listen(struct socket *sock, int backlog)
-{
- return -EOPNOTSUPP;
-}
-
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk, int len)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 1);
- }
+ default:
+ return -ENOPROTOOPT;
+ }
+ if(get_user(len,optlen))
+ return -EFAULT;
+ len=min(len,sizeof(int));
+ if(put_user(len, optlen))
+ return -EFAULT;
+ if(copy_to_user(optval,&val,len))
+ return -EFAULT;
+ return 0;
}
static int ipx_create(struct socket *sock, int protocol)
{
- ipx_socket *sk;
- sk=(ipx_socket *)sk_alloc(GFP_KERNEL);
+ struct sock *sk;
+ sk=sk_alloc(GFP_KERNEL);
if(sk==NULL)
return(-ENOMEM);
switch(sock->type)
{
case SOCK_DGRAM:
+ sock->ops = &ipx_dgram_ops;
break;
default:
- kfree_s((void *)sk,sizeof(*sk));
+ sk_free(sk);
return(-ESOCKTNOSUPPORT);
}
- sk->rcvbuf=SK_RMEM_MAX;
- sk->sndbuf=SK_WMEM_MAX;
- sk->prot=NULL; /* So we use default free mechanisms */
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- sk->send_head=NULL;
- skb_queue_head_init(&sk->back_log);
- sk->state=TCP_CLOSE;
- sk->socket=sock;
- sk->type=sock->type;
+ sock_init_data(sock,sk);
sk->mtu=IPX_MTU;
- sk->no_check = 1; /* Checksum off by default */
- if(sock!=NULL)
- {
- sock->data=(void *)sk;
- sk->sleep=sock->wait;
- }
-
- sk->state_change=def_callback1;
- sk->data_ready=def_callback2;
- sk->write_space=def_callback1;
- sk->error_report=def_callback1;
-
- sk->zapped=1;
+ sk->no_check = 1; /* Checksum off by default */
MOD_INC_USE_COUNT;
return 0;
}
static int ipx_release(struct socket *sock, struct socket *peer)
{
- ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sock *sk=sock->sk;
if(sk==NULL)
return(0);
if(!sk->dead)
sk->state_change(sk);
sk->dead=1;
- sock->data=NULL;
+ sock->sk=NULL;
ipx_destroy_socket(sk);
return(0);
}
@@ -1774,8 +1816,7 @@ static int ipx_dup(struct socket *newsock,struct socket *oldsock)
return(ipx_create(newsock,SOCK_DGRAM));
}
-static unsigned short
-ipx_first_free_socketnum(ipx_interface *intrfc)
+static unsigned short ipx_first_free_socketnum(ipx_interface *intrfc)
{
unsigned short socketNum = intrfc->if_sknum;
@@ -1791,21 +1832,21 @@ ipx_first_free_socketnum(ipx_interface *intrfc)
intrfc->if_sknum = socketNum;
return ntohs(socketNum);
}
-
+
static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- ipx_socket *sk;
+ struct sock *sk;
ipx_interface *intrfc;
struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
-
- sk=(ipx_socket *)sock->data;
-
+
+ sk=sock->sk;
+
if(sk->zapped==0)
return -EINVAL;
-
+
if(addr_len!=sizeof(struct sockaddr_ipx))
return -EINVAL;
-
+
intrfc = ipxitf_find_using_net(addr->sipx_network);
if (intrfc == NULL)
return -EADDRNOTAVAIL;
@@ -1843,13 +1884,11 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
memcpy(sk->protinfo.af_ipx.node, addr->sipx_node, IPX_NODE_LEN);
}
- if (ipxitf_find_internal_socket(intrfc,
- sk->protinfo.af_ipx.node,
+ if (ipxitf_find_internal_socket(intrfc,
+ sk->protinfo.af_ipx.node,
sk->protinfo.af_ipx.port) != NULL)
{
- if(sk->debug)
- printk("IPX: bind failed because port %X in"
- " use.\n", (int)addr->sipx_port);
+ SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
return -EADDRINUSE;
}
}
@@ -1860,14 +1899,12 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
* with the ipx routing ioctl()
*/
- memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
+ memcpy(sk->protinfo.af_ipx.node, intrfc->if_node,
IPX_NODE_LEN);
-
+
if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
- if(sk->debug)
- printk("IPX: bind failed because port %X in"
- " use.\n", (int)addr->sipx_port);
- return -EADDRINUSE;
+ SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
+ return -EADDRINUSE;
}
}
@@ -1877,40 +1914,37 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
an interface routed to IPX with the ipx routing ioctl() */
if(ipxitf_find_socket(intrfc, addr->sipx_port)!=NULL) {
- if(sk->debug)
- printk("IPX: bind failed because port %X in use.\n",
- (int)addr->sipx_port);
- return -EADDRINUSE;
+ SOCK_DEBUG(sk, "IPX: bind failed because port %X in use.\n", (int)addr->sipx_port);
+ return -EADDRINUSE;
}
#endif
ipxitf_insert_socket(intrfc, sk);
sk->zapped=0;
- if(sk->debug)
- printk("IPX: socket is bound.\n");
+ SOCK_DEBUG(sk, "IPX: socket is bound.\n");
return 0;
}
static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
- ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_ipx *addr;
-
- sk->state = TCP_CLOSE;
+
+ sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
if(addr_len!=sizeof(*addr))
return(-EINVAL);
addr=(struct sockaddr_ipx *)uaddr;
-
+
if(sk->protinfo.af_ipx.port==0)
/* put the autobinding in */
{
struct sockaddr_ipx uaddr;
int ret;
-
+
uaddr.sipx_port = 0;
uaddr.sipx_network = 0L;
#ifdef CONFIG_IPX_INTERN
@@ -1921,7 +1955,7 @@ static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
sizeof(struct sockaddr_ipx));
if (ret != 0) return (ret);
}
-
+
if(ipxrtr_lookup(addr->sipx_network)==NULL)
return -ENETUNREACH;
sk->protinfo.af_ipx.dest_addr.net=addr->sipx_network;
@@ -1941,8 +1975,8 @@ static int ipx_socketpair(struct socket *sock1, struct socket *sock2)
static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
{
- if(newsock->data) {
- kfree_s(newsock->data,sizeof(ipx_socket));
+ if(newsock->sk) {
+ sk_free(newsock->sk);
MOD_DEC_USE_COUNT;
}
return -EOPNOTSUPP;
@@ -1953,12 +1987,12 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
{
ipx_address *addr;
struct sockaddr_ipx sipx;
- ipx_socket *sk;
-
- sk=(ipx_socket *)sock->data;
-
+ struct sock *sk;
+
+ sk=sock->sk;
+
*uaddr_len = sizeof(struct sockaddr_ipx);
-
+
if(peer) {
if(sk->state!=TCP_ESTABLISHED)
return -ENOTCONN;
@@ -1972,7 +2006,7 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
#ifdef CONFIG_IPX_INTERN
memcpy(sipx.sipx_node, sk->protinfo.af_ipx.node, IPX_NODE_LEN);
#else
- memcpy(sipx.sipx_node,
+ memcpy(sipx.sipx_node,
sk->protinfo.af_ipx.intrfc->if_node, IPX_NODE_LEN);
#endif
@@ -1982,7 +2016,7 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
}
sipx.sipx_port = sk->protinfo.af_ipx.port;
}
-
+
sipx.sipx_family = AF_IPX;
sipx.sipx_type = sk->protinfo.af_ipx.type;
memcpy(uaddr,&sipx,sizeof(sipx));
@@ -1993,56 +2027,66 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
/*
* User to dump IPX packets (debugging)
*/
-void dump_data(char *str,unsigned char *d, int len) {
- static char h2c[] = "0123456789ABCDEF";
- int l,i;
- char *p, b[64];
- for (l=0;len > 0 && l<16;l++) {
- p = b;
- for (i=0; i < 8 ; i++, --len) {
- if (len > 0) {
- *(p++) = h2c[(d[i] >> 4) & 0x0f];
- *(p++) = h2c[d[i] & 0x0f];
- }
- else {
- *(p++) = ' ';
- *(p++) = ' ';
- }
- *(p++) = ' ';
- }
- *(p++) = '-';
- *(p++) = ' ';
- len += 8;
- for (i=0; i < 8 ; i++, --len)
- if (len > 0)
- *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.';
- else
+void dump_data(char *str,unsigned char *d, int len)
+{
+ static char h2c[] = "0123456789ABCDEF";
+ int l,i;
+ char *p, b[64];
+ for (l=0;len > 0 && l<16;l++)
+ {
+ p = b;
+ for (i=0; i < 8 ; i++, --len)
+ {
+ if (len > 0)
+ {
+ *(p++) = h2c[(d[i] >> 4) & 0x0f];
+ *(p++) = h2c[d[i] & 0x0f];
+ }
+ else
+ {
+ *(p++) = ' ';
+ *(p++) = ' ';
+ }
*(p++) = ' ';
- *p = '\000';
- d += i;
- printk("%s-%04X: %s\n",str,l*8,b);
- }
+ }
+ *(p++) = '-';
+ *(p++) = ' ';
+ len += 8;
+ for (i=0; i < 8 ; i++, --len)
+ {
+ if (len > 0)
+ *(p++) = ' '<= d[i] && d[i]<'\177' ? d[i] : '.';
+ else
+ *(p++) = ' ';
+ }
+ *p = '\000';
+ d += i;
+ printk(KERN_DEBUG"%s-%04X: %s\n",str,l*8,b);
+ }
}
-void dump_addr(char *str,ipx_address *p) {
- printk("%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
- str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
- p->node[3],p->node[4],p->node[5],ntohs(p->sock));
+void dump_addr(char *str,ipx_address *p)
+{
+ printk(KERN_DEBUG"%s: %08X:%02X%02X%02X%02X%02X%02X:%04X\n",
+ str,ntohl(p->net),p->node[0],p->node[1],p->node[2],
+ p->node[3],p->node[4],p->node[5],ntohs(p->sock));
}
-void dump_hdr(char *str,ipx_packet *p) {
- printk("%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n",
- str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize),
- p->ipx_tctrl,p->ipx_tctrl,p->ipx_type);
- dump_addr(" IPX-DST",&p->ipx_dest);
- dump_addr(" IPX-SRC",&p->ipx_source);
+void dump_hdr(char *str,struct ipxhdr *p)
+{
+ printk(KERN_DEBUG"%s: CHKSUM=%04X SIZE=%d (%04X) HOPS=%d (%02X) TYPE=%02X\n",
+ str,p->ipx_checksum,ntohs(p->ipx_pktsize),ntohs(p->ipx_pktsize),
+ p->ipx_tctrl,p->ipx_tctrl,p->ipx_type);
+ dump_addr(" IPX-DST",&p->ipx_dest);
+ dump_addr(" IPX-SRC",&p->ipx_source);
}
-void dump_pkt(char *str,ipx_packet *p) {
- int len = ntohs(p->ipx_pktsize);
- dump_hdr(str,p);
- if (len > 30)
- dump_data(str,(unsigned char *)p + 30, len - 30);
+void dump_pkt(char *str,struct ipxhdr *p)
+{
+ int len = ntohs(p->ipx_pktsize);
+ dump_hdr(str,p);
+ if (len > 30)
+ dump_data(str,(unsigned char *)p + 30, len - 30);
}
#endif
@@ -2050,14 +2094,12 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{
/* NULL here for pt means the packet was looped back */
ipx_interface *intrfc;
- ipx_packet *ipx;
-
-
- ipx=(ipx_packet *)skb->h.raw;
-
- /* Too small */
+ struct ipxhdr *ipx;
+
+ ipx = skb->nh.ipxh;
- if(ntohs(ipx->ipx_pktsize)<sizeof(ipx_packet)) {
+ /* Too small? */
+ if(ntohs(ipx->ipx_pktsize)<sizeof(struct ipxhdr)) {
kfree_skb(skb,FREE_READ);
return 0;
}
@@ -2070,13 +2112,13 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return 0;
}
}
-
+
/* Determine what local ipx endpoint this is */
intrfc = ipxitf_find_using_phys(dev, pt->type);
- if (intrfc == NULL)
+ if (intrfc == NULL)
{
if (ipxcfg_auto_create_interfaces &&
- ntohl(ipx->ipx_dest.net)!=0L)
+ ntohl(ipx->ipx_dest.net)!=0L)
{
intrfc = ipxitf_auto_create(dev, pt->type);
}
@@ -2091,28 +2133,29 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return ipxitf_rcv(intrfc, skb);
}
-static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock,
- int flags)
+static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
{
- ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)msg->msg_name;
struct sockaddr_ipx local_sipx;
int retval;
+ int flags = msg->msg_flags;
- if (sk->zapped)
+ if (sk->zapped)
return -EIO; /* Socket not bound */
- if(flags)
+ if (flags&~MSG_DONTWAIT)
return -EINVAL;
-
- if(usipx)
+
+ if(usipx)
{
- if(sk->protinfo.af_ipx.port == 0)
+ if(sk->protinfo.af_ipx.port == 0)
{
struct sockaddr_ipx uaddr;
int ret;
uaddr.sipx_port = 0;
- uaddr.sipx_network = 0L;
+ uaddr.sipx_network = 0L;
#ifdef CONFIG_IPX_INTERN
memcpy(uaddr.sipx_node, sk->protinfo.af_ipx.intrfc
->if_node, IPX_NODE_LEN);
@@ -2127,7 +2170,7 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
if(usipx->sipx_family != AF_IPX)
return -EINVAL;
}
- else
+ else
{
if(sk->state!=TCP_ESTABLISHED)
return -ENOTCONN;
@@ -2138,39 +2181,35 @@ static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nob
usipx->sipx_network=sk->protinfo.af_ipx.dest_addr.net;
memcpy(usipx->sipx_node,sk->protinfo.af_ipx.dest_addr.node,IPX_NODE_LEN);
}
-
- retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, noblock);
- if (retval < 0)
+
+ retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, flags&MSG_DONTWAIT);
+ if (retval < 0)
return retval;
return len;
}
-static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
- int flags, int *addr_len)
+static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- ipx_socket *sk=(ipx_socket *)sock->data;
+ struct sock *sk=sock->sk;
struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name;
- struct ipx_packet *ipx = NULL;
+ struct ipxhdr *ipx = NULL;
int copied = 0;
int truesize;
struct sk_buff *skb;
int err;
-
+
if (sk->zapped)
return -ENOTCONN;
-
- skb=skb_recv_datagram(sk,flags,noblock,&err);
+ skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&err);
if(skb==NULL)
return err;
-
- if(addr_len)
- *addr_len=sizeof(*sipx);
- ipx = (ipx_packet *)(skb->h.raw);
- truesize=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet);
+ ipx = skb->nh.ipxh;
+ truesize=ntohs(ipx->ipx_pktsize) - sizeof(struct ipxhdr);
copied = truesize;
if(copied > size)
@@ -2178,12 +2217,14 @@ static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int no
copied=size;
msg->msg_flags|=MSG_TRUNC;
}
-
- err = skb_copy_datagram_iovec(skb,sizeof(struct ipx_packet),msg->msg_iov,copied);
+
+ err = skb_copy_datagram_iovec(skb,sizeof(struct ipxhdr),msg->msg_iov,copied);
if (err)
return err;
+ msg->msg_namelen = sizeof(*sipx);
+
if(sipx)
{
sipx->sipx_family=AF_IPX;
@@ -2193,30 +2234,28 @@ static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int no
sipx->sipx_type = ipx->ipx_type;
}
skb_free_datagram(sk, skb);
+
return(copied);
-}
+}
+/*
+ * FIXME: We have to support shutdown really.
+ */
+
static int ipx_shutdown(struct socket *sk,int how)
{
return -EOPNOTSUPP;
}
-static int ipx_select(struct socket *sock , int sel_type, select_table *wait)
-{
- ipx_socket *sk=(ipx_socket *)sock->data;
-
- return datagram_select(sk,sel_type,wait);
-}
-
static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
{
long amount=0;
- ipx_socket *sk=(ipx_socket *)sock->data;
-
+ struct sock *sk=sock->sk;
+
switch(cmd)
{
case TIOCOUTQ:
- amount=sk->sndbuf-sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if(amount<0)
amount=0;
return put_user(amount, (int *)arg);
@@ -2225,7 +2264,7 @@ static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
struct sk_buff *skb;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if((skb=skb_peek(&sk->receive_queue))!=NULL)
- amount=skb->len-sizeof(struct ipx_packet);
+ amount=skb->len-sizeof(struct ipxhdr);
return put_user(amount, (int *)arg);
}
case SIOCADDRT:
@@ -2240,7 +2279,7 @@ static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
return -EPERM;
case SIOCGIFADDR:
return(ipxitf_ioctl(cmd,(void *)arg));
- case SIOCIPXCFGDATA:
+ case SIOCIPXCFGDATA:
{
return(ipxcfg_get_config_data((void *)arg));
}
@@ -2271,10 +2310,14 @@ static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
return(0);
}
-static struct proto_ops ipx_proto_ops = {
+static struct net_proto_family ipx_family_ops = {
AF_IPX,
-
- ipx_create,
+ ipx_create
+};
+
+static struct proto_ops ipx_dgram_ops = {
+ AF_IPX,
+
ipx_dup,
ipx_release,
ipx_bind,
@@ -2282,20 +2325,20 @@ static struct proto_ops ipx_proto_ops = {
ipx_socketpair,
ipx_accept,
ipx_getname,
- ipx_select,
+ datagram_poll,
ipx_ioctl,
- ipx_listen,
+ sock_no_listen,
ipx_shutdown,
ipx_setsockopt,
ipx_getsockopt,
- ipx_fcntl,
+ sock_no_fcntl,
ipx_sendmsg,
ipx_recvmsg
};
/* Called by protocol.c on kernel start up */
-static struct packet_type ipx_8023_packet_type =
+static struct packet_type ipx_8023_packet_type =
{
0, /* MUTTER ntohs(ETH_P_8023),*/
@@ -2305,7 +2348,7 @@ static struct packet_type ipx_8023_packet_type =
NULL,
};
-static struct packet_type ipx_dix_packet_type =
+static struct packet_type ipx_dix_packet_type =
{
0, /* MUTTER ntohs(ETH_P_IPX),*/
NULL, /* All devices */
@@ -2347,7 +2390,7 @@ static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
void
ipx_proto_init(struct net_proto *pro)
{
- (void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+ (void) sock_register(&ipx_family_ops);
pEII_datalink = make_EII_client();
ipx_dix_packet_type.type=htons(ETH_P_IPX);
@@ -2356,24 +2399,24 @@ ipx_proto_init(struct net_proto *pro)
p8023_datalink = make_8023_client();
ipx_8023_packet_type.type=htons(ETH_P_802_3);
dev_add_pack(&ipx_8023_packet_type);
-
+
if ((p8022_datalink = register_8022_client(ipx_8022_type, ipx_rcv)) == NULL)
printk(KERN_CRIT "IPX: Unable to register with 802.2\n");
if ((p8022tr_datalink = register_8022tr_client(ipx_8022_type, ipx_rcv)) == NULL)
printk(KERN_CRIT "IPX: Unable to register with 802.2TR\n");
-
+
if ((pSNAP_datalink = register_snap_client(ipx_snap_id, ipx_rcv)) == NULL)
printk(KERN_CRIT "IPX: Unable to register with SNAP\n");
-
+
register_netdevice_notifier(&ipx_dev_notifier);
#ifdef CONFIG_PROC_FS
proc_net_register(&ipx_procinfo);
proc_net_register(&ipx_if_procinfo);
proc_net_register(&ipx_rt_procinfo);
-#endif
-
- printk(KERN_INFO "Swansea University Computer Society IPX 0.35 for NET3.037\n");
+#endif
+
+ printk(KERN_INFO "Swansea University Computer Society IPX 0.38 for NET3.037\n");
printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
}
@@ -2382,17 +2425,16 @@ ipx_proto_init(struct net_proto *pro)
*
* Use counts are incremented/decremented when
* sockets are created/deleted.
- *
+ *
* Routes are always associated with an interface, and
* allocs/frees will remain properly accounted for by
* their associated interfaces.
- *
+ *
* Ergo, before the ipx module can be removed, all IPX
- * sockets be closed from user space.
+ * sockets be closed from user space.
*/
-static void
-ipx_proto_finito(void)
+static void ipx_proto_finito(void)
{ ipx_interface *ifc;
while (ipx_interfaces) {
@@ -2406,7 +2448,7 @@ ipx_proto_finito(void)
proc_net_unregister(PROC_NET_IPX_ROUTE);
proc_net_unregister(PROC_NET_IPX_INTERFACE);
proc_net_unregister(PROC_NET_IPX);
-#endif
+#endif
unregister_netdevice_notifier(&ipx_dev_notifier);
@@ -2427,15 +2469,16 @@ ipx_proto_finito(void)
destroy_EII_client(pEII_datalink);
pEII_datalink = NULL;
- (void) sock_unregister(ipx_proto_ops.family);
+ (void) sock_unregister(ipx_family_ops.family);
return;
}
+EXPORT_NO_SYMBOLS;
+
int init_module(void)
{
ipx_proto_init(NULL);
- register_symtab(0);
return 0;
}
diff --git a/net/lapb/Makefile b/net/lapb/Makefile
new file mode 100644
index 000000000..434e79aa0
--- /dev/null
+++ b/net/lapb/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile for the Linux LAPB layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+
+O_TARGET := lapb.o
+O_OBJS := lapb_in.o lapb_out.o lapb_subr.o lapb_timer.o
+M_OBJS := $(O_TARGET)
+
+OX_OBJS += lapb_iface.o
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c
new file mode 100644
index 000000000..d5b586e04
--- /dev/null
+++ b/net/lapb/lapb_iface.c
@@ -0,0 +1,418 @@
+/*
+ * LAPB release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * LAPB 001 Jonathan Naylor Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <net/lapb.h>
+
+static lapb_cb *volatile lapb_list = NULL;
+
+/*
+ * Free an allocated lapb control block. This is done to centralise
+ * the MOD count code.
+ */
+static void lapb_free_cb(lapb_cb *lapb)
+{
+ kfree_s(lapb, sizeof(lapb_cb));
+
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * Socket removal during an interrupt is now safe.
+ */
+static void lapb_remove_cb(lapb_cb *lapb)
+{
+ lapb_cb *s;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if ((s = lapb_list) == lapb) {
+ lapb_list = s->next;
+ restore_flags(flags);
+ return;
+ }
+
+ while (s != NULL && s->next != NULL) {
+ if (s->next == lapb) {
+ s->next = lapb->next;
+ restore_flags(flags);
+ return;
+ }
+
+ s = s->next;
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Add a socket to the bound sockets list.
+ */
+static void lapb_insert_cb(lapb_cb *lapb)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ lapb->next = lapb_list;
+ lapb_list = lapb;
+
+ restore_flags(flags);
+}
+
+/*
+ * Convert the integer token used by the device driver into a pointer
+ * to a LAPB control structure.
+ */
+static lapb_cb *lapb_tokentostruct(void *token)
+{
+ lapb_cb *lapb;
+
+ for (lapb = lapb_list; lapb != NULL; lapb = lapb->next)
+ if (lapb->token == token)
+ return lapb;
+
+ return NULL;
+}
+
+/*
+ * Create an empty LAPB control block.
+ */
+static lapb_cb *lapb_create_cb(void)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = (lapb_cb *)kmalloc(sizeof(*lapb), GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+
+ memset(lapb, 0x00, sizeof(*lapb));
+
+ skb_queue_head_init(&lapb->input_queue);
+ skb_queue_head_init(&lapb->write_queue);
+ skb_queue_head_init(&lapb->ack_queue);
+
+ init_timer(&lapb->timer);
+
+ lapb->t1 = LAPB_DEFAULT_T1;
+ lapb->t2 = LAPB_DEFAULT_T2;
+ lapb->n2 = LAPB_DEFAULT_N2;
+ lapb->mode = LAPB_DEFAULT_MODE;
+ lapb->window = LAPB_DEFAULT_WINDOW;
+ lapb->state = LAPB_STATE_0;
+
+ return lapb;
+}
+
+int lapb_register(void *token, struct lapb_register_struct *callbacks)
+{
+ lapb_cb *lapb;
+
+ if (lapb_tokentostruct(token) != NULL)
+ return LAPB_BADTOKEN;
+
+ if ((lapb = lapb_create_cb()) == NULL)
+ return LAPB_NOMEM;
+
+ lapb->token = token;
+ lapb->callbacks = *callbacks;
+
+ lapb_insert_cb(lapb);
+
+ lapb->t1timer = lapb->t1;
+
+ lapb_set_timer(lapb);
+
+ return LAPB_OK;
+}
+
+int lapb_unregister(void *token)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ del_timer(&lapb->timer);
+
+ lapb_clear_queues(lapb);
+
+ lapb_remove_cb(lapb);
+
+ lapb_free_cb(lapb);
+
+ return LAPB_OK;
+}
+
+int lapb_getparms(void *token, struct lapb_parms_struct *parms)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ parms->t1 = lapb->t1;
+ parms->t1timer = lapb->t1timer;
+ parms->t2 = lapb->t2;
+ parms->t2timer = lapb->t2timer;
+ parms->n2 = lapb->n2;
+ parms->n2count = lapb->n2count;
+ parms->state = lapb->state;
+ parms->window = lapb->window;
+ parms->mode = lapb->mode;
+
+ return LAPB_OK;
+}
+
+int lapb_setparms(void *token, struct lapb_parms_struct *parms)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ if (parms->t1 < 1)
+ return LAPB_INVALUE;
+
+ if (parms->t2 < 1)
+ return LAPB_INVALUE;
+
+ if (parms->n2 < 1)
+ return LAPB_INVALUE;
+
+ if (lapb->state == LAPB_STATE_0) {
+ if (parms->mode & LAPB_EXTENDED) {
+ if (parms->window < 1 || parms->window > 127)
+ return LAPB_INVALUE;
+ } else {
+ if (parms->window < 1 || parms->window > 7)
+ return LAPB_INVALUE;
+ }
+
+ lapb->mode = parms->mode;
+ lapb->window = parms->window;
+ }
+
+ lapb->t1 = parms->t1;
+ lapb->t2 = parms->t2;
+ lapb->n2 = parms->n2;
+
+ return LAPB_OK;
+}
+
+int lapb_connect_request(void *token)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ switch (lapb->state) {
+ case LAPB_STATE_1:
+ return LAPB_OK;
+ case LAPB_STATE_3:
+ case LAPB_STATE_4:
+ return LAPB_CONNECTED;
+ }
+
+ lapb_establish_data_link(lapb);
+
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S0 -> S1\n", lapb->token);
+#endif
+
+ lapb->state = LAPB_STATE_1;
+
+ return LAPB_OK;
+}
+
+int lapb_disconnect_request(void *token)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ switch (lapb->state) {
+ case LAPB_STATE_0:
+ return LAPB_NOTCONNECTED;
+
+ case LAPB_STATE_1:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX DISC(1)\n", lapb->token);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ return LAPB_NOTCONNECTED;
+
+ case LAPB_STATE_2:
+ return LAPB_OK;
+ }
+
+ lapb_clear_queues(lapb);
+ lapb->n2count = 0;
+ lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_2;
+
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S2\n", lapb->token);
+#endif
+
+ return LAPB_OK;
+}
+
+int lapb_data_request(void *token, struct sk_buff *skb)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4)
+ return LAPB_NOTCONNECTED;
+
+ skb_queue_tail(&lapb->write_queue, skb);
+
+ lapb_kick(lapb);
+
+ return LAPB_OK;
+}
+
+int lapb_data_received(void *token, struct sk_buff *skb)
+{
+ lapb_cb *lapb;
+
+ if ((lapb = lapb_tokentostruct(token)) == NULL)
+ return LAPB_BADTOKEN;
+
+ skb_queue_tail(&lapb->input_queue, skb);
+
+ return LAPB_OK;
+}
+
+void lapb_connect_confirmation(lapb_cb *lapb, int reason)
+{
+ if (lapb->callbacks.connect_confirmation != NULL)
+ (lapb->callbacks.connect_confirmation)(lapb->token, reason);
+}
+
+void lapb_connect_indication(lapb_cb *lapb, int reason)
+{
+ if (lapb->callbacks.connect_indication != NULL)
+ (lapb->callbacks.connect_indication)(lapb->token, reason);
+}
+
+void lapb_disconnect_confirmation(lapb_cb *lapb, int reason)
+{
+ if (lapb->callbacks.disconnect_confirmation != NULL)
+ (lapb->callbacks.disconnect_confirmation)(lapb->token, reason);
+}
+
+void lapb_disconnect_indication(lapb_cb *lapb, int reason)
+{
+ if (lapb->callbacks.disconnect_indication != NULL)
+ (lapb->callbacks.disconnect_indication)(lapb->token, reason);
+}
+
+int lapb_data_indication(lapb_cb *lapb, struct sk_buff *skb)
+{
+ int used = 0;
+
+ if (lapb->callbacks.data_indication != NULL) {
+ (lapb->callbacks.data_indication)(lapb->token, skb);
+ used = 1;
+ }
+
+ return used;
+}
+
+int lapb_data_transmit(lapb_cb *lapb, struct sk_buff *skb)
+{
+ int used = 0;
+
+ if (lapb->callbacks.data_transmit != NULL) {
+ (lapb->callbacks.data_transmit)(lapb->token, skb);
+ used = 1;
+ }
+
+ return used;
+}
+
+EXPORT_SYMBOL(lapb_register);
+EXPORT_SYMBOL(lapb_unregister);
+EXPORT_SYMBOL(lapb_getparms);
+EXPORT_SYMBOL(lapb_setparms);
+EXPORT_SYMBOL(lapb_connect_request);
+EXPORT_SYMBOL(lapb_disconnect_request);
+EXPORT_SYMBOL(lapb_data_request);
+EXPORT_SYMBOL(lapb_data_received);
+
+void lapb_proto_init(struct net_proto *pro)
+{
+ printk(KERN_INFO "LAPB for Linux. Version 0.01 for Linux NET3.038 (Linux 2.1)\n");
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ lapb_proto_init(NULL);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+}
+#endif
+
+#endif
diff --git a/net/lapb/lapb_in.c b/net/lapb/lapb_in.c
new file mode 100644
index 000000000..9aa367115
--- /dev/null
+++ b/net/lapb/lapb_in.c
@@ -0,0 +1,631 @@
+/*
+ * LAPB release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * LAPB 001 Jonathan Naulor Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ * State machine for state 0, Disconnected State.
+ * The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state0_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ switch (frame->type) {
+ case LAPB_SABM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 RX SABM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->state = LAPB_STATE_3;
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_connect_indication(lapb, LAPB_OK);
+ }
+ break;
+
+ case LAPB_SABME:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 RX SABME(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->state = LAPB_STATE_3;
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_connect_indication(lapb, LAPB_OK);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ }
+ break;
+
+ case LAPB_DISC:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S0 RX DISC(%d)\n", lapb->token, frame->pf);
+ printk(KERN_DEBUG "lapb: (%p) S0 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ break;
+
+ default:
+ break;
+ }
+
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * State machine for state 1, Awaiting Connection State.
+ * The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state1_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ switch (frame->type) {
+ case LAPB_SABM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 RX SABM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ }
+ break;
+
+ case LAPB_SABME:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 RX SABME(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ }
+ break;
+
+ case LAPB_DISC:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 RX DISC(%d)\n", lapb->token, frame->pf);
+ printk(KERN_DEBUG "lapb: (%p) S1 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ break;
+
+ case LAPB_UA:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 RX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ if (frame->pf) {
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_3;
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_connect_confirmation(lapb, LAPB_OK);
+ }
+ break;
+
+ case LAPB_DM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 RX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (frame->pf) {
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_REFUSED);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * State machine for state 2, Awaiting Release State.
+ * The handling of the timer(s) is in file lapb_timer.c
+ */
+static void lapb_state2_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ switch (frame->type) {
+ case LAPB_SABM:
+ case LAPB_SABME:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 RX {SABM,SABME}(%d)\n", lapb->token, frame->pf);
+ printk(KERN_DEBUG "lapb: (%p) S2 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ break;
+
+ case LAPB_DISC:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 RX DISC(%d)\n", lapb->token, frame->pf);
+ printk(KERN_DEBUG "lapb: (%p) S2 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ break;
+
+ case LAPB_UA:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 RX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ if (frame->pf) {
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb_disconnect_confirmation(lapb, LAPB_OK);
+ }
+ break;
+
+ case LAPB_DM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (frame->pf) {
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED);
+ }
+ break;
+
+ case LAPB_I:
+ case LAPB_REJ:
+ case LAPB_RNR:
+ case LAPB_RR:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 RX {I,REJ,RNR,RR}(%d)\n", lapb->token, frame->pf);
+ printk(KERN_DEBUG "lapb: (%p) S2 RX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (frame->pf) lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ break;
+
+ default:
+ break;
+ }
+
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file lapb_timer.c
+ */
+static void lapb_state3_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ int queued = 0;
+ int modulus;
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ switch (frame->type) {
+ case LAPB_SABM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX SABM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_requeue_frames(lapb);
+ }
+ break;
+
+ case LAPB_SABME:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX SABME(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_requeue_frames(lapb);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ }
+ break;
+
+ case LAPB_DISC:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX DISC(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
+#endif
+ lapb_clear_queues(lapb);
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_OK);
+ break;
+
+ case LAPB_DM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX DM(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
+#endif
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED);
+ break;
+
+ case LAPB_RNR:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX RNR(%d) R%d\n", lapb->token, frame->pf, frame->nr);
+#endif
+ lapb->condition |= LAPB_PEER_RX_BUSY_CONDITION;
+ lapb_check_need_response(lapb, frame->cr, frame->pf);
+ if (lapb_validate_nr(lapb, frame->nr)) {
+ lapb_check_iframes_acked(lapb, frame->nr);
+ } else {
+ lapb->frmr_data = *frame;
+ lapb->frmr_type = LAPB_FRMR_Z;
+ lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_4;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ }
+ break;
+
+ case LAPB_RR:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX RR(%d) R%d\n", lapb->token, frame->pf, frame->nr);
+#endif
+ lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+ lapb_check_need_response(lapb, frame->cr, frame->pf);
+ if (lapb_validate_nr(lapb, frame->nr)) {
+ lapb_check_iframes_acked(lapb, frame->nr);
+ } else {
+ lapb->frmr_data = *frame;
+ lapb->frmr_type = LAPB_FRMR_Z;
+ lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_4;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ }
+ break;
+
+ case LAPB_REJ:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX REJ(%d) R%d\n", lapb->token, frame->pf, frame->nr);
+#endif
+ lapb->condition &= ~LAPB_PEER_RX_BUSY_CONDITION;
+ lapb_check_need_response(lapb, frame->cr, frame->pf);
+ if (lapb_validate_nr(lapb, frame->nr)) {
+ lapb_frames_acked(lapb, frame->nr);
+ lapb->t1timer = 0;
+ lapb->n2count = 0;
+ lapb_requeue_frames(lapb);
+ } else {
+ lapb->frmr_data = *frame;
+ lapb->frmr_type = LAPB_FRMR_Z;
+ lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_4;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ }
+ break;
+
+ case LAPB_I:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX I(%d) S%d R%d\n", lapb->token, frame->pf, frame->ns, frame->nr);
+#endif
+ if (!lapb_validate_nr(lapb, frame->nr)) {
+ lapb->frmr_data = *frame;
+ lapb->frmr_type = LAPB_FRMR_Z;
+ lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_4;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ break;
+ }
+ if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) {
+ lapb_frames_acked(lapb, frame->nr);
+ } else {
+ lapb_check_iframes_acked(lapb, frame->nr);
+ }
+ if (frame->ns == lapb->vr) {
+ lapb->vr = (lapb->vr + 1) % modulus;
+ queued = lapb_data_indication(lapb, skb);
+ lapb->condition &= ~LAPB_REJECT_CONDITION;
+ if (frame->pf) {
+ lapb_enquiry_response(lapb);
+ } else {
+ if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) {
+ lapb->t2timer = lapb->t2;
+ lapb->condition |= LAPB_ACK_PENDING_CONDITION;
+ }
+ }
+ } else {
+ if (lapb->condition & LAPB_REJECT_CONDITION) {
+ if (frame->pf)
+ lapb_enquiry_response(lapb);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 TX REJ(%d) R%d\n", lapb->token, frame->pf, lapb->vr);
+#endif
+ lapb->condition |= LAPB_REJECT_CONDITION;
+ lapb_send_control(lapb, LAPB_REJ, frame->pf, LAPB_RESPONSE);
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+ }
+ }
+ break;
+
+ case LAPB_FRMR:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX FRMR(%d) %02X %02X %02X %02X %02X\n", lapb->token, frame->pf, skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4]);
+#endif
+ lapb_establish_data_link(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S1\n", lapb->token);
+#endif
+ lapb_requeue_frames(lapb);
+ lapb->state = LAPB_STATE_1;
+ break;
+
+ case LAPB_ILLEGAL:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S3 RX ILLEGAL(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb->frmr_data = *frame;
+ lapb->frmr_type = LAPB_FRMR_W;
+ lapb_transmit_frmr(lapb);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
+#endif
+ lapb->state = LAPB_STATE_4;
+ lapb->t1timer = lapb->t1;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!queued)
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * State machine for state 4, Frame Reject State.
+ * The handling of the timer(s) is in file lapb_timer.c.
+ */
+static void lapb_state4_machine(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ switch (frame->type) {
+ case LAPB_SABM:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 RX SABM(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->state = LAPB_STATE_3;
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_connect_indication(lapb, LAPB_OK);
+ }
+ break;
+
+ case LAPB_SABME:
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 RX SABME(%d)\n", lapb->token, frame->pf);
+#endif
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 TX UA(%d)\n", lapb->token, frame->pf);
+#endif
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb->state = LAPB_STATE_3;
+ lapb->condition = 0x00;
+ lapb->t1timer = 0;
+ lapb->t2timer = 0;
+ lapb->n2count = 0;
+ lapb->vs = 0;
+ lapb->vr = 0;
+ lapb->va = 0;
+ lapb_connect_indication(lapb, LAPB_OK);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S4 TX DM(%d)\n", lapb->token, frame->pf);
+#endif
+ lapb_send_control(lapb, LAPB_DM, frame->pf, LAPB_RESPONSE);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * Process an incoming LAPB frame
+ */
+void lapb_data_input(lapb_cb *lapb, struct sk_buff *skb)
+{
+ struct lapb_frame frame;
+
+ lapb_decode(lapb, skb, &frame);
+
+ switch (lapb->state) {
+ case LAPB_STATE_0:
+ lapb_state0_machine(lapb, skb, &frame);
+ break;
+ case LAPB_STATE_1:
+ lapb_state1_machine(lapb, skb, &frame);
+ break;
+ case LAPB_STATE_2:
+ lapb_state2_machine(lapb, skb, &frame);
+ break;
+ case LAPB_STATE_3:
+ lapb_state3_machine(lapb, skb, &frame);
+ break;
+ case LAPB_STATE_4:
+ lapb_state4_machine(lapb, skb, &frame);
+ break;
+ }
+}
+
+#endif
diff --git a/net/lapb/lapb_out.c b/net/lapb/lapb_out.c
new file mode 100644
index 000000000..1256e3a3c
--- /dev/null
+++ b/net/lapb/lapb_out.c
@@ -0,0 +1,233 @@
+/*
+ * LAPB release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * LAPB 001 Jonathan Naylor Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ * This procedure is passed a buffer descriptor for an iframe. It builds
+ * the rest of the control part of the frame and then writes it out.
+ */
+static void lapb_send_iframe(lapb_cb *lapb, struct sk_buff *skb, int poll_bit)
+{
+ unsigned char *frame;
+
+ if (skb == NULL)
+ return;
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ frame = skb_push(skb, 2);
+
+ frame[0] = LAPB_I;
+ frame[0] |= (lapb->vs << 1);
+ frame[1] = (poll_bit) ? LAPB_EPF : 0;
+ frame[1] |= (lapb->vr << 1);
+ } else {
+ frame = skb_push(skb, 1);
+
+ *frame = LAPB_I;
+ *frame |= (poll_bit) ? LAPB_SPF : 0;
+ *frame |= (lapb->vr << 5);
+ *frame |= (lapb->vs << 1);
+ }
+
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX I(%d) S%d R%d\n", lapb->token, lapb->state, poll_bit, lapb->vs, lapb->vr);
+#endif
+
+ lapb_transmit_buffer(lapb, skb, LAPB_COMMAND);
+}
+
+void lapb_kick(lapb_cb *lapb)
+{
+ struct sk_buff *skb, *skbn;
+ unsigned short modulus, start, end;
+
+ del_timer(&lapb->timer);
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs;
+ end = (lapb->va + lapb->window) % modulus;
+
+ if (!(lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) &&
+ start != end &&
+ skb_peek(&lapb->write_queue) != NULL) {
+
+ lapb->vs = start;
+
+ /*
+ * Dequeue the frame and copy it.
+ */
+ skb = skb_dequeue(&lapb->write_queue);
+
+ do {
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+ skb_queue_head(&lapb->write_queue, skb);
+ break;
+ }
+
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
+
+ /*
+ * Transmit the frame copy.
+ */
+ lapb_send_iframe(lapb, skbn, LAPB_POLLOFF);
+
+ lapb->vs = (lapb->vs + 1) % modulus;
+
+ /*
+ * Requeue the original data frame.
+ */
+ skb_queue_tail(&lapb->ack_queue, skb);
+
+ } while (lapb->vs != end && (skb = skb_dequeue(&lapb->write_queue)) != NULL);
+
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+
+ if (lapb->t1timer == 0)
+ lapb->t1timer = lapb->t1;
+ }
+
+ lapb_set_timer(lapb);
+}
+
+void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type)
+{
+ unsigned char *ptr;
+
+ ptr = skb_push(skb, 1);
+
+ if (lapb->mode & LAPB_MLP) {
+ if (lapb->mode & LAPB_DCE) {
+ if (type == LAPB_COMMAND)
+ *ptr = LAPB_ADDR_C;
+ if (type == LAPB_RESPONSE)
+ *ptr = LAPB_ADDR_D;
+ } else {
+ if (type == LAPB_COMMAND)
+ *ptr = LAPB_ADDR_D;
+ if (type == LAPB_RESPONSE)
+ *ptr = LAPB_ADDR_C;
+ }
+ } else {
+ if (lapb->mode & LAPB_DCE) {
+ if (type == LAPB_COMMAND)
+ *ptr = LAPB_ADDR_A;
+ if (type == LAPB_RESPONSE)
+ *ptr = LAPB_ADDR_B;
+ } else {
+ if (type == LAPB_COMMAND)
+ *ptr = LAPB_ADDR_B;
+ if (type == LAPB_RESPONSE)
+ *ptr = LAPB_ADDR_A;
+ }
+ }
+
+#if LAPB_DEBUG > 2
+ printk(KERN_DEBUG "lapb: (%p) S%d TX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]);
+#endif
+
+ if (!lapb_data_transmit(lapb, skb))
+ kfree_skb(skb, FREE_WRITE);
+}
+
+void lapb_establish_data_link(lapb_cb *lapb)
+{
+ lapb->condition = 0x00;
+ lapb->n2count = 0;
+
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX SABME(1)\n", lapb->token, lapb->state);
+#endif
+ lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX SABM(1)\n", lapb->token, lapb->state);
+#endif
+ lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
+ }
+
+ lapb->t2timer = 0;
+ lapb->t1timer = lapb->t1;
+}
+
+void lapb_enquiry_response(lapb_cb *lapb)
+{
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX RR(1) R%d\n", lapb->token, lapb->state, lapb->vr);
+#endif
+
+ lapb_send_control(lapb, LAPB_RR, LAPB_POLLON, LAPB_RESPONSE);
+
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+}
+
+void lapb_timeout_response(lapb_cb *lapb)
+{
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX RR(0) R%d\n", lapb->token, lapb->state, lapb->vr);
+#endif
+
+ lapb_send_control(lapb, LAPB_RR, LAPB_POLLOFF, LAPB_RESPONSE);
+
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+}
+
+void lapb_check_iframes_acked(lapb_cb *lapb, unsigned short nr)
+{
+ if (lapb->vs == nr) {
+ lapb_frames_acked(lapb, nr);
+ lapb->t1timer = 0;
+ lapb->n2count = 0;
+ } else {
+ if (lapb->va != nr) {
+ lapb_frames_acked(lapb, nr);
+ lapb->t1timer = lapb->t1;
+ }
+ }
+}
+
+void lapb_check_need_response(lapb_cb *lapb, int type, int pf)
+{
+ if (type == LAPB_COMMAND && pf)
+ lapb_enquiry_response(lapb);
+}
+
+#endif
diff --git a/net/lapb/lapb_subr.c b/net/lapb/lapb_subr.c
new file mode 100644
index 000000000..626e08927
--- /dev/null
+++ b/net/lapb/lapb_subr.c
@@ -0,0 +1,291 @@
+/*
+ * LAPB release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * LAPB 001 Jonathan Naylor Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ * This routine purges all the queues of frames.
+ */
+void lapb_clear_queues(lapb_cb *lapb)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&lapb->input_queue)) != NULL)
+ kfree_skb(skb, FREE_READ);
+
+ while ((skb = skb_dequeue(&lapb->write_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+
+ while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+void lapb_frames_acked(lapb_cb *lapb, unsigned short nr)
+{
+ struct sk_buff *skb;
+ int modulus;
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ /*
+ * Remove all the ack-ed frames from the ack queue.
+ */
+ if (lapb->va != nr) {
+ while (skb_peek(&lapb->ack_queue) != NULL && lapb->va != nr) {
+ skb = skb_dequeue(&lapb->ack_queue);
+ kfree_skb(skb, FREE_WRITE);
+ lapb->va = (lapb->va + 1) % modulus;
+ }
+ }
+}
+
+void lapb_requeue_frames(lapb_cb *lapb)
+{
+ struct sk_buff *skb, *skb_prev = NULL;
+
+ /*
+ * Requeue all the un-ack-ed frames on the output queue to be picked
+ * up by lapb_kick called from the timer. This arrangement handles the
+ * possibility of an empty output queue.
+ */
+ while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) {
+ if (skb_prev == NULL)
+ skb_queue_head(&lapb->write_queue, skb);
+ else
+ skb_append(skb_prev, skb);
+ skb_prev = skb;
+ }
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+int lapb_validate_nr(lapb_cb *lapb, unsigned short nr)
+{
+ unsigned short vc = lapb->va;
+ int modulus;
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ while (vc != lapb->vs) {
+ if (nr == vc) return 1;
+ vc = (vc + 1) % modulus;
+ }
+
+ if (nr == lapb->vs) return 1;
+
+ return 0;
+}
+
+/*
+ * This routine is the centralised routine for parsing the control
+ * information for the different frame formats.
+ */
+void lapb_decode(lapb_cb *lapb, struct sk_buff *skb, struct lapb_frame *frame)
+{
+ frame->type = LAPB_ILLEGAL;
+
+#if LAPB_DEBUG > 2
+ printk(KERN_DEBUG "lapb: (%p) S%d RX %02X %02X %02X\n", lapb->token, lapb->state, skb->data[0], skb->data[1], skb->data[2]);
+#endif
+
+ if (lapb->mode & LAPB_MLP) {
+ if (lapb->mode & LAPB_DCE) {
+ if (skb->data[0] == LAPB_ADDR_D)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_C)
+ frame->cr = LAPB_RESPONSE;
+ } else {
+ if (skb->data[0] == LAPB_ADDR_C)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_D)
+ frame->cr = LAPB_RESPONSE;
+ }
+ } else {
+ if (lapb->mode & LAPB_DCE) {
+ if (skb->data[0] == LAPB_ADDR_B)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_A)
+ frame->cr = LAPB_RESPONSE;
+ } else {
+ if (skb->data[0] == LAPB_ADDR_A)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_B)
+ frame->cr = LAPB_RESPONSE;
+ }
+ }
+
+ skb_pull(skb, 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ if ((skb->data[0] & LAPB_S) == 0) {
+ frame->type = LAPB_I; /* I frame - carries NR/NS/PF */
+ frame->ns = (skb->data[0] >> 1) & 0x7F;
+ frame->nr = (skb->data[1] >> 1) & 0x7F;
+ frame->pf = skb->data[1] & LAPB_EPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = skb->data[1];
+ skb_pull(skb, 2);
+ } else if ((skb->data[0] & LAPB_U) == 1) { /* S frame - take out PF/NR */
+ frame->type = skb->data[0] & 0x0F;
+ frame->nr = (skb->data[1] >> 1) & 0x7F;
+ frame->pf = skb->data[1] & LAPB_EPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = skb->data[1];
+ skb_pull(skb, 2);
+ } else if ((skb->data[0] & LAPB_U) == 3) { /* U frame - take out PF */
+ frame->type = skb->data[0] & ~LAPB_SPF;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = 0x00;
+ skb_pull(skb, 1);
+ }
+ } else {
+ if ((skb->data[0] & LAPB_S) == 0) {
+ frame->type = LAPB_I; /* I frame - carries NR/NS/PF */
+ frame->ns = (skb->data[0] >> 1) & 0x07;
+ frame->nr = (skb->data[0] >> 5) & 0x07;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ } else if ((skb->data[0] & LAPB_U) == 1) { /* S frame - take out PF/NR */
+ frame->type = skb->data[0] & 0x0F;
+ frame->nr = (skb->data[0] >> 5) & 0x07;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ } else if ((skb->data[0] & LAPB_U) == 3) { /* U frame - take out PF */
+ frame->type = skb->data[0] & ~LAPB_SPF;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ }
+
+ frame->control[0] = skb->data[0];
+
+ skb_pull(skb, 1);
+ }
+}
+
+/*
+ * This routine is called when the HDLC layer internally generates a
+ * command or response for the remote machine ( eg. RR, UA etc. ).
+ * Only supervisory or unnumbered frames are processed, FRMRs are handled
+ * by lapb_transmit_frmr below.
+ */
+void lapb_send_control(lapb_cb *lapb, int frametype, int poll_bit, int type)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+
+ if ((skb = alloc_skb(LAPB_HEADER_LEN + 3, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ if ((frametype & LAPB_U) == LAPB_U) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? LAPB_SPF : 0;
+ } else {
+ dptr = skb_put(skb, 2);
+ dptr[0] = frametype;
+ dptr[1] = (lapb->vr << 1);
+ dptr[1] |= (poll_bit) ? LAPB_EPF : 0;
+ }
+ } else {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? LAPB_SPF : 0;
+ if ((frametype & LAPB_U) == LAPB_S) /* S frames carry NR */
+ *dptr |= (lapb->vr << 5);
+ }
+
+ lapb_transmit_buffer(lapb, skb, type);
+}
+
+/*
+ * This routine generates FRMRs based on information previously stored in
+ * the LAPB control block.
+ */
+void lapb_transmit_frmr(lapb_cb *lapb)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+
+ if ((skb = alloc_skb(LAPB_HEADER_LEN + 7, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ dptr = skb_put(skb, 6);
+ *dptr++ = LAPB_FRMR;
+ *dptr++ = lapb->frmr_data.control[0];
+ *dptr++ = lapb->frmr_data.control[1];
+ *dptr++ = (lapb->vs << 1) & 0xFE;
+ *dptr = (lapb->vr << 1) & 0xFE;
+ if (lapb->frmr_data.cr == LAPB_RESPONSE)
+ *dptr |= 0x01;
+ dptr++;
+ *dptr++ = lapb->frmr_type;
+
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX FRMR %02X %02X %02X %02X %02X\n", lapb->token, lapb->state, skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5]);
+#endif
+ } else {
+ dptr = skb_put(skb, 4);
+ *dptr++ = LAPB_FRMR;
+ *dptr++ = lapb->frmr_data.control[0];
+ *dptr = (lapb->vs << 1) & 0x0E;
+ *dptr |= (lapb->vr << 5) & 0xE0;
+ if (lapb->frmr_data.cr == LAPB_RESPONSE)
+ *dptr |= 0x10;
+ dptr++;
+ *dptr++ = lapb->frmr_type;
+
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S%d TX FRMR %02X %02X %02X\n", lapb->token, lapb->state, skb->data[1], skb->data[2], skb->data[3]);
+#endif
+ }
+
+ lapb_transmit_buffer(lapb, skb, LAPB_RESPONSE);
+}
+
+#endif
diff --git a/net/lapb/lapb_timer.c b/net/lapb/lapb_timer.c
new file mode 100644
index 000000000..2679ff514
--- /dev/null
+++ b/net/lapb/lapb_timer.c
@@ -0,0 +1,208 @@
+/*
+ * LAPB release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * LAPB 001 Jonathan Naylor Started Coding
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_LAPB) || defined(CONFIG_LAPB_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+static void lapb_timer(unsigned long);
+
+/*
+ * Linux set timer
+ */
+void lapb_set_timer(lapb_cb *lapb)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ del_timer(&lapb->timer);
+ restore_flags(flags);
+
+ lapb->timer.data = (unsigned long)lapb;
+ lapb->timer.function = &lapb_timer;
+ lapb->timer.expires = jiffies + (HZ / 10);
+
+ add_timer(&lapb->timer);
+}
+
+/*
+ * LAPB TIMER
+ *
+ * This routine is called every 100ms. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+static void lapb_timer(unsigned long param)
+{
+ lapb_cb *lapb = (lapb_cb *)param;
+ struct sk_buff *skb;
+
+ /*
+ * Process all packet received since the last clock tick.
+ */
+ while ((skb = skb_dequeue(&lapb->input_queue)) != NULL)
+ lapb_data_input(lapb, skb);
+
+ /*
+ * If in a data transfer state, transmit any data.
+ */
+ if (lapb->state == LAPB_STATE_3)
+ lapb_kick(lapb);
+
+ /*
+ * T2 expiry code.
+ */
+ if (lapb->t2timer > 0 && --lapb->t2timer == 0) {
+ if (lapb->state == LAPB_STATE_3) {
+ if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+ lapb_timeout_response(lapb);
+ }
+ }
+ }
+
+ /*
+ * If T1 isn't running, or hasn't timed out yet, keep going.
+ */
+ if (lapb->t1timer == 0 || --lapb->t1timer > 0) {
+ lapb_set_timer(lapb);
+ return;
+ }
+
+ /*
+ * T1 has expired.
+ */
+ switch (lapb->state) {
+
+ /*
+ * If we are a DCE, keep going DM .. DM .. DM
+ */
+ case LAPB_STATE_0:
+ if (lapb->mode & LAPB_DCE)
+ lapb_send_control(lapb, LAPB_DM, LAPB_POLLOFF, LAPB_RESPONSE);
+ break;
+
+ /*
+ * Awaiting connection state, send SABM(E), up to N2 times.
+ */
+ case LAPB_STATE_1:
+ if (lapb->n2count == lapb->n2) {
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
+#endif
+ } else {
+ lapb->n2count++;
+ if (lapb->mode & LAPB_EXTENDED) {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX SABME(1)\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_SABME, LAPB_POLLON, LAPB_COMMAND);
+ } else {
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S1 TX SABM(1)\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
+ }
+ }
+ break;
+
+ /*
+ * Awaiting disconnection state, send DISC, up to N2 times.
+ */
+ case LAPB_STATE_2:
+ if (lapb->n2count == lapb->n2) {
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t2timer = 0;
+ lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
+#endif
+ } else {
+ lapb->n2count++;
+#if LAPB_DEBUG > 1
+ printk(KERN_DEBUG "lapb: (%p) S2 TX DISC(1)\n", lapb->token);
+#endif
+ lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
+ }
+ break;
+
+ /*
+ * Data transfer state, restransmit I frames, up to N2 times.
+ */
+ case LAPB_STATE_3:
+ if (lapb->n2count == lapb->n2) {
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
+#endif
+ } else {
+ lapb->n2count++;
+ lapb_requeue_frames(lapb);
+ }
+ break;
+
+ /*
+ * Frame reject state, restransmit FRMR frames, up to N2 times.
+ */
+ case LAPB_STATE_4:
+ if (lapb->n2count == lapb->n2) {
+ lapb_clear_queues(lapb);
+ lapb->state = LAPB_STATE_0;
+ lapb->t2timer = 0;
+ lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
+#if LAPB_DEBUG > 0
+ printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
+#endif
+ } else {
+ lapb->n2count++;
+ lapb_transmit_frmr(lapb);
+ }
+ break;
+ }
+
+ lapb->t1timer = lapb->t1;
+
+ lapb_set_timer(lapb);
+}
+
+#endif
diff --git a/net/netbeui/README b/net/netbeui/README
new file mode 100644
index 000000000..02e270b5f
--- /dev/null
+++ b/net/netbeui/README
@@ -0,0 +1,19 @@
+
+NetBEUI is a rather weird protocol. There are about three different set
+of connection and name spaces here.
+
+Firstly we have an array of 802.2 LLC links acting as reliable inter node
+links for the nodes we are talking to do. We create and tear these down as
+needed. In effect it goes around pretending ethernet is a set of bits of
+wire and running pseudo X.25 over it. The LLC code is elsewhere (net/802).
+
+Secondly we have the netbios name space. When we sit on multiple networks
+we have fun. Netbios isnt routable, so we have to arse around looking on
+all our devices for names.
+
+Thirdly we have logical netbeui sessions on top of the whole heap.
+
+ *Don't blame us*
+
+We didn't design the protocol.
+
diff --git a/net/netbeui/af_netbeui.c b/net/netbeui/af_netbeui.c
new file mode 100644
index 000000000..e6683d00f
--- /dev/null
+++ b/net/netbeui/af_netbeui.c
@@ -0,0 +1,658 @@
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/route.h>
+#include <linux/inet.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/termios.h> /* For TIOCOUTQ/INQ */
+#include <linux/poll.h>
+#include <net/datalink.h>
+#include <net/p8022.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+
+
+#undef NETBEUI_DEBUG
+
+
+#ifdef NETBEUI_DEBUG
+#define DPRINT(x) print(x)
+#else
+#define DPRINT(x)
+#endif
+
+#define min(a,b) (((a)<(b))?(a):(b))
+
+/***********************************************************************************************************************\
+* *
+* Handlers for the socket list. *
+* *
+\***********************************************************************************************************************/
+
+static netbeui_socket *netbeui_socket_list=NULL;
+
+/*
+ * Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ * handler using this technique. They can be added although we do not
+ * use this facility.
+ */
+
+extern inline void netbeui_remove_socket(netbeui_socket *sk)
+{
+ sklist_remove_socket(&netbeui_socket_list,sk);
+}
+
+extenr inline void netbeui_insert_socket(netbeui_socket *sk)
+{
+ sklist_insert_socket(&netbeui_socket_list,sk);
+ netbeui_socket_list=sk;
+ restore_flags(flags);
+}
+
+static void netbeui_destroy_socket(netbeui_socket *sk)
+{
+ /*
+ * Release netbios logical channels first
+ */
+ if(sk->af_nb.nb_link)
+ {
+ netbeui_delete_channel(sk->af_nb.nb_link);
+ sk->af_nb.nb_link=NULL;
+ }
+ if(sk->af_nb.src_name)
+ {
+ netbeui_release_name(sk->af_nb.src_name);
+ sk->af_nb.src_name=NULL;
+ }
+ if(sk->af_nb.dst_name)
+ {
+ netbeui_release_name(sk->af_nb.dst_name);
+ sk->af_nb.dst_name=NULL;
+ }
+ netbeui_remove_listener(sk);
+ sklist_destroy_socket(&netbeui_socket,sk);
+}
+
+/*
+ * Called from proc fs
+ */
+
+int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return 0;
+}
+
+/*
+ * A device event has occurred. Watch for devices going down and
+ * delete our use of them (iface and route).
+ */
+
+static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ if(event==NETDEV_DOWN)
+ {
+ /* Discard any use of this */
+ netbeui_drop_device((struct device *)ptr);
+ }
+ return NOTIFY_DONE;
+}
+
+/*******************************************************************************************************************\
+* *
+* Handling for system calls applied via the various interfaces to a netbeui socket object *
+* *
+\*******************************************************************************************************************/
+
+static int netbeui_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk=(netbeui_socket *)sock->data;
+ if(sk->state!=TCP_CLOSED)
+ return -EINVAL;
+ if(backlog<0)
+ return -EINVAL;
+ if(backlog<128)
+ sk->backlog=backlog;
+ else
+ sk->backlog=128;
+ sk->state=TCP_LISTEN;
+ sk->state_change(sk);
+ netbeui_add_listener(sk);
+ return 0;
+}
+
+/*
+ * Create a socket. Initialise the socket, blank the addresses
+ * set the state.
+ */
+
+static int netbeui_create(struct socket *sock, int protocol)
+{
+ netbeui_socket *sk;
+ sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
+ if(sk==NULL)
+ return(-ENOBUFS);
+ switch(sock->type)
+ {
+ case SOCK_DGRAM:
+ break;
+ case SOCK_SEQPACKET:
+ break;
+ default:
+ sk_free((void *)sk);
+ return(-ESOCKTNOSUPPORT);
+ }
+
+ MOD_INC_USE_COUNT;
+
+ sock_init_data(sock,sk);
+ sk->mtu=1500;
+ return(0);
+}
+
+/*
+ * Copy a socket. No work needed.
+ */
+
+static int netbeui_dup(struct socket *newsock,struct socket *oldsock)
+{
+ return(netbeui_create(newsock,oldsock->type));
+}
+
+/*
+ * Free a socket. No work needed
+ */
+
+static int netbeui_release(struct socket *sock, struct socket *peer)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ if(sk==NULL)
+ return(0);
+ if(!sk->dead)
+ sk->state_change(sk);
+ sk->dead=1;
+ sock->data=NULL;
+ netbeui_destroy_socket(sk);
+ return(0);
+}
+
+/*
+ * Set the address 'our end' of the connection.
+ */
+
+static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
+{
+ netbeui_socket *sk;
+ struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+ int err;
+
+ sk=(netbeui_socket *)sock->data;
+
+ if(sk->zapped==0)
+ return(-EINVAL);
+
+ if(addr_len!=sizeof(struct sockaddr_netbeui))
+ return -EINVAL;
+
+ if(addr->snb_family!=AF_NETBEUI)
+ return -EAFNOSUPPORT;
+
+ /*
+ * This will sleep. To meet POSIX it is non interruptible.
+ * Someone should give the 1003.1g authors an injection of
+ * imagination...
+ */
+
+ if(sk->af_nb.src_name!=NULL)
+ return -EINVAL;
+
+ /*
+ * Try and get the name. It may return various 'invalid' name
+ * problem reports or EADDRINUSE if we or another node holds
+ * the desired name.
+ */
+
+ sk->af_nb.src_name=netbeui_alloc_name(addr, &err);
+ if(sk->af_nb.src_name==NULL)
+ return err;
+ /*
+ * Add us to the active socket list
+ */
+ netbeui_insert_socket(sk);
+ sk->zapped=0;
+ return(0);
+}
+
+/*
+ * Set the address we talk to.
+ */
+
+static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
+ size_t addr_len, int flags)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+
+ /*
+ * Check pending operations
+ */
+
+ if(sk->state==TCP_ESTABLISHED && sock->state == SS_CONNECTING)
+ {
+ sock->state==SS_CONNECTED;
+ return 0;
+ }
+
+ if(sk->state == TCP_CLOSE & sock->state == SS_CONNECTING)
+ {
+ sock->state==SS_UNCONNECTED;
+ return -ECONNREFUSED;
+ }
+
+ if(sock->state == SS_CONNECTING && (flags & O_NONBLOCK))
+ return -EINPROGRESS;
+
+ if(sk->state==TCP_ESTABLISHED)
+ return -EISCONN;
+
+ /*
+ * If this is new it must really be new...
+ */
+
+ if(sk->af_nb.dst_name==NULL)
+ {
+ if(addr_len != sizeof(struct sockaddr_nb))
+ return -EINVAL;
+ if(addr->snb_family!=AF_NETBEUI)
+ return -EAFNOSUPPORT;
+ /*
+ * Try and find the name
+ */
+ }
+}
+
+/*
+ * Not relevant
+ */
+
+static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return(-EOPNOTSUPP);
+}
+
+/*
+ * WRITE ME
+ */
+
+static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ if(newsock->data)
+ sk_free(newsock->data);
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Find the name of a netbeui socket. Just copy the right
+ * fields into the sockaddr.
+ */
+
+static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
+ size_t *uaddr_len, int peer)
+{
+ struct sockaddr_netbeui snb;
+ netbeui_socket *sk;
+
+ sk=(netbeui_socket *)sock->data;
+ if(sk->zapped)
+ {
+ return -EINVAL;
+ }
+
+ *uaddr_len = sizeof(struct sockaddr_netbeui);
+
+ if(peer)
+ {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ }
+ else
+ {
+ }
+ snb.snb_family = AF_NETBEUI;
+ memcpy(uaddr,&snb,sizeof(snb));
+ return(0);
+}
+
+/*
+ * Receive a packet (in skb) from device dev.
+ */
+
+static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+ return nb_llc_rcv(skb);
+}
+
+static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ struct sockaddr_nb *usnb=(struct sockaddr_nb *)msg->msg_name;
+ struct sk_buff *skb;
+ struct device *dev;
+ struct nbhdr *nbp;
+ int size;
+ struct netbeui_route *rt;
+ int loopback=0;
+ int err;
+
+ if(flags)
+ return -EINVAL;
+
+ if(len>1500) /* - headers!! */
+ return -EMSGSIZE;
+
+ if(usnb)
+ {
+ if(sk->zapped)
+ {
+ if(netbeui_autobind(sk)<0)
+ return -EBUSY;
+ }
+
+ if(msg->msg_namelen <sizeof(*usnb))
+ return(-EINVAL);
+ if(usnb->snb_family != AF_NETBEUI)
+ return -EINVAL;
+ /* Check broadcast */
+ }
+ else
+ {
+ if(sk->state!=TCP_ESTABLISHED)
+ return -ENOTCONN;
+ /* Connected .. */
+ }
+
+ /* Build a packet */
+ SOCK_DEBUG(sk, "SK %p: Got address.\n",sk);
+ size=sizeof(struct nbhdr)+len+nb_dl->header_length; /* For headers */
+
+ SOCK_DEBUG(sk, "SK %p: Size needed %d, device %s\n", sk, size, dev->name);
+ size += dev->hard_header_len;
+ skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
+ if(skb==NULL)
+ return err;
+
+ skb->sk=sk;
+ skb->free=1;
+ skb->arp=1;
+ skb_reserve(skb,nb_dl->header_length);
+ skb_reserve(skb,dev->hard_header_len);
+ skb->dev=dev;
+ SOCK_DEBUG(sk, "SK %p: Begin build.\n", sk);
+ nbp=(struct nbhdr *)skb_put(skb,sizeof(struct nbhdr));
+ SOCK_DEBUG(sk, "SK %p: Copy user data (%d bytes).\n", sk, len);
+ err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
+ if (err)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return -EFAULT;
+ }
+
+#ifdef CONFIG_FIREWALL
+
+ if(call_out_firewall(AF_NETBEUI, skb->dev, nbp, NULL)!=FW_ACCEPT)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return -EPERM;
+ }
+
+#endif
+
+ if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1)
+ kfree_skb(skb, FREE_WRITE);
+ SOCK_DEBUG(sk, "SK %p: Done write (%d).\n", sk, len);
+ return len;
+}
+
+
+static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+ struct sockaddr_nb *snb=(struct sockaddr_nb *)msg->msg_name;
+ struct nbphdr *nbp = NULL;
+ int copied = 0;
+ struct sk_buff *skb;
+ int er = 0;
+
+ if(addr_len)
+ *addr_len=sizeof(*snb);
+
+ skb=skb_recv_datagram(sk,flags,noblock,&er);
+ if(skb==NULL)
+ return er;
+
+ snb = (struct nbphdr *)(skb->h.raw);
+ if(sk->type==SOCK_RAW)
+ {
+ copied=skb->len
+ if(copied > size)
+ {
+ copied=size;
+ msg->msg_flags|=MSG_TRUNC;
+ }
+ er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
+ if (er)
+ goto out;
+ }
+ else
+ {
+ copied=skb->len - sizeof(*nbp);
+ if (copied > size)
+ {
+ copied = size;
+ msg->msg_flags|=MSG_TRUNC;
+ }
+ er = skb_copy_datagram_iovec(skb,sizeof(*nbp),msg->msg_iov,copied);
+ if (er)
+ goto out;
+ }
+ if(snb)
+ {
+ sat->sat_family=AF_NETBEUI;
+ /* Copy name over */
+ }
+out:
+ skb_free_datagram(sk, skb);
+ return er ? er : (copied);
+}
+
+
+static int netbeui_shutdown(struct socket *sk,int how)
+{
+ return -EOPNOTSUPP;
+}
+
+static int netbeui_poll(struct socket *sock, poll_table *wait)
+{
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+
+ return datagram_poll(sk,wait);
+}
+
+/*
+ * Netbeui ioctl calls.
+ */
+
+static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+ long amount=0;
+ netbeui_socket *sk=(netbeui_socket *)sock->data;
+
+ switch(cmd)
+ {
+ /*
+ * Protocol layer
+ */
+ case TIOCOUTQ:
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ if(amount<0)
+ amount=0;
+ break;
+ case TIOCINQ:
+ {
+ struct sk_buff *skb;
+ /* These two are safe on a single CPU system as only user tasks fiddle here */
+ if((skb=skb_peek(&sk->receive_queue))!=NULL)
+ amount=skb->len-sizeof(struct ddpehdr);
+ break;
+ }
+ case SIOCGSTAMP:
+ if (sk)
+ {
+ if(sk->stamp.tv_sec==0)
+ return -ENOENT;
+ return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0;
+ }
+ return -EINVAL;
+ /*
+ * Routing
+ */
+ case SIOCADDRT:
+ case SIOCDELRT:
+ if(!suser())
+ return -EPERM;
+ return(nbrtr_ioctl(cmd,(void *)arg));
+ /*
+ * Interface
+ */
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFBRDADDR:
+ return nbif_ioctl(cmd,(void *)arg);
+ /*
+ * Physical layer ioctl calls
+ */
+ case SIOCSIFLINK:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCGIFFLAGS:
+ case SIOCSIFFLAGS:
+ case SIOCGIFMTU:
+ case SIOCGIFCONF:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+
+ return(dev_ioctl(cmd,(void *) arg));
+
+ case SIOCSIFMETRIC:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMEM:
+ case SIOCSIFMEM:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ return -EINVAL;
+
+ default:
+ return -EINVAL;
+ }
+ return put_user(amount, (int *)arg);
+}
+
+static struct proto_ops netbeui_proto_ops = {
+ AF_NETBEUI,
+
+ netbeui_create,
+ netbeui_dup,
+ netbeui_release,
+ netbeui_bind,
+ netbeui_connect,
+ netbeui_socketpair,
+ netbeui_accept,
+ netbeui_getname,
+ netbeui_poll,
+ netbeui_ioctl,
+ netbeui_listen,
+ netbeui_shutdown,
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
+ netbeui_sendmsg,
+ netbeui_recvmsg
+};
+
+static struct notifier_block nb_notifier={
+ nb_device_event,
+ NULL,
+ 0
+};
+
+static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_netbeui = {
+ PROC_NET_NETBEUI, 9, "netbeui",
+ S_IFREG | S_IRUGO, 1, 0, 0
+ 0, &proc_net_inode_operations,
+ netbeui_get_info
+};
+#endif
+
+/* Called by proto.c on kernel start up */
+
+void netbeui_proto_init(struct net_proto *pro)
+{
+ (void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
+ if ((nb_dl = register_8022_client(nb_8022_id, netbeui_rcv)) == NULL)
+ printk(KERN_CRIT "Unable to register Netbeui with 802.2.\n");
+
+ register_netdevice_notifier(&nb_notifier);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_netbeui);
+#endif
+
+ printk(KERN_INFO "NetBEUI 0.03 for Linux NET3.037\n");
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ netbeui_proto_init(NULL);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unsigned long flags;
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_NETBEUI);
+#endif
+ unregister_netdevice_notifier(&nb_notifier);
+ unregister_snap_client(nb_snap_id);
+ sock_unregister(netbeui_proto_ops.family);
+}
+
+#endif /* MODULE */
diff --git a/net/netbeui/netbeui_llc.c b/net/netbeui/netbeui_llc.c
new file mode 100644
index 000000000..198fe1ce1
--- /dev/null
+++ b/net/netbeui/netbeui_llc.c
@@ -0,0 +1,265 @@
+/*
+ * NET3: 802.2 LLC supervisor for the netbeui protocols.
+ *
+ * The basic aim is to provide a self managing link layer supervisor
+ * for netbeui. It creates and destroys the 802.2 virtual connections
+ * as needed, and copes with the various races when a link goes down
+ * just as its requested etc.
+ *
+ * The upper layers are presented with the notion of an nb_link which
+ * is a potentially shared object that represents a logical path
+ * between two hosts. Each nb_link has usage counts and users can
+ * treat it as if its their own.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/datalink.h>
+#include <net/p8022.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <net/llc.h>
+#include <net/netbeui.h>
+
+
+/*
+ * When this routine is called the netbeui layer has decided to
+ * drop the link. There is a tiny risk that we might reuse the
+ * link after we decide. Thus before we blast the link into little
+ * tiny pieces we must check....
+ */
+
+static void netbeui_do_destroy(struct nb_link *nb)
+{
+ /*
+ * Are we wanted again. Bring it back. Sigh, wish people
+ * would make up their minds 8)
+ */
+ if(nb->users>0)
+ {
+ nb->state=NETBEUI_CONNWAIT;
+ llc_connect_request(&nb->llc);
+ return;
+ }
+ /*
+ * Blam.... into oblivion it goes
+ */
+
+ llc_unregister(&nb->llc);
+ netbeui_free_link(nb);
+}
+
+/*
+ * Handle netbeui events. Basically that means keep it up when it
+ * should be up, down when it should be down and handle all the data.
+ */
+
+static void netbeui_event(llcptr llc)
+{
+ struct nb_link *nb=(struct nb_link *)llc;
+
+ /*
+ * See what has occured
+ */
+
+
+ /*
+ * Connect completion confirmation
+ */
+
+ if(llc->llc_callbacks&LLC_CONN_CONFIRM)
+ {
+ /*
+ * Link up if desired. Otherwise try frantically
+ * to close it.
+ */
+ if(nb->state!=NETBEUI_DEADWAIT)
+ {
+ /*
+ * Wake pending writers
+ */
+ nb->state=NETBEUI_OPEN;
+ netbeui_wakeup(nb);
+ }
+ else
+ llc_disconnect_request(llc);
+ }
+
+ /*
+ * Data is passed to the upper netbeui layer
+ */
+
+ if(llc->llc_callbacks&LLC_DATA_INDIC)
+ {
+ netbeu_rcv_stream(llc,llc->inc_skb);
+ /*
+ * Frame free is controlled by our stream processor
+ */
+ return;
+ }
+
+ /*
+ * We got disconnected
+ */
+
+ if(llc->llc_callbacks&LLC_DISC_INDICATION)
+ {
+ if(nb->state==NETBEUI_DEADWAIT)
+ {
+ netbeui_do_destroy(nb);
+ return;
+ }
+ if(nb->state==NETBEUI_DISCWAIT)
+ {
+ llc_connect_request(llc);
+ nb->state=NETBEUI_CONNWAIT;
+ }
+ }
+
+ /*
+ * Miscellaneous burps
+ */
+
+ if(llc->llc_callbacks&(LLC_RESET_INDIC_LOC|LLC_RESET_INDIC_REM|
+ LLC_RST_CONFIRM))
+ {
+ /*
+ * Reset.
+ * Q: Is tearing the link down the right answer ?
+ *
+ * For now we just carry on
+ */
+ }
+
+ /*
+ * Track link busy status
+ */
+
+ if(llc->llc_callbacks&LLC_REMOTE_BUSY)
+ nb->busy=1; /* Send no more for a bit */
+ if(llc->llc_callbacks&LLC_REMOTE_NOTBUSY)
+ {
+ /* Coming unbusy may wake sending threads */
+ nb->busy=0;
+ netbeui_wakeup(nb);
+ }
+ /*
+ * UI frames are passed to the upper netbeui layer.
+ */
+ if(llc->llc_callbacks&LLC_UI_DATA)
+ {
+ netbeui_rcv_dgram(llc,llc->inc_skb);
+ return;
+ }
+
+ /* We ignore TST, XID, FRMR stuff */
+ /* FIXME: We need to free frames here once I fix the callback! */
+ if(llc->inc_skb)
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * Netbeui has created a new logical link. As a result we will
+ * need to find or create a suitable 802.2 LLC session and join
+ * it.
+ */
+
+struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri)
+{
+ struct nb_link *nb=netbeui_find_channel(dev,remote_mac);
+ if(nb)
+ {
+ if(nb->state==NETBEUI_DEADWAIT)
+ {
+ /*
+ * We had commenced a final shutdown. We
+ * cannot abort that (we sent the packet) but
+ * we can shift the mode to DISCWAIT. That will
+ * cause the disconnect event to bounce us
+ * back into connected state.
+ */
+ nb->state==NETBEUI_DISCWAIT;
+ }
+ nb->users++;
+ return nb;
+ }
+ nb=netbeui_alloc_link(pri);
+ if(nb==NULL)
+ return NULL;
+
+ /*
+ * Internal book keeping
+ */
+
+ nb->dev=dev;
+ nb->users=1;
+ nb->busy=0;
+ nb->wakeup=NULL;
+ nb->state=NETBEUI_CONNWAIT;
+ memcpy(nb->remote_mac, remote_mac, ETH_ALEN);
+
+ /*
+ * Now try and attach an LLC.
+ */
+
+ if(register_cl2llc_client(&nb->llc,dev->name,netbeui_event,
+ remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0)
+ {
+ netbeui_free_link(nb);
+ return NULL;
+ }
+
+ /*
+ * Commence connection establishment.
+ */
+
+ llc_connect_request(&nb->llc);
+
+ /*
+ * Done
+ */
+
+ nb->next=nb_link_list;
+ nb_link_list=nb;
+
+ return nb;
+}
+
+/*
+ * A logical netbeui channel has died. If the channel has no
+ * further users we commence shutdown.
+ */
+
+int netbeui_delete_channel(struct nb_link *nb)
+{
+ nb->users--;
+
+ /*
+ * FIXME: Must remove ourselves from the nb_link chain when
+ * we add that bit
+ */
+
+ if(nb->users)
+ return 0;
+
+ /*
+ * Ensure we drop soon. The disconnect confirm will let
+ * us fix the deletion. If someone wants the link at
+ * the wrong moment nothing bad will occur. The create
+ * or the do_destroy will sort it.
+ */
+
+ nb->state = NETBEUI_DEADWAIT;
+ llc_disconnect_request(lp);
+ return 0;
+}
+
+
diff --git a/net/netbeui/netbeui_name.c b/net/netbeui/netbeui_name.c
new file mode 100644
index 000000000..d47fddd1b
--- /dev/null
+++ b/net/netbeui/netbeui_name.c
@@ -0,0 +1,159 @@
+/*
+ * NetBIOS name handler
+ */
+
+/*
+ * You must hold the netbios name lock before using these.
+ */
+
+struct nb_name *nb_name_find(struct device *dev,const char * name)
+{
+ struct nb_name *nb=nb_name_list;
+ while(nb!=NULL)
+ {
+ if((dev==NULL || dev==nb->dev) &&
+ strncmp(name,nb->name, NB_NAME_LEN)==0)
+ return nb;
+ nb=nb->next;
+ }
+ return NULL;
+}
+
+int nb_name_add(struct device *dev, const char *name, int ours, int pri)
+{
+ struct nb_name *nb=kmalloc(sizeof(*nb), pri);
+ if(nb==NULL)
+ return NULL;
+ nb->dev=dev;
+ strncpy(nb->name,name,NB_NAME_LEN);
+ nb->name[NB_NAME_LEN-1]=0;
+ nb->next=nb_name_list;
+ nb->ours=ours;
+ nb_name_list=nb;
+}
+
+void nb_name_delete(struct nb_name *nb)
+{
+ struct nb_name *i=&nb_name_list;
+ while((*i)!=NULL)
+ {
+ if(*i==nb)
+ {
+ *i=nb->next;
+ kfree_s(nb,sizeof(*nb));
+ return;
+ }
+ i=&((*i)->next);
+ }
+ printk(KERN_ERR "nb_name_delete: bad name pointer!\n");
+}
+
+/*
+ * NETBIOS name handlers
+ */
+
+static void nb_defend(struct device *dev, const char *name)
+{
+ struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
+ if(nskb==NULL)
+ return;
+ /* Build a name defence packet */
+ dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+}
+
+void netbeui_heard_name(struct device *dev, struct sk_buff *skb)
+{
+ struct nb_name *nb;
+ name=...
+
+ if((nb=nb_name_find(dev,name))!=NULL)
+ {
+ /*
+ * If we own the name then defend it
+ */
+ if(nb->our && !nb->state==NB_ACQUIRE)
+ nb_defend(dev,name);
+ /*
+ * A name has been resolved. Wake up pending
+ * connectors.
+ */
+ if(nb->state==NB_QUERY)
+ {
+ nb->state=NB_OTHER;
+ nb_complete(nb,skb);
+ }
+ }
+ kfree_skb(skb, FREE_READ);
+ return 0;
+}
+
+/*
+ * Handle incoming name defences
+ */
+
+void netbeui_name_defence(struct dev *dev, struct sk_buff *skb)
+{
+ struct nb_name *name;
+ name=
+
+ if((nb=nb_name_find(dev,name))!=NULL)
+ {
+ if(nb->ours)
+ {
+ /*
+ * We wanted it, we got told its used
+ */
+ if(nb->state==NB_ACQUIRE)
+ {
+ /*
+ * Fill in the record for its true
+ * owner. Set the state first as
+ * nb_complete may well delete the
+ * record.
+ */
+ nb->state=NB_OTHER;
+ nb_complete(nb,skb);
+ nb_wakeup();
+ }
+ /*
+ * We own it we got told its used. This is
+ * a deep cack even that can only occur when
+ * a bridge comes back and the net was split.
+ * Make sure both sides lose.
+ */
+ if(nb->state==NB_OURS || nb->state==NB_COLLIDE)
+ {
+ nb->state=NR_COLLIDE;
+ nb_wakeup();
+ /*
+ * Kill the other copy too
+ */
+ nb_defend(dev,name);
+ /*
+ * Timer expiry will delete our
+ * record.
+ */
+ nb_start_timer(nb, NB_TIME_COLLIDED);
+ }
+ }
+ }
+ kfree_skb(skb, FREE_READ);
+}
+
+void netbeui_name_query(struct dev *dev, struct sk_buff *skb)
+{
+ char *name=...
+ struct nb_name *nb=nb_find_name(dev,name);
+
+ if(nb!=NULL && nb->ours)
+ {
+ struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
+ if(nskb!=NULL)
+ {
+ /* Build a name reply packet */
+ dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+ }
+ }
+ kfree_skb(skb, FREE_READ);
+}
+
diff --git a/net/netlink.c b/net/netlink.c
index 355b35f79..8c3b0aecc 100644
--- a/net/netlink.c
+++ b/net/netlink.c
@@ -1,5 +1,5 @@
/*
- * SKIPLINK An implementation of a loadable kernel mode driver providing
+ * NETLINK An implementation of a loadable kernel mode driver providing
* multiple kernel/user space bidirectional communications links.
*
* Author: Alan Cox <alan@cymru.net>
@@ -17,15 +17,16 @@
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
-#include <linux/lp.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/skbuff.h>
#include <net/netlink.h>
+#include <asm/poll.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -35,8 +36,8 @@ static struct sk_buff_head skb_queue_rd[MAX_LINKS];
static int rdq_size[MAX_LINKS];
static struct wait_queue *read_space_wait[MAX_LINKS];
-static int active_map = 0;
-static int open_map = 0;
+static unsigned active_map = 0;
+static unsigned open_map = 0;
/*
* Device operations
@@ -63,19 +64,16 @@ int netlink_donothing(int minor, struct sk_buff *skb)
return -EINVAL;
}
-static int netlink_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
+static unsigned int netlink_poll(struct file *file, poll_table * wait)
{
- unsigned int minor = MINOR(inode->i_rdev);
- switch (sel_type) {
- case SEL_IN:
- if (skb_peek(&skb_queue_rd[minor])!=NULL)
- return 1;
- select_wait(&read_space_wait[minor], wait);
- break;
- case SEL_OUT:
- return 1;
- }
- return 0;
+ unsigned int mask;
+ unsigned int minor = MINOR(file->f_inode->i_rdev);
+
+ poll_wait(&read_space_wait[minor], wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (skb_peek(&skb_queue_rd[minor]))
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
}
/*
@@ -89,7 +87,6 @@ static long netlink_write(struct inode * inode, struct file * file,
unsigned int minor = MINOR(inode->i_rdev);
struct sk_buff *skb;
skb=alloc_skb(count, GFP_KERNEL);
- skb->free=1;
err = copy_from_user(skb_put(skb,count),buf, count);
return err ? -EFAULT : (netlink_handler[minor])(minor,skb);
}
@@ -140,22 +137,27 @@ static int netlink_open(struct inode * inode, struct file * file)
if(minor>=MAX_LINKS)
return -ENODEV;
- if(open_map&(1<<minor))
- return -EBUSY;
if(active_map&(1<<minor))
{
- open_map|=(1<<minor);
+ if (file->f_mode & FMODE_READ)
+ {
+ if (open_map&(1<<minor))
+ return -EBUSY;
+ open_map|=(1<<minor);
+ }
MOD_INC_USE_COUNT;
return 0;
}
return -EUNATCH;
}
-static void netlink_release(struct inode * inode, struct file * file)
+static int netlink_release(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
- open_map&=~(1<<minor);
+ if (file->f_mode & FMODE_READ)
+ open_map&=~(1<<minor);
MOD_DEC_USE_COUNT;
+ return 0;
}
@@ -180,7 +182,7 @@ static struct file_operations netlink_fops = {
netlink_read,
netlink_write,
NULL, /* netlink_readdir */
- netlink_select,
+ netlink_poll,
netlink_ioctl,
NULL, /* netlink_mmap */
netlink_open,
@@ -232,6 +234,215 @@ int netlink_post(int unit, struct sk_buff *skb)
return ret;
}
+
+/*
+ * "High" level netlink interface. (ANK)
+ *
+ * Features:
+ * - standard message format.
+ * - pseudo-reliable delivery. Messages can be still lost, but
+ * user level will know that they were lost and can
+ * recover (f.e. gated could reread FIB and device list)
+ * - messages are batched.
+ * - if user is not attached, we do not make useless work.
+ *
+ * Examples:
+ * - netlink_post equivalent (but with pseudo-reliable delivery)
+ * ctl.nlmsg_delay = 0;
+ * ctl.nlmsg_maxsize = <one message size>;
+ * ....
+ * msg = nlmsg_send(&ctl, ...);
+ * if (msg) {
+ * ... make it ...
+ * nlmsg_transmit(&ctl);
+ * }
+ *
+ * - batched messages.
+ * if nlmsg_delay==0, messages are delivered only
+ * by nlmsg_transmit, or when batch is completed,
+ * otherwise nlmsg_transmit is noop (only starts
+ * timer)
+ *
+ * ctl.nlmsg_delay = ...;
+ * ctl.nlmsg_maxsize = <one batch size>;
+ * ....
+ * msg = nlmsg_send(&ctl, ...);
+ * if (msg)
+ * ... make it ...
+ * ....
+ * msg = nlmsg_send(&ctl, ...);
+ * if (msg)
+ * ... make it ...
+ * ....
+ * if (ctl.nlmsg_skb)
+ * nlmsg_transmit(&ctl);
+ *
+ */
+
+/*
+ * Try to deliver queued messages.
+ * If the delivery fails (netlink is not attached or congested),
+ * do not free skb to avoid useless new message creation.
+ *
+ * Notes:
+ * - timer should be already stopped.
+ * - NET SPL.
+ */
+
+void nlmsg_flush(struct nlmsg_ctl *ctl)
+{
+ if (ctl->nlmsg_skb == NULL)
+ return;
+
+ if (netlink_post(ctl->nlmsg_unit, ctl->nlmsg_skb) == 0)
+ {
+ ctl->nlmsg_skb = NULL;
+ return;
+ }
+
+ ctl->nlmsg_timer.expires = jiffies + NLMSG_RECOVERY_TIMEO;
+ ctl->nlmsg_timer.data = (unsigned long)ctl;
+ ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+ add_timer(&ctl->nlmsg_timer);
+ return;
+}
+
+
+/*
+ * Allocate room for new message. If it is impossible,
+ * start "overrun" mode and return NULL.
+ *
+ * Notes:
+ * - NET SPL.
+ */
+
+void* nlmsg_send(struct nlmsg_ctl *ctl, unsigned long type, int len,
+ unsigned long seq, unsigned long pid)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ int rlen;
+
+ static __inline__ void nlmsg_lost(struct nlmsg_ctl *ctl,
+ unsigned long seq)
+ {
+ if (!ctl->nlmsg_overrun)
+ {
+ ctl->nlmsg_overrun_start = seq;
+ ctl->nlmsg_overrun_end = seq;
+ ctl->nlmsg_overrun = 1;
+ return;
+ }
+ if (!ctl->nlmsg_overrun_start)
+ ctl->nlmsg_overrun_start = seq;
+ if (seq)
+ ctl->nlmsg_overrun_end = seq;
+ }
+
+ if (!(open_map&(1<<ctl->nlmsg_unit)))
+ {
+ nlmsg_lost(ctl, seq);
+ return NULL;
+ }
+
+ rlen = NLMSG_ALIGN(len + sizeof(struct nlmsghdr));
+
+ if (rlen > ctl->nlmsg_maxsize)
+ {
+ printk(KERN_ERR "nlmsg_send: too big message\n");
+ return NULL;
+ }
+
+ if ((skb=ctl->nlmsg_skb) == NULL || skb_tailroom(skb) < rlen)
+ {
+ if (skb)
+ {
+ ctl->nlmsg_force++;
+ nlmsg_flush(ctl);
+ ctl->nlmsg_force--;
+ }
+
+ if (ctl->nlmsg_skb ||
+ (skb=alloc_skb(ctl->nlmsg_maxsize, GFP_ATOMIC)) == NULL)
+ {
+ printk (KERN_WARNING "nlmsg at unit %d overrunned\n", ctl->nlmsg_unit);
+ nlmsg_lost(ctl, seq);
+ return NULL;
+ }
+
+ ctl->nlmsg_skb = skb;
+
+ if (ctl->nlmsg_overrun)
+ {
+ int *seqp;
+ nlh = (struct nlmsghdr*)skb_put(skb, sizeof(struct nlmsghdr) + 2*sizeof(unsigned long));
+ nlh->nlmsg_type = NLMSG_OVERRUN;
+ nlh->nlmsg_len = sizeof(struct nlmsghdr) + 2*sizeof(unsigned long);
+ nlh->nlmsg_seq = 0;
+ nlh->nlmsg_pid = 0;
+ seqp = (int*)nlh->nlmsg_data;
+ seqp[0] = ctl->nlmsg_overrun_start;
+ seqp[1] = ctl->nlmsg_overrun_end;
+ ctl->nlmsg_overrun = 0;
+ }
+ if (ctl->nlmsg_timer.function)
+ {
+ del_timer(&ctl->nlmsg_timer);
+ ctl->nlmsg_timer.function = NULL;
+ }
+ if (ctl->nlmsg_delay)
+ {
+ ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+ ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+ ctl->nlmsg_timer.data = (unsigned long)ctl;
+ add_timer(&ctl->nlmsg_timer);
+ }
+ }
+
+ nlh = (struct nlmsghdr*)skb_put(skb, rlen);
+ nlh->nlmsg_type = type;
+ nlh->nlmsg_len = sizeof(struct nlmsghdr) + len;
+ nlh->nlmsg_seq = seq;
+ nlh->nlmsg_pid = pid;
+ return nlh->nlmsg_data;
+}
+
+/*
+ * Kick message queue.
+ * Two modes:
+ * - synchronous (delay==0). Messages are delivered immediately.
+ * - delayed. Do not deliver, but start delivery timer.
+ */
+
+void nlmsg_transmit(struct nlmsg_ctl *ctl)
+{
+ start_bh_atomic();
+
+ if (!ctl->nlmsg_delay)
+ {
+ if (ctl->nlmsg_timer.function)
+ {
+ del_timer(&ctl->nlmsg_timer);
+ ctl->nlmsg_timer.function = NULL;
+ }
+ ctl->nlmsg_force++;
+ nlmsg_flush(ctl);
+ ctl->nlmsg_force--;
+ end_bh_atomic();
+ return;
+ }
+ if (!ctl->nlmsg_timer.function)
+ {
+ ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+ ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+ ctl->nlmsg_timer.data = (unsigned long)ctl;
+ add_timer(&ctl->nlmsg_timer);
+ }
+
+ end_bh_atomic();
+}
+
+
int init_netlink(void)
{
int ct;
@@ -252,7 +463,7 @@ int init_netlink(void)
int init_module(void)
{
- printk(KERN_INFO "Network Kernel/User communications module 0.04\n");
+ printk(KERN_INFO "Network Kernel/User communications module 0.05\n");
return init_netlink();
}
diff --git a/net/netrom/Makefile b/net/netrom/Makefile
index da5a1f429..4ac78639b 100644
--- a/net/netrom/Makefile
+++ b/net/netrom/Makefile
@@ -8,9 +8,13 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := netrom.o
-O_OBJS := af_netrom.o sysctl_net_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
+O_OBJS := af_netrom.o nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
M_OBJS := $(O_TARGET)
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_netrom.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 6eac7e8a8..d66094134 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 005
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.037
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -29,10 +29,12 @@
* NET/ROM 004 Jonathan(G4KLX) Converted to module.
* NET/ROM 005 Jonathan(G4KLX) Linux 2.1
* Alan(GW4PTS) Started POSIXisms
+ * NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -72,13 +74,47 @@ int sysctl_netrom_transport_acknowledge_delay = NR_DEFAULT_T2;
int sysctl_netrom_transport_busy_delay = NR_DEFAULT_T4;
int sysctl_netrom_transport_requested_window_size = NR_DEFAULT_WINDOW;
int sysctl_netrom_transport_no_activity_timeout = NR_DEFAULT_IDLE;
-int sysctl_netrom_transport_packet_length = NR_DEFAULT_PACLEN;
-int sysctl_netrom_routing_control = 1;
+int sysctl_netrom_routing_control = NR_DEFAULT_ROUTING;
+int sysctl_netrom_link_fails_count = NR_DEFAULT_FAILS;
static unsigned short circuit = 0x101;
static struct sock *volatile nr_list = NULL;
+static struct proto_ops nr_proto_ops;
+
+static void nr_free_sock(struct sock *sk)
+{
+ kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
+
+ sk_free(sk);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static struct sock *nr_alloc_sock(void)
+{
+ struct sock *sk;
+ nr_cb *nr;
+
+ if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ memset(nr, 0x00, sizeof(*nr));
+
+ sk->protinfo.nr = nr;
+ nr->sk = sk;
+
+ return sk;
+}
+
/*
* Socket removal during an interrupt is now safe.
*/
@@ -86,7 +122,7 @@ static void nr_remove_socket(struct sock *sk)
{
struct sock *s;
unsigned long flags;
-
+
save_flags(flags);
cli();
@@ -115,7 +151,7 @@ static void nr_remove_socket(struct sock *sk)
static void nr_kill_by_device(struct device *dev)
{
struct sock *s;
-
+
for (s = nr_list; s != NULL; s = s->next) {
if (s->protinfo.nr->device == dev) {
s->protinfo.nr->state = NR_STATE_0;
@@ -138,7 +174,7 @@ static int nr_device_event(struct notifier_block *this, unsigned long event, voi
if (event != NETDEV_DOWN)
return NOTIFY_DONE;
-
+
nr_kill_by_device(dev);
nr_rt_device_down(dev);
@@ -253,15 +289,15 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer
{
struct sk_buff *skb;
unsigned long flags;
-
+
save_flags(flags);
cli();
-
+
del_timer(&sk->timer);
-
+
nr_remove_socket(sk);
nr_clear_queues(sk); /* Flush the queues */
-
+
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
if (skb->sk != sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
@@ -271,16 +307,16 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer
kfree_skb(skb, FREE_READ);
}
-
- if (sk->wmem_alloc || sk->rmem_alloc) { /* Defer: outstanding buffers */
+
+ if (atomic_read(&sk->wmem_alloc) != 0 || atomic_read(&sk->rmem_alloc) != 0) {
+ /* Defer: outstanding buffers */
init_timer(&sk->timer);
sk->timer.expires = jiffies + 10 * HZ;
sk->timer.function = nr_destroy_timer;
sk->timer.data = (unsigned long)sk;
add_timer(&sk->timer);
} else {
- kfree_s(sk->protinfo.nr, sizeof(*sk->protinfo.nr));
- sk_free(sk);
+ nr_free_sock(sk);
}
restore_flags(flags);
@@ -290,11 +326,6 @@ void nr_destroy_socket(struct sock *sk) /* Not static as it's used by the timer
* Handling for system calls applied via the various interfaces to a
* NET/ROM socket object.
*/
-
-static int nr_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- return -EINVAL;
-}
/*
* dl1bke 960311: set parameters for existing NET/ROM connections,
@@ -307,12 +338,12 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
struct sock *sk;
unsigned long flags;
int err;
-
+
if ((err = verify_area(VERIFY_READ, arg, sizeof(nr_ctl))) != 0)
return err;
copy_from_user(&nr_ctl, arg, sizeof(nr_ctl));
-
+
if ((sk = nr_find_socket(nr_ctl.index, nr_ctl.id)) == NULL)
return -ENOTCONN;
@@ -333,8 +364,7 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
case NETROM_T1:
if (nr_ctl.arg < 1)
return -EINVAL;
- sk->protinfo.nr->rtt = (nr_ctl.arg * PR_SLOWHZ) / 2;
- sk->protinfo.nr->t1 = nr_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.nr->t1 = nr_ctl.arg * NR_SLOWHZ;
save_flags(flags); cli();
if (sk->protinfo.nr->t1timer > sk->protinfo.nr->t1)
sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
@@ -345,7 +375,7 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
if (nr_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.nr->t2 = nr_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.nr->t2 = nr_ctl.arg * NR_SLOWHZ;
if (sk->protinfo.nr->t2timer > sk->protinfo.nr->t2)
sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
restore_flags(flags);
@@ -362,7 +392,7 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
if (nr_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.nr->t4 = nr_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.nr->t4 = nr_ctl.arg * NR_SLOWHZ;
if (sk->protinfo.nr->t4timer > sk->protinfo.nr->t4)
sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
restore_flags(flags);
@@ -372,20 +402,12 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
if (nr_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.nr->idle = nr_ctl.arg * 60 * PR_SLOWHZ;
+ sk->protinfo.nr->idle = nr_ctl.arg * 60 * NR_SLOWHZ;
if (sk->protinfo.nr->idletimer > sk->protinfo.nr->idle)
sk->protinfo.nr->idletimer = sk->protinfo.nr->idle;
restore_flags(flags);
break;
- case NETROM_PACLEN:
- if (nr_ctl.arg < 16 || nr_ctl.arg > 65535)
- return -EINVAL;
- if (nr_ctl.arg > 236) /* we probably want this */
- printk(KERN_WARNING "nr_ctl_ioctl: Warning --- huge paclen %d\n", (int)nr_ctl.arg);
- sk->protinfo.nr->paclen = nr_ctl.arg;
- break;
-
default:
return -EINVAL;
}
@@ -396,66 +418,53 @@ static int nr_ctl_ioctl(const unsigned int cmd, void *arg)
static int nr_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
- struct sock *sk;
- int err, opt;
-
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_setsockopt(sk, level, optname, optval, optlen);
+ struct sock *sk = sock->sk;
+ int opt;
if (level != SOL_NETROM)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
- if (optval == NULL)
+ if (optlen < sizeof(int))
return -EINVAL;
- if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
- return err;
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
- get_user(opt, (int *)optval);
-
switch (optname) {
case NETROM_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->rtt = (opt * PR_SLOWHZ) / 2;
+ sk->protinfo.nr->t1 = opt * NR_SLOWHZ;
return 0;
case NETROM_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->t2 = opt * PR_SLOWHZ;
+ sk->protinfo.nr->t2 = opt * NR_SLOWHZ;
return 0;
-
+
case NETROM_N2:
if (opt < 1 || opt > 31)
return -EINVAL;
sk->protinfo.nr->n2 = opt;
return 0;
-
+
case NETROM_T4:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->t4 = opt * PR_SLOWHZ;
+ sk->protinfo.nr->t4 = opt * NR_SLOWHZ;
return 0;
-
+
case NETROM_IDLE:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->idle = opt * 60 * PR_SLOWHZ;
+ sk->protinfo.nr->idle = opt * 60 * NR_SLOWHZ;
return 0;
-
+
case NETROM_HDRINCL:
sk->protinfo.nr->hdrincl = opt ? 1 : 0;
return 0;
- case NETROM_PACLEN:
- if (opt < 1 || opt > 65536)
- return -EINVAL;
- sk->protinfo.nr->paclen = opt;
- return 0;
-
default:
return -ENOPROTOOPT;
}
@@ -464,67 +473,59 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
static int nr_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
int val = 0;
- int err;
+ int len;
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_getsockopt(sk, level, optname, optval, optlen);
-
if (level != SOL_NETROM)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
+ if (get_user(len, optlen))
+ return -EFAULT;
+
switch (optname) {
case NETROM_T1:
- val = (sk->protinfo.nr->t1 * 2) / PR_SLOWHZ;
+ val = (sk->protinfo.nr->t1 * 2) / NR_SLOWHZ;
break;
-
+
case NETROM_T2:
- val = sk->protinfo.nr->t2 / PR_SLOWHZ;
+ val = sk->protinfo.nr->t2 / NR_SLOWHZ;
break;
-
+
case NETROM_N2:
val = sk->protinfo.nr->n2;
break;
-
+
case NETROM_T4:
- val = sk->protinfo.nr->t4 / PR_SLOWHZ;
+ val = sk->protinfo.nr->t4 / NR_SLOWHZ;
break;
-
+
case NETROM_IDLE:
- val = sk->protinfo.nr->idle / (PR_SLOWHZ * 60);
+ val = sk->protinfo.nr->idle / (NR_SLOWHZ * 60);
break;
-
+
case NETROM_HDRINCL:
val = sk->protinfo.nr->hdrincl;
break;
- case NETROM_PACLEN:
- val = sk->protinfo.nr->paclen;
- break;
-
default:
return -ENOPROTOOPT;
}
- if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
- return err;
-
- put_user(sizeof(int), optlen);
+ len = min(len, sizeof(int));
- if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
- return err;
+ if (put_user(len, optlen))
+ return -EFAULT;
- put_user(val, (int *)optval);
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
return 0;
}
static int nr_listen(struct socket *sock, int backlog)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk->state != TCP_LISTEN) {
memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN);
@@ -536,18 +537,6 @@ static int nr_listen(struct socket *sock, int backlog)
return -EOPNOTSUPP;
}
-static void def_callback1(struct sock *sk)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk, int len)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
static int nr_create(struct socket *sock, int protocol)
{
struct sock *sk;
@@ -556,85 +545,30 @@ static int nr_create(struct socket *sock, int protocol)
if (sock->type != SOCK_SEQPACKET || protocol != 0)
return -ESOCKTNOSUPPORT;
- if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+ if ((sk = nr_alloc_sock()) == NULL)
return -ENOMEM;
- if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return -ENOMEM;
- }
+ nr = sk->protinfo.nr;
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- init_timer(&sk->timer);
-
- sk->socket = sock;
- sk->type = sock->type;
- sk->protocol = protocol;
- sk->allocation = GFP_KERNEL;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->sndbuf = SK_WMEM_MAX;
- sk->state = TCP_CLOSE;
- sk->priority = SOPRI_NORMAL;
- sk->mtu = NETROM_MTU; /* 236 */
- sk->zapped = 1;
- sk->window = sysctl_netrom_transport_requested_window_size;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
-
- if (sock != NULL) {
- sock->data = (void *)sk;
- sk->sleep = sock->wait;
- }
+ sock_init_data(sock, sk);
+
+ sock->ops = &nr_proto_ops;
+ sk->protocol = protocol;
+ sk->mtu = NETROM_MTU; /* 236 */
skb_queue_head_init(&nr->ack_queue);
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
- nr->my_index = 0;
- nr->my_id = 0;
- nr->rtt = sysctl_netrom_transport_timeout / 2;
- nr->t1 = sysctl_netrom_transport_timeout;
- nr->t2 = sysctl_netrom_transport_acknowledge_delay;
- nr->n2 = sysctl_netrom_transport_maximum_tries;
- nr->t4 = sysctl_netrom_transport_busy_delay;
- nr->idle = sysctl_netrom_transport_no_activity_timeout;
- nr->paclen = sysctl_netrom_transport_packet_length;
-
- nr->t1timer = 0;
- nr->t2timer = 0;
- nr->t4timer = 0;
- nr->idletimer = 0;
- nr->n2count = 0;
-
- nr->va = 0;
- nr->vr = 0;
- nr->vs = 0;
- nr->vl = 0;
-
- nr->your_index = 0;
- nr->your_id = 0;
-
- nr->my_index = 0;
- nr->my_id = 0;
-
- nr->bpqext = 1;
- nr->fraglen = 0;
- nr->hdrincl = 0;
- nr->state = NR_STATE_0;
- nr->device = NULL;
-
- memset(&nr->source_addr, '\0', AX25_ADDR_LEN);
- memset(&nr->user_addr, '\0', AX25_ADDR_LEN);
- memset(&nr->dest_addr, '\0', AX25_ADDR_LEN);
-
- nr->sk = sk;
- sk->protinfo.nr = nr;
+ nr->t1 = sysctl_netrom_transport_timeout;
+ nr->t2 = sysctl_netrom_transport_acknowledge_delay;
+ nr->n2 = sysctl_netrom_transport_maximum_tries;
+ nr->t4 = sysctl_netrom_transport_busy_delay;
+ nr->idle = sysctl_netrom_transport_no_activity_timeout;
+ nr->window = sysctl_netrom_transport_requested_window_size;
+
+ nr->bpqext = 1;
+ nr->state = NR_STATE_0;
return 0;
}
@@ -647,82 +581,56 @@ static struct sock *nr_make_new(struct sock *osk)
if (osk->type != SOCK_SEQPACKET)
return NULL;
- if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+ if ((sk = nr_alloc_sock()) == NULL)
return NULL;
- if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return NULL;
- }
+ nr = sk->protinfo.nr;
+
+ sock_init_data(NULL, sk);
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- init_timer(&sk->timer);
-
- sk->type = osk->type;
- sk->socket = osk->socket;
- sk->priority = osk->priority;
- sk->protocol = osk->protocol;
- sk->rcvbuf = osk->rcvbuf;
- sk->sndbuf = osk->sndbuf;
- sk->debug = osk->debug;
- sk->state = TCP_ESTABLISHED;
- sk->window = osk->window;
- sk->mtu = osk->mtu;
- sk->sleep = osk->sleep;
- sk->zapped = osk->zapped;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->mtu = osk->mtu;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
skb_queue_head_init(&nr->ack_queue);
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
- nr->rtt = osk->protinfo.nr->rtt;
- nr->t1 = osk->protinfo.nr->t1;
- nr->t2 = osk->protinfo.nr->t2;
- nr->n2 = osk->protinfo.nr->n2;
- nr->t4 = osk->protinfo.nr->t4;
- nr->idle = osk->protinfo.nr->idle;
- nr->paclen = osk->protinfo.nr->paclen;
-
- nr->device = osk->protinfo.nr->device;
- nr->bpqext = osk->protinfo.nr->bpqext;
- nr->hdrincl = osk->protinfo.nr->hdrincl;
- nr->fraglen = 0;
-
- nr->t1timer = 0;
- nr->t2timer = 0;
- nr->t4timer = 0;
- nr->idletimer = 0;
- nr->n2count = 0;
-
- nr->va = 0;
- nr->vr = 0;
- nr->vs = 0;
- nr->vl = 0;
-
- sk->protinfo.nr = nr;
- nr->sk = sk;
+ nr->t1 = osk->protinfo.nr->t1;
+ nr->t2 = osk->protinfo.nr->t2;
+ nr->n2 = osk->protinfo.nr->n2;
+ nr->t4 = osk->protinfo.nr->t4;
+ nr->idle = osk->protinfo.nr->idle;
+ nr->window = osk->protinfo.nr->window;
+
+ nr->device = osk->protinfo.nr->device;
+ nr->bpqext = osk->protinfo.nr->bpqext;
+ nr->hdrincl = osk->protinfo.nr->hdrincl;
return sk;
}
static int nr_dup(struct socket *newsock, struct socket *oldsock)
{
- struct sock *sk = (struct sock *)oldsock->data;
+ struct sock *sk = oldsock->sk;
+
+ if (sk == NULL || newsock == NULL)
+ return -EINVAL;
return nr_create(newsock, sk->protocol);
}
static int nr_release(struct socket *sock, struct socket *peer)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk == NULL) return 0;
@@ -749,7 +657,7 @@ static int nr_release(struct socket *sock, struct socket *peer)
nr_write_internal(sk, NR_DISCACK);
sk->protinfo.nr->state = NR_STATE_0;
sk->state = TCP_CLOSE;
- sk->shutdown = SEND_SHUTDOWN;
+ sk->shutdown |= SEND_SHUTDOWN;
sk->state_change(sk);
sk->dead = 1;
nr_destroy_socket(sk);
@@ -759,7 +667,7 @@ static int nr_release(struct socket *sock, struct socket *peer)
nr_clear_queues(sk);
sk->protinfo.nr->n2count = 0;
nr_write_internal(sk, NR_DISCREQ);
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
+ sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
sk->protinfo.nr->t2timer = 0;
sk->protinfo.nr->t4timer = 0;
sk->protinfo.nr->state = NR_STATE_2;
@@ -774,30 +682,30 @@ static int nr_release(struct socket *sock, struct socket *peer)
break;
}
- sock->data = NULL;
- sk->socket = NULL; /* Not used, but we should do this. **/
+ sock->sk = NULL;
+ sk->socket = NULL; /* Not used, but we should do this */
return 0;
}
static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
struct device *dev;
ax25_address *user, *source;
-
- sk = (struct sock *)sock->data;
if (sk->zapped == 0)
return -EINVAL;
-
+
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
+ if (addr->fsa_ax25.sax25_family != AF_NETROM)
+ return -EINVAL;
+
if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
- if (sk->debug)
- printk("NET/ROM: bind failed: invalid node callsign\n");
+ SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n");
return -EADDRNOTAVAIL;
}
@@ -826,40 +734,40 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
nr_insert_socket(sk);
sk->zapped = 0;
-
- if (sk->debug)
- printk("NET/ROM: socket is bound\n");
-
+ SOCK_DEBUG(sk, "NET/ROM: socket is bound\n");
return 0;
}
static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
ax25_address *user, *source = NULL;
struct device *dev;
-
+
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
sock->state = SS_CONNECTED;
return 0; /* Connect completed during a ERESTARTSYS event */
}
-
+
if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
sock->state = SS_UNCONNECTED;
return -ECONNREFUSED;
}
-
+
if (sk->state == TCP_ESTABLISHED)
return -EISCONN; /* No reconnect on a seqpacket socket */
-
+
sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
+ if (addr->sax25_family != AF_NETROM)
+ return -EINVAL;
+
if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
sk->zapped = 0;
@@ -890,14 +798,14 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
sk->protinfo.nr->my_id = circuit % 256;
circuit++;
-
+
/* Move to connecting socket, start sending Connect Requests */
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
nr_establish_data_link(sk);
sk->protinfo.nr->state = NR_STATE_1;
nr_set_timer(sk);
-
+
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS;
@@ -920,14 +828,14 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
sock->state = SS_UNCONNECTED;
return sock_error(sk); /* Always set at this point */
}
-
+
sock->state = SS_CONNECTED;
sti();
-
+
return 0;
}
-
+
static int nr_socketpair(struct socket *sock1, struct socket *sock2)
{
return -EOPNOTSUPP;
@@ -939,19 +847,20 @@ static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
struct sock *newsk;
struct sk_buff *skb;
- if (newsock->data)
- sk_free(newsock->data);
+ if (newsock->sk != NULL)
+ nr_destroy_socket(newsock->sk);
- newsock->data = NULL;
-
- sk = (struct sock *)sock->data;
+ newsock->sk = NULL;
+
+ if ((sk = sock->sk) == NULL)
+ return -EINVAL;
if (sk->type != SOCK_SEQPACKET)
return -EOPNOTSUPP;
-
+
if (sk->state != TCP_LISTEN)
return -EINVAL;
-
+
/*
* The write queue this time is holding sockets ready to use
* hooked into the SABM we saved
@@ -961,7 +870,7 @@ static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
if (flags & O_NONBLOCK) {
sti();
- return 0;
+ return -EWOULDBLOCK;
}
interruptible_sleep_on(sk->sleep);
if (current->signal & ~current->blocked) {
@@ -979,7 +888,7 @@ static int nr_accept(struct socket *sock, struct socket *newsock, int flags)
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
sk->ack_backlog--;
- newsock->data = newsk;
+ newsock->sk = newsk;
return 0;
}
@@ -988,10 +897,8 @@ static int nr_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
- struct sock *sk;
-
- sk = (struct sock *)sock->data;
-
+ struct sock *sk = sock->sk;
+
if (peer != 0) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
@@ -1009,7 +916,7 @@ static int nr_getname(struct socket *sock, struct sockaddr *uaddr,
return 0;
}
-
+
int nr_rx_frame(struct sk_buff *skb, struct device *dev)
{
struct sock *sk;
@@ -1061,7 +968,7 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev)
if ((frametype & 0x0F) != NR_CONNREQ)
return 0;
-
+
sk = nr_find_listener(dest);
user = (ax25_address *)(skb->data + 21);
@@ -1086,18 +993,18 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev)
make->protinfo.nr->my_index = circuit / 256;
make->protinfo.nr->my_id = circuit % 256;
-
+
circuit++;
/* Window negotiation */
- if (window < make->window)
- make->window = window;
+ if (window < make->protinfo.nr->window)
+ make->protinfo.nr->window = window;
/* L4 timeout negotiation */
if (skb->len == 37) {
timeout = skb->data[36] * 256 + skb->data[35];
- if (timeout * PR_SLOWHZ < make->protinfo.nr->rtt * 2)
- make->protinfo.nr->rtt = (timeout * PR_SLOWHZ) / 2;
+ if (timeout * NR_SLOWHZ < make->protinfo.nr->t1)
+ make->protinfo.nr->t1 = timeout * NR_SLOWHZ;
make->protinfo.nr->bpqext = 1;
} else {
make->protinfo.nr->bpqext = 0;
@@ -1126,20 +1033,17 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev)
return 1;
}
-static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err;
struct sockaddr_ax25 sax;
struct sk_buff *skb;
unsigned char *asmptr;
int size;
-
- if (sk->err)
- return sock_error(sk);
- if (flags)
+ if (msg->msg_flags & ~MSG_DONTWAIT)
return -EINVAL;
if (sk->zapped)
@@ -1152,7 +1056,7 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nobl
if (sk->protinfo.nr->device == NULL)
return -ENETUNREACH;
-
+
if (usax) {
if (msg->msg_namelen < sizeof(sax))
return -EINVAL;
@@ -1167,33 +1071,23 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nobl
sax.sax25_family = AF_NETROM;
sax.sax25_call = sk->protinfo.nr->dest_addr;
}
-
- if (sk->debug)
- printk("NET/ROM: sendto: Addresses built.\n");
+ SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n");
/* Build a packet */
- if (sk->debug)
- printk("NET/ROM: sendto: building packet.\n");
-
+ SOCK_DEBUG(sk, "NET/ROM: sendto: building packet.\n");
size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
- if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+ if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
- skb->sk = sk;
- skb->free = 1;
- skb->arp = 1;
-
skb_reserve(skb, size - len);
-
+
/*
* Push down the NET/ROM header
*/
asmptr = skb_push(skb, NR_TRANSPORT_LEN);
-
- if (sk->debug)
- printk("Building NET/ROM Header.\n");
+ SOCK_DEBUG(sk, "Building NET/ROM Header.\n");
/* Build a NET/ROM Transport header */
@@ -1202,9 +1096,7 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nobl
*asmptr++ = 0; /* To be filled in later */
*asmptr++ = 0; /* Ditto */
*asmptr++ = NR_INFO;
-
- if (sk->debug)
- printk("Built header.\n");
+ SOCK_DEBUG(sk, "Built header.\n");
/*
* Put the data on the end
@@ -1213,15 +1105,11 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nobl
skb->h.raw = skb_put(skb, len);
asmptr = skb->h.raw;
-
- if (sk->debug)
- printk("NET/ROM: Appending user data\n");
+ SOCK_DEBUG(sk, "NET/ROM: Appending user data\n");
/* User data follows immediately after the NET/ROM transport header */
memcpy_fromiovec(asmptr, msg->msg_iov, len);
-
- if (sk->debug)
- printk("NET/ROM: Transmitting buffer\n");
+ SOCK_DEBUG(sk, "NET/ROM: Transmitting buffer\n");
if (sk->state != TCP_ESTABLISHED) {
kfree_skb(skb, FREE_WRITE);
@@ -1234,18 +1122,15 @@ static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nobl
}
-static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
- int flags, int *addr_len)
+static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
int copied;
struct sk_buff *skb;
int er;
- if (addr_len != NULL)
- *addr_len = sizeof(*sax);
-
/*
* This works for seqpacket too. The receiver has ordered the queue for
* us! We do one quick check first though
@@ -1255,7 +1140,7 @@ static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, int nob
return -ENOTCONN;
/* Now we can treat all alike */
- if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
return er;
if (!sk->protinfo.nr->hdrincl) {
@@ -1271,18 +1156,14 @@ static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, int nob
}
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-
- if (sax != NULL) {
- struct sockaddr_ax25 addr;
-
- addr.sax25_family = AF_NETROM;
- memcpy(&addr.sax25_call, skb->data + 7, AX25_ADDR_LEN);
-
- *sax = addr;
- *addr_len = sizeof(*sax);
+ if (sax != NULL) {
+ sax->sax25_family = AF_NETROM;
+ memcpy(sax->sax25_call.ax25_call, skb->data + 7, AX25_ADDR_LEN);
}
+ msg->msg_namelen = sizeof(*sax);
+
skb_free_datagram(sk, skb);
return copied;
@@ -1293,16 +1174,9 @@ static int nr_shutdown(struct socket *sk, int how)
return -EOPNOTSUPP;
}
-static int nr_select(struct socket *sock , int sel_type, select_table *wait)
-{
- struct sock *sk = (struct sock *)sock->data;
-
- return datagram_select(sk, sel_type, wait);
-}
-
static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
int err;
long amount = 0;
@@ -1310,7 +1184,7 @@ static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCOUTQ:
if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
return err;
- amount = sk->sndbuf - sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
put_user(amount, (int *)arg);
@@ -1376,43 +1250,43 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int
int len = 0;
off_t pos = 0;
off_t begin = 0;
-
+
cli();
- len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 n2 rtt wnd paclen Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 n2 wnd Snd-Q Rcv-Q\n");
for (s = nr_list; s != NULL; s = s->next) {
if ((dev = s->protinfo.nr->device) == NULL)
devname = "???";
else
devname = dev->name;
-
+
len += sprintf(buffer + len, "%-9s ",
ax2asc(&s->protinfo.nr->user_addr));
len += sprintf(buffer + len, "%-9s ",
ax2asc(&s->protinfo.nr->dest_addr));
- len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %3d %6d %5d %5d\n",
+ len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %5d %5d\n",
ax2asc(&s->protinfo.nr->source_addr),
devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id,
s->protinfo.nr->your_index, s->protinfo.nr->your_id,
s->protinfo.nr->state,
s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va,
- s->protinfo.nr->t1timer / PR_SLOWHZ,
- s->protinfo.nr->t1 / PR_SLOWHZ,
- s->protinfo.nr->t2timer / PR_SLOWHZ,
- s->protinfo.nr->t2 / PR_SLOWHZ,
- s->protinfo.nr->n2count, s->protinfo.nr->n2,
- s->protinfo.nr->rtt / PR_SLOWHZ,
- s->window, s->protinfo.nr->paclen,
- s->wmem_alloc, s->rmem_alloc);
-
+ s->protinfo.nr->t1timer / NR_SLOWHZ,
+ s->protinfo.nr->t1 / NR_SLOWHZ,
+ s->protinfo.nr->t2timer / NR_SLOWHZ,
+ s->protinfo.nr->t2 / NR_SLOWHZ,
+ s->protinfo.nr->n2count,
+ s->protinfo.nr->n2,
+ s->protinfo.nr->window,
+ atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -1427,10 +1301,15 @@ static int nr_get_info(char *buffer, char **start, off_t offset, int length, int
return(len);
}
-struct proto_ops nr_proto_ops = {
+static struct net_proto_family nr_family_ops =
+{
AF_NETROM,
-
- nr_create,
+ nr_create
+};
+
+static struct proto_ops nr_proto_ops = {
+ AF_NETROM,
+
nr_dup,
nr_release,
nr_bind,
@@ -1438,18 +1317,18 @@ struct proto_ops nr_proto_ops = {
nr_socketpair,
nr_accept,
nr_getname,
- nr_select,
+ datagram_poll,
nr_ioctl,
nr_listen,
nr_shutdown,
nr_setsockopt,
nr_getsockopt,
- nr_fcntl,
+ sock_no_fcntl,
nr_sendmsg,
nr_recvmsg
};
-struct notifier_block nr_dev_notifier = {
+static struct notifier_block nr_dev_notifier = {
nr_device_event,
0
};
@@ -1475,18 +1354,32 @@ static struct proc_dir_entry proc_net_nr_nodes = {
};
#endif
+static struct device dev_nr[] = {
+ {"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+ {"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+ {"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
+ {"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
+};
+
void nr_proto_init(struct net_proto *pro)
{
- sock_register(nr_proto_ops.family, &nr_proto_ops);
+ int i;
+
+ sock_register(&nr_family_ops);
register_netdevice_notifier(&nr_dev_notifier);
- printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.5 for AX25.034 Linux 2.1\n");
+ printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.1\n");
if (!ax25_protocol_register(AX25_P_NETROM, nr_route_frame))
printk(KERN_ERR "NET/ROM unable to register protocol with AX.25\n");
if (!ax25_linkfail_register(nr_link_failed))
printk(KERN_ERR "NET/ROM unable to register linkfail handler with AX.25\n");
+ for (i = 0; i < 4; i++)
+ register_netdev(&dev_nr[i]);
+
+#ifdef CONFIG_SYSCTL
nr_register_sysctl();
+#endif
#ifdef CONFIG_PROC_FS
proc_net_register(&proc_net_nr);
@@ -1495,4 +1388,46 @@ void nr_proto_init(struct net_proto *pro)
#endif
}
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ nr_proto_init(NULL);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ int i;
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_NR);
+ proc_net_unregister(PROC_NET_NR_NEIGH);
+ proc_net_unregister(PROC_NET_NR_NODES);
+#endif
+ nr_rt_free();
+
+ ax25_protocol_release(AX25_P_NETROM);
+ ax25_linkfail_release(nr_link_failed);
+
+ unregister_netdevice_notifier(&nr_dev_notifier);
+
+#ifdef CONFIG_SYSCTL
+ nr_unregister_sysctl();
+#endif
+ sock_unregister(AF_NETROM);
+
+ for (i = 0; i < 4; i++) {
+ if (dev_nr[i].priv != NULL) {
+ kfree(dev_nr[i].priv);
+ dev_nr[i].priv = NULL;
+ unregister_netdev(&dev_nr[i]);
+ }
+ }
+}
+
+#endif
+
#endif
diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c
index fbbd913e9..1387c6c2d 100644
--- a/net/netrom/nr_dev.c
+++ b/net/netrom/nr_dev.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.3.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -18,10 +18,12 @@
* NET/ROM 003 Jonathan(G4KLX) Put nr_rebuild_header into line with
* ax25_rebuild_header
* NET/ROM 004 Jonathan(G4KLX) Callsign registration with AX.25.
+ * NET/ROM 006 Hans(PE1AYX) Fixed interface to IP layer.
*/
#include <linux/config.h>
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
+#define __NO_VERSION__
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
@@ -59,7 +61,7 @@
int nr_rx_ip(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
if (!dev->start) {
stats->rx_errors++;
@@ -67,12 +69,16 @@ int nr_rx_ip(struct sk_buff *skb, struct device *dev)
}
stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
skb->protocol = htons(ETH_P_IP);
/* Spoof incoming device */
- skb->dev = dev;
+ skb->dev = dev;
+ skb->h.raw = skb->data;
+ skb->nh.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
- skb->h.raw = skb->data;
ip_rcv(skb, skb->dev, NULL);
return 1;
@@ -84,16 +90,16 @@ static int nr_header(struct sk_buff *skb, struct device *dev, unsigned short typ
unsigned char *buff = skb_push(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
memcpy(buff, (saddr != NULL) ? saddr : dev->dev_addr, dev->addr_len);
- buff[6] &= ~LAPB_C;
- buff[6] &= ~LAPB_E;
- buff[6] |= SSSID_SPARE;
+ buff[6] &= ~AX25_CBIT;
+ buff[6] &= ~AX25_EBIT;
+ buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
if (daddr != NULL)
memcpy(buff, daddr, dev->addr_len);
- buff[6] &= ~LAPB_C;
- buff[6] |= LAPB_E;
- buff[6] |= SSSID_SPARE;
+ buff[6] &= ~AX25_CBIT;
+ buff[6] |= AX25_EBIT;
+ buff[6] |= AX25_SSSID_SPARE;
buff += AX25_ADDR_LEN;
*buff++ = sysctl_netrom_network_ttl_initialiser;
@@ -106,49 +112,48 @@ static int nr_header(struct sk_buff *skb, struct device *dev, unsigned short typ
if (daddr != NULL)
return 37;
-
- return -37;
+
+ return -37;
}
-static int nr_rebuild_header(void *buff, struct device *dev,
- unsigned long raddr, struct sk_buff *skb)
+static int nr_rebuild_header(struct sk_buff *skb)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
- unsigned char *bp = (unsigned char *)buff;
+ struct device *dev = skb->dev;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
struct sk_buff *skbn;
+ unsigned char *bp = skb->data;
- if (!arp_query(bp + 7, raddr, dev)) {
- dev_kfree_skb(skb, FREE_WRITE);
+ if (!arp_find(bp + 7, skb)) {
+ kfree_skb(skb, FREE_WRITE);
return 1;
}
- bp[6] &= ~LAPB_C;
- bp[6] &= ~LAPB_E;
- bp[6] |= SSSID_SPARE;
+ bp[6] &= ~AX25_CBIT;
+ bp[6] &= ~AX25_EBIT;
+ bp[6] |= AX25_SSSID_SPARE;
bp += AX25_ADDR_LEN;
-
- bp[6] &= ~LAPB_C;
- bp[6] |= LAPB_E;
- bp[6] |= SSSID_SPARE;
+
+ bp[6] &= ~AX25_CBIT;
+ bp[6] |= AX25_EBIT;
+ bp[6] |= AX25_SSSID_SPARE;
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
return 1;
}
- skbn->sk = skb->sk;
-
- if (skbn->sk != NULL)
- atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
if (!nr_route_frame(skbn, NULL)) {
- dev_kfree_skb(skbn, FREE_WRITE);
+ kfree_skb(skbn, FREE_WRITE);
stats->tx_errors++;
}
stats->tx_packets++;
+ stats->tx_bytes += skbn->len;
return 1;
}
@@ -160,7 +165,7 @@ static int nr_set_mac_address(struct device *dev, void *addr)
ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
+
ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
return 0;
@@ -192,7 +197,7 @@ static int nr_close(struct device *dev)
static int nr_xmit(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
if (skb == NULL || dev == NULL)
return 0;
@@ -214,7 +219,7 @@ static int nr_xmit(struct sk_buff *skb, struct device *dev)
sti();
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
stats->tx_errors++;
@@ -225,16 +230,14 @@ static int nr_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
-static struct enet_statistics *nr_get_stats(struct device *dev)
+static struct net_device_stats *nr_get_stats(struct device *dev)
{
- return (struct enet_statistics *)dev->priv;
+ return (struct net_device_stats *)dev->priv;
}
int nr_init(struct device *dev)
{
- int i;
-
- dev->mtu = 236; /* MTU */
+ dev->mtu = NR_MAX_PACKET_SIZE;
dev->tbusy = 0;
dev->hard_start_xmit = nr_xmit;
dev->open = nr_open;
@@ -256,74 +259,16 @@ int nr_init(struct device *dev)
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
- if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
+ if ((dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL)) == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
dev->get_stats = nr_get_stats;
- /* Fill in the generic fields of the device structure. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&dev->buffs[i]);
+ dev_init_buffers(dev);
return 0;
};
-#ifdef MODULE
-extern struct proto_ops nr_proto_ops;
-extern struct notifier_block nr_dev_notifier;
-
-static struct device dev_nr[] = {
- {"nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
- {"nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
- {"nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init},
- {"nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, nr_init}
-};
-
-int init_module(void)
-{
- int i;
-
- for (i = 0; i < 4; i++)
- register_netdev(&dev_nr[i]);
-
- register_symtab(NULL);
-
- nr_proto_init(NULL);
-
- return 0;
-}
-
-void cleanup_module(void)
-{
- int i;
-
-#ifdef CONFIG_PROC_FS
- proc_net_unregister(PROC_NET_NR);
- proc_net_unregister(PROC_NET_NR_NEIGH);
- proc_net_unregister(PROC_NET_NR_NODES);
-#endif
- nr_rt_free();
-
- ax25_protocol_release(AX25_P_NETROM);
- ax25_linkfail_release(nr_link_failed);
-
- unregister_netdevice_notifier(&nr_dev_notifier);
-
- nr_unregister_sysctl();
-
- sock_unregister(nr_proto_ops.family);
-
- for (i = 0; i < 4; i++) {
- if (dev_nr[i].priv != NULL) {
- kfree(dev_nr[i].priv);
- dev_nr[i].priv = NULL;
- unregister_netdev(&dev_nr[i]);
- }
- }
-}
-
-#endif
-
#endif
diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c
index 85d28a114..cbddc677a 100644
--- a/net/netrom/nr_in.c
+++ b/net/netrom/nr_in.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -59,7 +59,7 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
return 0;
}
-
+
if (!more && sk->protinfo.nr->fraglen > 0) { /* End of fragment */
sk->protinfo.nr->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
@@ -67,10 +67,6 @@ static int nr_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL)
return 1;
- skbn->free = 1;
- skbn->arp = 1;
- skbn->sk = sk;
- sk->rmem_alloc += skbn->truesize;
skbn->h.raw = skbn->data;
skbo = skb_dequeue(&sk->protinfo.nr->frag_queue);
@@ -99,7 +95,6 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype
switch (frametype) {
case NR_CONNACK:
- nr_calculate_rtt(sk);
sk->protinfo.nr->your_index = skb->data[17];
sk->protinfo.nr->your_id = skb->data[18];
sk->protinfo.nr->t1timer = 0;
@@ -111,7 +106,7 @@ static int nr_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype
sk->protinfo.nr->vl = 0;
sk->protinfo.nr->state = NR_STATE_3;
sk->protinfo.nr->n2count = 0;
- sk->window = skb->data[20];
+ sk->protinfo.nr->window = skb->data[20];
sk->state = TCP_ESTABLISHED;
/* For WAIT_SABM connections we will produce an accept ready socket here */
if (!sk->dead)
@@ -215,10 +210,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
case NR_INFOACK | NR_NAK_FLAG:
case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
- sk->protinfo.nr->condition |= PEER_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
} else {
- sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
sk->protinfo.nr->t4timer = 0;
}
if (!nr_validate_nr(sk, nr)) {
@@ -228,14 +223,14 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
nr_frames_acked(sk, nr);
nr_send_nak_frame(sk);
} else {
- if (sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) {
+ if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
nr_frames_acked(sk, nr);
} else {
nr_check_iframes_acked(sk, nr);
}
}
break;
-
+
case NR_INFO:
case NR_INFO | NR_NAK_FLAG:
case NR_INFO | NR_CHOKE_FLAG:
@@ -245,10 +240,10 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
case NR_INFO | NR_NAK_FLAG | NR_MORE_FLAG:
case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
- sk->protinfo.nr->condition |= PEER_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
} else {
- sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
sk->protinfo.nr->t4timer = 0;
}
if (nr_validate_nr(sk, nr)) {
@@ -256,7 +251,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
nr_frames_acked(sk, nr);
nr_send_nak_frame(sk);
} else {
- if (sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) {
+ if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) {
nr_frames_acked(sk, nr);
} else {
nr_check_iframes_acked(sk, nr);
@@ -265,7 +260,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
}
queued = 1;
skb_queue_head(&sk->protinfo.nr->reseq_queue, skb);
- if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
+ if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
break;
skb_queue_head_init(&temp_queue);
do {
@@ -276,13 +271,12 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
if (nr_queue_rx_frame(sk, skbn, frametype & NR_MORE_FLAG) == 0) {
sk->protinfo.nr->vr = (sk->protinfo.nr->vr + 1) % NR_MODULUS;
} else {
- sk->protinfo.nr->condition |= OWN_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition |= NR_COND_OWN_RX_BUSY;
skb_queue_tail(&temp_queue, skbn);
}
} else if (nr_in_rx_window(sk, ns)) {
skb_queue_tail(&temp_queue, skbn);
} else {
- skbn->free = 1;
kfree_skb(skbn, FREE_READ);
}
}
@@ -293,12 +287,12 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
/*
* Window is full, ack it immediately.
*/
- if (((sk->protinfo.nr->vl + sk->window) % NR_MODULUS) == sk->protinfo.nr->vr) {
+ if (((sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS) == sk->protinfo.nr->vr) {
nr_enquiry_response(sk);
} else {
- if (!(sk->protinfo.nr->condition & ACK_PENDING_CONDITION)) {
+ if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) {
sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
- sk->protinfo.nr->condition |= ACK_PENDING_CONDITION;
+ sk->protinfo.nr->condition |= NR_COND_ACK_PENDING;
}
}
break;
@@ -314,7 +308,7 @@ static int nr_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype
int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb)
{
int queued = 0, frametype;
-
+
if (sk->protinfo.nr->state == NR_STATE_0)
return 0;
@@ -322,8 +316,7 @@ int nr_process_rx_frame(struct sock *sk, struct sk_buff *skb)
frametype = skb->data[19];
- switch (sk->protinfo.nr->state)
- {
+ switch (sk->protinfo.nr->state) {
case NR_STATE_1:
queued = nr_state1_machine(sk, skb, frametype);
break;
diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c
index 61935f30c..7ee32c736 100644
--- a/net/netrom/nr_out.c
+++ b/net/netrom/nr_out.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -50,11 +50,9 @@ void nr_output(struct sock *sk, struct sk_buff *skb)
{
struct sk_buff *skbn;
unsigned char transport[NR_TRANSPORT_LEN];
- int err, frontlen, len, mtu;
+ int err, frontlen, len;
- mtu = sk->protinfo.nr->paclen;
-
- if (skb->len - NR_TRANSPORT_LEN > mtu) {
+ if (skb->len - NR_TRANSPORT_LEN > NR_MAX_PACKET_SIZE) {
/* Save a copy of the Transport Header */
memcpy(transport, skb->data, NR_TRANSPORT_LEN);
skb_pull(skb, NR_TRANSPORT_LEN);
@@ -62,16 +60,12 @@ void nr_output(struct sock *sk, struct sk_buff *skb)
frontlen = skb_headroom(skb);
while (skb->len > 0) {
- if ((skbn = sock_alloc_send_skb(sk, frontlen + mtu, 0, 0, &err)) == NULL)
+ if ((skbn = sock_alloc_send_skb(sk, frontlen + NR_MAX_PACKET_SIZE, 0, 0, &err)) == NULL)
return;
- skbn->sk = sk;
- skbn->free = 1;
- skbn->arp = 1;
-
skb_reserve(skbn, frontlen);
- len = (mtu > skb->len) ? skb->len : mtu;
+ len = (NR_MAX_PACKET_SIZE > skb->len) ? skb->len : NR_MAX_PACKET_SIZE;
/* Copy the user data */
memcpy(skb_put(skbn, len), skb->data, len);
@@ -83,11 +77,10 @@ void nr_output(struct sock *sk, struct sk_buff *skb)
if (skb->len > 0)
skbn->data[4] |= NR_MORE_FLAG;
-
+
skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
}
-
- skb->free = 1;
+
kfree_skb(skb, FREE_WRITE);
} else {
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */
@@ -109,7 +102,7 @@ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb)
skb->data[2] = sk->protinfo.nr->vs;
skb->data[3] = sk->protinfo.nr->vr;
- if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
+ if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
skb->data[4] |= NR_CHOKE_FLAG;
nr_transmit_buffer(sk, skb);
@@ -118,22 +111,22 @@ static void nr_send_iframe(struct sock *sk, struct sk_buff *skb)
void nr_send_nak_frame(struct sock *sk)
{
struct sk_buff *skb, *skbn;
-
+
if ((skb = skb_peek(&sk->protinfo.nr->ack_queue)) == NULL)
return;
-
+
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
return;
skbn->data[2] = sk->protinfo.nr->va;
skbn->data[3] = sk->protinfo.nr->vr;
- if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)
+ if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
skbn->data[4] |= NR_CHOKE_FLAG;
nr_transmit_buffer(sk, skbn);
- sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
sk->protinfo.nr->t1timer = 0;
}
@@ -141,16 +134,15 @@ void nr_send_nak_frame(struct sock *sk)
void nr_kick(struct sock *sk)
{
struct sk_buff *skb, *skbn;
- int last = 1;
- unsigned short start, end, next;
+ unsigned short start, end;
del_timer(&sk->timer);
start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs;
- end = (sk->protinfo.nr->va + sk->window) % NR_MODULUS;
+ end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS;
- if (!(sk->protinfo.nr->condition & PEER_RX_BUSY_CONDITION) &&
- start != end &&
+ if (!(sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) &&
+ start != end &&
skb_peek(&sk->write_queue) != NULL) {
sk->protinfo.nr->vs = start;
@@ -171,29 +163,27 @@ void nr_kick(struct sock *sk)
break;
}
- next = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
- last = (next == end);
+ skb_set_owner_w(skbn, sk);
/*
* Transmit the frame copy.
*/
nr_send_iframe(sk, skbn);
- sk->protinfo.nr->vs = next;
+ sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
/*
* Requeue the original data frame.
*/
skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
- } while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+ } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
- sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
- if (sk->protinfo.nr->t1timer == 0) {
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
- }
+ if (sk->protinfo.nr->t1timer == 0)
+ sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
}
nr_set_timer(sk);
@@ -209,21 +199,19 @@ void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb)
dptr = skb_push(skb, NR_NETWORK_LEN);
memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] &= ~LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] &= ~AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
memcpy(dptr, &sk->protinfo.nr->dest_addr, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] |= LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] |= AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
*dptr++ = sysctl_netrom_network_ttl_initialiser;
- skb->arp = 1;
-
if (!nr_route_frame(skb, NULL)) {
kfree_skb(skb, FREE_WRITE);
@@ -249,7 +237,7 @@ void nr_establish_data_link(struct sock *sk)
nr_write_internal(sk, NR_CONNREQ);
sk->protinfo.nr->t2timer = 0;
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
+ sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
}
/*
@@ -258,32 +246,30 @@ void nr_establish_data_link(struct sock *sk)
void nr_enquiry_response(struct sock *sk)
{
int frametype = NR_INFOACK;
-
- if (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION) {
+
+ if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY) {
frametype |= NR_CHOKE_FLAG;
} else {
- if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL) {
+ if (skb_peek(&sk->protinfo.nr->reseq_queue) != NULL)
frametype |= NR_NAK_FLAG;
- }
}
-
+
nr_write_internal(sk, frametype);
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
- sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
}
void nr_check_iframes_acked(struct sock *sk, unsigned short nr)
{
if (sk->protinfo.nr->vs == nr) {
nr_frames_acked(sk, nr);
- nr_calculate_rtt(sk);
sk->protinfo.nr->t1timer = 0;
sk->protinfo.nr->n2count = 0;
} else {
if (sk->protinfo.nr->va != nr) {
nr_frames_acked(sk, nr);
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
+ sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
}
}
}
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index 73f5f0ba4..eb8ad7a35 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -20,8 +20,10 @@
* Change default quality for new neighbour when same
* as node callsign.
* Alan Cox(GW4PTS) Added the firewall hooks.
+ * NET/ROM 006 Jonathan(G4KLX) Added the setting of digipeated neighbours.
+ * Tomi(OH2BNS) Routing quality and link failure changes.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
#include <linux/errno.h>
@@ -82,6 +84,9 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
break;
+ if (nr_neigh != NULL)
+ nr_neigh->failed = 0;
+
if (quality == 0 && nr_neigh != NULL && nr_node != NULL)
return 0;
@@ -92,22 +97,20 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_neigh->callsign = *ax25;
nr_neigh->digipeat = NULL;
nr_neigh->dev = dev;
- if (ax25cmp(nr, ax25) == 0)
- nr_neigh->quality = quality;
- else
- nr_neigh->quality = sysctl_netrom_default_path_quality;
+ nr_neigh->quality = sysctl_netrom_default_path_quality;
nr_neigh->locked = 0;
nr_neigh->count = 0;
nr_neigh->number = nr_neigh_no++;
+ nr_neigh->failed = 0;
- if (ax25_digi != NULL) {
+ if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
kfree_s(nr_neigh, sizeof(*nr_neigh));
return -ENOMEM;
}
*nr_neigh->digipeat = *ax25_digi;
}
-
+
save_flags(flags);
cli();
@@ -117,6 +120,9 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
restore_flags(flags);
}
+ if (quality != 0 && ax25cmp(nr, ax25) == 0)
+ nr_neigh->quality = quality;
+
if (nr_node == NULL) {
if ((nr_node = (struct nr_node *)kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL)
return -ENOMEM;
@@ -130,7 +136,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_node->routes[0].quality = quality;
nr_node->routes[0].obs_count = obs_count;
nr_node->routes[0].neighbour = nr_neigh;
-
+
save_flags(flags);
cli();
@@ -138,15 +144,15 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_node_list = nr_node;
restore_flags(flags);
-
+
nr_neigh->count++;
return 0;
- } else {
- if (nr_node->mnemonic[0] == '\0')
- strcpy(nr_node->mnemonic, mnemonic);
}
+ if (quality != 0)
+ strcpy(nr_node->mnemonic, mnemonic);
+
for (found = 0, i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
nr_node->routes[i].quality = quality;
@@ -165,7 +171,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_node->routes[0].quality = quality;
nr_node->routes[0].obs_count = obs_count;
nr_node->routes[0].neighbour = nr_neigh;
-
+
nr_node->which++;
nr_node->count++;
nr_neigh->count++;
@@ -173,10 +179,10 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
/* It must be better than the worst */
if (quality > nr_node->routes[2].quality) {
nr_node->routes[2].neighbour->count--;
-
+
if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked)
nr_remove_neigh(nr_node->routes[2].neighbour);
-
+
nr_node->routes[2].quality = quality;
nr_node->routes[2].obs_count = obs_count;
nr_node->routes[2].neighbour = nr_neigh;
@@ -239,7 +245,7 @@ static void nr_remove_node(struct nr_node *nr_node)
{
struct nr_node *s;
unsigned long flags;
-
+
save_flags(flags);
cli();
@@ -306,7 +312,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct d
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
int i;
-
+
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (ax25cmp(callsign, &nr_node->callsign) == 0)
break;
@@ -318,16 +324,16 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct d
break;
if (nr_neigh == NULL) return -EINVAL;
-
+
for (i = 0; i < nr_node->count; i++) {
if (nr_node->routes[i].neighbour == nr_neigh) {
nr_neigh->count--;
if (nr_neigh->count == 0 && !nr_neigh->locked)
nr_remove_neigh(nr_neigh);
-
+
nr_node->count--;
-
+
if (nr_node->count == 0) {
nr_remove_node(nr_node);
} else {
@@ -351,7 +357,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct d
/*
* Lock a neighbour with a quality.
*/
-static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int quality)
+static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
unsigned long flags;
@@ -374,10 +380,19 @@ static int nr_add_neigh(ax25_address *callsign, struct device *dev, unsigned int
nr_neigh->locked = 1;
nr_neigh->count = 0;
nr_neigh->number = nr_neigh_no++;
+ nr_neigh->failed = 0;
+
+ if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
+ if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
+ kfree_s(nr_neigh, sizeof(*nr_neigh));
+ return -ENOMEM;
+ }
+ *nr_neigh->digipeat = *ax25_digi;
+ }
save_flags(flags);
cli();
-
+
nr_neigh->next = nr_neigh_list;
nr_neigh_list = nr_neigh;
@@ -434,9 +449,9 @@ static int nr_dec_obs(void)
case 1: /* From 1 -> 0 */
nr_neigh = s->routes[i].neighbour;
-
+
nr_neigh->count--;
-
+
if (nr_neigh->count == 0 && !nr_neigh->locked)
nr_remove_neigh(nr_neigh);
@@ -478,14 +493,14 @@ void nr_rt_device_down(struct device *dev)
while (nr_neigh != NULL) {
s = nr_neigh;
nr_neigh = nr_neigh->next;
-
+
if (s->dev == dev) {
nr_node = nr_node_list;
while (nr_node != NULL) {
t = nr_node;
nr_node = nr_node->next;
-
+
for (i = 0; i < t->count; i++) {
if (t->routes[i].neighbour == s) {
t->count--;
@@ -500,11 +515,11 @@ void nr_rt_device_down(struct device *dev)
}
}
}
-
+
if (t->count <= 0)
nr_remove_node(t);
}
-
+
nr_remove_neigh(s);
}
}
@@ -552,10 +567,29 @@ struct device *nr_dev_get(ax25_address *addr)
for (dev = dev_base; dev != NULL; dev = dev->next)
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0)
return dev;
-
+
return NULL;
}
+static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+{
+ static ax25_digi ax25_digi;
+ int i;
+
+ if (ndigis == 0)
+ return NULL;
+
+ for (i = 0; i < ndigis; i++) {
+ ax25_digi.calls[i] = digipeaters[i];
+ ax25_digi.repeated[i] = 0;
+ }
+
+ ax25_digi.ndigi = ndigis;
+ ax25_digi.lastrepeat = 0;
+
+ return &ax25_digi;
+}
+
/*
* Handle the ioctls that control the routing functions.
*/
@@ -573,15 +607,19 @@ int nr_rt_ioctl(unsigned int cmd, void *arg)
copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
return -EINVAL;
+ if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS)
+ return -EINVAL;
switch (nr_route.type) {
case NETROM_NODE:
return nr_add_node(&nr_route.callsign,
nr_route.mnemonic,
&nr_route.neighbour,
- NULL, dev, nr_route.quality,
+ nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+ dev, nr_route.quality,
nr_route.obs_count);
case NETROM_NEIGH:
return nr_add_neigh(&nr_route.callsign,
+ nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
dev, nr_route.quality);
default:
return -EINVAL;
@@ -626,9 +664,11 @@ void nr_link_failed(ax25_address *callsign, struct device *dev)
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev)
break;
-
+
if (nr_neigh == NULL) return;
-
+
+ if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return;
+
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh)
nr_node->which++;
@@ -645,20 +685,18 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
struct nr_node *nr_node;
struct device *dev;
unsigned char *dptr;
-
-#ifdef CONFIG_FIREWALL
- if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+
+ if (ax25 != NULL && call_in_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
return 0;
- if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+ if (ax25 == NULL && call_out_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
return 0;
-#endif
nr_src = (ax25_address *)(skb->data + 0);
nr_dest = (ax25_address *)(skb->data + 7);
if (ax25 != NULL)
nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
- ax25->device, 0, sysctl_netrom_network_ttl_initialiser);
+ ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
if ((dev = nr_dev_get(nr_dest)) != NULL) /* Its for me */
return nr_rx_frame(skb, dev);
@@ -682,15 +720,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
if ((dev = nr_dev_first()) == NULL)
return 0;
-#ifdef CONFIG_FIREWALL
- if (ax25 != NULL && call_fw_firewall(PF_NETROM, skb->dev, skb->data, NULL) != FW_ACCEPT)
+ if (ax25 != NULL && call_fw_firewall(PF_NETROM, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
return 0;
-#endif
dptr = skb_push(skb, 1);
*dptr = AX25_P_NETROM;
- return ax25_send_frame(skb, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+ return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
}
int nr_nodes_get_info(char *buffer, char **start, off_t offset,
@@ -750,19 +786,28 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset,
int len = 0;
off_t pos = 0;
off_t begin = 0;
+ int i;
cli();
- len += sprintf(buffer, "addr callsign dev qual lock count\n");
+ len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n");
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
- len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d\n",
+ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d",
nr_neigh->number,
ax2asc(&nr_neigh->callsign),
nr_neigh->dev ? nr_neigh->dev->name : "???",
nr_neigh->quality,
nr_neigh->locked,
- nr_neigh->count);
+ nr_neigh->count,
+ nr_neigh->failed);
+
+ if (nr_neigh->digipeat != NULL) {
+ for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
+ }
+
+ len += sprintf(buffer + len, "\n");
pos = begin + len;
@@ -770,7 +815,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset,
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c
index ab788f6af..eb1b225f8 100644
--- a/net/netrom/nr_subr.c
+++ b/net/netrom/nr_subr.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -16,7 +16,7 @@
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c
* NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
#include <linux/errno.h>
@@ -48,25 +48,17 @@ void nr_clear_queues(struct sock *sk)
{
struct sk_buff *skb;
- while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
- skb->sk = sk;
- skb->free = 1;
+ while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
- while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) {
- skb->sk = sk;
- skb->free = 1;
+ while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
- while ((skb = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL) {
+ while ((skb = skb_dequeue(&sk->protinfo.nr->reseq_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
- while ((skb = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL) {
+ while ((skb = skb_dequeue(&sk->protinfo.nr->frag_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
}
/*
@@ -84,8 +76,6 @@ void nr_frames_acked(struct sock *sk, unsigned short nr)
if (sk->protinfo.nr->va != nr) {
while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) {
skb = skb_dequeue(&sk->protinfo.nr->ack_queue);
- skb->sk = sk;
- skb->free = 1;
kfree_skb(skb, FREE_WRITE);
sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS;
}
@@ -122,7 +112,7 @@ int nr_validate_nr(struct sock *sk, unsigned short nr)
if (nr == vc) return 1;
vc = (vc + 1) % NR_MODULUS;
}
-
+
if (nr == sk->protinfo.nr->vs) return 1;
return 0;
@@ -134,7 +124,7 @@ int nr_validate_nr(struct sock *sk, unsigned short nr)
int nr_in_rx_window(struct sock *sk, unsigned short ns)
{
unsigned short vc = sk->protinfo.nr->vr;
- unsigned short vt = (sk->protinfo.nr->vl + sk->window) % NR_MODULUS;
+ unsigned short vt = (sk->protinfo.nr->vl + sk->protinfo.nr->window) % NR_MODULUS;
while (vc != vt) {
if (ns == vc) return 1;
@@ -155,7 +145,7 @@ void nr_write_internal(struct sock *sk, int frametype)
int len, timeout;
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
-
+
switch (frametype & 0x0F) {
case NR_CONNREQ:
len += 17;
@@ -171,7 +161,7 @@ void nr_write_internal(struct sock *sk, int frametype)
printk(KERN_ERR "nr_write_internal: invalid frame type %d\n", frametype);
return;
}
-
+
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
return;
@@ -185,22 +175,22 @@ void nr_write_internal(struct sock *sk, int frametype)
switch (frametype & 0x0F) {
case NR_CONNREQ:
- timeout = (sk->protinfo.nr->rtt / PR_SLOWHZ) * 2;
+ timeout = sk->protinfo.nr->t1 / NR_SLOWHZ;
*dptr++ = sk->protinfo.nr->my_index;
*dptr++ = sk->protinfo.nr->my_id;
*dptr++ = 0;
*dptr++ = 0;
*dptr++ = frametype;
- *dptr++ = sk->window;
+ *dptr++ = sk->protinfo.nr->window;
memcpy(dptr, &sk->protinfo.nr->user_addr, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] &= ~LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] &= ~AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
memcpy(dptr, &sk->protinfo.nr->source_addr, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] &= ~LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] &= ~AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
*dptr++ = timeout % 256;
*dptr++ = timeout / 256;
@@ -212,7 +202,7 @@ void nr_write_internal(struct sock *sk, int frametype)
*dptr++ = sk->protinfo.nr->my_index;
*dptr++ = sk->protinfo.nr->my_id;
*dptr++ = frametype;
- *dptr++ = sk->window;
+ *dptr++ = sk->protinfo.nr->window;
if (sk->protinfo.nr->bpqext) *dptr++ = sysctl_netrom_network_ttl_initialiser;
break;
@@ -234,8 +224,6 @@ void nr_write_internal(struct sock *sk, int frametype)
break;
}
- skb->free = 1;
-
nr_transmit_buffer(sk, skb);
}
@@ -259,15 +247,15 @@ void nr_transmit_dm(struct sk_buff *skb)
dptr = skb_put(skbn, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
memcpy(dptr, skb->data + 7, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] &= ~LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] &= ~AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
memcpy(dptr, skb->data + 0, AX25_ADDR_LEN);
- dptr[6] &= ~LAPB_C;
- dptr[6] |= LAPB_E;
- dptr[6] |= SSSID_SPARE;
+ dptr[6] &= ~AX25_CBIT;
+ dptr[6] |= AX25_EBIT;
+ dptr[6] |= AX25_SSSID_SPARE;
dptr += AX25_ADDR_LEN;
*dptr++ = sysctl_netrom_network_ttl_initialiser;
@@ -279,49 +267,8 @@ void nr_transmit_dm(struct sk_buff *skb)
*dptr++ = NR_CONNACK | NR_CHOKE_FLAG;
*dptr++ = 0;
- skbn->free = 1;
- skbn->sk = NULL;
-
if (!nr_route_frame(skbn, NULL))
kfree_skb(skbn, FREE_WRITE);
}
-/*
- * Exponential backoff for NET/ROM
- */
-unsigned short nr_calculate_t1(struct sock *sk)
-{
- int n, t;
-
- for (t = 2, n = 0; n < sk->protinfo.nr->n2count; n++)
- t *= 2;
-
- if (t > 8) t = 8;
-
- return t * sk->protinfo.nr->rtt;
-}
-
-/*
- * Calculate the Round Trip Time
- */
-void nr_calculate_rtt(struct sock *sk)
-{
- if (sk->protinfo.nr->t1timer > 0 && sk->protinfo.nr->n2count == 0)
- sk->protinfo.nr->rtt = (9 * sk->protinfo.nr->rtt + sk->protinfo.nr->t1 - sk->protinfo.nr->t1timer) / 10;
-
-#ifdef NR_T1CLAMPLO
- /* Don't go below one tenth of a second */
- if (sk->protinfo.nr->rtt < (NR_T1CLAMPLO))
- sk->protinfo.nr->rtt = (NR_T1CLAMPLO);
-#else /* Failsafe - some people might have sub 1/10th RTTs :-) **/
- if (sk->protinfo.nr->rtt == 0)
- sk->protinfo.nr->rtt = PR_SLOWHZ;
-#endif
-#ifdef NR_T1CLAMPHI
- /* OR above clamped seconds **/
- if (sk->protinfo.nr->rtt > (NR_T1CLAMPHI))
- sk->protinfo.nr->rtt = (NR_T1CLAMPHI);
-#endif
-}
-
#endif
diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c
index 0149851fd..22d029b43 100644
--- a/net/netrom/nr_timer.c
+++ b/net/netrom/nr_timer.c
@@ -1,10 +1,10 @@
/*
- * NET/ROM release 004
+ * NET/ROM release 006
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 1.2.1 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -43,37 +43,20 @@
static void nr_timer(unsigned long);
/*
- * Linux set/reset timer routines
+ * Linux set timer
*/
void nr_set_timer(struct sock *sk)
{
unsigned long flags;
-
- save_flags(flags);
- cli();
- del_timer(&sk->timer);
- restore_flags(flags);
- sk->timer.next = sk->timer.prev = NULL;
- sk->timer.data = (unsigned long)sk;
- sk->timer.function = &nr_timer;
-
- sk->timer.expires = jiffies+10;
- add_timer(&sk->timer);
-}
-
-static void nr_reset_timer(struct sock *sk)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
del_timer(&sk->timer);
restore_flags(flags);
sk->timer.data = (unsigned long)sk;
sk->timer.function = &nr_timer;
- sk->timer.expires = jiffies+10;
+ sk->timer.expires = jiffies + (HZ / 10);
+
add_timer(&sk->timer);
}
@@ -102,11 +85,12 @@ static void nr_timer(unsigned long param)
/*
* Check for the state of the receive buffer.
*/
- if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.nr->condition & OWN_RX_BUSY_CONDITION)) {
- sk->protinfo.nr->condition &= ~OWN_RX_BUSY_CONDITION;
- nr_write_internal(sk, NR_INFOACK);
- sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+ if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) &&
+ (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)) {
+ sk->protinfo.nr->condition &= ~NR_COND_OWN_RX_BUSY;
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
+ nr_write_internal(sk, NR_INFOACK);
break;
}
/*
@@ -121,19 +105,19 @@ static void nr_timer(unsigned long param)
if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) {
if (sk->protinfo.nr->state == NR_STATE_3) {
- if (sk->protinfo.nr->condition & ACK_PENDING_CONDITION) {
- sk->protinfo.nr->condition &= ~ACK_PENDING_CONDITION;
+ if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) {
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
nr_enquiry_response(sk);
}
}
}
if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) {
- sk->protinfo.nr->condition &= ~PEER_RX_BUSY_CONDITION;
+ sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
}
if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) {
- nr_reset_timer(sk);
+ nr_set_timer(sk);
return;
}
@@ -187,7 +171,7 @@ static void nr_timer(unsigned long param)
break;
}
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1 = nr_calculate_t1(sk);
+ sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
nr_set_timer(sk);
}
diff --git a/net/netrom/sysctl_net_netrom.c b/net/netrom/sysctl_net_netrom.c
index 3cbc0b761..2502885a3 100644
--- a/net/netrom/sysctl_net_netrom.c
+++ b/net/netrom/sysctl_net_netrom.c
@@ -16,18 +16,18 @@
static int min_quality[] = {0}, max_quality[] = {255};
static int min_obs[] = {0}, max_obs[] = {255};
static int min_ttl[] = {0}, max_ttl[] = {255};
-static int min_t1[] = {5 * PR_SLOWHZ};
-static int max_t1[] = {600 * PR_SLOWHZ};
+static int min_t1[] = {5 * NR_SLOWHZ};
+static int max_t1[] = {600 * NR_SLOWHZ};
static int min_n2[] = {2}, max_n2[] = {127};
-static int min_t2[] = {1 * PR_SLOWHZ};
-static int max_t2[] = {60 * PR_SLOWHZ};
-static int min_t4[] = {1 * PR_SLOWHZ};
-static int max_t4[] = {1000 * PR_SLOWHZ};
+static int min_t2[] = {1 * NR_SLOWHZ};
+static int max_t2[] = {60 * NR_SLOWHZ};
+static int min_t4[] = {1 * NR_SLOWHZ};
+static int max_t4[] = {1000 * NR_SLOWHZ};
static int min_window[] = {1}, max_window[] = {127};
-static int min_idle[] = {0 * PR_SLOWHZ};
-static int max_idle[] = {65535 * PR_SLOWHZ};
-static int min_n1[] = {1}, max_n1[] = {236};
+static int min_idle[] = {0 * NR_SLOWHZ};
+static int max_idle[] = {65535 * NR_SLOWHZ};
static int min_route[] = {0}, max_route[] = {1};
+static int min_fails[] = {1}, max_fails[] = {10};
static struct ctl_table_header *nr_table_header;
@@ -59,12 +59,12 @@ static ctl_table nr_table[] = {
{NET_NETROM_TRANSPORT_NO_ACTIVITY_TIMEOUT, "transport_no_activity_timeout",
&sysctl_netrom_transport_no_activity_timeout, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle},
- {NET_NETROM_TRANSPORT_PACKET_LENGTH, "transport_packet_length",
- &sysctl_netrom_transport_packet_length, sizeof(int), 0644, NULL,
- &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_n1, &max_n1},
{NET_NETROM_ROUTING_CONTROL, "routing_control",
&sysctl_netrom_routing_control, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+ {NET_NETROM_LINK_FAILS_COUNT, "link_fails_count",
+ &sysctl_netrom_link_fails_count, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_fails, &max_fails},
{0}
};
diff --git a/net/netsyms.c b/net/netsyms.c
index 22f253d63..34946a5b7 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -14,6 +14,7 @@
#include <linux/netdevice.h>
#include <linux/trdevice.h>
#include <linux/ioport.h>
+#include <net/neighbour.h>
#ifdef CONFIG_INET
#include <linux/ip.h>
@@ -25,12 +26,17 @@
#include <net/tcp.h>
#include <net/icmp.h>
#include <net/route.h>
+#include <net/scm.h>
#include <net/inet_common.h>
#include <linux/net_alias.h>
+#include <linux/mroute.h>
+
+extern struct net_proto_family inet_family_ops;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#include <linux/in6.h>
#include <net/ndisc.h>
+#include <net/dst.h>
#include <net/transp_v6.h>
#endif
@@ -44,10 +50,13 @@
#include <linux/net_alias.h>
#endif
-#if defined(CONFIG_ULTRA) || defined(CONFIG_WD80x3) || \
- defined(CONFIG_EL2) || defined(CONFIG_NE2000) || \
- defined(CONFIG_E2100) || defined(CONFIG_HPLAN_PLUS) || \
- defined(CONFIG_HPLAN) || defined(CONFIG_AC3200)
+#include <net/scm.h>
+
+#if defined(CONFIG_ULTRA) || defined(CONFIG_WD80x3) || \
+ defined(CONFIG_EL2) || defined(CONFIG_NE2000) || \
+ defined(CONFIG_E2100) || defined(CONFIG_HPLAN_PLUS) || \
+ defined(CONFIG_HPLAN) || defined(CONFIG_AC3200) || \
+ defined(CONFIG_ES3210)
#include "../drivers/net/8390.h"
#endif
@@ -60,211 +69,264 @@ extern void destroy_EII_client(struct datalink_proto *);
extern void destroy_8023_client(struct datalink_proto *);
#endif
+#ifdef CONFIG_ATALK_MODULE
+#include <net/sock.h>
+#endif
+
extern char *skb_push_errstr;
extern char *skb_put_errstr;
-static struct symbol_table net_syms = {
-#include <linux/symtab_begin.h>
-
- /* Skbuff symbols. */
- X(skb_push_errstr),
- X(skb_put_errstr),
-
- /* Socket layer registration */
- X(sock_register),
- X(sock_unregister),
-
- /* Socket layer support routines */
- X(memcpy_fromiovec),
- X(sock_setsockopt),
- X(sock_getsockopt),
- X(sk_alloc),
- X(sk_free),
- X(sock_wake_async),
- X(sock_alloc_send_skb),
- X(skb_recv_datagram),
- X(skb_free_datagram),
- X(skb_copy_datagram),
- X(skb_copy_datagram_iovec),
- X(datagram_select),
+/* Skbuff symbols. */
+EXPORT_SYMBOL(skb_push_errstr);
+EXPORT_SYMBOL(skb_put_errstr);
+
+/* Socket layer registration */
+EXPORT_SYMBOL(sock_register);
+EXPORT_SYMBOL(sock_unregister);
+
+/* Socket layer support routines */
+EXPORT_SYMBOL(memcpy_fromiovec);
+EXPORT_SYMBOL(sock_create);
+EXPORT_SYMBOL(sock_alloc);
+EXPORT_SYMBOL(sock_release);
+EXPORT_SYMBOL(sock_setsockopt);
+EXPORT_SYMBOL(sock_getsockopt);
+EXPORT_SYMBOL(sock_sendmsg);
+EXPORT_SYMBOL(sock_recvmsg);
+EXPORT_SYMBOL(sk_alloc);
+EXPORT_SYMBOL(sk_free);
+EXPORT_SYMBOL(sock_wake_async);
+EXPORT_SYMBOL(sock_alloc_send_skb);
+EXPORT_SYMBOL(sock_init_data);
+EXPORT_SYMBOL(sock_no_fcntl);
+EXPORT_SYMBOL(sock_no_listen);
+EXPORT_SYMBOL(sock_no_getsockopt);
+EXPORT_SYMBOL(sock_no_setsockopt);
+EXPORT_SYMBOL(sock_rfree);
+EXPORT_SYMBOL(sock_wfree);
+EXPORT_SYMBOL(skb_recv_datagram);
+EXPORT_SYMBOL(skb_free_datagram);
+EXPORT_SYMBOL(skb_copy_datagram);
+EXPORT_SYMBOL(skb_copy_datagram_iovec);
+EXPORT_SYMBOL(skb_realloc_headroom);
+EXPORT_SYMBOL(datagram_poll);
+EXPORT_SYMBOL(put_cmsg);
+
+EXPORT_SYMBOL(neigh_table_init);
+/* Declared in <net/neighbour.h> but not defined?
+ EXPORT_SYMBOL(neigh_table_destroy);
+ EXPORT_SYMBOL(neigh_table_run_bh);
+*/
+EXPORT_SYMBOL(neigh_alloc);
+EXPORT_SYMBOL(neigh_table_ins);
+EXPORT_SYMBOL(neigh_queue_ins);
+EXPORT_SYMBOL(neigh_unlink);
+EXPORT_SYMBOL(neigh_lookup);
+EXPORT_SYMBOL(ntbl_walk_table);
+EXPORT_SYMBOL(neigh_tbl_run_bh);
+
+/* dst_entry */
+EXPORT_SYMBOL(dst_alloc);
+EXPORT_SYMBOL(__dst_free);
+EXPORT_SYMBOL(dst_total);
+
+/* Needed by smbfs.o */
+EXPORT_SYMBOL(__scm_destroy);
+EXPORT_SYMBOL(__scm_send);
#ifdef CONFIG_IPX_MODULE
- X(make_8023_client),
- X(destroy_8023_client),
- X(make_EII_client),
- X(destroy_EII_client),
+EXPORT_SYMBOL(make_8023_client);
+EXPORT_SYMBOL(destroy_8023_client);
+EXPORT_SYMBOL(make_EII_client);
+EXPORT_SYMBOL(destroy_EII_client);
#endif
-#ifdef CONFIG_INET
- /* Internet layer registration */
- X(get_new_socknum),
- X(inet_add_protocol),
- X(inet_del_protocol),
- X(rarp_ioctl_hook),
- X(init_etherdev),
- X(ip_rt_route),
- X(icmp_send),
- X(ip_options_compile),
- X(ip_rt_put),
- X(arp_send),
- X(ip_id_count),
- X(ip_send_check),
-#ifdef CONFIG_IP_FORWARD
- X(ip_forward),
+#ifdef CONFIG_ATALK_MODULE
+EXPORT_SYMBOL(sklist_destroy_socket);
+EXPORT_SYMBOL(sklist_insert_socket);
+#endif
+
+#ifdef CONFIG_SMB_FS_MODULE
+EXPORT_SYMBOL(scm_detach_fds);
#endif
+#ifdef CONFIG_INET
+/* Internet layer registration */
+EXPORT_SYMBOL(inet_add_protocol);
+EXPORT_SYMBOL(inet_del_protocol);
+EXPORT_SYMBOL(rarp_ioctl_hook);
+EXPORT_SYMBOL(init_etherdev);
+EXPORT_SYMBOL(ip_route_output);
+EXPORT_SYMBOL(icmp_send);
+EXPORT_SYMBOL(ip_options_compile);
+EXPORT_SYMBOL(ip_rt_put);
+EXPORT_SYMBOL(arp_send);
+EXPORT_SYMBOL(ip_id_count);
+EXPORT_SYMBOL(ip_send_check);
+EXPORT_SYMBOL(ip_fragment);
+EXPORT_SYMBOL(ip_dev_find_tunnel);
+EXPORT_SYMBOL(inet_family_ops);
+
#ifdef CONFIG_IPV6_MODULE
- /* inet functions common to v4 and v6 */
- X(inet_proto_ops),
- X(inet_remove_sock),
- X(inet_release),
- X(inet_connect),
- X(inet_accept),
- X(inet_select),
- X(inet_listen),
- X(inet_shutdown),
- X(inet_setsockopt),
- X(inet_getsockopt),
- X(inet_fcntl),
- X(inet_sendmsg),
- X(inet_recvmsg),
- X(tcp_sock_array),
- X(udp_sock_array),
- X(destroy_sock),
- X(ip_queue_xmit),
- X(csum_partial),
- X(ip_my_addr),
- X(skb_copy),
- X(dev_lockct),
- X(ndisc_eth_hook),
- X(memcpy_fromiovecend),
- X(csum_partial_copy),
- X(csum_partial_copy_fromiovecend),
- X(__release_sock),
- X(net_timer),
- X(inet_put_sock),
- /* UDP/TCP exported functions for TCPv6 */
- X(udp_ioctl),
- X(udp_connect),
- X(udp_sendmsg),
- X(tcp_cache_zap),
- X(tcp_close),
- X(tcp_accept),
- X(tcp_write_wakeup),
- X(tcp_read_wakeup),
- X(tcp_select),
- X(tcp_ioctl),
- X(tcp_shutdown),
- X(tcp_setsockopt),
- X(tcp_getsockopt),
- X(tcp_recvmsg),
- X(tcp_send_synack),
- X(sock_wfree),
- X(sock_wmalloc),
- X(tcp_reset_xmit_timer),
- X(tcp_parse_options),
- X(tcp_rcv_established),
- X(tcp_init_xmit_timers),
- X(tcp_clear_xmit_timers),
- X(tcp_slt_array),
- X(__tcp_inc_slow_timer),
- X(tcp_statistics),
- X(tcp_rcv_state_process),
- X(tcp_do_sendmsg),
- X(tcp_v4_build_header),
- X(tcp_v4_rebuild_header),
- X(tcp_v4_send_check),
- X(tcp_v4_conn_request),
- X(tcp_v4_syn_recv_sock),
- X(tcp_v4_backlog_rcv),
- X(tcp_v4_connect),
- X(ip_chk_addr),
- X(net_reset_timer),
- X(net_delete_timer),
- X(udp_prot),
- X(tcp_prot),
- X(ipv4_specific),
+/* inet functions common to v4 and v6 */
+EXPORT_SYMBOL(inet_stream_ops);
+EXPORT_SYMBOL(inet_dgram_ops);
+EXPORT_SYMBOL(inet_release);
+EXPORT_SYMBOL(inet_stream_connect);
+EXPORT_SYMBOL(inet_dgram_connect);
+EXPORT_SYMBOL(inet_accept);
+EXPORT_SYMBOL(inet_poll);
+EXPORT_SYMBOL(inet_listen);
+EXPORT_SYMBOL(inet_shutdown);
+EXPORT_SYMBOL(inet_setsockopt);
+EXPORT_SYMBOL(inet_getsockopt);
+EXPORT_SYMBOL(inet_sendmsg);
+EXPORT_SYMBOL(inet_recvmsg);
+
+/* Socket demultiplexing. */
+EXPORT_SYMBOL(tcp_good_socknum);
+EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_listening_hash);
+EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(udp_good_socknum);
+EXPORT_SYMBOL(udp_hash);
+
+EXPORT_SYMBOL(destroy_sock);
+EXPORT_SYMBOL(ip_queue_xmit);
+EXPORT_SYMBOL(csum_partial);
+EXPORT_SYMBOL(dev_lockct);
+EXPORT_SYMBOL(memcpy_fromiovecend);
+EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
+EXPORT_SYMBOL(__release_sock);
+EXPORT_SYMBOL(net_timer);
+/* UDP/TCP exported functions for TCPv6 */
+EXPORT_SYMBOL(udp_ioctl);
+EXPORT_SYMBOL(udp_connect);
+EXPORT_SYMBOL(udp_sendmsg);
+EXPORT_SYMBOL(tcp_close);
+EXPORT_SYMBOL(tcp_accept);
+EXPORT_SYMBOL(tcp_write_wakeup);
+EXPORT_SYMBOL(tcp_read_wakeup);
+EXPORT_SYMBOL(tcp_poll);
+EXPORT_SYMBOL(tcp_ioctl);
+EXPORT_SYMBOL(tcp_shutdown);
+EXPORT_SYMBOL(tcp_setsockopt);
+EXPORT_SYMBOL(tcp_getsockopt);
+EXPORT_SYMBOL(tcp_recvmsg);
+EXPORT_SYMBOL(tcp_send_synack);
+EXPORT_SYMBOL(sock_wmalloc);
+EXPORT_SYMBOL(tcp_reset_xmit_timer);
+EXPORT_SYMBOL(tcp_parse_options);
+EXPORT_SYMBOL(tcp_rcv_established);
+EXPORT_SYMBOL(tcp_init_xmit_timers);
+EXPORT_SYMBOL(tcp_clear_xmit_timers);
+EXPORT_SYMBOL(tcp_slt_array);
+EXPORT_SYMBOL(__tcp_inc_slow_timer);
+EXPORT_SYMBOL(tcp_statistics);
+EXPORT_SYMBOL(tcp_rcv_state_process);
+EXPORT_SYMBOL(tcp_do_sendmsg);
+EXPORT_SYMBOL(tcp_v4_build_header);
+EXPORT_SYMBOL(tcp_v4_rebuild_header);
+EXPORT_SYMBOL(tcp_v4_send_check);
+EXPORT_SYMBOL(tcp_v4_conn_request);
+EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
+EXPORT_SYMBOL(tcp_v4_do_rcv);
+EXPORT_SYMBOL(tcp_v4_connect);
+EXPORT_SYMBOL(__ip_chk_addr);
+EXPORT_SYMBOL(net_reset_timer);
+EXPORT_SYMBOL(net_delete_timer);
+EXPORT_SYMBOL(udp_prot);
+EXPORT_SYMBOL(tcp_prot);
+EXPORT_SYMBOL(tcp_openreq_cachep);
+EXPORT_SYMBOL(ipv4_specific);
#endif
#if defined(CONFIG_ULTRA) || defined(CONFIG_WD80x3) || \
defined(CONFIG_EL2) || defined(CONFIG_NE2000) || \
defined(CONFIG_E2100) || defined(CONFIG_HPLAN_PLUS) || \
- defined(CONFIG_HPLAN) || defined(CONFIG_AC3200)
- /* If 8390 NIC support is built in, we will need these. */
- X(ei_open),
- X(ei_close),
- X(ei_debug),
- X(ei_interrupt),
- X(ethdev_init),
- X(NS8390_init),
+ defined(CONFIG_HPLAN) || defined(CONFIG_AC3200) || \
+ defined(CONFIG_ES3210)
+/* If 8390 NIC support is built in, we will need these. */
+EXPORT_SYMBOL(ei_open);
+EXPORT_SYMBOL(ei_close);
+EXPORT_SYMBOL(ei_debug);
+EXPORT_SYMBOL(ei_interrupt);
+EXPORT_SYMBOL(ethdev_init);
+EXPORT_SYMBOL(NS8390_init);
#endif
#ifdef CONFIG_TR
- X(tr_setup),
- X(tr_type_trans),
+EXPORT_SYMBOL(tr_setup);
+EXPORT_SYMBOL(tr_type_trans);
+EXPORT_SYMBOL(register_trdev);
+EXPORT_SYMBOL(unregister_trdev);
+EXPORT_SYMBOL(init_trdev);
+EXPORT_SYMBOL(tr_freedev);
#endif
-
+
#ifdef CONFIG_NET_ALIAS
#include <linux/net_alias.h>
#endif
+/* Used by at least ipip.c. */
+EXPORT_SYMBOL(ipv4_config);
+#ifdef CONFIG_IP_MROUTE
+EXPORT_SYMBOL(ip_mr_find_tunnel);
+#endif
+
#endif /* CONFIG_INET */
- /* Device callback registration */
- X(register_netdevice_notifier),
- X(unregister_netdevice_notifier),
+/* Device callback registration */
+EXPORT_SYMBOL(register_netdevice_notifier);
+EXPORT_SYMBOL(unregister_netdevice_notifier);
#ifdef CONFIG_NET_ALIAS
- X(register_net_alias_type),
- X(unregister_net_alias_type),
+EXPORT_SYMBOL(register_net_alias_type);
+EXPORT_SYMBOL(unregister_net_alias_type);
#endif
- /* support for loadable net drivers */
-#ifdef CONFIG_INET
- X(register_netdev),
- X(unregister_netdev),
- X(ether_setup),
- X(eth_type_trans),
- X(eth_copy_and_sum),
- X(arp_query),
- X(alloc_skb),
- X(kfree_skb),
- X(skb_clone),
- X(dev_alloc_skb),
- X(dev_kfree_skb),
- X(skb_device_unlock),
- X(skb_device_locked),
- X(netif_rx),
- X(dev_tint),
- X(irq2dev_map),
- X(dev_add_pack),
- X(dev_remove_pack),
- X(dev_get),
- X(dev_ioctl),
- X(dev_queue_xmit),
- X(dev_base),
- X(dev_close),
- X(dev_mc_add),
- X(arp_find),
- X(n_tty_ioctl),
- X(tty_register_ldisc),
- X(kill_fasync),
- X(arp_query),
- X(ip_rcv),
- X(arp_rcv),
-#endif /* CONFIG_INET */
+/* support for loadable net drivers */
+#ifdef CONFIG_NET
+EXPORT_SYMBOL(register_netdev);
+EXPORT_SYMBOL(unregister_netdev);
+EXPORT_SYMBOL(ether_setup);
+EXPORT_SYMBOL(dev_new_index);
+EXPORT_SYMBOL(dev_get_by_index);
+EXPORT_SYMBOL(eth_type_trans);
+EXPORT_SYMBOL(eth_copy_and_sum);
+EXPORT_SYMBOL(alloc_skb);
+EXPORT_SYMBOL(__kfree_skb);
+EXPORT_SYMBOL(skb_clone);
+EXPORT_SYMBOL(skb_copy);
+EXPORT_SYMBOL(dev_alloc_skb);
+EXPORT_SYMBOL(netif_rx);
+EXPORT_SYMBOL(dev_tint);
+EXPORT_SYMBOL(irq2dev_map);
+EXPORT_SYMBOL(dev_add_pack);
+EXPORT_SYMBOL(dev_remove_pack);
+EXPORT_SYMBOL(dev_get);
+EXPORT_SYMBOL(dev_alloc);
+EXPORT_SYMBOL(dev_alloc_name);
+EXPORT_SYMBOL(dev_ioctl);
+EXPORT_SYMBOL(dev_queue_xmit);
+#ifdef CONFIG_IP_ACCT
+EXPORT_SYMBOL(ip_acct_output);
+#endif
+EXPORT_SYMBOL(dev_base);
+EXPORT_SYMBOL(dev_close);
+EXPORT_SYMBOL(dev_mc_add);
+EXPORT_SYMBOL(arp_find);
+EXPORT_SYMBOL(arp_find_1);
+EXPORT_SYMBOL(n_tty_ioctl);
+EXPORT_SYMBOL(tty_register_ldisc);
+EXPORT_SYMBOL(kill_fasync);
+EXPORT_SYMBOL(ip_rcv);
+EXPORT_SYMBOL(arp_rcv);
+#endif /* CONFIG_NET */
#ifdef CONFIG_NETLINK
- X(netlink_attach),
- X(netlink_detach),
- X(netlink_donothing),
- X(netlink_post),
+EXPORT_SYMBOL(netlink_attach);
+EXPORT_SYMBOL(netlink_detach);
+EXPORT_SYMBOL(netlink_donothing);
+EXPORT_SYMBOL(netlink_post);
#endif /* CONFIG_NETLINK */
-
-#include <linux/symtab_end.h>
-};
-
-void export_net_symbols(void)
-{
- register_symtab(&net_syms);
-}
diff --git a/net/protocols.c b/net/protocols.c
index d5090bc47..24e67cde1 100644
--- a/net/protocols.c
+++ b/net/protocols.c
@@ -13,6 +13,7 @@
#define CONFIG_UNIX /* always present... */
#ifdef CONFIG_UNIX
+#include <linux/un.h>
#include <net/af_unix.h>
#endif
@@ -24,13 +25,18 @@ extern void inet6_proto_init(struct net_proto *pro);
#endif /* INET */
#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
+#define NEED_802
#include <net/ipxcall.h>
-#include <net/p8022call.h>
-#include <net/p8022trcall.h>
#endif
+
#ifdef CONFIG_X25
#include <net/x25call.h>
#endif
+
+#ifdef CONFIG_LAPB
+#include <net/lapbcall.h>
+#endif
+
#ifdef CONFIG_AX25
#include <net/ax25call.h>
#ifdef CONFIG_NETROM
@@ -40,19 +46,35 @@ extern void inet6_proto_init(struct net_proto *pro);
#include <net/rosecall.h>
#endif
#endif
+
#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
-#if ! ( defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) )
-#include <net/p8022call.h>
-#include <net/p8022trcall.h>
-#endif
+#define NEED_802
#include <net/atalkcall.h>
#endif
+
+#if defined(CONFIG_NETBEUI)
+#define NEED_LLC
+#include <net/netbeuicall.h>
+#endif
+
#include <net/psnapcall.h>
+
#ifdef CONFIG_TR
#include <linux/netdevice.h>
#include <linux/trdevice.h>
extern void rif_init(struct net_proto *);
#endif
+
+#ifdef NEED_LLC
+#define NEED_802
+#include <net/llccall.h>
+#endif
+
+#ifdef NEED_802
+#include <net/p8022call.h>
+#include <net/p8022trcall.h>
+#endif
+
/*
* Protocol Table
*/
@@ -61,15 +83,21 @@ struct net_proto protocols[] = {
#ifdef CONFIG_UNIX
{ "UNIX", unix_proto_init }, /* Unix domain socket family */
#endif
-#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) || \
- defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
+
+#ifdef NEED_802
{ "802.2", p8022_proto_init }, /* 802.2 demultiplexor */
{ "802.2TR", p8022tr_proto_init }, /* 802.2 demultiplexor */
{ "SNAP", snap_proto_init }, /* SNAP demultiplexor */
#endif
+
#ifdef CONFIG_TR
{ "RIF", rif_init }, /* RIF for Token ring */
#endif
+
+#ifdef NEED_LLC
+ { "802.2LLC", llc_init }, /* 802.2 LLC */
+#endif
+
#ifdef CONFIG_AX25
{ "AX.25", ax25_proto_init }, /* Amateur Radio AX.25 */
#ifdef CONFIG_NETROM
@@ -79,20 +107,29 @@ struct net_proto protocols[] = {
{ "Rose", rose_proto_init }, /* Amateur Radio X.25 PLP */
#endif
#endif
+
#ifdef CONFIG_INET
{ "INET", inet_proto_init }, /* TCP/IP */
#ifdef CONFIG_IPV6
{ "INET6", inet6_proto_init}, /* IPv6 */
#endif
#endif
+
#ifdef CONFIG_IPX
{ "IPX", ipx_proto_init }, /* IPX */
#endif
+
#ifdef CONFIG_ATALK
{ "DDP", atalk_proto_init }, /* Netatalk Appletalk driver */
#endif
+
+#ifdef CONFIG_LAPB
+ { "LAPB", lapb_proto_init }, /* LAPB protocols */
+#endif
+
#ifdef CONFIG_X25
{ "X.25", x25_proto_init }, /* CCITT X.25 Packet Layer */
#endif
+
{ NULL, NULL } /* End marker */
};
diff --git a/net/rose/Makefile b/net/rose/Makefile
index 0d71de9cf..7eb55881e 100644
--- a/net/rose/Makefile
+++ b/net/rose/Makefile
@@ -8,9 +8,13 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := rose.o
-O_OBJS := af_rose.o sysctl_net_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
+O_OBJS := af_rose.o rose_dev.o rose_in.o rose_link.o rose_out.o rose_route.o rose_subr.o rose_timer.o
M_OBJS := $(O_TARGET)
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_rose.o
+endif
+
include $(TOPDIR)/Rules.make
tar:
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 4b8acd3f8..f173dedaf 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -14,10 +14,14 @@
*
* History
* Rose 001 Jonathan(G4KLX) Cloned from af_netrom.c.
+ * Alan(GW4PTS) Hacked up for newer API stuff
+ * Terry (VK2KTJ) Added support for variable length
+ * address masks.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
@@ -54,12 +58,18 @@ int sysctl_rose_call_request_timeout = ROSE_DEFAULT_T1;
int sysctl_rose_reset_request_timeout = ROSE_DEFAULT_T2;
int sysctl_rose_clear_request_timeout = ROSE_DEFAULT_T3;
int sysctl_rose_no_activity_timeout = ROSE_DEFAULT_IDLE;
-int sysctl_rose_routing_control = 1;
+int sysctl_rose_ack_hold_back_timeout = ROSE_DEFAULT_HB;
+int sysctl_rose_routing_control = ROSE_DEFAULT_ROUTING;
+int sysctl_rose_link_fail_timeout = ROSE_DEFAULT_FAIL_TIMEOUT;
static unsigned int lci = 1;
static struct sock *volatile rose_list = NULL;
+static struct proto_ops rose_proto_ops;
+
+ax25_address rose_callsign;
+
/*
* Convert a Rose address into text.
*/
@@ -92,10 +102,67 @@ int rosecmp(rose_address *addr1, rose_address *addr2)
for (i = 0; i < 5; i++)
if (addr1->rose_addr[i] != addr2->rose_addr[i])
return 1;
-
+
+ return 0;
+}
+
+/*
+ * Compare two Rose addresses for only mask digits, 0 == equal.
+ */
+int rosecmpm(rose_address *addr1, rose_address *addr2, unsigned short mask)
+{
+ int i, j;
+
+ if (mask > 10)
+ return 1;
+
+ for (i = 0; i < mask; i++) {
+ j = i / 2;
+
+ if ((i % 2) != 0) {
+ if ((addr1->rose_addr[j] & 0x0F) != (addr2->rose_addr[j] & 0x0F))
+ return 1;
+ } else {
+ if ((addr1->rose_addr[j] & 0xF0) != (addr2->rose_addr[j] & 0xF0))
+ return 1;
+ }
+ }
+
return 0;
}
+static void rose_free_sock(struct sock *sk)
+{
+ kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
+
+ sk_free(sk);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static struct sock *rose_alloc_sock(void)
+{
+ struct sock *sk;
+ rose_cb *rose;
+
+ if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ memset(rose, 0x00, sizeof(*rose));
+
+ sk->protinfo.rose = rose;
+ rose->sk = sk;
+
+ return sk;
+}
+
/*
* Socket removal during an interrupt is now safe.
*/
@@ -103,7 +170,7 @@ static void rose_remove_socket(struct sock *sk)
{
struct sock *s;
unsigned long flags;
-
+
save_flags(flags);
cli();
@@ -155,11 +222,11 @@ static int rose_device_event(struct notifier_block *this, unsigned long event, v
if (event != NETDEV_DOWN)
return NOTIFY_DONE;
-
+
rose_kill_by_device(dev);
rose_rt_device_down(dev);
rose_link_device_down(dev);
-
+
return NOTIFY_DONE;
}
@@ -173,8 +240,8 @@ static void rose_insert_socket(struct sock *sk)
save_flags(flags);
cli();
- sk->next = rose_list;
- rose_list = sk;
+ sk->next = rose_list;
+ rose_list = sk;
restore_flags(flags);
}
@@ -271,15 +338,15 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time
{
struct sk_buff *skb;
unsigned long flags;
-
+
save_flags(flags);
cli();
-
+
del_timer(&sk->timer);
-
+
rose_remove_socket(sk);
rose_clear_queues(sk); /* Flush the queues */
-
+
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
if (skb->sk != sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
@@ -289,16 +356,16 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time
kfree_skb(skb, FREE_READ);
}
-
- if (sk->wmem_alloc || sk->rmem_alloc) { /* Defer: outstanding buffers */
+
+ if (atomic_read(&sk->wmem_alloc) != 0 || atomic_read(&sk->rmem_alloc) != 0) {
+ /* Defer: outstanding buffers */
init_timer(&sk->timer);
sk->timer.expires = jiffies + 10 * HZ;
sk->timer.function = rose_destroy_timer;
sk->timer.data = (unsigned long)sk;
add_timer(&sk->timer);
} else {
- kfree_s(sk->protinfo.rose, sizeof(*sk->protinfo.rose));
- sk_free(sk);
+ rose_free_sock(sk);
}
restore_flags(flags);
@@ -308,11 +375,6 @@ void rose_destroy_socket(struct sock *sk) /* Not static as it's used by the time
* Handling for system calls applied via the various interfaces to a
* Rose socket object.
*/
-
-static int rose_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- return -EINVAL;
-}
/*
* dl1bke 960311: set parameters for existing Rose connections,
@@ -352,21 +414,11 @@ static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
rose_set_timer(sk);
break;
- case ROSE_T0:
- if (rose_ctl.arg < 1)
- return -EINVAL;
- if (sk->protinfo.rose->neighbour != NULL) {
- save_flags(flags); cli();
- sk->protinfo.rose->neighbour->t0 = rose_ctl.arg * PR_SLOWHZ;
- restore_flags(flags);
- }
- break;
-
case ROSE_T1:
if (rose_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.rose->t1 = rose_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.rose->t1 = rose_ctl.arg * ROSE_SLOWHZ;
restore_flags(flags);
break;
@@ -374,7 +426,7 @@ static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
if (rose_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.rose->t2 = rose_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.rose->t2 = rose_ctl.arg * ROSE_SLOWHZ;
restore_flags(flags);
break;
@@ -382,7 +434,15 @@ static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
if (rose_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.rose->t3 = rose_ctl.arg * PR_SLOWHZ;
+ sk->protinfo.rose->t3 = rose_ctl.arg * ROSE_SLOWHZ;
+ restore_flags(flags);
+ break;
+
+ case ROSE_HOLDBACK:
+ if (rose_ctl.arg < 1)
+ return -EINVAL;
+ save_flags(flags); cli();
+ sk->protinfo.rose->hb = rose_ctl.arg * ROSE_SLOWHZ;
restore_flags(flags);
break;
@@ -390,7 +450,7 @@ static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
if (rose_ctl.arg < 1)
return -EINVAL;
save_flags(flags); cli();
- sk->protinfo.rose->idle = rose_ctl.arg * 60 * PR_SLOWHZ;
+ sk->protinfo.rose->idle = rose_ctl.arg * 60 * ROSE_SLOWHZ;
restore_flags(flags);
break;
@@ -404,57 +464,49 @@ static int rose_ctl_ioctl(const unsigned int cmd, void *arg)
static int rose_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
- struct sock *sk;
- int err, opt;
-
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_setsockopt(sk, level, optname, optval, optlen);
+ struct sock *sk = sock->sk;
+ int opt;
if (level != SOL_ROSE)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
- if (optval == NULL)
+ if (optlen < sizeof(int))
return -EINVAL;
- if ((err = verify_area(VERIFY_READ, optval, sizeof(int))) != 0)
- return err;
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
- get_user(opt, (int *)optval);
-
switch (optname) {
- case ROSE_T0:
+ case ROSE_T1:
if (opt < 1)
return -EINVAL;
- if (sk->protinfo.rose->neighbour != NULL)
- sk->protinfo.rose->neighbour->t0 = opt * PR_SLOWHZ;
+ sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ;
return 0;
- case ROSE_T1:
+ case ROSE_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t1 = opt * PR_SLOWHZ;
+ sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ;
return 0;
- case ROSE_T2:
+ case ROSE_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t2 = opt * PR_SLOWHZ;
+ sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ;
return 0;
-
- case ROSE_T3:
+
+ case ROSE_HOLDBACK:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t3 = opt * PR_SLOWHZ;
+ sk->protinfo.rose->hb = opt * ROSE_SLOWHZ;
return 0;
-
+
case ROSE_IDLE:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->idle = opt * 60 * PR_SLOWHZ;
+ sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ;
return 0;
-
+
case ROSE_HDRINCL:
sk->protinfo.rose->hdrincl = opt ? 1 : 0;
return 0;
@@ -467,42 +519,37 @@ static int rose_setsockopt(struct socket *sock, int level, int optname,
static int rose_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
int val = 0;
- int err;
+ int len;
- sk = (struct sock *)sock->data;
-
- if (level == SOL_SOCKET)
- return sock_getsockopt(sk, level, optname, optval, optlen);
-
if (level != SOL_ROSE)
- return -EOPNOTSUPP;
+ return -ENOPROTOOPT;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
switch (optname) {
- case ROSE_T0:
- if (sk->protinfo.rose->neighbour != NULL)
- val = sk->protinfo.rose->neighbour->t0 / PR_SLOWHZ;
- else
- val = sysctl_rose_restart_request_timeout / PR_SLOWHZ;
- break;
-
case ROSE_T1:
- val = sk->protinfo.rose->t1 / PR_SLOWHZ;
+ val = sk->protinfo.rose->t1 / ROSE_SLOWHZ;
break;
-
+
case ROSE_T2:
- val = sk->protinfo.rose->t2 / PR_SLOWHZ;
+ val = sk->protinfo.rose->t2 / ROSE_SLOWHZ;
break;
-
+
case ROSE_T3:
- val = sk->protinfo.rose->t3 / PR_SLOWHZ;
+ val = sk->protinfo.rose->t3 / ROSE_SLOWHZ;
break;
-
+
+ case ROSE_HOLDBACK:
+ val = sk->protinfo.rose->hb / ROSE_SLOWHZ;
+ break;
+
case ROSE_IDLE:
- val = sk->protinfo.rose->idle / (PR_SLOWHZ * 60);
+ val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60);
break;
-
+
case ROSE_HDRINCL:
val = sk->protinfo.rose->hdrincl;
break;
@@ -511,22 +558,20 @@ static int rose_getsockopt(struct socket *sock, int level, int optname,
return -ENOPROTOOPT;
}
- if ((err = verify_area(VERIFY_WRITE, optlen, sizeof(int))) != 0)
- return err;
-
- put_user(sizeof(int), (unsigned long *)optlen);
+ len = min(len, sizeof(int));
- if ((err = verify_area(VERIFY_WRITE, optval, sizeof(int))) != 0)
- return err;
+ if (put_user(len, optlen))
+ return -EFAULT;
- put_user(val, (unsigned long *)optval);
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
return 0;
}
static int rose_listen(struct socket *sock, int backlog)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk->state != TCP_LISTEN) {
sk->protinfo.rose->dest_ndigis = 0;
@@ -541,18 +586,6 @@ static int rose_listen(struct socket *sock, int backlog)
return -EOPNOTSUPP;
}
-static void def_callback1(struct sock *sk)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
-static void def_callback2(struct sock *sk, int len)
-{
- if (!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
-
static int rose_create(struct socket *sock, int protocol)
{
struct sock *sk;
@@ -561,77 +594,26 @@ static int rose_create(struct socket *sock, int protocol)
if (sock->type != SOCK_SEQPACKET || protocol != 0)
return -ESOCKTNOSUPPORT;
- if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+ if ((sk = rose_alloc_sock()) == NULL)
return -ENOMEM;
- if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return -ENOMEM;
- }
+ rose = sk->protinfo.rose;
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- init_timer(&sk->timer);
-
- sk->socket = sock;
- sk->type = sock->type;
- sk->protocol = protocol;
- sk->allocation = GFP_KERNEL;
- sk->rcvbuf = SK_RMEM_MAX;
- sk->sndbuf = SK_WMEM_MAX;
- sk->state = TCP_CLOSE;
- sk->priority = SOPRI_NORMAL;
- sk->mtu = ROSE_MTU; /* 128 */
- sk->zapped = 1;
- sk->window = ROSE_DEFAULT_WINDOW;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
-
- if (sock != NULL) {
- sock->data = (void *)sk;
- sk->sleep = sock->wait;
- }
+ sock_init_data(sock, sk);
+
+ sock->ops = &rose_proto_ops;
+ sk->protocol = protocol;
+ sk->mtu = ROSE_MTU; /* 128 */
- skb_queue_head_init(&rose->ack_queue);
skb_queue_head_init(&rose->frag_queue);
- rose->lci = 0;
-
- rose->t1 = sysctl_rose_call_request_timeout;
- rose->t2 = sysctl_rose_reset_request_timeout;
- rose->t3 = sysctl_rose_clear_request_timeout;
- rose->idle = sysctl_rose_no_activity_timeout;
-
- rose->timer = 0;
-
- rose->va = 0;
- rose->vr = 0;
- rose->vs = 0;
- rose->vl = 0;
+ rose->t1 = sysctl_rose_call_request_timeout;
+ rose->t2 = sysctl_rose_reset_request_timeout;
+ rose->t3 = sysctl_rose_clear_request_timeout;
+ rose->hb = sysctl_rose_ack_hold_back_timeout;
+ rose->idle = sysctl_rose_no_activity_timeout;
- rose->fraglen = 0;
- rose->hdrincl = 0;
- rose->state = ROSE_STATE_0;
- rose->neighbour = NULL;
- rose->device = NULL;
-
- rose->source_ndigis = 0;
- rose->dest_ndigis = 0;
-
- memset(&rose->source_addr, '\0', ROSE_ADDR_LEN);
- memset(&rose->dest_addr, '\0', ROSE_ADDR_LEN);
- memset(&rose->source_call, '\0', AX25_ADDR_LEN);
- memset(&rose->dest_call, '\0', AX25_ADDR_LEN);
- memset(&rose->source_digi, '\0', AX25_ADDR_LEN);
- memset(&rose->dest_digi, '\0', AX25_ADDR_LEN);
-
- rose->sk = sk;
- sk->protinfo.rose = rose;
+ rose->state = ROSE_STATE_0;
return 0;
}
@@ -644,73 +626,52 @@ static struct sock *rose_make_new(struct sock *osk)
if (osk->type != SOCK_SEQPACKET)
return NULL;
- if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+ if ((sk = rose_alloc_sock()) == NULL)
return NULL;
- if ((rose = (rose_cb *)kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return NULL;
- }
+ rose = sk->protinfo.rose;
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->back_log);
-
- init_timer(&sk->timer);
-
- sk->type = osk->type;
- sk->socket = osk->socket;
- sk->priority = osk->priority;
- sk->protocol = osk->protocol;
- sk->rcvbuf = osk->rcvbuf;
- sk->sndbuf = osk->sndbuf;
- sk->debug = osk->debug;
- sk->state = TCP_ESTABLISHED;
- sk->window = osk->window;
- sk->mtu = osk->mtu;
- sk->sleep = osk->sleep;
- sk->zapped = osk->zapped;
-
- sk->state_change = def_callback1;
- sk->data_ready = def_callback2;
- sk->write_space = def_callback1;
- sk->error_report = def_callback1;
-
- skb_queue_head_init(&rose->ack_queue);
- skb_queue_head_init(&rose->frag_queue);
+ sock_init_data(NULL, sk);
- rose->t1 = osk->protinfo.rose->t1;
- rose->t2 = osk->protinfo.rose->t2;
- rose->t3 = osk->protinfo.rose->t3;
- rose->idle = osk->protinfo.rose->idle;
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->mtu = osk->mtu;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
- rose->device = osk->protinfo.rose->device;
- rose->hdrincl = osk->protinfo.rose->hdrincl;
- rose->fraglen = 0;
+ skb_queue_head_init(&rose->frag_queue);
- rose->timer = 0;
+ rose->t1 = osk->protinfo.rose->t1;
+ rose->t2 = osk->protinfo.rose->t2;
+ rose->t3 = osk->protinfo.rose->t3;
+ rose->hb = osk->protinfo.rose->hb;
+ rose->idle = osk->protinfo.rose->idle;
- rose->va = 0;
- rose->vr = 0;
- rose->vs = 0;
- rose->vl = 0;
-
- sk->protinfo.rose = rose;
- rose->sk = sk;
+ rose->device = osk->protinfo.rose->device;
+ rose->hdrincl = osk->protinfo.rose->hdrincl;
return sk;
}
static int rose_dup(struct socket *newsock, struct socket *oldsock)
{
- struct sock *sk = (struct sock *)oldsock->data;
+ struct sock *sk = oldsock->sk;
+
+ if (sk == NULL || newsock == NULL)
+ return -EINVAL;
return rose_create(newsock, sk->protocol);
}
static int rose_release(struct socket *sock, struct socket *peer)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
if (sk == NULL) return 0;
@@ -724,15 +685,6 @@ static int rose_release(struct socket *sock, struct socket *peer)
rose_destroy_socket(sk);
break;
- case ROSE_STATE_1:
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- rose_destroy_socket(sk);
- break;
-
case ROSE_STATE_2:
sk->protinfo.rose->state = ROSE_STATE_0;
sk->state = TCP_CLOSE;
@@ -742,6 +694,7 @@ static int rose_release(struct socket *sock, struct socket *peer)
rose_destroy_socket(sk);
break;
+ case ROSE_STATE_1:
case ROSE_STATE_3:
case ROSE_STATE_4:
rose_clear_queues(sk);
@@ -759,7 +712,7 @@ static int rose_release(struct socket *sock, struct socket *peer)
break;
}
- sock->data = NULL;
+ sock->sk = NULL;
sk->socket = NULL; /* Not used, but we should do this. **/
return 0;
@@ -767,22 +720,22 @@ static int rose_release(struct socket *sock, struct socket *peer)
static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
struct device *dev;
ax25_address *user, *source;
-
- sk = (struct sock *)sock->data;
if (sk->zapped == 0)
return -EINVAL;
-
+
if (addr_len != sizeof(struct sockaddr_rose))
return -EINVAL;
+ if (addr->srose_family != AF_ROSE)
+ return -EINVAL;
+
if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
- if (sk->debug)
- printk("Rose: bind failed: invalid address\n");
+ SOCK_DEBUG(sk, "Rose: bind failed: invalid address\n");
return -EADDRNOTAVAIL;
}
@@ -806,39 +759,39 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
rose_insert_socket(sk);
sk->zapped = 0;
-
- if (sk->debug)
- printk("Rose: socket is bound\n");
-
+ SOCK_DEBUG(sk, "Rose: socket is bound\n");
return 0;
}
static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
ax25_address *user;
struct device *dev;
-
+
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
sock->state = SS_CONNECTED;
return 0; /* Connect completed during a ERESTARTSYS event */
}
-
+
if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
sock->state = SS_UNCONNECTED;
return -ECONNREFUSED;
}
-
+
if (sk->state == TCP_ESTABLISHED)
return -EISCONN; /* No reconnect on a seqpacket socket */
-
- sk->state = TCP_CLOSE;
+
+ sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED;
if (addr_len != sizeof(struct sockaddr_rose))
return -EINVAL;
+ if (addr->srose_family != AF_ROSE)
+ return -EINVAL;
+
if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL)
return -ENETUNREACH;
@@ -875,11 +828,11 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
rose_write_internal(sk, ROSE_CALL_REQUEST);
rose_set_timer(sk);
-
+
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS;
-
+
cli(); /* To avoid races on the sleep */
/*
@@ -898,14 +851,14 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
sock->state = SS_UNCONNECTED;
return sock_error(sk); /* Always set at this point */
}
-
+
sock->state = SS_CONNECTED;
sti();
-
+
return 0;
}
-
+
static int rose_socketpair(struct socket *sock1, struct socket *sock2)
{
return -EOPNOTSUPP;
@@ -917,19 +870,20 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
struct sock *newsk;
struct sk_buff *skb;
- if (newsock->data)
- sk_free(newsock->data);
+ if (newsock->sk != NULL)
+ rose_destroy_socket(newsock->sk);
- newsock->data = NULL;
-
- sk = (struct sock *)sock->data;
+ newsock->sk = NULL;
+
+ if ((sk = sock->sk) == NULL)
+ return -EINVAL;
if (sk->type != SOCK_SEQPACKET)
return -EOPNOTSUPP;
-
+
if (sk->state != TCP_LISTEN)
return -EINVAL;
-
+
/*
* The write queue this time is holding sockets ready to use
* hooked into the SABM we saved
@@ -939,7 +893,7 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
if (flags & O_NONBLOCK) {
sti();
- return 0;
+ return -EWOULDBLOCK;
}
interruptible_sleep_on(sk->sleep);
if (current->signal & ~current->blocked) {
@@ -957,7 +911,7 @@ static int rose_accept(struct socket *sock, struct socket *newsock, int flags)
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
sk->ack_backlog--;
- newsock->data = newsk;
+ newsock->sk = newsk;
return 0;
}
@@ -966,10 +920,8 @@ static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
- struct sock *sk;
-
- sk = (struct sock *)sock->data;
-
+ struct sock *sk = sock->sk;
+
if (peer != 0) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
@@ -996,11 +948,11 @@ static int rose_getname(struct socket *sock, struct sockaddr *uaddr,
return 0;
}
-
+
int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_neigh *neigh, unsigned int lci)
{
struct sock *sk;
- struct sock *make;
+ struct sock *make;
rose_cb rose;
skb->sk = NULL; /* Initially we don't know who it's for */
@@ -1015,7 +967,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne
if (!rose_parse_facilities(skb, &rose)) {
return 0;
}
-
+
sk = rose_find_listener(&rose.source_call);
/*
@@ -1040,7 +992,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne
make->protinfo.rose->source_digi = rose.source_digi;
make->protinfo.rose->neighbour = neigh;
make->protinfo.rose->device = dev;
-
+
rose_write_internal(make, ROSE_CALL_ACCEPTED);
make->protinfo.rose->condition = 0x00;
@@ -1064,20 +1016,18 @@ int rose_rx_call_request(struct sk_buff *skb, struct device *dev, struct rose_ne
return 1;
}
-static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
int err;
struct sockaddr_rose srose;
struct sk_buff *skb;
unsigned char *asmptr;
int size;
-
- if (sk->err)
- return sock_error(sk);
- if (flags)
+ if (msg->msg_flags & ~MSG_DONTWAIT)
return -EINVAL;
if (sk->zapped)
@@ -1090,8 +1040,8 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
if (sk->protinfo.rose->device == NULL)
return -ENETUNREACH;
-
- if (usrose) {
+
+ if (usrose != NULL) {
if (msg->msg_namelen < sizeof(srose))
return -EINVAL;
srose = *usrose;
@@ -1118,42 +1068,30 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
srose.srose_digi = sk->protinfo.rose->dest_digi;
}
}
-
- if (sk->debug)
- printk("Rose: sendto: Addresses built.\n");
+ SOCK_DEBUG(sk, "Rose: sendto: Addresses built.\n");
/* Build a packet */
- if (sk->debug)
- printk("Rose: sendto: building packet.\n");
-
+ SOCK_DEBUG(sk, "Rose: sendto: building packet.\n");
size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
- if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+ if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
- skb->sk = sk;
- skb->free = 1;
- skb->arp = 1;
-
skb_reserve(skb, size - len);
-
+
/*
* Push down the Rose header
*/
asmptr = skb_push(skb, ROSE_MIN_LEN);
-
- if (sk->debug)
- printk("Building Rose Header.\n");
+ SOCK_DEBUG(sk, "Building Rose Header.\n");
/* Build a Rose Transport header */
- *asmptr++ = ((sk->protinfo.rose->lci >> 8) & 0x0F) | GFI;
+ *asmptr++ = ((sk->protinfo.rose->lci >> 8) & 0x0F) | ROSE_GFI;
*asmptr++ = (sk->protinfo.rose->lci >> 0) & 0xFF;
*asmptr++ = ROSE_DATA;
-
- if (sk->debug)
- printk("Built header.\n");
+ SOCK_DEBUG(sk, "Built header.\n");
/*
* Put the data on the end
@@ -1162,15 +1100,11 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
skb->h.raw = skb_put(skb, len);
asmptr = skb->h.raw;
-
- if (sk->debug)
- printk("Rose: Appending user data\n");
+ SOCK_DEBUG(sk, "Rose: Appending user data\n");
/* User data follows immediately after the Rose transport header */
memcpy_fromiovec(asmptr, msg->msg_iov, len);
-
- if (sk->debug)
- printk("Rose: Transmitting buffer\n");
+ SOCK_DEBUG(sk, "Rose: Transmitting buffer\n");
if (sk->state != TCP_ESTABLISHED) {
kfree_skb(skb, FREE_WRITE);
@@ -1183,18 +1117,15 @@ static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
}
-static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
- int flags, int *addr_len)
+static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
int copied;
struct sk_buff *skb;
int er;
- if (addr_len != NULL)
- *addr_len = sizeof(*srose);
-
/*
* This works for seqpacket too. The receiver has ordered the queue for
* us! We do one quick check first though
@@ -1203,7 +1134,7 @@ static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
return -ENOTCONN;
/* Now we can treat all alike */
- if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
return er;
if (!sk->protinfo.rose->hdrincl) {
@@ -1212,32 +1143,28 @@ static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int n
}
copied = skb->len;
-
+
if (copied > size) {
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
-
+
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-
+
if (srose != NULL) {
- struct sockaddr_rose addr;
-
- addr.srose_family = AF_ROSE;
- addr.srose_addr = sk->protinfo.rose->dest_addr;
- addr.srose_call = sk->protinfo.rose->dest_call;
- addr.srose_ndigis = 0;
+ srose->srose_family = AF_ROSE;
+ srose->srose_addr = sk->protinfo.rose->dest_addr;
+ srose->srose_call = sk->protinfo.rose->dest_call;
+ srose->srose_ndigis = 0;
if (sk->protinfo.rose->dest_ndigis == 1) {
- addr.srose_ndigis = 1;
- addr.srose_digi = sk->protinfo.rose->dest_digi;
+ srose->srose_ndigis = 1;
+ srose->srose_digi = sk->protinfo.rose->dest_digi;
}
-
- *srose = addr;
-
- *addr_len = sizeof(*srose);
}
+ msg->msg_namelen = sizeof(struct sockaddr_rose);
+
skb_free_datagram(sk, skb);
return copied;
@@ -1248,16 +1175,9 @@ static int rose_shutdown(struct socket *sk, int how)
return -EOPNOTSUPP;
}
-static int rose_select(struct socket *sock , int sel_type, select_table *wait)
-{
- struct sock *sk = (struct sock *)sock->data;
-
- return datagram_select(sk, sel_type, wait);
-}
-
static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct sock *sk = (struct sock *)sock->data;
+ struct sock *sk = sock->sk;
int err;
long amount = 0;
@@ -1265,7 +1185,7 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case TIOCOUTQ:
if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
return err;
- amount = sk->sndbuf - sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
put_user(amount, (unsigned int *)arg);
@@ -1310,11 +1230,22 @@ static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if (!suser()) return -EPERM;
return rose_rt_ioctl(cmd, (void *)arg);
- case SIOCRSCTLCON:
+ case SIOCRSCTLCON:
if (!suser()) return -EPERM;
return rose_ctl_ioctl(cmd, (void *)arg);
-
- default:
+
+ case SIOCRSL2CALL:
+ if (!suser()) return -EPERM;
+ if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0)
+ return err;
+ if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+ ax25_listen_release(&rose_callsign, NULL);
+ copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address));
+ if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+ ax25_listen_register(&rose_callsign, NULL);
+ return 0;
+
+ default:
return dev_ioctl(cmd, (void *)arg);
}
@@ -1330,10 +1261,10 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i
int len = 0;
off_t pos = 0;
off_t begin = 0;
-
+
cli();
- len += sprintf(buffer, "dest_addr dest_call dest_digi src_addr src_call src_digi dev lci st vs vr va t t1 t2 t3 Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb Snd-Q Rcv-Q\n");
for (s = rose_list; s != NULL; s = s->next) {
if ((dev = s->protinfo.rose->device) == NULL)
@@ -1344,35 +1275,31 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i
len += sprintf(buffer + len, "%-10s %-9s ",
rose2asc(&s->protinfo.rose->dest_addr),
ax2asc(&s->protinfo.rose->dest_call));
- len += sprintf(buffer + len, "%-9s ",
- ax2asc(&s->protinfo.rose->dest_digi));
if (ax25cmp(&s->protinfo.rose->source_call, &null_ax25_address) == 0)
callsign = "??????-?";
else
callsign = ax2asc(&s->protinfo.rose->source_call);
- len += sprintf(buffer + len, "%-10s %-9s ",
- rose2asc(&s->protinfo.rose->source_addr),
- callsign);
- len += sprintf(buffer + len, "%-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %5d %5d\n",
- ax2asc(&s->protinfo.rose->source_digi),
+ len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n",
+ rose2asc(&s->protinfo.rose->source_addr), callsign,
devname, s->protinfo.rose->lci & 0x0FFF,
s->protinfo.rose->state,
s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
- s->protinfo.rose->timer / PR_SLOWHZ,
- s->protinfo.rose->t1 / PR_SLOWHZ,
- s->protinfo.rose->t2 / PR_SLOWHZ,
- s->protinfo.rose->t3 / PR_SLOWHZ,
- s->wmem_alloc, s->rmem_alloc);
-
+ s->protinfo.rose->timer / ROSE_SLOWHZ,
+ s->protinfo.rose->t1 / ROSE_SLOWHZ,
+ s->protinfo.rose->t2 / ROSE_SLOWHZ,
+ s->protinfo.rose->t3 / ROSE_SLOWHZ,
+ s->protinfo.rose->hb / ROSE_SLOWHZ,
+ atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+
pos = begin + len;
if (pos < offset) {
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -1387,10 +1314,14 @@ static int rose_get_info(char *buffer, char **start, off_t offset, int length, i
return(len);
}
-struct proto_ops rose_proto_ops = {
+static struct net_proto_family rose_family_ops = {
AF_ROSE,
-
- rose_create,
+ rose_create
+};
+
+static struct proto_ops rose_proto_ops = {
+ AF_ROSE,
+
rose_dup,
rose_release,
rose_bind,
@@ -1398,62 +1329,132 @@ struct proto_ops rose_proto_ops = {
rose_socketpair,
rose_accept,
rose_getname,
- rose_select,
+ datagram_poll,
rose_ioctl,
rose_listen,
rose_shutdown,
rose_setsockopt,
rose_getsockopt,
- rose_fcntl,
+ sock_no_fcntl,
rose_sendmsg,
rose_recvmsg
};
-struct notifier_block rose_dev_notifier = {
+static struct notifier_block rose_dev_notifier = {
rose_device_event,
0
};
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_rose = {
+ PROC_NET_RS, 4, "rose",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rose_get_info
+};
+static struct proc_dir_entry proc_net_rose_neigh = {
+ PROC_NET_RS_NEIGH, 10, "rose_neigh",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rose_neigh_get_info
+};
+static struct proc_dir_entry proc_net_rose_nodes = {
+ PROC_NET_RS_NODES, 10, "rose_nodes",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rose_nodes_get_info
+};
+static struct proc_dir_entry proc_net_rose_routes = {
+ PROC_NET_RS_ROUTES, 11, "rose_routes",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ rose_routes_get_info
+};
+#endif
+
+static struct device dev_rose[] = {
+ {"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+ {"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+ {"rose2", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+ {"rose3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+ {"rose4", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
+ {"rose5", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
+};
+
void rose_proto_init(struct net_proto *pro)
{
- sock_register(rose_proto_ops.family, &rose_proto_ops);
+ int i;
+
+ rose_callsign = null_ax25_address;
+
+ sock_register(&rose_family_ops);
register_netdevice_notifier(&rose_dev_notifier);
- printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.034 Linux 2.1\n");
+ printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.035 Linux 2.1\n");
if (!ax25_protocol_register(AX25_P_ROSE, rose_route_frame))
printk(KERN_ERR "Rose unable to register protocol with AX.25\n");
if (!ax25_linkfail_register(rose_link_failed))
printk(KERN_ERR "Rose unable to register linkfail handler with AX.25\n");
+ for (i = 0; i < 6; i++)
+ register_netdev(&dev_rose[i]);
+
+#ifdef CONFIG_SYSCTL
rose_register_sysctl();
+#endif
#ifdef CONFIG_PROC_FS
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RS, 4, "rose",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rose_get_info
- });
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RS_NEIGH, 10, "rose_neigh",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rose_neigh_get_info
- });
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RS_NODES, 10, "rose_nodes",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rose_nodes_get_info
- });
-
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_RS_ROUTES, 11, "rose_routes",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- rose_routes_get_info
- });
-#endif
+ proc_net_register(&proc_net_rose);
+ proc_net_register(&proc_net_rose_neigh);
+ proc_net_register(&proc_net_rose_nodes);
+ proc_net_register(&proc_net_rose_routes);
+#endif
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ rose_proto_init(NULL);
+
+ return 0;
}
+void cleanup_module(void)
+{
+ int i;
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_RS);
+ proc_net_unregister(PROC_NET_RS_NEIGH);
+ proc_net_unregister(PROC_NET_RS_NODES);
+ proc_net_unregister(PROC_NET_RS_ROUTES);
+#endif
+ rose_rt_free();
+
+ ax25_protocol_release(AX25_P_ROSE);
+ ax25_linkfail_release(rose_link_failed);
+
+ if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
+ ax25_listen_release(&rose_callsign, NULL);
+
+#ifdef CONFIG_SYSCTL
+ rose_unregister_sysctl();
+#endif
+ unregister_netdevice_notifier(&rose_dev_notifier);
+
+ sock_unregister(AF_ROSE);
+
+ for (i = 0; i < 6; i++) {
+ if (dev_rose[i].priv != NULL) {
+ kfree(dev_rose[i].priv);
+ dev_rose[i].priv = NULL;
+ unregister_netdev(&dev_rose[i]);
+ }
+ }
+}
+
+#endif
+
#endif
diff --git a/net/rose/rose_dev.c b/net/rose/rose_dev.c
index 7fe6a00e6..ebab50e0d 100644
--- a/net/rose/rose_dev.c
+++ b/net/rose/rose_dev.c
@@ -1,10 +1,10 @@
/*
* Rose release 001
*
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -14,10 +14,12 @@
*
* History
* Rose 001 Jonathan(G4KLX) Cloned from nr_dev.c.
+ * Hans(PE1AYX) Fixed interface to IP layer.
*/
#include <linux/config.h>
#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
+#define __NO_VERSION__
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/kernel.h>
@@ -55,7 +57,7 @@
int rose_rx_ip(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
if (!dev->start) {
stats->rx_errors++;
@@ -63,12 +65,16 @@ int rose_rx_ip(struct sk_buff *skb, struct device *dev)
}
stats->rx_packets++;
+ stats->rx_bytes += skb->len;
+
skb->protocol = htons(ETH_P_IP);
/* Spoof incoming device */
- skb->dev = dev;
+ skb->dev = dev;
+ skb->h.raw = skb->data;
+ skb->nh.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
- skb->h.raw = skb->data;
ip_rcv(skb, skb->dev, NULL);
return 1;
@@ -79,7 +85,7 @@ static int rose_header(struct sk_buff *skb, struct device *dev, unsigned short t
{
unsigned char *buff = skb_push(skb, ROSE_MIN_LEN + 2);
- *buff++ = GFI | Q_BIT;
+ *buff++ = ROSE_GFI | ROSE_Q_BIT;
*buff++ = 0x00;
*buff++ = ROSE_DATA;
*buff++ = 0x7F;
@@ -87,40 +93,39 @@ static int rose_header(struct sk_buff *skb, struct device *dev, unsigned short t
if (daddr != NULL)
return 37;
-
- return -37;
+
+ return -37;
}
-static int rose_rebuild_header(void *buff, struct device *dev,
- unsigned long raddr, struct sk_buff *skb)
+static int rose_rebuild_header(struct sk_buff *skb)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
- unsigned char *bp = (unsigned char *)buff;
+ struct device *dev = skb->dev;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
+ unsigned char *bp = (unsigned char *)skb->data;
struct sk_buff *skbn;
- if (!arp_query(bp + 7, raddr, dev)) {
- dev_kfree_skb(skb, FREE_WRITE);
+ if (!arp_find(bp + 7, skb)) {
+ kfree_skb(skb, FREE_WRITE);
return 1;
}
if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
return 1;
}
- skbn->sk = skb->sk;
-
- if (skbn->sk != NULL)
- atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
if (!rose_route_frame(skbn, NULL)) {
- dev_kfree_skb(skbn, FREE_WRITE);
+ kfree_skb(skbn, FREE_WRITE);
stats->tx_errors++;
}
stats->tx_packets++;
+ stats->tx_bytes += skbn->len;
return 1;
}
@@ -132,7 +137,7 @@ static int rose_set_mac_address(struct device *dev, void *addr)
ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
+
ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
return 0;
@@ -155,16 +160,16 @@ static int rose_close(struct device *dev)
dev->tbusy = 1;
dev->start = 0;
- ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
-
MOD_DEC_USE_COUNT;
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+
return 0;
}
static int rose_xmit(struct sk_buff *skb, struct device *dev)
{
- struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
+ struct net_device_stats *stats = (struct net_device_stats *)dev->priv;
if (skb == NULL || dev == NULL)
return 0;
@@ -186,7 +191,7 @@ static int rose_xmit(struct sk_buff *skb, struct device *dev)
sti();
- dev_kfree_skb(skb, FREE_WRITE);
+ kfree_skb(skb, FREE_WRITE);
stats->tx_errors++;
@@ -197,16 +202,14 @@ static int rose_xmit(struct sk_buff *skb, struct device *dev)
return 0;
}
-static struct enet_statistics *rose_get_stats(struct device *dev)
+static struct net_device_stats *rose_get_stats(struct device *dev)
{
- return (struct enet_statistics *)dev->priv;
+ return (struct net_device_stats *)dev->priv;
}
int rose_init(struct device *dev)
{
- int i;
-
- dev->mtu = ROSE_PACLEN - 2;
+ dev->mtu = ROSE_MAX_PACKET_SIZE - 2;
dev->tbusy = 0;
dev->hard_start_xmit = rose_xmit;
dev->open = rose_open;
@@ -228,71 +231,16 @@ int rose_init(struct device *dev)
dev->pa_mask = 0;
dev->pa_alen = sizeof(unsigned long);
- if ((dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL)) == NULL)
+ if ((dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL)) == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct enet_statistics));
+ memset(dev->priv, 0, sizeof(struct net_device_stats));
dev->get_stats = rose_get_stats;
- /* Fill in the generic fields of the device structure. */
- for (i = 0; i < DEV_NUMBUFFS; i++)
- skb_queue_head_init(&dev->buffs[i]);
+ dev_init_buffers(dev);
return 0;
};
-#ifdef MODULE
-extern struct proto_ops rose_proto_ops;
-extern struct notifier_block rose_dev_notifier;
-
-static struct device dev_rose[] = {
- {"rose0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init},
- {"rose1", 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, rose_init}
-};
-
-int init_module(void)
-{
- register_netdev(&dev_rose[0]);
- register_netdev(&dev_rose[1]);
-
- register_symtab(NULL);
-
- rose_proto_init(NULL);
-
- return 0;
-}
-
-void cleanup_module(void)
-{
- int i;
-
-#ifdef CONFIG_PROC_FS
- proc_net_unregister(PROC_NET_RS);
- proc_net_unregister(PROC_NET_RS_NEIGH);
- proc_net_unregister(PROC_NET_RS_NODES);
- proc_net_unregister(PROC_NET_RS_ROUTES);
-#endif
- rose_rt_free();
-
- ax25_protocol_release(AX25_P_ROSE);
- ax25_linkfail_release(rose_link_failed);
-
- rose_unregister_sysctl();
-
- unregister_netdevice_notifier(&rose_dev_notifier);
-
- sock_unregister(rose_proto_ops.family);
-
- for (i = 0; i < 2; i++) {
- if (dev_rose[i].priv != NULL) {
- kfree(dev_rose[i].priv);
- dev_rose[i].priv = NULL;
- unregister_netdev(&dev_rose[i]);
- }
- }
-}
-
-#endif
-
#endif
diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 20374dbb1..66521efa8 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -1,10 +1,10 @@
/*
* Rose release 001
*
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -56,7 +56,7 @@ static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
return 0;
}
-
+
if (!more && sk->protinfo.rose->fraglen > 0) { /* End of fragment */
sk->protinfo.rose->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
@@ -64,10 +64,6 @@ static int rose_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
if ((skbn = alloc_skb(sk->protinfo.rose->fraglen, GFP_ATOMIC)) == NULL)
return 1;
- skbn->free = 1;
- skbn->arp = 1;
- skbn->sk = sk;
- sk->rmem_alloc += skbn->truesize;
skbn->h.raw = skbn->data;
skbo = skb_dequeue(&sk->protinfo.rose->frag_queue);
@@ -96,19 +92,21 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
switch (frametype) {
case ROSE_CALL_ACCEPTED:
- sk->protinfo.rose->timer = 0;
- sk->protinfo.rose->vs = 0;
- sk->protinfo.rose->va = 0;
- sk->protinfo.rose->vr = 0;
- sk->protinfo.rose->vl = 0;
- sk->protinfo.rose->state = ROSE_STATE_3;
- sk->state = TCP_ESTABLISHED;
+ sk->protinfo.rose->condition = 0x00;
+ sk->protinfo.rose->timer = 0;
+ sk->protinfo.rose->vs = 0;
+ sk->protinfo.rose->va = 0;
+ sk->protinfo.rose->vr = 0;
+ sk->protinfo.rose->vl = 0;
+ sk->protinfo.rose->state = ROSE_STATE_3;
+ sk->state = TCP_ESTABLISHED;
if (!sk->dead)
sk->state_change(sk);
break;
case ROSE_CLEAR_REQUEST:
rose_clear_queues(sk);
+ rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
sk->protinfo.rose->state = ROSE_STATE_0;
sk->state = TCP_CLOSE;
sk->err = ECONNREFUSED;
@@ -136,7 +134,9 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
switch (frametype) {
case ROSE_CLEAR_REQUEST:
+ rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
case ROSE_CLEAR_CONFIRMATION:
+ rose_clear_queues(sk);
sk->protinfo.rose->state = ROSE_STATE_0;
sk->state = TCP_CLOSE;
sk->err = 0;
@@ -166,9 +166,9 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
switch (frametype) {
case ROSE_RESET_REQUEST:
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
sk->protinfo.rose->condition = 0x00;
+ sk->protinfo.rose->timer = 0;
sk->protinfo.rose->vs = 0;
sk->protinfo.rose->vr = 0;
sk->protinfo.rose->va = 0;
@@ -189,11 +189,10 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
case ROSE_RR:
case ROSE_RNR:
- if (frametype == ROSE_RNR) {
- sk->protinfo.rose->condition |= PEER_RX_BUSY_CONDITION;
- } else {
- sk->protinfo.rose->condition &= ~PEER_RX_BUSY_CONDITION;
- }
+ if (frametype == ROSE_RNR)
+ sk->protinfo.rose->condition |= ROSE_COND_PEER_RX_BUSY;
+ else
+ sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
if (!rose_validate_nr(sk, nr)) {
rose_clear_queues(sk);
rose_write_internal(sk, ROSE_RESET_REQUEST);
@@ -205,16 +204,16 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
sk->protinfo.rose->state = ROSE_STATE_4;
sk->protinfo.rose->timer = sk->protinfo.rose->t2;
} else {
- if (sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) {
- rose_frames_acked(sk, nr);
+ if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
+ sk->protinfo.rose->va = nr;
} else {
rose_check_iframes_acked(sk, nr);
}
}
break;
-
+
case ROSE_DATA: /* XXX */
- sk->protinfo.rose->condition &= ~PEER_RX_BUSY_CONDITION;
+ sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
if (!rose_validate_nr(sk, nr)) {
rose_clear_queues(sk);
rose_write_internal(sk, ROSE_RESET_REQUEST);
@@ -227,26 +226,33 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
sk->protinfo.rose->timer = sk->protinfo.rose->t2;
break;
}
- if (sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) {
- rose_frames_acked(sk, nr);
+ if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
+ sk->protinfo.rose->va = nr;
} else {
rose_check_iframes_acked(sk, nr);
}
- if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)
+ if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)
break;
if (ns == sk->protinfo.rose->vr) {
if (rose_queue_rx_frame(sk, skb, m) == 0) {
sk->protinfo.rose->vr = (sk->protinfo.rose->vr + 1) % ROSE_MODULUS;
queued = 1;
} else {
- sk->protinfo.rose->condition |= OWN_RX_BUSY_CONDITION;
+ sk->protinfo.rose->condition |= ROSE_COND_OWN_RX_BUSY;
}
}
/*
- * If the window is full, ack the frame.
+ * If the window is full, ack the frame, else start the
+ * acknowledge hold back timer.
*/
- if (((sk->protinfo.rose->vl + sk->window) % ROSE_MODULUS) == sk->protinfo.rose->vr)
+ if (((sk->protinfo.rose->vl + ROSE_MAX_WINDOW_SIZE) % ROSE_MODULUS) == sk->protinfo.rose->vr) {
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+ sk->protinfo.rose->timer = 0;
rose_enquiry_response(sk);
+ } else {
+ sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING;
+ sk->protinfo.rose->timer = sk->protinfo.rose->hb;
+ }
break;
default:
@@ -266,8 +272,9 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
{
switch (frametype) {
- case ROSE_RESET_CONFIRMATION:
case ROSE_RESET_REQUEST:
+ rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
+ case ROSE_RESET_CONFIRMATION:
sk->protinfo.rose->timer = 0;
sk->protinfo.rose->condition = 0x00;
sk->protinfo.rose->va = 0;
@@ -302,7 +309,7 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
{
int queued = 0, frametype, ns, nr, q, d, m;
-
+
if (sk->protinfo.rose->state == ROSE_STATE_0)
return 0;
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index d0bf308f0..9f1a0c0f5 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -44,37 +44,20 @@
static void rose_link_timer(unsigned long);
/*
- * Linux set/reset timer routines
+ * Linux set timer
*/
-static void rose_link_set_timer(struct rose_neigh *neigh)
+void rose_link_set_timer(struct rose_neigh *neigh)
{
unsigned long flags;
-
- save_flags(flags);
- cli();
- del_timer(&neigh->timer);
- restore_flags(flags);
-
- neigh->timer.next = neigh->timer.prev = NULL;
- neigh->timer.data = (unsigned long)neigh;
- neigh->timer.function = &rose_link_timer;
- neigh->timer.expires = jiffies + 10;
- add_timer(&neigh->timer);
-}
-
-static void rose_link_reset_timer(struct rose_neigh *neigh)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
del_timer(&neigh->timer);
restore_flags(flags);
neigh->timer.data = (unsigned long)neigh;
neigh->timer.function = &rose_link_timer;
- neigh->timer.expires = jiffies + 10;
+ neigh->timer.expires = jiffies + (HZ / 10);
+
add_timer(&neigh->timer);
}
@@ -88,19 +71,56 @@ static void rose_link_timer(unsigned long param)
{
struct rose_neigh *neigh = (struct rose_neigh *)param;
- if (neigh->t0timer == 0 || --neigh->t0timer > 0) {
- rose_link_reset_timer(neigh);
- return;
+ if (neigh->ftimer > 0)
+ neigh->ftimer--;
+
+ if (neigh->t0timer > 0) {
+ neigh->t0timer--;
+
+ if (neigh->t0timer == 0) {
+ rose_transmit_restart_request(neigh);
+ neigh->t0timer = sysctl_rose_restart_request_timeout;
+ }
}
- /*
- * T0 for a link has expired.
- */
- rose_transmit_restart_request(neigh);
+ if (neigh->ftimer > 0 || neigh->t0timer > 0)
+ rose_link_set_timer(neigh);
+ else
+ del_timer(&neigh->timer);
+}
- neigh->t0timer = neigh->t0;
+/*
+ * Interface to ax25_send_frame. Changes my level 2 callsign depending
+ * on whether we have a global ROSE callsign or use the default port
+ * callsign.
+ */
+static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
+{
+ ax25_address *rose_call;
+
+ if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
+ rose_call = (ax25_address *)neigh->dev->dev_addr;
+ else
+ rose_call = &rose_callsign;
+
+ return ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+}
+
+/*
+ * Interface to ax25_link_up. Changes my level 2 callsign depending
+ * on whether we have a global ROSE callsign or use the default port
+ * callsign.
+ */
+static int rose_link_up(struct rose_neigh *neigh)
+{
+ ax25_address *rose_call;
- rose_link_set_timer(neigh);
+ if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
+ rose_call = (ax25_address *)neigh->dev->dev_addr;
+ else
+ rose_call = &rose_callsign;
+
+ return ax25_link_up(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
}
/*
@@ -127,7 +147,7 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne
case ROSE_DIAGNOSTIC:
printk(KERN_WARNING "rose: diagnostic #%d\n", skb->data[3]);
break;
-
+
default:
printk(KERN_WARNING "rose: received unknown %02X with LCI 000\n", frametype);
break;
@@ -135,7 +155,7 @@ void rose_link_rx_restart(struct sk_buff *skb, struct rose_neigh *neigh, unsigne
if (neigh->restarted) {
while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
- if (!ax25_send_frame(skbn, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skbn, neigh))
kfree_skb(skbn, FREE_WRITE);
}
}
@@ -159,16 +179,13 @@ void rose_transmit_restart_request(struct rose_neigh *neigh)
dptr = skb_put(skb, ROSE_MIN_LEN + 3);
*dptr++ = AX25_P_ROSE;
- *dptr++ = GFI;
+ *dptr++ = ROSE_GFI;
*dptr++ = 0x00;
*dptr++ = ROSE_RESTART_REQUEST;
*dptr++ = 0x00;
*dptr++ = 0;
- skb->free = 1;
- skb->sk = NULL;
-
- if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
}
@@ -191,14 +208,11 @@ void rose_transmit_restart_confirmation(struct rose_neigh *neigh)
dptr = skb_put(skb, ROSE_MIN_LEN + 1);
*dptr++ = AX25_P_ROSE;
- *dptr++ = GFI;
+ *dptr++ = ROSE_GFI;
*dptr++ = 0x00;
*dptr++ = ROSE_RESTART_CONFIRMATION;
- skb->free = 1;
- skb->sk = NULL;
-
- if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
}
@@ -221,15 +235,12 @@ void rose_transmit_diagnostic(struct rose_neigh *neigh, unsigned char diag)
dptr = skb_put(skb, ROSE_MIN_LEN + 2);
*dptr++ = AX25_P_ROSE;
- *dptr++ = GFI;
+ *dptr++ = ROSE_GFI;
*dptr++ = 0x00;
*dptr++ = ROSE_DIAGNOSTIC;
*dptr++ = diag;
- skb->free = 1;
- skb->sk = NULL;
-
- if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
}
@@ -253,16 +264,13 @@ void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, uns
dptr = skb_put(skb, ROSE_MIN_LEN + 3);
*dptr++ = AX25_P_ROSE;
- *dptr++ = ((lci >> 8) & 0x0F) | GFI;
+ *dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI;
*dptr++ = ((lci >> 0) & 0xFF);
*dptr++ = ROSE_CLEAR_REQUEST;
*dptr++ = cause;
*dptr++ = 0x00;
- skb->free = 1;
- skb->sk = NULL;
-
- if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
}
@@ -270,29 +278,24 @@ void rose_transmit_link(struct sk_buff *skb, struct rose_neigh *neigh)
{
unsigned char *dptr;
-#ifdef CONFIG_FIREWALL
- if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+ if (call_fw_firewall(PF_ROSE, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
return;
-#endif
- if (!ax25_link_up((ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->dev))
+ if (!rose_link_up(neigh))
neigh->restarted = 0;
dptr = skb_push(skb, 1);
*dptr++ = AX25_P_ROSE;
- skb->arp = 1;
- skb->free = 1;
-
if (neigh->restarted) {
- if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
+ if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
} else {
skb_queue_tail(&neigh->queue, skb);
-
+
if (neigh->t0timer == 0) {
rose_transmit_restart_request(neigh);
- neigh->t0timer = neigh->t0;
+ neigh->t0timer = sysctl_rose_restart_request_timeout;
rose_link_set_timer(neigh);
}
}
diff --git a/net/rose/rose_out.c b/net/rose/rose_out.c
index 50b2587f8..8ab4d9325 100644
--- a/net/rose/rose_out.c
+++ b/net/rose/rose_out.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -49,7 +49,7 @@ void rose_output(struct sock *sk, struct sk_buff *skb)
unsigned char header[ROSE_MIN_LEN];
int err, frontlen, len;
- if (skb->len - ROSE_MIN_LEN > ROSE_PACLEN) {
+ if (skb->len - ROSE_MIN_LEN > ROSE_MAX_PACKET_SIZE) {
/* Save a copy of the Header */
memcpy(header, skb->data, ROSE_MIN_LEN);
skb_pull(skb, ROSE_MIN_LEN);
@@ -57,16 +57,12 @@ void rose_output(struct sock *sk, struct sk_buff *skb)
frontlen = skb_headroom(skb);
while (skb->len > 0) {
- if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_PACLEN, 0, 0, &err)) == NULL)
+ if ((skbn = sock_alloc_send_skb(sk, frontlen + ROSE_MAX_PACKET_SIZE, 0, 0, &err)) == NULL)
return;
- skbn->sk = sk;
- skbn->free = 1;
- skbn->arp = 1;
-
skb_reserve(skbn, frontlen);
- len = (ROSE_PACLEN > skb->len) ? skb->len : ROSE_PACLEN;
+ len = (ROSE_MAX_PACKET_SIZE > skb->len) ? skb->len : ROSE_MAX_PACKET_SIZE;
/* Copy the user data */
memcpy(skb_put(skbn, len), skb->data, len);
@@ -77,12 +73,11 @@ void rose_output(struct sock *sk, struct sk_buff *skb)
memcpy(skbn->data, header, ROSE_MIN_LEN);
if (skb->len > 0)
- skbn->data[2] |= M_BIT;
-
+ skbn->data[2] |= ROSE_M_BIT;
+
skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
}
-
- skb->free = 1;
+
kfree_skb(skb, FREE_WRITE);
} else {
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */
@@ -109,55 +104,36 @@ static void rose_send_iframe(struct sock *sk, struct sk_buff *skb)
void rose_kick(struct sock *sk)
{
- struct sk_buff *skb, *skbn;
- int last = 1;
- unsigned short start, end, next;
+ struct sk_buff *skb;
+ unsigned short end;
del_timer(&sk->timer);
- start = (skb_peek(&sk->protinfo.rose->ack_queue) == NULL) ? sk->protinfo.rose->va : sk->protinfo.rose->vs;
- end = (sk->protinfo.rose->va + sk->window) % ROSE_MODULUS;
+ end = (sk->protinfo.rose->va + ROSE_MAX_WINDOW_SIZE) % ROSE_MODULUS;
- if (!(sk->protinfo.rose->condition & PEER_RX_BUSY_CONDITION) &&
- start != end &&
+ if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) &&
+ sk->protinfo.rose->vs != end &&
skb_peek(&sk->write_queue) != NULL) {
-
- sk->protinfo.rose->vs = start;
-
/*
* Transmit data until either we're out of data to send or
* the window is full.
*/
- /*
- * Dequeue the frame and copy it.
- */
skb = skb_dequeue(&sk->write_queue);
do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- skb_queue_head(&sk->write_queue, skb);
- break;
- }
-
- next = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
- last = (next == end);
-
/*
- * Transmit the frame copy.
+ * Transmit the frame.
*/
- rose_send_iframe(sk, skbn);
-
- sk->protinfo.rose->vs = next;
+ rose_send_iframe(sk, skb);
- /*
- * Requeue the original data frame.
- */
- skb_queue_tail(&sk->protinfo.rose->ack_queue, skb);
+ sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
- } while (!last && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+ } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
- sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+ sk->protinfo.rose->timer = 0;
}
rose_set_timer(sk);
@@ -170,23 +146,23 @@ void rose_kick(struct sock *sk)
void rose_enquiry_response(struct sock *sk)
{
- if (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION) {
+ if (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)
rose_write_internal(sk, ROSE_RNR);
- } else {
+ else
rose_write_internal(sk, ROSE_RR);
- }
- sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+ sk->protinfo.rose->timer = 0;
}
void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
{
if (sk->protinfo.rose->vs == nr) {
- rose_frames_acked(sk, nr);
+ sk->protinfo.rose->va = nr;
} else {
- if (sk->protinfo.rose->va != nr) {
- rose_frames_acked(sk, nr);
- }
+ if (sk->protinfo.rose->va != nr)
+ sk->protinfo.rose->va = nr;
}
}
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 9396831b3..19a53d40d 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -14,8 +14,10 @@
*
* History
* Rose 001 Jonathan(G4KLX) Cloned from nr_route.c.
+ * Terry(VK2KTJ) Added support for variable length
+ * address masks.
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
#include <linux/errno.h>
@@ -60,13 +62,13 @@ static void rose_remove_neigh(struct rose_neigh *);
*/
static int rose_add_node(struct rose_route_struct *rose_route, struct device *dev)
{
- struct rose_node *rose_node;
+ struct rose_node *rose_node, *rose_tmpn, *rose_tmpp;
struct rose_neigh *rose_neigh;
unsigned long flags;
int i;
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
- if (rosecmp(&rose_route->address, &rose_node->address) == 0)
+ if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0))
break;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
@@ -84,8 +86,8 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct device *de
rose_neigh->number = rose_neigh_no++;
rose_neigh->restarted = 0;
skb_queue_head_init(&rose_neigh->queue);
- rose_neigh->t0 = sysctl_rose_restart_request_timeout;
rose_neigh->t0timer = 0;
+ rose_neigh->ftimer = 0;
init_timer(&rose_neigh->timer);
if (rose_route->ndigis != 0) {
@@ -97,40 +99,72 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct device *de
for (i = 0; i < rose_route->ndigis; i++)
rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i];
}
-
+
save_flags(flags); cli();
rose_neigh->next = rose_neigh_list;
rose_neigh_list = rose_neigh;
restore_flags(flags);
}
+ /*
+ * This is a new node to be inserted into the list. Find where it needs
+ * to be inserted into the list, and insert it. We want to be sure
+ * to order the list in descending order of mask size to ensure that
+ * later when we are searching this list the first match will be the
+ * best match.
+ */
if (rose_node == NULL) {
+ rose_tmpn = rose_node_list;
+ rose_tmpp = NULL;
+
+ while (rose_tmpn != NULL) {
+ if (rose_tmpn->mask > rose_route->mask) {
+ rose_tmpp = rose_tmpn;
+ rose_tmpn = rose_tmpn->next;
+ } else {
+ break;
+ }
+ }
+
+ /* create new node */
if ((rose_node = (struct rose_node *)kmalloc(sizeof(*rose_node), GFP_ATOMIC)) == NULL)
return -ENOMEM;
rose_node->address = rose_route->address;
- rose_node->which = 0;
+ rose_node->mask = rose_route->mask;
rose_node->count = 1;
-
rose_node->neighbour[0] = rose_neigh;
-
+
save_flags(flags); cli();
- rose_node->next = rose_node_list;
- rose_node_list = rose_node;
+
+ if (rose_tmpn == NULL) {
+ if (rose_tmpp == NULL) { /* Empty list */
+ rose_node_list = rose_node;
+ rose_node->next = NULL;
+ } else {
+ rose_tmpp->next = rose_node;
+ rose_node->next = NULL;
+ }
+ } else {
+ if (rose_tmpp == NULL) { /* 1st node */
+ rose_node->next = rose_node_list;
+ rose_node_list = rose_node;
+ } else {
+ rose_tmpp->next = rose_node;
+ rose_node->next = rose_tmpn;
+ }
+ }
+
restore_flags(flags);
-
+
rose_neigh->count++;
return 0;
}
- /* We have space at the bottom, slot it in */
+ /* We have space, slot it in */
if (rose_node->count < 3) {
- rose_node->neighbour[2] = rose_node->neighbour[1];
- rose_node->neighbour[1] = rose_node->neighbour[0];
-
- rose_node->neighbour[0] = rose_neigh;
-
+ rose_node->neighbour[rose_node->count] = rose_neigh;
rose_node->count++;
rose_neigh->count++;
}
@@ -174,10 +208,10 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
struct sk_buff *skb;
del_timer(&rose_neigh->timer);
-
+
while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
-
+
save_flags(flags);
cli();
@@ -244,9 +278,9 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct device *de
struct rose_node *rose_node;
struct rose_neigh *rose_neigh;
int i;
-
+
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
- if (rosecmp(&rose_route->address, &rose_node->address) == 0)
+ if ((rose_node->mask == rose_route->mask) && (rosecmpm(&rose_route->address, &rose_node->address, rose_route->mask) == 0))
break;
if (rose_node == NULL) return -EINVAL;
@@ -256,16 +290,16 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct device *de
break;
if (rose_neigh == NULL) return -EINVAL;
-
+
for (i = 0; i < rose_node->count; i++) {
if (rose_node->neighbour[i] == rose_neigh) {
rose_neigh->count--;
if (rose_neigh->count == 0)
rose_remove_neigh(rose_neigh);
-
+
rose_node->count--;
-
+
if (rose_node->count == 0) {
rose_remove_node(rose_node);
} else {
@@ -298,14 +332,14 @@ void rose_rt_device_down(struct device *dev)
while (rose_neigh != NULL) {
s = rose_neigh;
rose_neigh = rose_neigh->next;
-
+
if (s->dev == dev) {
rose_node = rose_node_list;
while (rose_node != NULL) {
t = rose_node;
rose_node = rose_node->next;
-
+
for (i = 0; i < t->count; i++) {
if (t->neighbour[i] == s) {
t->count--;
@@ -320,11 +354,11 @@ void rose_rt_device_down(struct device *dev)
}
}
}
-
+
if (t->count <= 0)
rose_remove_node(t);
}
-
+
rose_remove_neigh(s);
}
}
@@ -340,7 +374,7 @@ void rose_route_device_down(struct device *dev)
while (rose_route != NULL) {
s = rose_route;
rose_route = rose_route->next;
-
+
if (s->neigh1->dev == dev || s->neigh2->dev == dev)
rose_remove_route(s);
}
@@ -397,16 +431,19 @@ struct device *rose_dev_get(rose_address *addr)
struct rose_neigh *rose_get_neigh(rose_address *addr)
{
struct rose_node *node;
-
+ int i;
+
for (node = rose_node_list; node != NULL; node = node->next)
- if (rosecmp(&node->address, addr) == 0)
+ if (rosecmpm(addr, &node->address, node->mask) == 0)
break;
-
+
if (node == NULL) return NULL;
-
- if (node->which >= node->count) return NULL;
-
- return node->neighbour[node->which];
+
+ for (i = 0; i < node->count; i++)
+ if (node->neighbour[i]->ftimer == 0)
+ return node->neighbour[i];
+
+ return NULL;
}
/*
@@ -428,6 +465,9 @@ int rose_rt_ioctl(unsigned int cmd, void *arg)
return -EINVAL;
if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */
return -EINVAL;
+ if (rose_route.mask > 10) /* Mask can't be more than 10 digits */
+ return -EINVAL;
+
return rose_add_node(&rose_route, dev);
case SIOCDELRT:
@@ -452,25 +492,22 @@ int rose_rt_ioctl(unsigned int cmd, void *arg)
void rose_link_failed(ax25_address *callsign, struct device *dev)
{
struct rose_neigh *rose_neigh;
- struct rose_node *rose_node;
struct sk_buff *skb;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev)
break;
-
+
if (rose_neigh == NULL) return;
rose_neigh->restarted = 0;
rose_neigh->t0timer = 0;
- del_timer(&rose_neigh->timer);
+ rose_neigh->ftimer = sysctl_rose_link_fail_timeout;
+
+ rose_link_set_timer(rose_neigh);
while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
-
- for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
- if (rose_node->which < rose_node->count && rose_node->neighbour[rose_node->which] == rose_neigh)
- rose_node->which++;
}
/*
@@ -479,57 +516,56 @@ void rose_link_failed(ax25_address *callsign, struct device *dev)
void rose_link_device_down(struct device *dev)
{
struct rose_neigh *rose_neigh;
- struct rose_node *rose_node;
struct sk_buff *skb;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
if (rose_neigh->dev == dev) {
rose_neigh->restarted = 0;
rose_neigh->t0timer = 0;
+ rose_neigh->ftimer = 0;
del_timer(&rose_neigh->timer);
while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
-
- for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
- if (rose_node->which < rose_node->count && rose_node->neighbour[rose_node->which] == rose_neigh)
- rose_node->which++;
}
}
}
/*
- * Route a frame to an appropriate AX.25 connection. A NULL ax25_cb
- * indicates an internally generated frame.
+ * Route a frame to an appropriate AX.25 connection.
*/
int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
{
struct rose_neigh *rose_neigh, *new_neigh;
- struct rose_node *rose_node;
struct rose_route *rose_route;
- rose_address *dest_addr;
+ rose_address *src_addr, *dest_addr;
struct sock *sk;
unsigned short frametype;
unsigned int lci;
struct device *dev;
unsigned long flags;
-#ifdef CONFIG_FIREWALL
- if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL) != FW_ACCEPT)
+ if (call_in_firewall(PF_ROSE, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
return 0;
-#endif
frametype = skb->data[2];
lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
+ src_addr = (rose_address *)(skb->data + 9);
+ dest_addr = (rose_address *)(skb->data + 4);
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
- if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->device == rose_neigh->dev)
+ if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev)
break;
if (rose_neigh == NULL)
return 0;
/*
+ * Obviously the link is working, halt the ftimer.
+ */
+ rose_neigh->ftimer = 0;
+
+ /*
* LCI of zero is always for us, and its always a restart
* frame.
*/
@@ -549,12 +585,9 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
/*
* Is is a Call Request and is it for us ?
*/
- if (frametype == ROSE_CALL_REQUEST) {
- dest_addr = (rose_address *)(skb->data + 4);
-
+ if (frametype == ROSE_CALL_REQUEST)
if ((dev = rose_dev_get(dest_addr)) != NULL)
return rose_rx_call_request(skb, dev, rose_neigh, lci);
- }
if (!sysctl_rose_routing_control) {
rose_transmit_clear_request(rose_neigh, lci, 0x0D);
@@ -598,18 +631,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
if (frametype != ROSE_CALL_REQUEST) /* XXX */
return 0;
- dest_addr = (rose_address *)(skb->data + 4);
-
- /*
- * Create a new route entry, if we can.
- */
- for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next)
- if (rosecmp(&rose_node->address, dest_addr) == 0)
- break;
- /*
- * Its an unknown node, or is unreachable.
- */
- if (rose_node == NULL || rose_node->which >= rose_node->count) {
+ if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) {
rose_transmit_clear_request(rose_neigh, lci, 0x0D);
return 0;
}
@@ -619,8 +641,6 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
return 0;
}
- new_neigh = rose_node->neighbour[rose_node->which];
-
rose_route->lci1 = lci;
rose_route->neigh1 = rose_neigh;
rose_route->lci2 = rose_new_lci(new_neigh->dev);
@@ -651,12 +671,12 @@ int rose_nodes_get_info(char *buffer, char **start, off_t offset,
cli();
- len += sprintf(buffer, "address w n neigh neigh neigh\n");
+ len += sprintf(buffer, "address mask n neigh neigh neigh\n");
for (rose_node = rose_node_list; rose_node != NULL; rose_node = rose_node->next) {
- len += sprintf(buffer + len, "%-10s %d %d",
+ len += sprintf(buffer + len, "%-10s %04d %d",
rose2asc(&rose_node->address),
- rose_node->which + 1,
+ rose_node->mask,
rose_node->count);
for (i = 0; i < rose_node->count; i++)
@@ -696,17 +716,17 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset,
cli();
- len += sprintf(buffer, "addr callsign dev count restart t0\n");
+ len += sprintf(buffer, "addr callsign dev count restart t0 tf\n");
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
- len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3d/%03d\n",
+ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3d %3d\n",
rose_neigh->number,
ax2asc(&rose_neigh->callsign),
rose_neigh->dev ? rose_neigh->dev->name : "???",
rose_neigh->count,
(rose_neigh->restarted) ? "yes" : "no",
- rose_neigh->t0timer / PR_SLOWHZ,
- rose_neigh->t0 / PR_SLOWHZ);
+ rose_neigh->t0timer / ROSE_SLOWHZ,
+ rose_neigh->ftimer / ROSE_SLOWHZ);
pos = begin + len;
@@ -714,7 +734,7 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset,
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -757,7 +777,7 @@ int rose_routes_get_info(char *buffer, char **start, off_t offset,
len = 0;
begin = pos;
}
-
+
if (pos > offset + length)
break;
}
@@ -786,21 +806,21 @@ void rose_rt_free(void)
while (rose_neigh != NULL) {
s = rose_neigh;
rose_neigh = rose_neigh->next;
-
+
rose_remove_neigh(s);
}
while (rose_node != NULL) {
t = rose_node;
rose_node = rose_node->next;
-
+
rose_remove_node(t);
}
while (rose_route != NULL) {
u = rose_route;
rose_route = rose_route->next;
-
+
rose_remove_route(u);
}
}
diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c
index 0c1c83fa8..5befcbb60 100644
--- a/net/rose/rose_subr.c
+++ b/net/rose/rose_subr.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -15,7 +15,7 @@
* History
* Rose 001 Jonathan(G4KLX) Cloned from nr_subr.c
*/
-
+
#include <linux/config.h>
#if defined(CONFIG_ROSE) || defined(CONFIG_ROSE_MODULE)
#include <linux/errno.h>
@@ -47,62 +47,11 @@ void rose_clear_queues(struct sock *sk)
{
struct sk_buff *skb;
- while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
- skb->sk = sk;
- skb->free = 1;
- kfree_skb(skb, FREE_WRITE);
- }
-
- while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) {
- skb->sk = sk;
- skb->free = 1;
+ while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- }
- while ((skb = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL) {
+ while ((skb = skb_dequeue(&sk->protinfo.rose->frag_queue)) != NULL)
kfree_skb(skb, FREE_READ);
- }
-}
-
-/*
- * This routine purges the input queue of those frames that have been
- * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
- * SDL diagram.
- */
-void rose_frames_acked(struct sock *sk, unsigned short nr)
-{
- struct sk_buff *skb;
-
- /*
- * Remove all the ack-ed frames from the ack queue.
- */
- if (sk->protinfo.rose->va != nr) {
- while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) {
- skb = skb_dequeue(&sk->protinfo.rose->ack_queue);
- skb->sk = sk;
- skb->free = 1;
- kfree_skb(skb, FREE_WRITE);
- sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS;
- }
- }
-}
-
-/*
- * Requeue all the un-ack-ed frames on the output queue to be picked
- * up by rose_kick called from the timer. This arrangement handles the
- * possibility of an empty output queue.
- */
-void rose_requeue_frames(struct sock *sk)
-{
- struct sk_buff *skb, *skb_prev = NULL;
-
- while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) {
- if (skb_prev == NULL)
- skb_queue_head(&sk->write_queue, skb);
- else
- skb_append(skb_prev, skb);
- skb_prev = skb;
- }
}
/*
@@ -117,7 +66,7 @@ int rose_validate_nr(struct sock *sk, unsigned short nr)
if (nr == vc) return 1;
vc = (vc + 1) % ROSE_MODULUS;
}
-
+
if (nr == sk->protinfo.rose->vs) return 1;
return 0;
@@ -136,7 +85,7 @@ void rose_write_internal(struct sock *sk, int frametype)
int len, faclen = 0;
len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
-
+
switch (frametype) {
case ROSE_CALL_REQUEST:
len += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
@@ -153,7 +102,7 @@ void rose_write_internal(struct sock *sk, int frametype)
len += 1;
break;
}
-
+
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
return;
@@ -168,9 +117,9 @@ void rose_write_internal(struct sock *sk, int frametype)
lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF;
switch (frametype) {
-
+
case ROSE_CALL_REQUEST:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
*dptr++ = 0xAA;
@@ -183,7 +132,7 @@ void rose_write_internal(struct sock *sk, int frametype)
break;
case ROSE_CALL_ACCEPTED:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
*dptr++ = 0x00; /* Address length */
@@ -192,7 +141,7 @@ void rose_write_internal(struct sock *sk, int frametype)
case ROSE_CLEAR_REQUEST:
case ROSE_RESET_REQUEST:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
*dptr++ = 0x00; /* XXX */
@@ -200,7 +149,7 @@ void rose_write_internal(struct sock *sk, int frametype)
break;
case ROSE_INTERRUPT:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
*dptr++ = 0x00; /* XXX */
@@ -209,16 +158,16 @@ void rose_write_internal(struct sock *sk, int frametype)
case ROSE_RR:
case ROSE_RNR:
case ROSE_REJ:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr = frametype;
*dptr++ |= (sk->protinfo.rose->vr << 5) & 0xE0;
break;
-
+
case ROSE_CLEAR_CONFIRMATION:
case ROSE_INTERRUPT_CONFIRMATION:
case ROSE_RESET_CONFIRMATION:
- *dptr++ = GFI | lci1;
+ *dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
break;
@@ -235,11 +184,11 @@ void rose_write_internal(struct sock *sk, int frametype)
int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
{
unsigned char *frame;
-
+
frame = skb->data;
-
+
*ns = *nr = *q = *d = *m = 0;
-
+
switch (frame[2]) {
case ROSE_CALL_REQUEST:
case ROSE_CALL_ACCEPTED:
@@ -267,14 +216,14 @@ int rose_decode(struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
}
if ((frame[2] & 0x01) == ROSE_DATA) {
- *q = (frame[0] & Q_BIT) == Q_BIT;
- *d = (frame[0] & D_BIT) == D_BIT;
- *m = (frame[2] & M_BIT) == M_BIT;
+ *q = (frame[0] & ROSE_Q_BIT) == ROSE_Q_BIT;
+ *d = (frame[0] & ROSE_D_BIT) == ROSE_D_BIT;
+ *m = (frame[2] & ROSE_M_BIT) == ROSE_M_BIT;
*nr = (frame[2] >> 5) & 0x07;
*ns = (frame[2] >> 1) & 0x07;
return ROSE_DATA;
}
-
+
return ROSE_ILLEGAL;
}
@@ -289,7 +238,7 @@ static int rose_parse_national(unsigned char *p, rose_cb *rose, int len)
n += 2;
len -= 2;
break;
-
+
case 0x40:
if (*p == FAC_NATIONAL_RAND)
rose->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF);
@@ -303,7 +252,7 @@ static int rose_parse_national(unsigned char *p, rose_cb *rose, int len)
n += 4;
len -= 4;
break;
-
+
case 0xC0:
l = p[1];
if (*p == FAC_NATIONAL_DEST_DIGI) {
@@ -336,7 +285,7 @@ static int rose_parse_ccitt(unsigned char *p, rose_cb *rose, int len)
n += 2;
len -= 2;
break;
-
+
case 0x40:
p += 3;
n += 3;
@@ -348,7 +297,7 @@ static int rose_parse_ccitt(unsigned char *p, rose_cb *rose, int len)
n += 4;
len -= 4;
break;
-
+
case 0xC0:
l = p[1];
if (*p == FAC_CCITT_DEST_NSAP) {
@@ -386,7 +335,7 @@ int rose_parse_facilities(struct sk_buff *skb, rose_cb *rose)
p = skb->data + len + 4;
facilities_len = *p++;
-
+
if (facilities_len == 0)
return 0;
@@ -401,13 +350,13 @@ int rose_parse_facilities(struct sk_buff *skb, rose_cb *rose)
facilities_len -= len + 1;
p += len + 1;
break;
-
+
case FAC_CCITT: /* CCITT */
len = rose_parse_ccitt(p + 1, rose, facilities_len - 1);
facilities_len -= len + 1;
p += len + 1;
break;
-
+
default:
printk(KERN_DEBUG "rose_parse_facilities: unknown facilities family %02X\n", *p);
facilities_len--;
@@ -425,7 +374,7 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
unsigned char *p = buffer + 1;
char *callsign;
int len;
-
+
/* National Facilities */
if (rose->rand != 0 || rose->source_ndigis == 1 || rose->dest_ndigis == 1) {
*p++ = 0x00;
@@ -436,7 +385,7 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
*p++ = (rose->rand >> 8) & 0xFF;
*p++ = (rose->rand >> 0) & 0xFF;
}
-
+
if (rose->source_ndigis == 1) {
*p++ = FAC_NATIONAL_SRC_DIGI;
*p++ = AX25_ADDR_LEN;
@@ -454,9 +403,9 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
*p++ = 0x00;
*p++ = FAC_CCITT;
-
+
*p++ = FAC_CCITT_DEST_NSAP;
-
+
callsign = ax2asc(&rose->dest_call);
*p++ = strlen(callsign) + 10;
@@ -469,7 +418,7 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
memcpy(p, callsign, strlen(callsign));
p += strlen(callsign);
-
+
*p++ = FAC_CCITT_SRC_NSAP;
callsign = ax2asc(&rose->source_call);
@@ -487,7 +436,7 @@ int rose_create_facilities(unsigned char *buffer, rose_cb *rose)
len = p - buffer;
buffer[0] = len - 1;
-
+
return len;
}
diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c
index 313756847..869f312f5 100644
--- a/net/rose/rose_timer.c
+++ b/net/rose/rose_timer.c
@@ -4,7 +4,7 @@
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
- * This code REQUIRES 2.1.0 or higher/ NET3.029
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
*
* This module:
* This module is free software; you can redistribute it and/or
@@ -43,37 +43,20 @@
static void rose_timer(unsigned long);
/*
- * Linux set/reset timer routines
+ * Linux set timer
*/
void rose_set_timer(struct sock *sk)
{
unsigned long flags;
-
- save_flags(flags);
- cli();
- del_timer(&sk->timer);
- restore_flags(flags);
-
- sk->timer.next = sk->timer.prev = NULL;
- sk->timer.data = (unsigned long)sk;
- sk->timer.function = &rose_timer;
-
- sk->timer.expires = jiffies + 10;
- add_timer(&sk->timer);
-}
-static void rose_reset_timer(struct sock *sk)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
del_timer(&sk->timer);
restore_flags(flags);
sk->timer.data = (unsigned long)sk;
sk->timer.function = &rose_timer;
- sk->timer.expires = jiffies + 10;
+ sk->timer.expires = jiffies + (HZ / 10);
+
add_timer(&sk->timer);
}
@@ -102,9 +85,12 @@ static void rose_timer(unsigned long param)
/*
* Check for the state of the receive buffer.
*/
- if (sk->rmem_alloc < (sk->rcvbuf / 2) && (sk->protinfo.rose->condition & OWN_RX_BUSY_CONDITION)) {
- sk->protinfo.rose->condition &= ~OWN_RX_BUSY_CONDITION;
+ if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) &&
+ (sk->protinfo.rose->condition & ROSE_COND_OWN_RX_BUSY)) {
+ sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY;
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->timer = 0;
rose_write_internal(sk, ROSE_RR);
break;
}
@@ -119,15 +105,22 @@ static void rose_timer(unsigned long param)
}
if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) {
- rose_reset_timer(sk);
+ rose_set_timer(sk);
return;
}
/*
- * Timer has expired, it may have been T1, T2, or T3. We can tell
+ * Timer has expired, it may have been T1, T2, T3 or HB. We can tell
* by the socket state.
*/
switch (sk->protinfo.rose->state) {
+ case ROSE_STATE_3: /* HB */
+ if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) {
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+ rose_enquiry_response(sk);
+ }
+ break;
+
case ROSE_STATE_1: /* T1 */
case ROSE_STATE_4: /* T2 */
rose_write_internal(sk, ROSE_CLEAR_REQUEST);
diff --git a/net/rose/sysctl_net_rose.c b/net/rose/sysctl_net_rose.c
index 558702dbd..c899a1837 100644
--- a/net/rose/sysctl_net_rose.c
+++ b/net/rose/sysctl_net_rose.c
@@ -10,12 +10,13 @@
#include <net/ax25.h>
#include <net/rose.h>
-static int min_timer[] = {1 * PR_SLOWHZ};
-static int max_timer[] = {300 * PR_SLOWHZ};
-static int min_idle[] = {0 * PR_SLOWHZ};
-static int max_idle[] = {65535 * PR_SLOWHZ};
-static int min_route[] = {0};
-static int max_route[] = {0};
+static int min_timer[] = {1 * ROSE_SLOWHZ};
+static int max_timer[] = {300 * ROSE_SLOWHZ};
+static int min_idle[] = {0 * ROSE_SLOWHZ};
+static int max_idle[] = {65535 * ROSE_SLOWHZ};
+static int min_route[] = {0}, max_route[] = {1};
+static int min_ftimer[] = {60 * ROSE_SLOWHZ};
+static int max_ftimer[] = {600 * ROSE_SLOWHZ};
static struct ctl_table_header *rose_table_header;
@@ -35,9 +36,15 @@ static ctl_table rose_table[] = {
{NET_ROSE_NO_ACTIVITY_TIMEOUT, "no_activity_timeout",
&sysctl_rose_no_activity_timeout, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_idle, &max_idle},
+ {NET_ROSE_ACK_HOLD_BACK_TIMEOUT, "acknowledge_hold_back_timeout",
+ &sysctl_rose_ack_hold_back_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
{NET_ROSE_ROUTING_CONTROL, "routing_control",
&sysctl_rose_routing_control, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL, &min_route, &max_route},
+ {NET_ROSE_LINK_FAIL_TIMEOUT, "link_fail_timeout",
+ &sysctl_rose_link_fail_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_ftimer, &max_ftimer},
{0}
};
diff --git a/net/socket.c b/net/socket.c
index e96ec9d05..2e53ed446 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -35,6 +35,10 @@
* Alan Cox : sendmsg/recvmsg basics.
* Tom Dyas : Export net symbols.
* Marcin Dalecki : Fixed problems with CONFIG_NET="n".
+ * Alan Cox : Added thread locking to sys_* calls
+ * for sockets. May have errors at the
+ * moment.
+ * Kevin Buhr : Fixed the dumb errors in the above.
*
*
* This program is free software; you can redistribute it and/or
@@ -44,11 +48,8 @@
*
*
* This module is effectively the top level interface to the BSD socket
- * paradigm. Because it is very simple it works well for Unix domain sockets,
- * but requires a whole layer of substructure for the other protocols.
+ * paradigm.
*
- * In addition it lacks an effective kernel -> kernel interface to go with
- * the user one.
*/
#include <linux/config.h>
@@ -56,16 +57,20 @@
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/fcntl.h>
+#include <linux/file.h>
#include <linux/net.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/firewall.h>
+#include <linux/wanrouter.h>
#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
#include <linux/kerneld.h>
@@ -76,9 +81,17 @@
#include <asm/system.h>
#include <asm/uaccess.h>
-#if defined(CONFIG_MODULES) && defined(CONFIG_NET)
-extern void export_net_symbols(void);
-#endif
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/rarp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/scm.h>
+
static long long sock_lseek(struct inode *inode, struct file *file,
long long offset, int whence);
@@ -87,8 +100,8 @@ static long sock_read(struct inode *inode, struct file *file,
static long sock_write(struct inode *inode, struct file *file,
const char *buf, unsigned long size);
-static void sock_close(struct inode *inode, struct file *file);
-static int sock_select(struct inode *inode, struct file *file, int which, select_table *seltable);
+static int sock_close(struct inode *inode, struct file *file);
+static unsigned int sock_poll(struct file *file, poll_table *wait);
static int sock_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg);
static int sock_fasync(struct inode *inode, struct file *filp, int on);
@@ -104,7 +117,7 @@ static struct file_operations socket_file_ops = {
sock_read,
sock_write,
NULL, /* readdir */
- sock_select,
+ sock_poll,
sock_ioctl,
NULL, /* mmap */
NULL, /* no special open code... */
@@ -116,10 +129,13 @@ static struct file_operations socket_file_ops = {
/*
* The protocol list. Each protocol is registered in here.
*/
-static struct proto_ops *pops[NPROTO];
+
+struct net_proto_family *net_families[NPROTO];
+
/*
* Statistics counters of the socket lists
*/
+
static int sockets_in_use = 0;
/*
@@ -147,7 +163,7 @@ int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen)
{
int err;
int len;
-
+
if((err=get_user(len, ulen)))
return err;
if(len>klen)
@@ -200,15 +216,7 @@ static int get_fd(struct inode *inode)
return fd;
}
-
-/*
- * Go from an inode to its socket slot.
- *
- * The original socket implementation wasn't very clever, which is
- * why this exists at all..
- */
-
-__inline struct socket *socki_lookup(struct inode *inode)
+extern __inline__ struct socket *socki_lookup(struct inode *inode)
{
return &inode->u.socket_i;
}
@@ -217,24 +225,33 @@ __inline struct socket *socki_lookup(struct inode *inode)
* Go from a file number to its socket slot.
*/
-extern __inline struct socket *sockfd_lookup(int fd, struct file **pfile)
+extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
{
struct file *file;
struct inode *inode;
- if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd]))
+ if (!(file = fget(fd)))
+ {
+ *err = -EBADF;
return NULL;
+ }
inode = file->f_inode;
- if (!inode || !inode->i_sock)
+ if (!inode || !inode->i_sock || !socki_lookup(inode))
+ {
+ *err = -ENOTSOCK;
+ fput(file,inode);
return NULL;
-
- if (pfile)
- *pfile = file;
+ }
return socki_lookup(inode);
}
+extern __inline__ void sockfd_put(struct socket *sock)
+{
+ fput(sock->file,sock->inode);
+}
+
/*
* Allocate a socket.
*/
@@ -247,78 +264,86 @@ struct socket *sock_alloc(void)
inode = get_empty_inode();
if (!inode)
return NULL;
+ sock = socki_lookup(inode);
inode->i_mode = S_IFSOCK;
inode->i_sock = 1;
inode->i_uid = current->uid;
inode->i_gid = current->gid;
- sock = &inode->u.socket_i;
+ sock->inode = inode;
+ init_waitqueue(&sock->wait);
+ sock->fasync_list = NULL;
sock->state = SS_UNCONNECTED;
sock->flags = 0;
sock->ops = NULL;
- sock->data = NULL;
- sock->conn = NULL;
- sock->iconn = NULL;
- sock->next = NULL;
+ sock->sk = NULL;
sock->file = NULL;
- sock->wait = &inode->i_wait;
- sock->inode = inode; /* "backlink": we could use pointer arithmetic instead */
- sock->fasync_list = NULL;
+
sockets_in_use++;
return sock;
}
-/*
- * Release a socket.
- */
-
-static inline void sock_release_peer(struct socket *peer)
-{
- peer->state = SS_DISCONNECTING;
- wake_up_interruptible(peer->wait);
- sock_wake_async(peer, 1);
-}
-
void sock_release(struct socket *sock)
{
int oldstate;
- struct socket *peersock, *nextsock;
if ((oldstate = sock->state) != SS_UNCONNECTED)
sock->state = SS_DISCONNECTING;
- /*
- * Wake up anyone waiting for connections.
- */
-
- for (peersock = sock->iconn; peersock; peersock = nextsock)
- {
- nextsock = peersock->next;
- sock_release_peer(peersock);
- }
-
- /*
- * Wake up anyone we're connected to. First, we release the
- * protocol, to give it a chance to flush data, etc.
- */
-
- peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
if (sock->ops)
- sock->ops->release(sock, peersock);
- if (peersock)
- sock_release_peer(peersock);
+ sock->ops->release(sock, NULL);
+
--sockets_in_use; /* Bookkeeping.. */
sock->file=NULL;
- iput(SOCK_INODE(sock));
+ iput(sock->inode);
+}
+
+int sock_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+ int err;
+ struct scm_cookie scm;
+
+ if (!sock->ops->sendmsg)
+ return -EOPNOTSUPP;
+
+ err = scm_send(sock, msg, &scm);
+ if (err < 0)
+ return err;
+
+ err = sock->ops->sendmsg(sock, msg, size, &scm);
+
+ scm_destroy(&scm);
+
+ return err;
}
+int sock_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags)
+{
+ struct scm_cookie scm;
+
+ if (!sock->ops->recvmsg)
+ return -EOPNOTSUPP;
+
+ memset(&scm, 0, sizeof(scm));
+
+ size = sock->ops->recvmsg(sock, msg, size, flags, &scm);
+
+ if (size < 0)
+ return size;
+
+ scm_recv(sock, msg, &scm, flags);
+
+ return size;
+}
+
+
/*
* Sockets are not seekable.
*/
static long long sock_lseek(struct inode *inode, struct file *file,
- long long offset, int whence)
+ long long offset, int whence)
{
return -ESPIPE;
}
@@ -329,40 +354,40 @@ static long long sock_lseek(struct inode *inode, struct file *file,
*/
static long sock_read(struct inode *inode, struct file *file,
- char *ubuf, unsigned long size)
+ char *ubuf, unsigned long size)
{
struct socket *sock;
int err;
struct iovec iov;
struct msghdr msg;
-
- sock = socki_lookup(inode);
- if (sock->flags & SO_ACCEPTCON)
- return(-EINVAL);
- if(size<0)
- return -EINVAL;
- if(size==0) /* Match SYS5 behaviour */
+ sock = socki_lookup(inode);
+
+ if (size==0) /* Match SYS5 behaviour */
return 0;
if ((err=verify_area(VERIFY_WRITE,ubuf,size))<0)
return err;
msg.msg_name=NULL;
+ msg.msg_namelen=0;
msg.msg_iov=&iov;
msg.msg_iovlen=1;
msg.msg_control=NULL;
+ msg.msg_controllen=0;
iov.iov_base=ubuf;
iov.iov_len=size;
- return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), 0,&msg.msg_namelen));
+ return sock_recvmsg(sock, &msg, size,
+ !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT);
}
+
/*
* Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is
* readable by the user process.
*/
static long sock_write(struct inode *inode, struct file *file,
- const char *ubuf, unsigned long size)
+ const char *ubuf, unsigned long size)
{
struct socket *sock;
int err;
@@ -371,11 +396,6 @@ static long sock_write(struct inode *inode, struct file *file,
sock = socki_lookup(inode);
- if (sock->flags & SO_ACCEPTCON)
- return(-EINVAL);
-
- if(size<0)
- return -EINVAL;
if(size==0) /* Match SYS5 behaviour */
return 0;
@@ -383,15 +403,41 @@ static long sock_write(struct inode *inode, struct file *file,
return err;
msg.msg_name=NULL;
+ msg.msg_namelen=0;
msg.msg_iov=&iov;
msg.msg_iovlen=1;
msg.msg_control=NULL;
+ msg.msg_controllen=0;
+ msg.msg_flags=!(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
iov.iov_base=(void *)ubuf;
iov.iov_len=size;
- return(sock->ops->sendmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK),0));
+ return sock_sendmsg(sock, &msg, size);
}
+int sock_readv_writev(int type, struct inode * inode, struct file * file,
+ const struct iovec * iov, long count, long size)
+{
+ struct msghdr msg;
+ struct socket *sock;
+
+ sock = socki_lookup(inode);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_iov = (struct iovec *) iov;
+ msg.msg_iovlen = count;
+ msg.msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+
+ /* read() does a VERIFY_WRITE */
+ if (type == VERIFY_WRITE)
+ return sock_recvmsg(sock, &msg, size, msg.msg_flags);
+ return sock_sendmsg(sock, &msg, size);
+}
+
+
/*
* With an ioctl arg may well be a user mode pointer, but we don't know what to do
* with it - that's up to the protocol still.
@@ -400,38 +446,42 @@ static long sock_write(struct inode *inode, struct file *file,
int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- struct socket *sock;
- sock = socki_lookup(inode);
- return(sock->ops->ioctl(sock, cmd, arg));
+ struct socket *sock = socki_lookup(inode);
+ return sock->ops->ioctl(sock, cmd, arg);
}
-static int sock_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
+static unsigned int sock_poll(struct file *file, poll_table * wait)
{
struct socket *sock;
- sock = socki_lookup(inode);
+ sock = socki_lookup(file->f_inode);
/*
- * We can't return errors to select, so it's either yes or no.
+ * We can't return errors to poll, so it's either yes or no.
*/
- if (sock->ops->select)
- return(sock->ops->select(sock, sel_type, wait));
- return(0);
+ if (sock->ops->poll)
+ return sock->ops->poll(sock, wait);
+ return 0;
}
-void sock_close(struct inode *inode, struct file *filp)
+int sock_close(struct inode *inode, struct file *filp)
{
/*
- * It's possible the inode is NULL if we're closing an unfinished socket.
+ * It was possible the inode is NULL we were
+ * closing an unfinished socket.
*/
- if (!inode)
- return;
+ if (!inode)
+ {
+ printk(KERN_DEBUG "sock_close: NULL inode\n");
+ return 0;
+ }
sock_fasync(inode, filp, 0);
sock_release(socki_lookup(inode));
+ return 0;
}
/*
@@ -458,8 +508,8 @@ static int sock_fasync(struct inode *inode, struct file *filp, int on)
save_flags(flags);
cli();
- for(fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
- if(fa->fa_file==filp)
+ for (fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
+ if (fa->fa_file==filp)
break;
if(on)
@@ -477,7 +527,7 @@ static int sock_fasync(struct inode *inode, struct file *filp, int on)
}
else
{
- if(fa!=NULL)
+ if (fa!=NULL)
{
*prev=fa->fa_next;
kfree_s(fa,sizeof(struct fasync_struct));
@@ -512,33 +562,17 @@ int sock_wake_async(struct socket *sock, int how)
}
-/*
- * Perform the socket system call. we locate the appropriate
- * family, then create a fresh socket.
- */
-
-static int find_protocol_family(int family)
+int sock_create(int family, int type, int protocol, struct socket **res)
{
- register int i;
- for (i = 0; i < NPROTO; i++)
- {
- if (pops[i] == NULL)
- continue;
- if (pops[i]->family == family)
- return i;
- }
- return -1;
-}
-
-asmlinkage int sys_socket(int family, int type, int protocol)
-{
- int i, fd;
+ int i;
struct socket *sock;
- struct proto_ops *ops;
-
- /* Locate the correct protocol family. */
- i = find_protocol_family(family);
+ /*
+ * Check protocol is in range
+ */
+ if(family<0||family>=NPROTO)
+ return -EINVAL;
+
#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
/* Attempt to load a protocol module if the find failed.
*
@@ -546,21 +580,16 @@ asmlinkage int sys_socket(int family, int type, int protocol)
* requested real, full-featured networking support upon configuration.
* Otherwise module support will break!
*/
- if (i < 0)
+ if (net_families[family]==NULL)
{
char module_name[30];
sprintf(module_name,"net-pf-%d",family);
request_module(module_name);
- i = find_protocol_family(family);
}
#endif
- if (i < 0)
- {
+ if (net_families[family]==NULL)
return -EINVAL;
- }
-
- ops = pops[i];
/*
* Check that this is a type that we know how to manipulate and
@@ -569,9 +598,9 @@ asmlinkage int sys_socket(int family, int type, int protocol)
*/
if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
- type != SOCK_SEQPACKET && type != SOCK_RAW &&
- type != SOCK_PACKET) || protocol < 0)
- return(-EINVAL);
+ type != SOCK_SEQPACKET && type != SOCK_RAW &&
+ type != SOCK_PACKET) || protocol < 0)
+ return -EINVAL;
/*
* Allocate the socket and allow the family to set things up. if
@@ -582,27 +611,46 @@ asmlinkage int sys_socket(int family, int type, int protocol)
if (!(sock = sock_alloc()))
{
printk(KERN_WARNING "socket: no more sockets\n");
- return(-ENOSR); /* Was: EAGAIN, but we are out of
- system resources! */
+ return -ENFILE; /* Not exactly a match, but its the
+ closest posix thing */
}
sock->type = type;
- sock->ops = ops;
- if ((i = sock->ops->create(sock, protocol)) < 0)
+
+ if ((i = net_families[family]->create(sock, protocol)) < 0)
{
sock_release(sock);
- return(i);
+ return i;
}
- if ((fd = get_fd(SOCK_INODE(sock))) < 0)
+ *res = sock;
+ return 0;
+}
+
+asmlinkage int sys_socket(int family, int type, int protocol)
+{
+ int fd, err;
+ struct socket *sock;
+
+ lock_kernel();
+
+ if ((err = sock_create(family, type, protocol, &sock)) < 0)
+ goto out;
+
+ if ((fd = get_fd(sock->inode)) < 0)
{
sock_release(sock);
- return(-EINVAL);
+ err = -EINVAL;
+ }
+ else
+ {
+ sock->file = current->files->fd[fd];
+ err = fd;
}
- sock->file=current->files->fd[fd];
-
- return(fd);
+out:
+ unlock_kernel();
+ return err;
}
/*
@@ -612,54 +660,67 @@ asmlinkage int sys_socket(int family, int type, int protocol)
asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2])
{
int fd1, fd2, i;
- struct socket *sock1, *sock2;
- int er;
+ struct socket *sock1=NULL, *sock2=NULL;
+ int err;
+
+ lock_kernel();
/*
* Obtain the first socket and check if the underlying protocol
* supports the socketpair call.
*/
- if ((fd1 = sys_socket(family, type, protocol)) < 0)
- return(fd1);
- sock1 = sockfd_lookup(fd1, NULL);
+ if ((fd1 = sys_socket(family, type, protocol)) < 0) {
+ err = fd1;
+ goto out;
+ }
+
+ sock1 = sockfd_lookup(fd1, &err);
+ if (!sock1)
+ goto out;
+ err = -EOPNOTSUPP;
if (!sock1->ops->socketpair)
{
sys_close(fd1);
- return(-EINVAL);
+ goto out;
}
/*
* Now grab another socket and try to connect the two together.
*/
-
+ err = -EINVAL;
if ((fd2 = sys_socket(family, type, protocol)) < 0)
{
sys_close(fd1);
- return(-EINVAL);
+ goto out;
}
- sock2 = sockfd_lookup(fd2, NULL);
+ sock2 = sockfd_lookup(fd2,&err);
+ if (!sock2)
+ goto out;
if ((i = sock1->ops->socketpair(sock1, sock2)) < 0)
{
sys_close(fd1);
sys_close(fd2);
- return(i);
+ err = i;
}
-
- sock1->conn = sock2;
- sock2->conn = sock1;
- sock1->state = SS_CONNECTED;
- sock2->state = SS_CONNECTED;
-
- er = put_user(fd1, &usockvec[0]);
- if (!er)
- er = put_user(fd2, &usockvec[1]);
- if (er) {
- sys_close(fd1);
- sys_close(fd2);
+ else
+ {
+ err = put_user(fd1, &usockvec[0]);
+ if (!err)
+ err = put_user(fd2, &usockvec[1]);
+ if (err) {
+ sys_close(fd1);
+ sys_close(fd2);
+ }
}
- return er;
+out:
+ if(sock1)
+ sockfd_put(sock1);
+ if(sock2)
+ sockfd_put(sock2);
+ unlock_kernel();
+ return err;
}
@@ -674,24 +735,18 @@ asmlinkage int sys_socketpair(int family, int type, int protocol, int usockvec[2
asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen)
{
struct socket *sock;
- int i;
char address[MAX_SOCK_ADDR];
int err;
- if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
- return(-EBADF);
-
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
- return err;
-
- if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0)
+ lock_kernel();
+ if((sock = sockfd_lookup(fd,&err))!=NULL)
{
- return(i);
- }
- return(0);
+ if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0)
+ err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
@@ -704,23 +759,16 @@ asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen)
asmlinkage int sys_listen(int fd, int backlog)
{
struct socket *sock;
- int err=-EOPNOTSUPP;
+ int err;
- if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if (sock->state != SS_UNCONNECTED)
- return(-EINVAL);
-
- if (sock->ops && sock->ops->listen)
+ lock_kernel();
+ if((sock = sockfd_lookup(fd, &err))!=NULL)
{
err=sock->ops->listen(sock, backlog);
- if(!err)
- sock->flags |= SO_ACCEPTCON;
+ sockfd_put(sock);
}
- return(err);
+ unlock_kernel();
+ return err;
}
@@ -731,66 +779,66 @@ asmlinkage int sys_listen(int fd, int backlog)
* space and move it to user at the very end. This is unclean because
* we open the socket then return an error.
*
- * 1003.1g addcs the ability to recvmsg() to query connection pending
+ * 1003.1g adds the ability to recvmsg() to query connection pending
* status to recvmsg. We need to add that support in a way thats
* clean when we restucture accept also.
*/
asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
{
- struct file *file;
+ struct inode *inode;
struct socket *sock, *newsock;
- int i;
+ int err;
char address[MAX_SOCK_ADDR];
int len;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, &file)))
- return(-ENOTSOCK);
- if (sock->state != SS_UNCONNECTED)
- {
- return(-EINVAL);
- }
- if (!(sock->flags & SO_ACCEPTCON))
- {
- return(-EINVAL);
- }
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if (!(newsock = sock_alloc()))
+ {
+ printk(KERN_WARNING "accept: no more sockets\n");
+ err=-EMFILE;
+ goto out;
+ }
- if (!(newsock = sock_alloc()))
- {
- printk(KERN_WARNING "accept: no more sockets\n");
- return(-ENOSR); /* Was: EAGAIN, but we are out of system
- resources! */
- }
- newsock->type = sock->type;
- newsock->ops = sock->ops;
- if ((i = sock->ops->dup(newsock, sock)) < 0)
- {
- sock_release(newsock);
- return(i);
- }
+ inode = newsock->inode;
+ newsock->type = sock->type;
- i = newsock->ops->accept(sock, newsock, file->f_flags);
- if ( i < 0)
- {
- sock_release(newsock);
- return(i);
- }
+ if ((err = sock->ops->dup(newsock, sock)) < 0)
+ {
+ sock_release(newsock);
+ goto out;
+ }
- if ((fd = get_fd(SOCK_INODE(newsock))) < 0)
- {
- sock_release(newsock);
- return(-EINVAL);
- }
- newsock->file=current->files->fd[fd];
+ err = newsock->ops->accept(sock, newsock, current->files->fd[fd]->f_flags);
+
+ if (err < 0)
+ {
+ sock_release(newsock);
+ goto out;
+ }
+ newsock = socki_lookup(inode);
+
+ if ((err = get_fd(inode)) < 0)
+ {
+ sock_release(newsock);
+ err=-EINVAL;
+ goto out;
+ }
+
+ newsock->file = current->files->fd[err];
- if (upeer_sockaddr)
- {
- newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
- move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
+ if (upeer_sockaddr)
+ {
+ newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
+ move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
+ }
+out:
+ sockfd_put(sock);
}
- return(fd);
+ unlock_kernel();
+ return err;
}
@@ -809,47 +857,19 @@ asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_ad
asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
{
struct socket *sock;
- struct file *file;
- int i;
char address[MAX_SOCK_ADDR];
int err;
- if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL)
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, &file)))
- return(-ENOTSOCK);
-
- if((err=move_addr_to_kernel(uservaddr,addrlen,address))<0)
- return err;
-
- switch(sock->state)
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd,&err))!=NULL)
{
- case SS_UNCONNECTED:
- /* This is ok... continue with connect */
- break;
- case SS_CONNECTED:
- /* Socket is already connected */
- if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */
- break;
- return -EISCONN;
- case SS_CONNECTING:
- /* Not yet connected... we will check this. */
-
- /*
- * FIXME: for all protocols what happens if you start
- * an async connect fork and both children connect. Clean
- * this up in the protocols!
- */
- break;
- default:
- return(-EINVAL);
- }
- i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, file->f_flags);
- if (i < 0)
- {
- return(i);
+ if((err=move_addr_to_kernel(uservaddr,addrlen,address))>=0)
+ err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
+ current->files->fd[fd]->f_flags);
+ sockfd_put(sock);
}
- return(0);
+ unlock_kernel();
+ return err;
}
/*
@@ -864,17 +884,15 @@ asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockadd
int len;
int err;
- if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
- if(err)
- return err;
- if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
- return err;
- return 0;
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if((err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0))==0)
+ err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
/*
@@ -889,17 +907,15 @@ asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockadd
int len;
int err;
- if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
- if(err)
- return err;
- if((err=move_addr_to_user(address,len, usockaddr, usockaddr_len))<0)
- return err;
- return 0;
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if((err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1))==0)
+ err=move_addr_to_user(address,len, usockaddr, usockaddr_len);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
/*
@@ -910,29 +926,34 @@ asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockadd
asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags)
{
struct socket *sock;
- struct file *file;
int err;
struct msghdr msg;
struct iovec iov;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if(len<0)
- return -EINVAL;
- err=verify_area(VERIFY_READ, buff, len);
- if(err)
- return err;
-
- iov.iov_base=buff;
- iov.iov_len=len;
- msg.msg_name=NULL;
- msg.msg_iov=&iov;
- msg.msg_iovlen=1;
- msg.msg_control=NULL;
- return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if(len>=0)
+ {
+ iov.iov_base=buff;
+ iov.iov_len=len;
+ msg.msg_name=NULL;
+ msg.msg_namelen=0;
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+ msg.msg_control=NULL;
+ msg.msg_controllen=0;
+ if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
+ msg.msg_flags=flags;
+ err=sock_sendmsg(sock, &msg, len);
+ }
+ else
+ err=-EINVAL;
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
/*
@@ -945,78 +966,41 @@ asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags,
struct sockaddr *addr, int addr_len)
{
struct socket *sock;
- struct file *file;
char address[MAX_SOCK_ADDR];
int err;
struct msghdr msg;
struct iovec iov;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if(len<0)
- return -EINVAL;
- err=verify_area(VERIFY_READ,buff,len);
- if(err)
- return err;
-
- iov.iov_base=buff;
- iov.iov_len=len;
- msg.msg_name = NULL;
- msg.msg_namelen = 0;
- msg.msg_iov=&iov;
- msg.msg_iovlen=1;
- msg.msg_control=NULL;
- if (addr && addr_len) {
- err=move_addr_to_kernel(addr,addr_len,address);
- if (err < 0)
- return err;
- msg.msg_name=address;
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd,&err))!=NULL)
+ {
+ iov.iov_base=buff;
+ iov.iov_len=len;
+ msg.msg_name=NULL;
+ msg.msg_iov=&iov;
+ msg.msg_iovlen=1;
+ msg.msg_control=NULL;
+ msg.msg_controllen=0;
msg.msg_namelen=addr_len;
+ if(addr)
+ {
+ err=move_addr_to_kernel(addr,addr_len,address);
+ if (err < 0)
+ goto bad;
+ msg.msg_name=address;
+ }
+ if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
+ msg.msg_flags=flags;
+ err=sock_sendmsg(sock, &msg, len);
+bad:
+ sockfd_put(sock);
}
-
- return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK),
- flags));
+ unlock_kernel();
+ return err;
}
-/*
- * Receive a datagram from a socket. Call the protocol recvmsg method
- */
-
-asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags)
-{
- struct iovec iov;
- struct msghdr msg;
- struct socket *sock;
- struct file *file;
- int err;
-
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
-
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if(size<0)
- return -EINVAL;
- if(size==0)
- return 0;
- err=verify_area(VERIFY_WRITE, ubuf, size);
- if(err)
- return err;
-
- msg.msg_name=NULL;
- msg.msg_iov=&iov;
- msg.msg_iovlen=1;
- msg.msg_control=NULL;
- iov.iov_base=ubuf;
- iov.iov_len=size;
-
- return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen));
-}
/*
* Receive a frame from the socket and optionally record the address of the
@@ -1028,41 +1012,43 @@ asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags,
struct sockaddr *addr, int *addr_len)
{
struct socket *sock;
- struct file *file;
struct iovec iov;
struct msghdr msg;
char address[MAX_SOCK_ADDR];
- int err;
- int alen;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
- if(size<0)
- return -EINVAL;
- if(size==0)
- return 0;
+ int err,err2;
+
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ msg.msg_control=NULL;
+ msg.msg_controllen=0;
+ msg.msg_iovlen=1;
+ msg.msg_iov=&iov;
+ iov.iov_len=size;
+ iov.iov_base=ubuf;
+ msg.msg_name=address;
+ msg.msg_namelen=MAX_SOCK_ADDR;
+ err=sock_recvmsg(sock, &msg, size,
+ (current->files->fd[fd]->f_flags & O_NONBLOCK) ? (flags | MSG_DONTWAIT) : flags);
+ if(err>=0 && addr!=NULL)
+ {
+ err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len);
+ if(err2<0)
+ err=err2;
+ }
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
+}
- err=verify_area(VERIFY_WRITE,ubuf,size);
- if(err)
- return err;
-
- msg.msg_control=NULL;
- msg.msg_iovlen=1;
- msg.msg_iov=&iov;
- iov.iov_len=size;
- iov.iov_base=ubuf;
- msg.msg_name=address;
- msg.msg_namelen=MAX_SOCK_ADDR;
- size=sock->ops->recvmsg(sock, &msg, size, (file->f_flags & O_NONBLOCK),
- flags, &alen);
-
- if(size<0)
- return size;
- if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
- return err;
+/*
+ * Receive a datagram from a socket.
+ */
- return size;
+asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags)
+{
+ return sys_recvfrom(fd,ubuf,size,flags, NULL, NULL);
}
/*
@@ -1072,15 +1058,20 @@ asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags,
asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen)
{
+ int err;
struct socket *sock;
- struct file *file;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if (level == SOL_SOCKET)
+ err=sock_setsockopt(sock,level,optname,optval,optlen);
+ else
+ err=sock->ops->setsockopt(sock, level, optname, optval, optlen);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
/*
@@ -1090,17 +1081,20 @@ asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int
asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
{
+ int err;
struct socket *sock;
- struct file *file;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if (!sock->ops->getsockopt)
- return(0);
- return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ if (level == SOL_SOCKET)
+ err=sock_getsockopt(sock,level,optname,optval,optlen);
+ else
+ err=sock->ops->getsockopt(sock, level, optname, optval, optlen);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
@@ -1110,71 +1104,86 @@ asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int
asmlinkage int sys_shutdown(int fd, int how)
{
+ int err;
struct socket *sock;
- struct file *file;
-
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
- return(sock->ops->shutdown(sock, how));
+ lock_kernel();
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ err=sock->ops->shutdown(sock, how);
+ sockfd_put(sock);
+ }
+ unlock_kernel();
+ return err;
}
/*
* BSD sendmsg interface
*/
-asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags)
+asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags)
{
struct socket *sock;
- struct file *file;
char address[MAX_SOCK_ADDR];
- struct iovec iov[UIO_MAXIOV];
+ struct iovec iov[UIO_FASTIOV];
+ unsigned char ctl[sizeof(struct cmsghdr) + 20]; /* 20 is size of ipv6_pktinfo */
struct msghdr msg_sys;
- void * krn_msg_ctl = NULL;
- int err;
+ int err= -EINVAL;
int total_len;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
- if(sock->ops->sendmsg==NULL)
- return -EOPNOTSUPP;
+ lock_kernel();
if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
- return -EFAULT;
-
+ {
+ err=-EFAULT;
+ goto out;
+ }
/* do not move before msg_sys is valid */
- if(msg_sys.msg_iovlen>UIO_MAXIOV)
- return -EINVAL;
-
+ if (msg_sys.msg_iovlen>UIO_MAXIOV)
+ goto out;
/* This will also move the address data into kernel space */
err = verify_iovec(&msg_sys, iov, address, VERIFY_READ);
if (err < 0)
- return err;
+ goto out;
total_len=err;
-
- if (msg_sys.msg_control)
+
+ if (msg_sys.msg_controllen)
{
- krn_msg_ctl = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
- err = copy_from_user(krn_msg_ctl, msg_sys.msg_control,
- msg_sys.msg_controllen);
+ if (msg_sys.msg_controllen > sizeof(ctl))
+ {
+ char *tmp = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
+ if (tmp == NULL)
+ {
+ err = -ENOBUFS;
+ goto failed2;
+ }
+ err = copy_from_user(tmp, msg_sys.msg_control, msg_sys.msg_controllen);
+ msg_sys.msg_control = tmp;
+ } else {
+ err = copy_from_user(ctl, msg_sys.msg_control, msg_sys.msg_controllen);
+ msg_sys.msg_control = ctl;
+ }
if (err)
- return -EFAULT;
- msg_sys.msg_control = krn_msg_ctl;
+ goto failed;
}
+ msg_sys.msg_flags = flags;
+ if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+ msg_sys.msg_flags |= MSG_DONTWAIT;
- err = sock->ops->sendmsg(sock, &msg_sys, total_len,
- (file->f_flags&O_NONBLOCK), flags);
-
- if (msg_sys.msg_control)
+ if ((sock = sockfd_lookup(fd,&err))!=NULL)
{
- kfree(krn_msg_ctl);
+ err = sock_sendmsg(sock, &msg_sys, total_len);
+ sockfd_put(sock);
}
+failed:
+ if (msg_sys.msg_controllen && msg_sys.msg_control != ctl)
+ kfree(msg_sys.msg_control);
+failed2:
+ if (msg_sys.msg_iov != iov)
+ kfree(msg_sys.msg_iov);
+out:
+ unlock_kernel();
return err;
}
@@ -1185,88 +1194,80 @@ asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags)
asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags)
{
struct socket *sock;
- struct file *file;
- struct iovec iov[UIO_MAXIOV];
+ struct iovec iovstack[UIO_FASTIOV];
+ struct iovec *iov=iovstack;
struct msghdr msg_sys;
- void *usr_msg_ctl = NULL;
- void *krn_msg_ctl = NULL;
+ unsigned long cmsg_ptr;
int err;
int total_len;
- int len;
+ int len = 0;
/* kernel mode address */
char addr[MAX_SOCK_ADDR];
- int addr_len;
/* user mode address pointers */
struct sockaddr *uaddr;
int *uaddr_len;
- if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
- return(-EBADF);
- if (!(sock = sockfd_lookup(fd, NULL)))
- return(-ENOTSOCK);
-
+ lock_kernel();
if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
- return -EFAULT;
-
- if(msg_sys.msg_iovlen>UIO_MAXIOV)
- return -EINVAL;
-
+ {
+ err=-EFAULT;
+ goto out;
+ }
+ if (msg_sys.msg_iovlen>UIO_MAXIOV)
+ {
+ err=-EINVAL;
+ goto out;
+ }
+
/*
- * save the user-mode address (verify_iovec will change the
- * kernel msghdr to use the kernel address space)
+ * Save the user-mode address (verify_iovec will change the
+ * kernel msghdr to use the kernel address space)
*/
+
uaddr = msg_sys.msg_name;
uaddr_len = &msg->msg_namelen;
- err=verify_iovec(&msg_sys,iov,addr, VERIFY_WRITE);
- if(err<0)
- return err;
+ err=verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
+ if (err<0)
+ goto out;
total_len=err;
+ cmsg_ptr = (unsigned long)msg_sys.msg_control;
+ msg_sys.msg_flags = 0;
+ if (current->files->fd[fd]->f_flags&O_NONBLOCK)
+ flags |= MSG_DONTWAIT;
- if (msg_sys.msg_control)
- {
- usr_msg_ctl = msg_sys.msg_control;
- krn_msg_ctl = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
- err = copy_from_user(krn_msg_ctl, usr_msg_ctl,
- msg_sys.msg_controllen);
- if (err)
- return -EFAULT;
- msg_sys.msg_control = krn_msg_ctl;
- }
-
- if(sock->ops->recvmsg==NULL)
- return -EOPNOTSUPP;
- len=sock->ops->recvmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags, &addr_len);
- if(len<0)
- return len;
- if (uaddr != NULL)
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
{
- err = move_addr_to_user(addr, addr_len, uaddr, uaddr_len);
+ err=sock_recvmsg(sock, &msg_sys, total_len, flags);
+ if(err>=0)
+ len=err;
+ sockfd_put(sock);
}
-
- if (msg_sys.msg_control)
- {
- if (!err)
- {
- err = copy_to_user(usr_msg_ctl, krn_msg_ctl,
- msg_sys.msg_controllen);
- if (err)
- err = -EFAULT;
- }
- kfree(krn_msg_ctl);
- }
-
- return err ? err : len;
+ if (msg_sys.msg_iov != iov)
+ kfree(msg_sys.msg_iov);
+
+ if (uaddr != NULL && err>=0)
+ err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
+ if (err>=0 && (put_user(msg_sys.msg_flags, &msg->msg_flags) ||
+ put_user((unsigned long)msg_sys.msg_control-cmsg_ptr, &msg->msg_controllen)))
+ err = -EFAULT;
+out:
+ unlock_kernel();
+ if(err<0)
+ return err;
+ return len;
}
/*
* Perform a file control on a socket file descriptor.
+ *
+ * FIXME: does this need an fd lock ?
*/
int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
@@ -1274,20 +1275,14 @@ int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
struct socket *sock;
sock = socki_lookup (filp->f_inode);
- if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
- return(sock->ops->fcntl(sock, cmd, arg));
+ if (sock && sock->ops && sock->ops->fcntl)
+ return sock->ops->fcntl(sock, cmd, arg);
return(-EINVAL);
}
/*
- * System call vectors. Since I (RIB) want to rewrite sockets as streams,
- * we have this level of indirection. Not a lot of overhead, since more of
- * the work is done via read/write/select directly.
- *
- * I'm now expanding this up to a higher level to separate the assorted
- * kernel/user space manipulations and global assumptions from the protocol
- * layers proper - AC.
+ * System call vectors.
*
* Argument checking cleaned up. Saved 20% in size.
*/
@@ -1298,113 +1293,99 @@ asmlinkage int sys_socketcall(int call, unsigned long *args)
4,4,4,6,6,2,5,5,3,3};
unsigned long a[6];
unsigned long a0,a1;
+ int err = -EINVAL;
+ lock_kernel();
if(call<1||call>SYS_RECVMSG)
- return -EINVAL;
-
+ goto out;
+ err = -EFAULT;
+
+ /*
+ * Ideally we want to precompute the maths, but unsigned long
+ * isnt a fixed size....
+ */
+
if ((copy_from_user(a, args, nargs[call] * sizeof(unsigned long))))
- return -EFAULT;
+ goto out;
a0=a[0];
a1=a[1];
-
switch(call)
{
case SYS_SOCKET:
- return(sys_socket(a0,a1,a[2]));
+ err = sys_socket(a0,a1,a[2]);
+ break;
case SYS_BIND:
- return(sys_bind(a0,(struct sockaddr *)a1,
- a[2]));
+ err = sys_bind(a0,(struct sockaddr *)a1, a[2]);
+ break;
case SYS_CONNECT:
- return(sys_connect(a0, (struct sockaddr *)a1,
- a[2]));
+ err = sys_connect(a0, (struct sockaddr *)a1, a[2]);
+ break;
case SYS_LISTEN:
- return(sys_listen(a0,a1));
+ err = sys_listen(a0,a1);
+ break;
case SYS_ACCEPT:
- return(sys_accept(a0,(struct sockaddr *)a1,
- (int *)a[2]));
+ err = sys_accept(a0,(struct sockaddr *)a1, (int *)a[2]);
+ break;
case SYS_GETSOCKNAME:
- return(sys_getsockname(a0,(struct sockaddr *)a1,
- (int *)a[2]));
+ err = sys_getsockname(a0,(struct sockaddr *)a1, (int *)a[2]);
+ break;
case SYS_GETPEERNAME:
- return(sys_getpeername(a0, (struct sockaddr *)a1,
- (int *)a[2]));
+ err = sys_getpeername(a0, (struct sockaddr *)a1, (int *)a[2]);
+ break;
case SYS_SOCKETPAIR:
- return(sys_socketpair(a0,a1,
- a[2],
- (int *)a[3]));
+ err = sys_socketpair(a0,a1, a[2], (int *)a[3]);
+ break;
case SYS_SEND:
- return(sys_send(a0,
- (void *)a1,
- a[2],
- a[3]));
+ err = sys_send(a0, (void *)a1, a[2], a[3]);
+ break;
case SYS_SENDTO:
- return(sys_sendto(a0,(void *)a1,
- a[2],
- a[3],
- (struct sockaddr *)a[4],
- a[5]));
+ err = sys_sendto(a0,(void *)a1, a[2], a[3],
+ (struct sockaddr *)a[4], a[5]);
+ break;
case SYS_RECV:
- return(sys_recv(a0,
- (void *)a1,
- a[2],
- a[3]));
+ err = sys_recv(a0, (void *)a1, a[2], a[3]);
+ break;
case SYS_RECVFROM:
- return(sys_recvfrom(a0,
- (void *)a1,
- a[2],
- a[3],
- (struct sockaddr *)a[4],
- (int *)a[5]));
+ err = sys_recvfrom(a0, (void *)a1, a[2], a[3],
+ (struct sockaddr *)a[4], (int *)a[5]);
+ break;
case SYS_SHUTDOWN:
- return(sys_shutdown(a0,a1));
+ err = sys_shutdown(a0,a1);
+ break;
case SYS_SETSOCKOPT:
- return(sys_setsockopt(a0,
- a1,
- a[2],
- (char *)a[3],
- a[4]));
+ err = sys_setsockopt(a0, a1, a[2], (char *)a[3], a[4]);
+ break;
case SYS_GETSOCKOPT:
- return(sys_getsockopt(a0,
- a1,
- a[2],
- (char *)a[3],
- (int *)a[4]));
+ err = sys_getsockopt(a0, a1, a[2], (char *)a[3], (int *)a[4]);
+ break;
case SYS_SENDMSG:
- return sys_sendmsg(a0,
- (struct msghdr *) a1,
- a[2]);
+ err = sys_sendmsg(a0, (struct msghdr *) a1, a[2]);
+ break;
case SYS_RECVMSG:
- return sys_recvmsg(a0,
- (struct msghdr *) a1,
- a[2]);
+ err = sys_recvmsg(a0, (struct msghdr *) a1, a[2]);
+ break;
+ default:
+ err = -EINVAL;
+ break;
}
- return -EINVAL; /* to keep gcc happy */
+out:
+ unlock_kernel();
+ return err;
}
+
/*
* This function is called by a protocol handler that wants to
* advertise its address family, and have it linked into the
* SOCKET module.
*/
-int sock_register(int family, struct proto_ops *ops)
+int sock_register(struct net_proto_family *ops)
{
- int i;
-
- cli();
- for(i = 0; i < NPROTO; i++)
- {
- if (pops[i] != NULL)
- continue;
- pops[i] = ops;
- pops[i]->family = family;
- sti();
- return(i);
- }
- sti();
- return(-ENOMEM);
+ net_families[ops->family]=ops;
+ return 0;
}
/*
@@ -1415,22 +1396,8 @@ int sock_register(int family, struct proto_ops *ops)
int sock_unregister(int family)
{
- int i;
-
- cli();
- for(i = 0; i < NPROTO; i++)
- {
- if (pops[i] == NULL)
- continue;
- if (pops[i]->family == family)
- {
- pops[i]=NULL;
- sti();
- return(i);
- }
- }
- sti();
- return(-ENOENT);
+ net_families[family]=NULL;
+ return 0;
}
void proto_init(void)
@@ -1448,18 +1415,26 @@ void proto_init(void)
/* We're all done... */
}
+extern void sk_init(void);
void sock_init(void)
{
int i;
- printk(KERN_INFO "Swansea University Computer Society NET3.037 for Linux 2.1\n");
+ printk(KERN_INFO "Swansea University Computer Society NET3.039 for Linux 2.1\n");
/*
* Initialize all address (protocol) families.
*/
- for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
+ for (i = 0; i < NPROTO; i++)
+ net_families[i] = NULL;
+
+ /*
+ * Initialize sock SLAB cache.
+ */
+
+ sk_init();
/*
* The netlink device handler may be needed early.
@@ -1467,13 +1442,14 @@ void sock_init(void)
#ifdef CONFIG_NETLINK
init_netlink();
-#endif
+#endif
+
/*
- * Attach the routing/device information port.
+ * Wan router layer.
*/
-#if defined(CONFIG_RTNETLINK)
- netlink_attach(NETLINK_ROUTE, netlink_donothing);
+#ifdef CONFIG_WAN_ROUTER
+ wanrouter_init();
#endif
/*
@@ -1489,14 +1465,6 @@ void sock_init(void)
*/
proto_init();
-
- /*
- * Export networking symbols to the world.
- */
-
-#if defined(CONFIG_MODULES) && defined(CONFIG_NET)
- export_net_symbols();
-#endif
}
int socket_get_info(char *buffer, char **start, off_t offset, int length)
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
new file mode 100644
index 000000000..1bf4f6070
--- /dev/null
+++ b/net/sunrpc/Makefile
@@ -0,0 +1,23 @@
+#
+# Makefile for Linux kernel SUN RPC
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := sunrpc.o
+O_OBJS := clnt.o xprt.o sched.o \
+ auth.o auth_null.o auth_unix.o \
+ svc.o svcsock.o svcauth.o \
+ pmap_clnt.o xdr.o sysctl.o
+OX_OBJS := sunrpc_syms.o
+
+ifeq ($(CONFIG_PROC_FS),y)
+ O_OBJS += stats.o
+endif
+
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
new file mode 100644
index 000000000..2ac1f7b7f
--- /dev/null
+++ b/net/sunrpc/auth.c
@@ -0,0 +1,295 @@
+/*
+ * linux/fs/nfs/rpcauth.c
+ *
+ * Generic RPC authentication API.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+#define RPC_MAXFLAVOR 8
+
+static struct rpc_authops * auth_flavors[RPC_MAXFLAVOR] = {
+ &authnull_ops, /* AUTH_NULL */
+ &authunix_ops, /* AUTH_UNIX */
+ NULL, /* others can be loadable modules */
+};
+
+int
+rpcauth_register(struct rpc_authops *ops)
+{
+ unsigned int flavor;
+
+ if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
+ return -EINVAL;
+ if (auth_flavors[flavor] != NULL)
+ return -EPERM; /* what else? */
+ auth_flavors[flavor] = ops;
+ return 0;
+}
+
+int
+rpcauth_unregister(struct rpc_authops *ops)
+{
+ unsigned int flavor;
+
+ if ((flavor = ops->au_flavor) >= RPC_MAXFLAVOR)
+ return -EINVAL;
+ if (auth_flavors[flavor] != ops)
+ return -EPERM; /* what else? */
+ auth_flavors[flavor] = NULL;
+ return 0;
+}
+
+struct rpc_auth *
+rpcauth_create(unsigned int flavor, struct rpc_clnt *clnt)
+{
+ struct rpc_authops *ops;
+
+ if (flavor >= RPC_MAXFLAVOR || !(ops = auth_flavors[flavor]))
+ return NULL;
+ clnt->cl_auth = ops->create(clnt);
+ return clnt->cl_auth;
+}
+
+void
+rpcauth_destroy(struct rpc_auth *auth)
+{
+ auth->au_ops->destroy(auth);
+}
+
+/*
+ * Initialize RPC credential cache
+ */
+void
+rpcauth_init_credcache(struct rpc_auth *auth)
+{
+ memset(auth->au_credcache, 0, sizeof(auth->au_credcache));
+ auth->au_nextgc = jiffies + (auth->au_expire >> 1);
+}
+
+/*
+ * Clear the RPC credential cache
+ */
+void
+rpcauth_free_credcache(struct rpc_auth *auth)
+{
+ struct rpc_cred **q, *cred;
+ void (*destroy)(struct rpc_cred *);
+ int i;
+
+ if (!(destroy = auth->au_ops->crdestroy))
+ destroy = (void (*)(struct rpc_cred *)) rpc_free;
+
+ for (i = 0; i < RPC_CREDCACHE_NR; i++) {
+ q = &auth->au_credcache[i];
+ while ((cred = *q) != NULL) {
+ *q = cred->cr_next;
+ destroy(cred);
+ }
+ }
+}
+
+/*
+ * Remove stale credentials. Avoid sleeping inside the loop.
+ */
+static void
+rpcauth_gc_credcache(struct rpc_auth *auth)
+{
+ struct rpc_cred **q, *cred, *free = NULL;
+ int i, safe = 0;
+
+ dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
+ for (i = 0; i < RPC_CREDCACHE_NR; i++) {
+ q = &auth->au_credcache[i];
+ while ((cred = *q) != NULL) {
+ if (++safe > 500) {
+ printk("RPC: rpcauth_gc_credcache looping!\n");
+ break;
+ }
+ if (!cred->cr_count && cred->cr_expire < jiffies) {
+ *q = cred->cr_next;
+ cred->cr_next = free;
+ free = cred;
+ continue;
+ }
+ q = &cred->cr_next;
+ }
+ }
+ while ((cred = free) != NULL) {
+ free = cred->cr_next;
+ rpc_free(cred);
+ }
+ auth->au_nextgc = jiffies + auth->au_expire;
+}
+
+/*
+ * Insert credential into cache
+ */
+inline void
+rpcauth_insert_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+ int nr;
+
+ nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+ cred->cr_next = auth->au_credcache[nr];
+ auth->au_credcache[nr] = cred;
+ cred->cr_expire = jiffies + auth->au_expire;
+ cred->cr_count++;
+}
+
+/*
+ * Look up a process' credentials in the authentication cache
+ */
+static struct rpc_cred *
+rpcauth_lookup_credcache(struct rpc_task *task)
+{
+ struct rpc_auth *auth = task->tk_auth;
+ struct rpc_cred **q, *cred = NULL;
+ int nr;
+
+ nr = RPC_DO_ROOTOVERRIDE(task)? 0 : (current->uid % RPC_CREDCACHE_NR);
+
+ if (auth->au_nextgc < jiffies)
+ rpcauth_gc_credcache(auth);
+
+ q = &auth->au_credcache[nr];
+ while ((cred = *q) != NULL) {
+ if (auth->au_ops->crmatch(task, cred)) {
+ *q = cred->cr_next;
+ break;
+ }
+ q = &cred->cr_next;
+ }
+
+ if (!cred)
+ cred = auth->au_ops->crcreate(task);
+
+ rpcauth_insert_credcache(auth, cred);
+
+ return (struct rpc_cred *) cred;
+}
+
+/*
+ * Remove cred handle from cache
+ */
+static inline void
+rpcauth_remove_credcache(struct rpc_auth *auth, struct rpc_cred *cred)
+{
+ struct rpc_cred **q, *cr;
+ int nr;
+
+ nr = (cred->cr_uid % RPC_CREDCACHE_NR);
+ q = &auth->au_credcache[nr];
+ while ((cr = *q) != NULL) {
+ if (cred == cr) {
+ *q = cred->cr_next;
+ return;
+ }
+ q = &cred->cr_next;
+ }
+}
+
+struct rpc_cred *
+rpcauth_lookupcred(struct rpc_task *task)
+{
+ dprintk("RPC: %4d looking up %s cred\n",
+ task->tk_pid, task->tk_auth->au_ops->au_name);
+ return task->tk_cred = rpcauth_lookup_credcache(task);
+}
+
+int
+rpcauth_matchcred(struct rpc_task *task, struct rpc_cred *cred)
+{
+ struct rpc_auth *auth = task->tk_auth;
+
+ dprintk("RPC: %4d matching %s cred %p\n",
+ task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+ return auth->au_ops->crmatch(task, cred);
+}
+
+void
+rpcauth_holdcred(struct rpc_task *task)
+{
+ dprintk("RPC: %4d holding %s cred %p\n",
+ task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
+ if (task->tk_cred)
+ task->tk_cred->cr_count++;
+}
+
+void
+rpcauth_releasecred(struct rpc_task *task)
+{
+ struct rpc_auth *auth = task->tk_auth;
+ struct rpc_cred *cred;
+
+ dprintk("RPC: %4d releasing %s cred %p\n",
+ task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+ if ((cred = task->tk_cred) != NULL) {
+ cred->cr_count--;
+ if (cred->cr_flags & RPCAUTH_CRED_DEAD) {
+ rpcauth_remove_credcache(auth, cred);
+ if (!cred->cr_count)
+ auth->au_ops->crdestroy(cred);
+ }
+ task->tk_cred = NULL;
+ }
+}
+
+u32 *
+rpcauth_marshcred(struct rpc_task *task, u32 *p)
+{
+ struct rpc_auth *auth = task->tk_auth;
+
+ dprintk("RPC: %4d marshaling %s cred %p\n",
+ task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+ return auth->au_ops->crmarshal(task, p,
+ task->tk_flags & RPC_CALL_REALUID);
+}
+
+u32 *
+rpcauth_checkverf(struct rpc_task *task, u32 *p)
+{
+ struct rpc_auth *auth = task->tk_auth;
+
+ dprintk("RPC: %4d validating %s cred %p\n",
+ task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+ return auth->au_ops->crvalidate(task, p);
+}
+
+int
+rpcauth_refreshcred(struct rpc_task *task)
+{
+ struct rpc_auth *auth = task->tk_auth;
+
+ dprintk("RPC: %4d refreshing %s cred %p\n",
+ task->tk_pid, auth->au_ops->au_name, task->tk_cred);
+ task->tk_status = auth->au_ops->crrefresh(task);
+ return task->tk_status;
+}
+
+void
+rpcauth_invalcred(struct rpc_task *task)
+{
+ dprintk("RPC: %4d invalidating %s cred %p\n",
+ task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_cred);
+ if (task->tk_cred)
+ task->tk_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
+}
+
+int
+rpcauth_uptodatecred(struct rpc_task *task)
+{
+ return !(task->tk_cred) ||
+ (task->tk_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
+}
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
new file mode 100644
index 000000000..fcd63b5a2
--- /dev/null
+++ b/net/sunrpc/auth_null.c
@@ -0,0 +1,131 @@
+/*
+ * linux/net/sunrpc/rpcauth_null.c
+ *
+ * AUTH_NULL authentication. Really :-)
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/utsname.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+static struct rpc_auth *
+nul_create(struct rpc_clnt *clnt)
+{
+ struct rpc_auth *auth;
+
+ dprintk("RPC: creating NULL authenticator for client %p\n", clnt);
+ if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth))))
+ return NULL;
+ auth->au_cslack = 4;
+ auth->au_rslack = 2;
+ auth->au_ops = &authnull_ops;
+ auth->au_expire = 1800 * HZ;
+ rpcauth_init_credcache(auth);
+
+ return (struct rpc_auth *) auth;
+}
+
+static void
+nul_destroy(struct rpc_auth *auth)
+{
+ dprintk("RPC: destroying NULL authenticator %p\n", auth);
+ rpc_free(auth);
+}
+
+/*
+ * Create NULL creds for current process
+ */
+static struct rpc_cred *
+nul_create_cred(struct rpc_task *task)
+{
+ struct rpc_cred *cred;
+
+ if (!(cred = (struct rpc_cred *) rpc_malloc(task, sizeof(*cred))))
+ return NULL;
+ cred->cr_count = 0;
+ cred->cr_flags = RPCAUTH_CRED_UPTODATE;
+
+ return cred;
+}
+
+/*
+ * Destroy cred handle.
+ */
+static void
+nul_destroy_cred(struct rpc_cred *cred)
+{
+ rpc_free(cred);
+}
+
+/*
+ * Match cred handle against current process
+ */
+static int
+nul_match(struct rpc_task *task, struct rpc_cred *cred)
+{
+ return 1;
+}
+
+/*
+ * Marshal credential.
+ */
+static u32 *
+nul_marshal(struct rpc_task *task, u32 *p, int ruid)
+{
+ *p++ = htonl(RPC_AUTH_NULL);
+ *p++ = 0;
+ *p++ = htonl(RPC_AUTH_NULL);
+ *p++ = 0;
+
+ return p;
+}
+
+/*
+ * Refresh credential. This is a no-op for AUTH_NULL
+ */
+static int
+nul_refresh(struct rpc_task *task)
+{
+ return task->tk_status = -EACCES;
+}
+
+static u32 *
+nul_validate(struct rpc_task *task, u32 *p)
+{
+ u32 n = ntohl(*p++);
+
+ if (n != RPC_AUTH_NULL) {
+ printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
+ return NULL;
+ }
+ if ((n = ntohl(*p++)) != 0) {
+ printk("RPC: bad verf size: %ld\n", (unsigned long) n);
+ return NULL;
+ }
+
+ return p;
+}
+
+struct rpc_authops authnull_ops = {
+ RPC_AUTH_NULL,
+#ifdef RPC_DEBUG
+ "NULL",
+#endif
+ nul_create,
+ nul_destroy,
+ nul_create_cred,
+ nul_destroy_cred,
+ nul_match,
+ nul_marshal,
+ nul_refresh,
+ nul_validate
+};
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
new file mode 100644
index 000000000..761bfd242
--- /dev/null
+++ b/net/sunrpc/auth_unix.c
@@ -0,0 +1,238 @@
+/*
+ * linux/net/sunrpc/rpcauth_unix.c
+ *
+ * UNIX-style authentication; no AUTH_SHORT support
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/utsname.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/auth.h>
+
+struct unx_cred {
+ struct rpc_cred uc_base;
+ uid_t uc_fsuid;
+ gid_t uc_gid, uc_fsgid;
+ gid_t uc_gids[16];
+};
+#define uc_uid uc_base.cr_uid
+#define uc_count uc_base.cr_count
+#define uc_flags uc_base.cr_flags
+#define uc_expire uc_base.cr_expire
+
+#define UNX_CRED_EXPIRE (60 * HZ)
+
+#ifndef DONT_FILLIN_HOSTNAME
+/* # define UNX_MAXNODENAME (sizeof(system_utsname.nodename)-1) */
+# define UNX_MAXNODENAME 32
+# define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2))
+#else
+# define UNX_WRITESLACK 20
+#endif
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_AUTH
+#endif
+
+static struct rpc_auth *
+unx_create(struct rpc_clnt *clnt)
+{
+ struct rpc_auth *auth;
+
+ dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
+ if (!(auth = (struct rpc_auth *) rpc_allocate(0, sizeof(*auth))))
+ return NULL;
+ auth->au_cslack = UNX_WRITESLACK;
+ auth->au_rslack = 2; /* assume AUTH_NULL verf */
+ auth->au_expire = UNX_CRED_EXPIRE;
+ auth->au_ops = &authunix_ops;
+
+ rpcauth_init_credcache(auth);
+
+ return auth;
+}
+
+static void
+unx_destroy(struct rpc_auth *auth)
+{
+ dprintk("RPC: destroying UNIX authenticator %p\n", auth);
+ rpcauth_free_credcache(auth);
+ rpc_free(auth);
+}
+
+static struct rpc_cred *
+unx_create_cred(struct rpc_task *task)
+{
+ struct unx_cred *cred;
+ int i;
+
+ dprintk("RPC: allocating UNIX cred for uid %d gid %d\n",
+ current->uid, current->gid);
+
+ if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred))))
+ return NULL;
+
+ cred->uc_count = 0;
+ cred->uc_flags = RPCAUTH_CRED_UPTODATE;
+ if (RPC_DO_ROOTOVERRIDE(task)) {
+ cred->uc_uid = cred->uc_fsuid = 0;
+ cred->uc_gid = cred->uc_fsgid = 0;
+ cred->uc_gids[0] = NOGROUP;
+ } else {
+ cred->uc_uid = current->uid;
+ cred->uc_gid = current->gid;
+ cred->uc_fsuid = current->fsuid;
+ cred->uc_fsgid = current->fsgid;
+ for (i = 0; i < 16 && i < NGROUPS; i++)
+ cred->uc_gids[i] = (gid_t) current->groups[i];
+ }
+
+ return (struct rpc_cred *) cred;
+}
+
+struct rpc_cred *
+authunix_fake_cred(struct rpc_task *task, uid_t uid, gid_t gid)
+{
+ struct unx_cred *cred;
+
+ dprintk("RPC: allocating fake UNIX cred for uid %d gid %d\n",
+ uid, gid);
+
+ if (!(cred = (struct unx_cred *) rpc_malloc(task, sizeof(*cred))))
+ return NULL;
+
+ cred->uc_count = 1;
+ cred->uc_flags = RPCAUTH_CRED_DEAD|RPCAUTH_CRED_UPTODATE;
+ cred->uc_uid = uid;
+ cred->uc_gid = gid;
+ cred->uc_fsuid = uid;
+ cred->uc_fsgid = gid;
+ cred->uc_gids[0] = (gid_t) NOGROUP;
+
+ return task->tk_cred = (struct rpc_cred *) cred;
+}
+
+static void
+unx_destroy_cred(struct rpc_cred *cred)
+{
+ rpc_free(cred);
+}
+
+/*
+ * Match credentials against current process creds.
+ * The root_override argument takes care of cases where the caller may
+ * request root creds (e.g. for NFS swapping).
+ */
+static int
+unx_match(struct rpc_task * task, struct rpc_cred *rcred)
+{
+ struct unx_cred *cred = (struct unx_cred *) rcred;
+ int i;
+
+ if (!RPC_DO_ROOTOVERRIDE(task)) {
+ if (cred->uc_uid != current->uid
+ || cred->uc_gid != current->gid
+ || cred->uc_fsuid != current->fsuid
+ || cred->uc_fsgid != current->fsgid)
+ return 0;
+
+ for (i = 0; i < 16 && i < NGROUPS; i++)
+ if (cred->uc_gids[i] != (gid_t) current->groups[i])
+ return 0;
+ return 1;
+ }
+ return (cred->uc_uid == 0 && cred->uc_fsuid == 0
+ && cred->uc_gid == 0 && cred->uc_fsgid == 0
+ && cred->uc_gids[0] == (gid_t) NOGROUP);
+}
+
+/*
+ * Marshal credentials.
+ * Maybe we should keep a cached credential for performance reasons.
+ */
+static u32 *
+unx_marshal(struct rpc_task *task, u32 *p, int ruid)
+{
+ struct unx_cred *cred = (struct unx_cred *) task->tk_cred;
+ u32 *base, *hold;
+ int i, n;
+
+ *p++ = htonl(RPC_AUTH_UNIX);
+ base = p++;
+ *p++ = htonl(jiffies/HZ);
+#ifndef DONT_FILLIN_HOSTNAME
+ if ((n = strlen((char *) system_utsname.nodename)) > UNX_MAXNODENAME)
+ n = UNX_MAXNODENAME;
+ *p++ = htonl(n);
+ memcpy(p, system_utsname.nodename, n);
+ p += (n + 3) >> 2;
+#else
+ *p++ = 0;
+#endif
+ if (ruid) {
+ *p++ = htonl((u32) cred->uc_uid);
+ *p++ = htonl((u32) cred->uc_gid);
+ } else {
+ *p++ = htonl((u32) cred->uc_fsuid);
+ *p++ = htonl((u32) cred->uc_fsgid);
+ }
+ hold = p++;
+ for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
+ *p++ = htonl((u32) cred->uc_gids[i]);
+ *hold = htonl(p - hold - 1); /* gid array length */
+ *base = htonl((p - base - 1) << 2); /* cred length */
+
+ *p++ = htonl(RPC_AUTH_NULL);
+ *p++ = htonl(0);
+
+ return p;
+}
+
+/*
+ * Refresh credentials. This is a no-op for AUTH_UNIX
+ */
+static int
+unx_refresh(struct rpc_task *task)
+{
+ task->tk_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
+ return task->tk_status = -EACCES;
+}
+
+static u32 *
+unx_validate(struct rpc_task *task, u32 *p)
+{
+ u32 n = ntohl(*p++);
+
+ if (n != RPC_AUTH_NULL && n != RPC_AUTH_UNIX && n != RPC_AUTH_SHORT) {
+ printk("RPC: bad verf flavor: %ld\n", (unsigned long) n);
+ return NULL;
+ }
+ if ((n = ntohl(*p++)) > 400) {
+ printk("RPC: giant verf size: %ld\n", (unsigned long) n);
+ return NULL;
+ }
+ task->tk_auth->au_rslack = (n >> 2) + 2;
+ p += (n >> 2);
+
+ return p;
+}
+
+struct rpc_authops authunix_ops = {
+ RPC_AUTH_UNIX,
+#ifdef RPC_DEBUG
+ "UNIX",
+#endif
+ unx_create,
+ unx_destroy,
+ unx_create_cred,
+ unx_destroy_cred,
+ unx_match,
+ unx_marshal,
+ unx_refresh,
+ unx_validate
+};
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
new file mode 100644
index 000000000..bcdb13610
--- /dev/null
+++ b/net/sunrpc/clnt.c
@@ -0,0 +1,761 @@
+/*
+ * linux/net/sunrpc/rpcclnt.c
+ *
+ * This file contains the high-level RPC interface.
+ * It is modeled as a finite state machine to support both synchronous
+ * and asynchronous requests.
+ *
+ * - RPC header generation and argument serialization.
+ * - Credential refresh.
+ * - TCP reconnect handling (when finished).
+ * - Retry of operation when it is suspected the operation failed because
+ * of uid squashing on the server, or when the credentials were stale
+ * and need to be refreshed, or when a packet was damaged in transit.
+ * This may be have to be moved to the VFS layer.
+ *
+ * NB: BSD uses a more intelligent approach to guessing when a request
+ * or reply has been lost by keeping the RTO estimate for each procedure.
+ * We currently make do with a constant timeout value.
+ *
+ * Copyright (C) 1992,1993 Rick Sladkey <jrs@world.std.com>
+ * Copyright (C) 1995,1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+
+#include <linux/sunrpc/clnt.h>
+
+
+#define RPC_SLACK_SPACE 1024 /* total overkill */
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_CALL
+#endif
+
+static struct wait_queue * destroy_wait = NULL;
+
+
+static void call_bind(struct rpc_task *task);
+static void call_reserve(struct rpc_task *task);
+static void call_reserveresult(struct rpc_task *task);
+static void call_allocate(struct rpc_task *task);
+static void call_encode(struct rpc_task *task);
+static void call_decode(struct rpc_task *task);
+static void call_transmit(struct rpc_task *task);
+static void call_receive(struct rpc_task *task);
+static void call_status(struct rpc_task *task);
+static void call_refresh(struct rpc_task *task);
+static void call_refreshresult(struct rpc_task *task);
+static void call_timeout(struct rpc_task *task);
+static void call_reconnect(struct rpc_task *task);
+static u32 * call_header(struct rpc_task *task);
+static u32 * call_verify(struct rpc_task *task);
+
+#define _S(nr) (1 << ((nr) - 1))
+
+/*
+ * Create an RPC client
+ * FIXME: This should also take a flags argument (as in task->tk_flags).
+ * It's called (among others) from pmap_create_client, which may in
+ * turn be called by an async task. In this case, rpciod should not be
+ * made to sleep too long.
+ */
+struct rpc_clnt *
+rpc_create_client(struct rpc_xprt *xprt, char *servname,
+ struct rpc_program *program, u32 vers, int flavor)
+{
+ struct rpc_version *version;
+ struct rpc_clnt *clnt;
+
+ dprintk("RPC: creating %s client for %s (xprt %p)\n",
+ program->name, servname, xprt);
+
+ if (!xprt)
+ return NULL;
+ if (vers>= program->nrvers || !(version = program->version[vers]))
+ return NULL;
+
+ if (!(clnt = (struct rpc_clnt *) rpc_allocate(0, sizeof(*clnt)))) {
+ printk("RPC: out of memory in rpc_create_client\n");
+ return NULL;
+ }
+ memset(clnt, 0, sizeof(*clnt));
+
+ clnt->cl_xprt = xprt;
+ clnt->cl_procinfo = version->procs;
+ clnt->cl_maxproc = version->nrprocs;
+ clnt->cl_server = servname;
+ clnt->cl_protname = program->name;
+ clnt->cl_port = xprt->addr.sin_port;
+ clnt->cl_prog = program->number;
+ clnt->cl_vers = version->number;
+ clnt->cl_prot = IPPROTO_UDP;
+ clnt->cl_stats = program->stats;
+ clnt->cl_bindwait = RPC_INIT_WAITQ("bindwait");
+
+ if (!clnt->cl_port)
+ clnt->cl_autobind = 1;
+
+ if (!rpcauth_create(flavor, clnt)) {
+ printk("RPC: Couldn't create auth handle (flavor %d)\n",
+ flavor);
+ rpc_free(clnt);
+ return NULL;
+ }
+ return clnt;
+}
+
+/*
+ * Properly shut down an RPC client, terminating all outstanding
+ * requests.
+ */
+int
+rpc_shutdown_client(struct rpc_clnt *clnt)
+{
+ dprintk("RPC: shutting down %s client for %s\n",
+ clnt->cl_protname, clnt->cl_server);
+ while (clnt->cl_users) {
+ dprintk("sigmask %08lx\n", current->signal);
+ dprintk("users %d\n", clnt->cl_users);
+ clnt->cl_dead = 1;
+ rpc_killall_tasks(clnt);
+ sleep_on(&destroy_wait);
+ }
+ return rpc_destroy_client(clnt);
+}
+
+/*
+ * Delete an RPC client
+ */
+int
+rpc_destroy_client(struct rpc_clnt *clnt)
+{
+ dprintk("RPC: destroying %s client for %s\n",
+ clnt->cl_protname, clnt->cl_server);
+
+ rpcauth_destroy(clnt->cl_auth);
+ if (clnt->cl_xprt) {
+ xprt_destroy(clnt->cl_xprt);
+ clnt->cl_xprt = NULL;
+ }
+ rpc_free(clnt);
+ return 0;
+}
+
+/*
+ * Release an RPC client
+ */
+void
+rpc_release_client(struct rpc_clnt *clnt)
+{
+ dprintk("RPC: rpc_release_client(%p, %d)\n",
+ clnt, clnt->cl_users);
+ if (--(clnt->cl_users) == 0) {
+ wake_up(&destroy_wait);
+ if (clnt->cl_oneshot || clnt->cl_dead)
+ rpc_destroy_client(clnt);
+ }
+ dprintk("RPC: rpc_release_client done\n");
+}
+
+/*
+ * Default callback for async RPC calls
+ */
+static void
+rpc_default_callback(struct rpc_task *task)
+{
+ rpc_release_task(task);
+}
+
+/*
+ * New rpc_call implementation
+ */
+int
+rpc_do_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp,
+ int flags, rpc_action func, void *data)
+{
+ struct rpc_task my_task, *task = &my_task;
+ unsigned long oldmask, sigallow = _S(SIGKILL);
+ int async, status;
+
+ /* Turn off various signals */
+ if (clnt->cl_intr) {
+ struct sigaction *action = current->sig->action;
+ if (action[SIGINT-1].sa_handler == SIG_DFL)
+ sigallow |= _S(SIGINT);
+ if (action[SIGQUIT-1].sa_handler == SIG_DFL)
+ sigallow |= _S(SIGQUIT);
+ }
+ oldmask = current->blocked;
+ current->blocked |= ~sigallow;
+
+ /* Create/initialize a new RPC task */
+ if ((async = (flags & RPC_TASK_ASYNC)) != 0) {
+ if (!func)
+ func = rpc_default_callback;
+ if (!(task = rpc_new_task(clnt, func, flags))) {
+ current->blocked = oldmask;
+ return -ENOMEM;
+ }
+ task->tk_calldata = data;
+ } else {
+ rpc_init_task(task, clnt, NULL, flags);
+ }
+
+ /* Bind the user cred, set up the call info struct and
+ * execute the task */
+ if (rpcauth_lookupcred(task) != NULL) {
+ rpc_call_setup(task, proc, argp, resp, 0);
+ rpc_execute(task);
+ } else
+ async = 0;
+
+ if (!async) {
+ status = task->tk_status;
+ rpc_release_task(task);
+ } else
+ status = 0;
+
+ current->blocked = oldmask;
+ return status;
+}
+
+
+void
+rpc_call_setup(struct rpc_task *task, u32 proc,
+ void *argp, void *resp, int flags)
+{
+ task->tk_action = call_bind;
+ task->tk_proc = proc;
+ task->tk_argp = argp;
+ task->tk_resp = resp;
+ task->tk_flags |= flags;
+
+ /* Increment call count */
+ rpcproc_count(task->tk_client, proc)++;
+}
+
+/*
+ * Restart an (async) RPC call. Usually called from within the
+ * exit handler.
+ */
+void
+rpc_restart_call(struct rpc_task *task)
+{
+ if (task->tk_flags & RPC_TASK_KILLED) {
+ rpc_release_task(task);
+ return;
+ }
+ task->tk_action = call_bind;
+ rpcproc_count(task->tk_client, task->tk_proc)++;
+}
+
+/*
+ * 0. Get the server port number if not yet set
+ */
+static void
+call_bind(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+
+ task->tk_action = call_reserve;
+ task->tk_status = 0;
+ if (!clnt->cl_port)
+ rpc_getport(task, clnt);
+}
+
+/*
+ * 1. Reserve an RPC call slot
+ */
+static void
+call_reserve(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+
+ dprintk("RPC: %4d call_reserve\n", task->tk_pid);
+ if (!clnt->cl_port) {
+ printk(KERN_NOTICE "%s: couldn't bind to server %s - %s.\n",
+ clnt->cl_protname, clnt->cl_server,
+ clnt->cl_softrtry? "giving up" : "retrying");
+ if (!clnt->cl_softrtry) {
+ rpc_delay(task, 5*HZ);
+ return;
+ }
+ rpc_exit(task, -EIO);
+ return;
+ }
+ if (!rpcauth_uptodatecred(task)) {
+ task->tk_action = call_refresh;
+ return;
+ }
+ task->tk_action = call_reserveresult;
+ task->tk_timeout = clnt->cl_timeout.to_resrvval;
+ task->tk_status = 0;
+ clnt->cl_stats->rpccnt++;
+ xprt_reserve(task);
+}
+
+/*
+ * 1b. Grok the result of xprt_reserve()
+ */
+static void
+call_reserveresult(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_reserveresult (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (task->tk_status >= 0) {
+ task->tk_action = call_allocate;
+ } else if (task->tk_status == -EAGAIN) {
+ task->tk_timeout = task->tk_client->cl_timeout.to_resrvval;
+ task->tk_status = 0;
+ xprt_reserve(task);
+ return;
+ } else if (task->tk_status == -ETIMEDOUT) {
+ task->tk_action = call_timeout;
+ } else {
+ task->tk_action = NULL;
+ }
+ if (!task->tk_rqstp)
+ rpc_exit(task, -EIO);
+}
+
+/*
+ * 2. Allocate the buffer. For details, see sched.c:rpc_malloc.
+ * (Note: buffer memory is freed in rpc_task_release).
+ */
+static void
+call_allocate(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ unsigned int bufsiz;
+
+ dprintk("RPC: %4d call_allocate (status %d)\n",
+ task->tk_pid, task->tk_status);
+ task->tk_action = call_encode;
+ if (task->tk_buffer)
+ return;
+
+ /* FIXME: compute buffer requirements more exactly using
+ * auth->au_wslack */
+ bufsiz = rpcproc_bufsiz(clnt, task->tk_proc) + RPC_SLACK_SPACE;
+
+ if ((task->tk_buffer = rpc_malloc(task, bufsiz)) != NULL)
+ return;
+
+ if (!signalled()) {
+ xprt_release(task);
+ task->tk_action = call_reserve;
+ rpc_delay(task, HZ);
+ return;
+ }
+
+ rpc_exit(task, -ERESTARTSYS);
+}
+
+/*
+ * 3. Encode arguments of an RPC call
+ */
+static void
+call_encode(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ struct rpc_rqst *req = task->tk_rqstp;
+ unsigned int bufsiz;
+ kxdrproc_t encode;
+ int status;
+ u32 *p;
+
+ dprintk("RPC: %4d call_encode (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ task->tk_action = call_transmit;
+
+ /* Default buffer setup */
+ bufsiz = rpcproc_bufsiz(clnt, task->tk_proc)+RPC_SLACK_SPACE;
+ req->rq_svec[0].iov_base = task->tk_buffer;
+ req->rq_svec[0].iov_len = bufsiz;
+ req->rq_slen = 0;
+ req->rq_snr = 1;
+ req->rq_rvec[0].iov_base = task->tk_buffer;
+ req->rq_rvec[0].iov_len = bufsiz;
+ req->rq_rlen = bufsiz;
+ req->rq_rnr = 1;
+
+ if (task->tk_proc > clnt->cl_maxproc) {
+ printk(KERN_WARNING "%s (vers %d): bad procedure number %d\n",
+ clnt->cl_protname, clnt->cl_vers, task->tk_proc);
+ rpc_exit(task, -EIO);
+ return;
+ }
+
+ /* Encode header and provided arguments */
+ encode = rpcproc_encode(clnt, task->tk_proc);
+ if (!(p = call_header(task))) {
+ rpc_exit(task, -EIO);
+ } else
+ if ((status = encode(req, p, task->tk_argp)) < 0) {
+ printk(KERN_WARNING "%s: can't encode arguments: %d\n",
+ clnt->cl_protname, -status);
+ rpc_exit(task, status);
+ }
+}
+
+/*
+ * 4. Transmit the RPC request
+ */
+static void
+call_transmit(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_transmit (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ task->tk_action = call_receive;
+ task->tk_status = 0;
+ xprt_transmit(task);
+}
+
+/*
+ * 5. Wait for the RPC reply
+ */
+static void
+call_receive(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_receive (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ /* In case of error, evaluate status */
+ if (task->tk_status < 0) {
+ task->tk_action = call_status;
+ return;
+ }
+
+ /* If we have no decode function, this means we're performing
+ * a void call (a la lockd message passing). */
+ if (!rpcproc_decode(task->tk_client, task->tk_proc)) {
+ rpc_remove_wait_queue(task); /* remove from xprt_pending */
+ task->tk_action = NULL;
+ return;
+ }
+
+ task->tk_action = call_status;
+ xprt_receive(task);
+}
+
+/*
+ * 6. Sort out the RPC call status
+ */
+static void
+call_status(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ struct rpc_rqst *req;
+ int status = task->tk_status;
+
+ dprintk("RPC: %4d call_status (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (status >= 0) {
+ task->tk_action = call_decode;
+ } else if (status == -ETIMEDOUT) {
+ task->tk_action = call_timeout;
+ } else if (status == -EAGAIN) {
+ if (!(req = task->tk_rqstp))
+ task->tk_action = call_reserve;
+ else if (!task->tk_buffer)
+ task->tk_action = call_allocate;
+ else if (req->rq_damaged)
+ task->tk_action = call_encode;
+ else
+ task->tk_action = call_transmit;
+ } else if (status == -ENOTCONN) {
+ task->tk_action = call_reconnect;
+ } else if (status == -ECONNREFUSED && clnt->cl_autobind) {
+ task->tk_action = call_bind;
+ clnt->cl_port = 0;
+ } else {
+ if (clnt->cl_chatty)
+ printk("%s: RPC call returned error %d\n",
+ clnt->cl_protname, -status);
+ task->tk_action = NULL;
+ return;
+ }
+}
+
+/*
+ * 6a. Handle RPC timeout
+ * We do not release the request slot, so we keep using the
+ * same XID for all retransmits.
+ */
+static void
+call_timeout(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ struct rpc_rqst *req = task->tk_rqstp;
+
+ if (req) {
+ struct rpc_timeout *to = &req->rq_timeout;
+
+ if (xprt_adjust_timeout(to)) {
+ dprintk("RPC: %4d call_timeout (minor timeo)\n",
+ task->tk_pid);
+ goto minor_timeout;
+ }
+ if ((to->to_initval <<= 1) > to->to_maxval)
+ to->to_initval = to->to_maxval;
+ }
+
+ dprintk("RPC: %4d call_timeout (major timeo)\n", task->tk_pid);
+ if (clnt->cl_softrtry) {
+ if (clnt->cl_chatty && !task->tk_exit)
+ printk("%s: server %s not responding, timed out\n",
+ clnt->cl_protname, clnt->cl_server);
+ rpc_exit(task, -EIO);
+ return;
+ }
+ if (clnt->cl_chatty && !(task->tk_flags & RPC_CALL_MAJORSEEN)) {
+ printk("%s: server %s not responding, still trying\n",
+ clnt->cl_protname, clnt->cl_server);
+ task->tk_flags |= RPC_CALL_MAJORSEEN;
+ }
+ if (clnt->cl_autobind)
+ clnt->cl_port = 0;
+
+minor_timeout:
+ if (!clnt->cl_port) {
+ task->tk_action = call_bind;
+ } else if (!req) {
+ task->tk_action = call_reserve;
+ } else if (req->rq_damaged) {
+ task->tk_action = call_encode;
+ clnt->cl_stats->rpcretrans++;
+ } else {
+ task->tk_action = call_transmit;
+ clnt->cl_stats->rpcretrans++;
+ }
+ task->tk_status = 0;
+}
+
+/*
+ * 6b. Reconnect to the RPC server (TCP case)
+ */
+static void
+call_reconnect(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_reconnect status %d\n",
+ task->tk_pid, task->tk_status);
+ if (task->tk_status == 0) {
+ task->tk_action = call_status;
+ task->tk_status = -EAGAIN;
+ return;
+ }
+ task->tk_client->cl_stats->netreconn++;
+ xprt_reconnect(task);
+}
+
+/*
+ * 7. Decode the RPC reply
+ */
+static void
+call_decode(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ struct rpc_rqst *req = task->tk_rqstp;
+ kxdrproc_t decode = rpcproc_decode(clnt, task->tk_proc);
+ u32 *p;
+
+ dprintk("RPC: %4d call_decode (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (clnt->cl_chatty && (task->tk_flags & RPC_CALL_MAJORSEEN)) {
+ printk("%s: server %s OK\n",
+ clnt->cl_protname, clnt->cl_server);
+ task->tk_flags &= ~RPC_CALL_MAJORSEEN;
+ }
+
+ if (task->tk_status < 12) {
+ printk("%s: too small RPC reply size (%d bytes)\n",
+ clnt->cl_protname, task->tk_status);
+ rpc_exit(task, -EIO);
+ return;
+ }
+
+ /* Verify the RPC header */
+ if (!(p = call_verify(task)))
+ return;
+
+ /*
+ * The following is an NFS-specific hack to cater for setuid
+ * processes whose uid is mapped to nobody on the server.
+ */
+ if (task->tk_client->cl_prog == 100003 && ntohl(*p) == NFSERR_PERM) {
+ if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
+ dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
+ task->tk_flags ^= RPC_CALL_REALUID;
+ task->tk_action = call_encode;
+ return;
+ }
+ }
+
+ task->tk_action = NULL;
+ task->tk_status = decode(req, p, task->tk_resp);
+ dprintk("RPC: %4d call_decode result %d\n", task->tk_pid,
+ task->tk_status);
+}
+
+/*
+ * 8. Refresh the credentials if rejected by the server
+ */
+static void
+call_refresh(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_refresh\n", task->tk_pid);
+
+ xprt_release(task); /* Must do to obtain new XID */
+ task->tk_action = call_refreshresult;
+ task->tk_status = 0;
+ task->tk_client->cl_stats->rpcauthrefresh++;
+ rpcauth_refreshcred(task);
+}
+
+/*
+ * 8a. Process the results of a credential refresh
+ */
+static void
+call_refreshresult(struct rpc_task *task)
+{
+ dprintk("RPC: %4d call_refreshresult (status %d)\n",
+ task->tk_pid, task->tk_status);
+
+ if (task->tk_status < 0) {
+ task->tk_status = -EACCES;
+ task->tk_action = NULL;
+ } else
+ task->tk_action = call_reserve;
+}
+
+/*
+ * Call header serialization
+ */
+static u32 *
+call_header(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+ struct rpc_xprt *xprt = clnt->cl_xprt;
+ u32 *p = task->tk_buffer;
+
+ /* FIXME: check buffer size? */
+ if (xprt->stream)
+ *p++ = 0; /* fill in later */
+ *p++ = task->tk_rqstp->rq_xid; /* XID */
+ *p++ = htonl(RPC_CALL); /* CALL */
+ *p++ = htonl(RPC_VERSION); /* RPC version */
+ *p++ = htonl(clnt->cl_prog); /* program number */
+ *p++ = htonl(clnt->cl_vers); /* program version */
+ *p++ = htonl(task->tk_proc); /* procedure */
+ return rpcauth_marshcred(task, p);
+}
+
+/*
+ * Reply header verification
+ */
+static u32 *
+call_verify(struct rpc_task *task)
+{
+ u32 *p = task->tk_buffer, n;
+
+ p += 1; /* skip XID */
+
+ if ((n = ntohl(*p++)) != RPC_REPLY) {
+ printk("call_verify: not an RPC reply: %x\n", n);
+ goto garbage;
+ }
+ if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
+ int error = -EACCES;
+
+ if ((n = ntohl(*p++)) != RPC_AUTH_ERROR) {
+ printk("call_verify: RPC call rejected: %x\n", n);
+ } else
+ switch ((n = ntohl(*p++))) {
+ case RPC_AUTH_REJECTEDCRED:
+ case RPC_AUTH_REJECTEDVERF:
+ if (!task->tk_cred_retry--)
+ break;
+ dprintk("RPC: %4d call_verify: retry stale creds\n",
+ task->tk_pid);
+ rpcauth_invalcred(task);
+ task->tk_action = call_refresh;
+ return NULL;
+ case RPC_AUTH_BADCRED:
+ case RPC_AUTH_BADVERF:
+ /* possibly garbled cred/verf? */
+ if (!task->tk_garb_retry--)
+ break;
+ dprintk("RPC: %4d call_verify: retry garbled creds\n",
+ task->tk_pid);
+ task->tk_action = call_encode;
+ return NULL;
+ case RPC_AUTH_TOOWEAK:
+ printk("call_verify: server requires stronger "
+ "authentication.\n");
+ default:
+ printk("call_verify: unknown auth error: %x\n", n);
+ error = -EIO;
+ }
+ dprintk("RPC: %4d call_verify: call rejected %d\n",
+ task->tk_pid, n);
+ rpc_exit(task, error);
+ return NULL;
+ }
+ if (!(p = rpcauth_checkverf(task, p)))
+ goto garbage; /* bad verifier, retry */
+ switch ((n = ntohl(*p++))) {
+ case RPC_SUCCESS:
+ return p;
+ case RPC_GARBAGE_ARGS:
+ break; /* retry */
+ default:
+ printk("call_verify: server accept status: %x\n", n);
+ /* Also retry */
+ }
+
+garbage:
+ dprintk("RPC: %4d call_verify: server saw garbage\n", task->tk_pid);
+ task->tk_client->cl_stats->rpcgarbage++;
+ if (0 && task->tk_garb_retry--) {
+ task->tk_action = call_encode;
+ return NULL;
+ }
+ rpc_exit(task, -EIO);
+ return NULL;
+}
+
+#ifdef MODULE
+int
+init_module(void)
+{
+#ifdef RPC_DEBUG
+ rpc_register_sysctl();
+#endif
+ rpc_proc_init();
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+#ifdef RPC_DEBUG
+ rpc_unregister_sysctl();
+#endif
+ rpc_proc_exit();
+}
+#endif
diff --git a/net/sunrpc/pmap_clnt.c b/net/sunrpc/pmap_clnt.c
new file mode 100644
index 000000000..cb1a641e7
--- /dev/null
+++ b/net/sunrpc/pmap_clnt.c
@@ -0,0 +1,268 @@
+/*
+ * linux/net/sunrpc/pmap.c
+ *
+ * Portmapper client.
+ *
+ * FIXME: In a secure environment, we may want to use an authentication
+ * flavor other than AUTH_NULL.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/uio.h>
+#include <linux/in.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/sched.h>
+
+#ifdef RPC_DEBUG
+# define RPCDBG_FACILITY RPCDBG_PMAP
+#endif
+
+#define PMAP_SET 1
+#define PMAP_UNSET 2
+#define PMAP_GETPORT 3
+
+static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int);
+static void pmap_getport_done(struct rpc_task *);
+extern struct rpc_program pmap_program;
+
+/*
+ * Obtain the port for a given RPC service on a given host. This one can
+ * be called for an ongoing RPC request.
+ */
+void
+rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
+{
+ struct rpc_portmap *map = &clnt->cl_pmap;
+ struct sockaddr_in *sap = &clnt->cl_xprt->addr;
+ struct rpc_clnt *pmap_clnt;
+ struct rpc_task *child;
+
+ dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n",
+ task->tk_pid, clnt->cl_server,
+ map->pm_prog, map->pm_vers, map->pm_prot);
+
+ if (clnt->cl_binding) {
+ rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0);
+ return;
+ }
+ clnt->cl_binding = 1;
+
+ task->tk_status = 0;
+ if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) {
+ task->tk_status = -EACCES;
+ goto bailout;
+ }
+ if (!(child = rpc_new_child(pmap_clnt, task))) {
+ rpc_destroy_client(pmap_clnt);
+ goto bailout;
+ }
+
+ /* Setup the call info struct */
+ rpc_call_setup(child, PMAP_GETPORT, map, &clnt->cl_port, 0);
+
+ /* ... and run the child task */
+ rpc_run_child(task, child, pmap_getport_done);
+ return;
+
+bailout:
+ clnt->cl_binding = 0;
+ rpc_wake_up(&clnt->cl_bindwait);
+ task->tk_status = -EIO;
+ task->tk_action = NULL;
+}
+
+#ifdef CONFIG_ROOT_NFS
+int
+rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
+{
+ struct rpc_portmap map = { prog, vers, prot, 0 };
+ struct rpc_clnt *pmap_clnt;
+ char hostname[32];
+ int status;
+
+ dprintk("RPC: rpc_getport_external(%s, %d, %d, %d)\n",
+ in_ntoa(sin->sin_addr.s_addr), prog, vers, prot);
+
+ strcpy(hostname, in_ntoa(sin->sin_addr.s_addr));
+ if (!(pmap_clnt = pmap_create(hostname, sin, prot)))
+ return -EACCES;
+
+ /* Setup the call info struct */
+ status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0);
+
+ if (status >= 0) {
+ if (map.pm_port != 0)
+ return map.pm_port;
+ status = -EACCES;
+ }
+ return status;
+}
+#endif
+
+static void
+pmap_getport_done(struct rpc_task *task)
+{
+ struct rpc_clnt *clnt = task->tk_client;
+
+ dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n",
+ task->tk_pid, task->tk_status, clnt->cl_port);
+ if (task->tk_status < 0) {
+ /* Make the calling task exit with an error */
+ task->tk_action = NULL;
+ } else if (clnt->cl_port == 0) {
+ /* Program not registered */
+ task->tk_status = -EACCES;
+ task->tk_action = NULL;
+ } else {
+ /* byte-swap port number first */
+ clnt->cl_port = htons(clnt->cl_port);
+ clnt->cl_xprt->addr.sin_port = clnt->cl_port;
+ }
+ clnt->cl_binding = 0;
+ rpc_wake_up(&clnt->cl_bindwait);
+}
+
+/*
+ * Set or unset a port registration with the local portmapper.
+ * port == 0 means unregister, port != 0 means register.
+ */
+int
+rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
+{
+ struct sockaddr_in sin;
+ struct rpc_portmap map;
+ struct rpc_clnt *pmap_clnt;
+ unsigned int error = 0;
+
+ dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n",
+ prog, vers, prot, port);
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (!(pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP))) {
+ dprintk("RPC: couldn't create pmap client\n");
+ return -EACCES;
+ }
+
+ map.pm_prog = prog;
+ map.pm_vers = vers;
+ map.pm_prot = prot;
+ map.pm_port = port;
+
+ error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET,
+ &map, okay, 0);
+
+ if (error < 0) {
+ printk(KERN_WARNING
+ "RPC: failed to contact portmap (errno %d).\n",
+ error);
+ }
+ dprintk("RPC: registration status %d/%d\n", error, *okay);
+
+ /* Client deleted automatically because cl_oneshot == 1 */
+ return error;
+}
+
+static struct rpc_clnt *
+pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto)
+{
+ struct rpc_xprt *xprt;
+ struct rpc_clnt *clnt;
+
+ /* printk("pmap: create xprt\n"); */
+ if (!(xprt = xprt_create_proto(proto, srvaddr, NULL)))
+ return NULL;
+ xprt->addr.sin_port = htons(RPC_PMAP_PORT);
+
+ /* printk("pmap: create clnt\n"); */
+ clnt = rpc_create_client(xprt, hostname,
+ &pmap_program, RPC_PMAP_VERSION,
+ RPC_AUTH_NULL);
+ if (!clnt) {
+ xprt_destroy(xprt);
+ } else {
+ clnt->cl_softrtry = 1;
+ clnt->cl_chatty = 1;
+ clnt->cl_oneshot = 1;
+ }
+ return clnt;
+}
+
+/*
+ * XDR encode/decode functions for PMAP
+ */
+static int
+xdr_error(struct rpc_rqst *req, u32 *p, void *dummy)
+{
+ return -EIO;
+}
+
+static int
+xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
+{
+ dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n",
+ map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port);
+ *p++ = htonl(map->pm_prog);
+ *p++ = htonl(map->pm_vers);
+ *p++ = htonl(map->pm_prot);
+ *p++ = htonl(map->pm_port);
+
+ req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
+ return 0;
+}
+
+static int
+xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
+{
+ *portp = (unsigned short) ntohl(*p++);
+ return 0;
+}
+
+static int
+xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
+{
+ *boolp = (unsigned int) ntohl(*p++);
+ return 0;
+}
+
+static struct rpc_procinfo pmap_procedures[4] = {
+ { "pmap_null",
+ (kxdrproc_t) xdr_error,
+ (kxdrproc_t) xdr_error, 0, 0 },
+ { "pmap_set",
+ (kxdrproc_t) xdr_encode_mapping,
+ (kxdrproc_t) xdr_decode_bool, 4, 1 },
+ { "pmap_unset",
+ (kxdrproc_t) xdr_encode_mapping,
+ (kxdrproc_t) xdr_decode_bool, 4, 1 },
+ { "pmap_get",
+ (kxdrproc_t) xdr_encode_mapping,
+ (kxdrproc_t) xdr_decode_port, 4, 1 },
+};
+
+static struct rpc_version pmap_version2 = {
+ 2, 4, pmap_procedures
+};
+
+static struct rpc_version * pmap_version[] = {
+ NULL,
+ NULL,
+ &pmap_version2,
+};
+
+static struct rpc_stat pmap_stats;
+
+struct rpc_program pmap_program = {
+ "portmap",
+ RPC_PMAP_PROGRAM,
+ sizeof(pmap_version)/sizeof(pmap_version[0]),
+ pmap_version,
+ &pmap_stats,
+};
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
new file mode 100644
index 000000000..960093cad
--- /dev/null
+++ b/net/sunrpc/sched.c
@@ -0,0 +1,805 @@
+/*
+ * linux/net/sunrpc/sched.c
+ *
+ * Scheduling for synchronous and asynchronous RPC requests.
+ *
+ * Copyright (C) 1996 Olaf Kirch, <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#define __KERNEL_SYSCALLS__
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <linux/sunrpc/clnt.h>
+
+#ifdef RPC_DEBUG
+#define RPCDBG_FACILITY RPCDBG_SCHED
+static int rpc_task_id = 0;
+#endif
+
+#define _S(signo) (1 << ((signo)-1))
+
+/*
+ * We give RPC the same get_free_pages priority as NFS
+ */
+#define GFP_RPC GFP_NFS
+
+static void __rpc_default_timer(struct rpc_task *task);
+static void rpciod_killall(void);
+
+/*
+ * When an asynchronous RPC task is activated within a bottom half
+ * handler, or while executing another RPC task, it is put on
+ * schedq, and rpciod is woken up.
+ */
+static struct rpc_wait_queue schedq = RPC_INIT_WAITQ("schedq");
+
+/*
+ * RPC tasks that create another task (e.g. for contacting the portmapper)
+ * will wait on this queue for their child's completion
+ */
+static struct rpc_wait_queue childq = RPC_INIT_WAITQ("childq");
+
+/*
+ * All RPC tasks are linked into this list
+ */
+static struct rpc_task * all_tasks = NULL;
+
+/*
+ * rpciod-related stuff
+ */
+static struct wait_queue * rpciod_idle = NULL;
+static struct wait_queue * rpciod_killer = NULL;
+static int rpciod_sema = 0;
+static pid_t rpciod_pid = 0;
+static int rpc_inhibit = 0;
+
+/*
+ * This is the last-ditch buffer for NFS swap requests
+ */
+static u32 swap_buffer[PAGE_SIZE >> 2];
+static int swap_buffer_used = 0;
+
+/*
+ * Add new request to wait queue.
+ *
+ * Swapper tasks always get inserted at the head of the queue.
+ * This should avoid many nasty memory deadlocks and hopefully
+ * improve overall performance.
+ * Everyone else gets appended to the queue to ensure proper FIFO behavior.
+ */
+void
+rpc_add_wait_queue(struct rpc_wait_queue *queue, struct rpc_task *task)
+{
+ if (task->tk_rpcwait) {
+ if (task->tk_rpcwait != queue)
+ printk(KERN_WARNING "RPC: doubly enqueued task!\n");
+ return;
+ }
+ if (RPC_IS_SWAPPER(task))
+ rpc_insert_list(&queue->task, task);
+ else
+ rpc_append_list(&queue->task, task);
+ task->tk_rpcwait = queue;
+
+ dprintk("RPC: %4d added to queue %p \"%s\"\n",
+ task->tk_pid, queue, rpc_qname(queue));
+}
+
+/*
+ * Remove request from queue
+ */
+void
+rpc_remove_wait_queue(struct rpc_task *task)
+{
+ struct rpc_wait_queue *queue;
+
+ if (!(queue = task->tk_rpcwait))
+ return;
+ rpc_remove_list(&queue->task, task);
+ task->tk_rpcwait = NULL;
+
+ dprintk("RPC: %4d removed from queue %p \"%s\"\n",
+ task->tk_pid, queue, rpc_qname(queue));
+}
+
+/*
+ * Set up a timer for the current task.
+ */
+inline void
+rpc_add_timer(struct rpc_task *task, rpc_action timer)
+{
+ unsigned long expires = jiffies + task->tk_timeout;
+
+ dprintk("RPC: %4d setting alarm for %lu ms\n",
+ task->tk_pid, task->tk_timeout * 1000 / HZ);
+ if (!timer)
+ timer = __rpc_default_timer;
+ if (expires < jiffies) {
+ printk("RPC: bad timeout value %ld - setting to 10 sec!\n",
+ task->tk_timeout);
+ expires = jiffies + 10 * HZ;
+ }
+ task->tk_timer.expires = expires;
+ task->tk_timer.data = (unsigned long) task;
+ task->tk_timer.function = (void (*)(unsigned long)) timer;
+ task->tk_timer.prev = NULL;
+ task->tk_timer.next = NULL;
+ add_timer(&task->tk_timer);
+}
+
+/*
+ * Delete any timer for the current task.
+ * Must be called with interrupts off.
+ */
+inline void
+rpc_del_timer(struct rpc_task *task)
+{
+ if (task->tk_timeout) {
+ dprintk("RPC: %4d deleting timer\n", task->tk_pid);
+ del_timer(&task->tk_timer);
+ task->tk_timeout = 0;
+ }
+}
+
+/*
+ * Make an RPC task runnable.
+ */
+static inline void
+rpc_make_runnable(struct rpc_task *task)
+{
+ if (task->tk_timeout) {
+ printk("RPC: task w/ running timer in rpc_make_runnable!!\n");
+ return;
+ }
+ if (RPC_IS_ASYNC(task)) {
+ rpc_add_wait_queue(&schedq, task);
+ wake_up(&rpciod_idle);
+ } else {
+ wake_up(&task->tk_wait);
+ }
+ task->tk_flags |= RPC_TASK_RUNNING;
+}
+
+/*
+ * Prepare for sleeping on a wait queue.
+ * By always appending tasks to the list we ensure FIFO behavior.
+ * NB: An RPC task will only receive interrupt-driven events as long
+ * as it's on a wait queue.
+ */
+static void
+__rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
+ rpc_action action, rpc_action timer)
+{
+ unsigned long oldflags;
+
+ dprintk("RPC: %4d sleep_on(queue \"%s\" time %ld)\n", task->tk_pid,
+ rpc_qname(q), jiffies);
+
+ /*
+ * Protect the execution below.
+ */
+ save_flags(oldflags); cli();
+
+ rpc_add_wait_queue(q, task);
+ task->tk_callback = action;
+ if (task->tk_timeout)
+ rpc_add_timer(task, timer);
+ task->tk_flags &= ~RPC_TASK_RUNNING;
+
+ restore_flags(oldflags);
+ return;
+}
+
+void
+rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
+ rpc_action action, rpc_action timer)
+{
+ __rpc_sleep_on(q, task, action, timer);
+}
+
+/*
+ * Wake up a single task -- must be invoked with bottom halves off.
+ *
+ * It would probably suffice to cli/sti the del_timer and remove_wait_queue
+ * operations individually.
+ */
+static void
+__rpc_wake_up(struct rpc_task *task)
+{
+ dprintk("RPC: %4d __rpc_wake_up (now %ld inh %d)\n",
+ task->tk_pid, jiffies, rpc_inhibit);
+
+#ifdef RPC_DEBUG
+ if (task->tk_magic != 0xf00baa) {
+ printk("RPC: attempt to wake up non-existing task!\n");
+ rpc_debug = ~0;
+ return;
+ }
+#endif
+ rpc_del_timer(task);
+ if (task->tk_rpcwait != &schedq)
+ rpc_remove_wait_queue(task);
+ if (!RPC_IS_RUNNING(task)) {
+ rpc_make_runnable(task);
+ task->tk_flags |= RPC_TASK_CALLBACK;
+ }
+ dprintk("RPC: __rpc_wake_up done\n");
+}
+
+/*
+ * Default timeout handler if none specified by user
+ */
+static void
+__rpc_default_timer(struct rpc_task *task)
+{
+ dprintk("RPC: %d timeout (default timer)\n", task->tk_pid);
+ task->tk_status = -ETIMEDOUT;
+ task->tk_timeout = 0;
+ __rpc_wake_up(task);
+}
+
+/*
+ * Wake up the specified task
+ */
+void
+rpc_wake_up_task(struct rpc_task *task)
+{
+ unsigned long oldflags;
+
+ save_flags(oldflags); cli();
+ __rpc_wake_up(task);
+ restore_flags(oldflags);
+}
+
+/*
+ * Wake up the next task on the wait queue.
+ */
+struct rpc_task *
+rpc_wake_up_next(struct rpc_wait_queue *queue)
+{
+ unsigned long oldflags;
+ struct rpc_task *task;
+
+ dprintk("RPC: wake_up_next(%p \"%s\")\n", queue, rpc_qname(queue));
+ save_flags(oldflags); cli();
+ if ((task = queue->task) != 0)
+ __rpc_wake_up(task);
+ restore_flags(oldflags);
+
+ return task;
+}
+
+/*
+ * Wake up all tasks on a queue
+ */
+void
+rpc_wake_up(struct rpc_wait_queue *queue)
+{
+ unsigned long oldflags;
+
+ save_flags(oldflags); cli();
+ while (queue->task)
+ __rpc_wake_up(queue->task);
+ restore_flags(oldflags);
+}
+
+/*
+ * Wake up all tasks on a queue, and set their status value.
+ */
+void
+rpc_wake_up_status(struct rpc_wait_queue *queue, int status)
+{
+ struct rpc_task *task;
+ unsigned long oldflags;
+
+ save_flags(oldflags); cli();
+ while ((task = queue->task) != NULL) {
+ task->tk_status = status;
+ __rpc_wake_up(task);
+ }
+ restore_flags(oldflags);
+}
+
+/*
+ * Run a task at a later time
+ */
+static void __rpc_atrun(struct rpc_task *);
+void
+rpc_delay(struct rpc_task *task, unsigned long delay)
+{
+ static struct rpc_wait_queue delay_queue;
+
+ task->tk_timeout = delay;
+ rpc_sleep_on(&delay_queue, task, NULL, __rpc_atrun);
+}
+
+static void
+__rpc_atrun(struct rpc_task *task)
+{
+ task->tk_status = 0;
+ __rpc_wake_up(task);
+}
+
+/*
+ * This is the RPC `scheduler' (or rather, the finite state machine).
+ */
+static int
+__rpc_execute(struct rpc_task *task)
+{
+ unsigned long oldflags;
+ int status = 0;
+
+ dprintk("RPC: %4d rpc_execute flgs %x\n",
+ task->tk_pid, task->tk_flags);
+
+ if (!RPC_IS_RUNNING(task)) {
+ printk("RPC: rpc_execute called for sleeping task!!\n");
+ return 0;
+ }
+
+ while (1) {
+ /*
+ * Execute any pending callback.
+ */
+ if (task->tk_flags & RPC_TASK_CALLBACK) {
+ task->tk_flags &= ~RPC_TASK_CALLBACK;
+ if (task->tk_callback) {
+ task->tk_callback(task);
+ task->tk_callback = NULL;
+ }
+ }
+
+ /*
+ * No handler for next step means exit.
+ */
+ if (!task->tk_action)
+ break;
+
+ /*
+ * Perform the next FSM step.
+ * tk_action may be NULL when the task has been killed
+ * by someone else.
+ */
+ if (RPC_IS_RUNNING(task) && task->tk_action)
+ task->tk_action(task);
+
+ /*
+ * Check whether task is sleeping.
+ * Note that if the task may go to sleep in tk_action,
+ * and the RPC reply arrives before we get here, it will
+ * have state RUNNING, but will still be on schedq.
+ */
+ save_flags(oldflags); cli();
+ if (RPC_IS_RUNNING(task)) {
+ if (task->tk_rpcwait == &schedq)
+ rpc_remove_wait_queue(task);
+ } else while (!RPC_IS_RUNNING(task)) {
+ if (RPC_IS_ASYNC(task)) {
+ restore_flags(oldflags);
+ return 0;
+ }
+
+ /* sync task: sleep here */
+ dprintk("RPC: %4d sync task going to sleep\n",
+ task->tk_pid);
+ current->timeout = 0;
+ interruptible_sleep_on(&task->tk_wait);
+
+ /* When the task received a signal, remove from
+ * any queues etc, and make runnable again. */
+ if (signalled())
+ __rpc_wake_up(task);
+
+ dprintk("RPC: %4d sync task resuming\n",
+ task->tk_pid);
+ }
+ restore_flags(oldflags);
+
+ /*
+ * When a sync task receives a signal, it exits with
+ * -ERESTARTSYS. In order to catch any callbacks that
+ * clean up after sleeping on some queue, we don't
+ * break the loop here, but go around once more.
+ */
+ if (!RPC_IS_ASYNC(task) && signalled()) {
+ dprintk("RPC: %4d got signal (map %08lx)\n",
+ task->tk_pid,
+ current->signal & ~current->blocked);
+ rpc_exit(task, -ERESTARTSYS);
+ }
+ }
+
+ dprintk("RPC: %4d exit() = %d\n", task->tk_pid, task->tk_status);
+ if (task->tk_exit) {
+ status = task->tk_status;
+ task->tk_exit(task);
+ }
+
+ return status;
+}
+
+/*
+ * User-visible entry point to the scheduler.
+ * The recursion protection is for debugging. It should go away once
+ * the code has stabilized.
+ */
+void
+rpc_execute(struct rpc_task *task)
+{
+ static int executing = 0;
+ int incr = RPC_IS_ASYNC(task)? 1 : 0;
+
+ if (incr && (executing || rpc_inhibit)) {
+ printk("RPC: rpc_execute called recursively!\n");
+ return;
+ }
+ executing += incr;
+ __rpc_execute(task);
+ executing -= incr;
+}
+
+/*
+ * This is our own little scheduler for async RPC tasks.
+ */
+static void
+__rpc_schedule(void)
+{
+ struct rpc_task *task;
+ int count = 0;
+ unsigned long oldflags;
+
+ dprintk("RPC: rpc_schedule enter\n");
+ while (1) {
+ save_flags(oldflags); cli();
+ if (!(task = schedq.task))
+ break;
+ rpc_del_timer(task);
+ rpc_remove_wait_queue(task);
+ task->tk_flags |= RPC_TASK_RUNNING;
+ restore_flags(oldflags);
+
+ __rpc_execute(task);
+
+ if (++count >= 200) {
+ count = 0;
+ need_resched = 1;
+ }
+ if (need_resched)
+ schedule();
+ }
+ restore_flags(oldflags);
+ dprintk("RPC: rpc_schedule leave\n");
+}
+
+/*
+ * Allocate memory for RPC purpose.
+ *
+ * This is yet another tricky issue: For sync requests issued by
+ * a user process, we want to make kmalloc sleep if there isn't
+ * enough memory. Async requests should not sleep too excessively
+ * because that will block rpciod (but that's not dramatic when
+ * it's starved of memory anyway). Finally, swapout requests should
+ * never sleep at all, and should not trigger another swap_out
+ * request through kmalloc which would just increase memory contention.
+ *
+ * I hope the following gets it right, which gives async requests
+ * a slight advantage over sync requests (good for writeback, debatable
+ * for readahead):
+ *
+ * sync user requests: GFP_KERNEL
+ * async requests: GFP_RPC (== GFP_NFS)
+ * swap requests: GFP_ATOMIC (or new GFP_SWAPPER)
+ */
+void *
+rpc_allocate(unsigned int flags, unsigned int size)
+{
+ u32 *buffer;
+ int gfp;
+
+ if (flags & RPC_TASK_SWAPPER)
+ gfp = GFP_ATOMIC;
+ else if (flags & RPC_TASK_ASYNC)
+ gfp = GFP_RPC;
+ else
+ gfp = GFP_KERNEL;
+
+ do {
+ if ((buffer = (u32 *) kmalloc(size, gfp)) != NULL) {
+ dprintk("RPC: allocated buffer %p\n", buffer);
+ return buffer;
+ }
+ if ((flags & RPC_TASK_SWAPPER) && !swap_buffer_used++) {
+ dprintk("RPC: used last-ditch swap buffer\n");
+ return swap_buffer;
+ }
+ if (flags & RPC_TASK_ASYNC)
+ return NULL;
+ current->timeout = jiffies + (HZ >> 4);
+ schedule();
+ } while (!signalled());
+
+ return NULL;
+}
+
+void
+rpc_free(void *buffer)
+{
+ if (buffer != swap_buffer) {
+ kfree(buffer);
+ return;
+ }
+ swap_buffer_used = 0;
+}
+
+/*
+ * Creation and deletion of RPC task structures
+ */
+inline void
+rpc_init_task(struct rpc_task *task, struct rpc_clnt *clnt,
+ rpc_action callback, int flags)
+{
+ memset(task, 0, sizeof(*task));
+ task->tk_client = clnt;
+ task->tk_flags = RPC_TASK_RUNNING | flags;
+ task->tk_exit = callback;
+ if (current->uid != current->fsuid || current->gid != current->fsgid)
+ task->tk_flags |= RPC_TASK_SETUID;
+
+ /* Initialize retry counters */
+ task->tk_garb_retry = 2;
+ task->tk_cred_retry = 2;
+ task->tk_suid_retry = 1;
+
+ /* Add to global list of all tasks */
+ task->tk_next_task = all_tasks;
+ task->tk_prev_task = NULL;
+ if (all_tasks)
+ all_tasks->tk_prev_task = task;
+ all_tasks = task;
+
+ if (clnt)
+ clnt->cl_users++;
+
+#ifdef RPC_DEBUG
+ task->tk_magic = 0xf00baa;
+ task->tk_pid = rpc_task_id++;
+#endif
+ dprintk("RPC: %4d new task procpid %d\n", task->tk_pid,
+ current->pid);
+}
+
+struct rpc_task *
+rpc_new_task(struct rpc_clnt *clnt, rpc_action callback, int flags)
+{
+ struct rpc_task *task;
+
+ if (!(task = (struct rpc_task *) rpc_allocate(flags, sizeof(*task))))
+ return NULL;
+
+ rpc_init_task(task, clnt, callback, flags);
+
+ dprintk("RPC: %4d allocated task\n", task->tk_pid);
+ task->tk_flags |= RPC_TASK_DYNAMIC;
+ return task;
+}
+
+void
+rpc_release_task(struct rpc_task *task)
+{
+ struct rpc_task *next, *prev;
+
+ dprintk("RPC: %4d release task\n", task->tk_pid);
+
+ /* Remove from global task list */
+ prev = task->tk_prev_task;
+ next = task->tk_next_task;
+ if (next)
+ next->tk_prev_task = prev;
+ if (prev)
+ prev->tk_next_task = next;
+ else
+ all_tasks = next;
+
+ /* Release resources */
+ if (task->tk_rqstp)
+ xprt_release(task);
+ if (task->tk_cred)
+ rpcauth_releasecred(task);
+ if (task->tk_buffer) {
+ rpc_free(task->tk_buffer);
+ task->tk_buffer = NULL;
+ }
+ if (task->tk_client) {
+ rpc_release_client(task->tk_client);
+ task->tk_client = NULL;
+ }
+
+#ifdef RPC_DEBUG
+ task->tk_magic = 0;
+#endif
+
+ if (task->tk_flags & RPC_TASK_DYNAMIC) {
+ dprintk("RPC: %4d freeing task\n", task->tk_pid);
+ task->tk_flags &= ~RPC_TASK_DYNAMIC;
+ rpc_free(task);
+ }
+}
+
+/*
+ * Handling of RPC child tasks
+ * We can't simply call wake_up(parent) here, because the
+ * parent task may already have gone away
+ */
+static inline struct rpc_task *
+rpc_find_parent(struct rpc_task *child)
+{
+ struct rpc_task *temp, *parent;
+
+ parent = (struct rpc_task *) child->tk_calldata;
+ for (temp = childq.task; temp; temp = temp->tk_next) {
+ if (temp == parent)
+ return parent;
+ }
+ return NULL;
+}
+
+static void
+rpc_child_exit(struct rpc_task *child)
+{
+ struct rpc_task *parent;
+
+ if ((parent = rpc_find_parent(child)) != NULL) {
+ parent->tk_status = child->tk_status;
+ rpc_wake_up_task(parent);
+ }
+ rpc_release_task(child);
+}
+
+struct rpc_task *
+rpc_new_child(struct rpc_clnt *clnt, struct rpc_task *parent)
+{
+ struct rpc_task *task;
+
+ if (!(task = rpc_new_task(clnt, NULL, RPC_TASK_ASYNC|RPC_TASK_CHILD))) {
+ parent->tk_status = -ENOMEM;
+ return NULL;
+ }
+ task->tk_exit = rpc_child_exit;
+ task->tk_calldata = parent;
+
+ return task;
+}
+
+void
+rpc_run_child(struct rpc_task *task, struct rpc_task *child, rpc_action func)
+{
+ rpc_make_runnable(child);
+ rpc_sleep_on(&childq, task, func, NULL);
+}
+
+/*
+ * Kill all tasks for the given client.
+ * XXX: kill their descendants as well?
+ */
+void
+rpc_killall_tasks(struct rpc_clnt *clnt)
+{
+ struct rpc_task **q, *rovr;
+
+ dprintk("RPC: killing all tasks for client %p\n", clnt);
+ rpc_inhibit++;
+ for (q = &all_tasks; (rovr = *q); q = &rovr->tk_next_task) {
+ if (!clnt || rovr->tk_client == clnt) {
+ rovr->tk_flags |= RPC_TASK_KILLED;
+ rpc_exit(rovr, -EIO);
+ rpc_wake_up_task(rovr);
+ }
+ }
+ rpc_inhibit--;
+}
+
+/*
+ * This is the rpciod kernel thread
+ */
+static int
+rpciod(void *ptr)
+{
+ struct wait_queue **assassin = (struct wait_queue **) ptr;
+ unsigned long oldflags;
+ int rounds = 0;
+
+ lock_kernel();
+ rpciod_pid = current->pid;
+
+ MOD_INC_USE_COUNT;
+ /* exit_files(current); */
+ exit_mm(current);
+ current->blocked |= ~_S(SIGKILL);
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "rpciod");
+
+ dprintk("RPC: rpciod starting (pid %d)\n", rpciod_pid);
+ while (rpciod_sema) {
+ if (signalled()) {
+ if (current->signal & _S(SIGKILL)) {
+ rpciod_killall();
+ } else {
+ printk("rpciod: ignoring signal (%d users)\n",
+ rpciod_sema);
+ }
+ current->signal &= current->blocked;
+ }
+ __rpc_schedule();
+
+ if (++rounds >= 64) /* safeguard */
+ schedule();
+ save_flags(oldflags); cli();
+ if (!schedq.task) {
+ dprintk("RPC: rpciod back to sleep\n");
+ interruptible_sleep_on(&rpciod_idle);
+ dprintk("RPC: switch to rpciod\n");
+ }
+ restore_flags(oldflags);
+ }
+
+ dprintk("RPC: rpciod shutdown commences\n");
+ if (all_tasks) {
+ printk("rpciod: active tasks at shutdown?!\n");
+ rpciod_killall();
+ }
+
+ rpciod_pid = 0;
+ wake_up(assassin);
+
+ dprintk("RPC: rpciod exiting\n");
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void
+rpciod_killall(void)
+{
+ while (all_tasks) {
+ unsigned long oldsig = current->signal;
+
+ current->signal = 0;
+ rpc_killall_tasks(NULL);
+ __rpc_schedule();
+ current->timeout = jiffies + HZ / 100;
+ need_resched = 1;
+ schedule();
+ current->signal = oldsig;
+ }
+}
+
+void
+rpciod_up(void)
+{
+ dprintk("rpciod_up pid %d sema %d\n", rpciod_pid, rpciod_sema);
+ if (!(rpciod_sema++) || !rpciod_pid)
+ kernel_thread(rpciod, &rpciod_killer, 0);
+}
+
+void
+rpciod_down(void)
+{
+ dprintk("rpciod_down pid %d sema %d\n", rpciod_pid, rpciod_sema);
+ if (--rpciod_sema > 0)
+ return;
+
+ rpciod_sema = 0;
+ kill_proc(rpciod_pid, SIGKILL, 1);
+ while (rpciod_pid) {
+ if (signalled())
+ return;
+ interruptible_sleep_on(&rpciod_killer);
+ }
+}
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
new file mode 100644
index 000000000..90a23a232
--- /dev/null
+++ b/net/sunrpc/stats.c
@@ -0,0 +1,175 @@
+/*
+ * linux/net/sunrpc/stats.c
+ *
+ * procfs-based user access to generic RPC statistics. The stats files
+ * reside in /proc/net/rpc.
+ *
+ * The read routines assume that the buffer passed in is just big enough.
+ * If you implement an RPC service that has its own stats routine which
+ * appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
+ * limit.
+ *
+ * Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY RPCDBG_MISC
+
+static struct proc_dir_entry *proc_net_rpc = 0;
+
+/*
+ * Get RPC client stats
+ */
+int
+rpc_proc_read(char *buffer, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ struct rpc_stat *statp = (struct rpc_stat *) data;
+ struct rpc_program *prog = statp->program;
+ struct rpc_version *vers;
+ int len, i, j;
+
+ len = sprintf(buffer,
+ "net %d %d %d %d\n",
+ statp->netcnt,
+ statp->netudpcnt,
+ statp->nettcpcnt,
+ statp->nettcpconn);
+ len += sprintf(buffer + len,
+ "rpc %d %d %d\n",
+ statp->rpccnt,
+ statp->rpcretrans,
+ statp->rpcauthrefresh);
+
+ for (i = 0; i < prog->nrvers; i++) {
+ if (!(vers = prog->version[i]))
+ continue;
+ len += sprintf(buffer + len, "proc%d %d",
+ vers->number, vers->nrprocs);
+ for (j = 0; j < vers->nrprocs; j++)
+ len += sprintf(buffer + len, " %d",
+ vers->procs[j].p_count);
+ buffer[len++] = '\n';
+ }
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+ return len;
+}
+
+/*
+ * Get RPC server stats
+ */
+int
+svc_proc_read(char *buffer, char **start, off_t offset, int count,
+ int *eof, void *data)
+{
+ struct svc_stat *statp = (struct svc_stat *) data;
+ struct svc_program *prog = statp->program;
+ struct svc_procedure *proc;
+ struct svc_version *vers;
+ int len, i, j;
+
+ len = sprintf(buffer,
+ "net %d %d %d %d\n",
+ statp->netcnt,
+ statp->netudpcnt,
+ statp->nettcpcnt,
+ statp->nettcpconn);
+ len += sprintf(buffer + len,
+ "rpc %d %d %d %d %d\n",
+ statp->rpccnt,
+ statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
+ statp->rpcbadfmt,
+ statp->rpcbadauth,
+ statp->rpcbadclnt);
+
+ for (i = 0; i < prog->pg_nvers; i++) {
+ if (!(vers = prog->pg_vers[i]) || !(proc = vers->vs_proc))
+ continue;
+ len += sprintf(buffer + len, "proc%d %d", i, vers->vs_nproc);
+ for (j = 0; j < vers->vs_nproc; j++, proc++)
+ len += sprintf(buffer + len, " %d", proc->pc_count);
+ buffer[len++] = '\n';
+ }
+
+ if (offset >= len) {
+ *start = buffer;
+ *eof = 1;
+ return 0;
+ }
+ *start = buffer + offset;
+ if ((len -= offset) > count)
+ return count;
+ *eof = 1;
+ return len;
+}
+
+/*
+ * Register/unregister RPC proc files
+ */
+static inline struct proc_dir_entry *
+do_register(const char *name, void *data, int issvc)
+{
+ struct proc_dir_entry *ent;
+
+ dprintk("RPC: registering /proc/net/rpc/%s\n", name);
+ ent = create_proc_entry(name, 0, proc_net_rpc);
+ ent->read_proc = issvc? svc_proc_read : rpc_proc_read;
+ ent->data = data;
+
+ return ent;
+}
+
+struct proc_dir_entry *
+rpc_proc_register(struct rpc_stat *statp)
+{
+ return do_register(statp->program->name, statp, 0);
+}
+
+void
+rpc_proc_unregister(const char *name)
+{
+ remove_proc_entry(name, proc_net_rpc);
+}
+
+struct proc_dir_entry *
+svc_proc_register(struct svc_stat *statp)
+{
+ return do_register(statp->program->pg_name, statp, 1);
+}
+
+void
+svc_proc_unregister(const char *name)
+{
+ remove_proc_entry(name, proc_net_rpc);
+}
+
+void
+rpc_proc_init(void)
+{
+ dprintk("RPC: registering /proc/net/rpc\n");
+ if (!proc_net_rpc)
+ proc_net_rpc = create_proc_entry("net/rpc", S_IFDIR, 0);
+}
+
+void
+rpc_proc_exit(void)
+{
+ dprintk("RPC: unregistering /proc/net/rpc\n");
+ if (proc_net_rpc)
+ remove_proc_entry("net/rpc", 0);
+ proc_net_rpc = 0;
+}
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
new file mode 100644
index 000000000..3e1d57873
--- /dev/null
+++ b/net/sunrpc/sunrpc_syms.c
@@ -0,0 +1,101 @@
+/*
+ * linux/net/sunrpc/sunrpc_syms.c
+ *
+ * Symbols exported by the sunrpc module.
+ *
+ * Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <linux/uio.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/auth.h>
+
+/* RPC scheduler */
+EXPORT_SYMBOL(rpc_allocate);
+EXPORT_SYMBOL(rpc_free);
+EXPORT_SYMBOL(rpc_execute);
+EXPORT_SYMBOL(rpc_init_task);
+EXPORT_SYMBOL(rpc_release_task);
+EXPORT_SYMBOL(rpc_sleep_on);
+EXPORT_SYMBOL(rpc_wake_up_next);
+EXPORT_SYMBOL(rpc_wake_up_task);
+EXPORT_SYMBOL(rpc_new_child);
+EXPORT_SYMBOL(rpc_run_child);
+EXPORT_SYMBOL(rpciod_down);
+EXPORT_SYMBOL(rpciod_up);
+
+/* RPC client functions */
+EXPORT_SYMBOL(rpc_create_client);
+EXPORT_SYMBOL(rpc_destroy_client);
+EXPORT_SYMBOL(rpc_shutdown_client);
+EXPORT_SYMBOL(rpc_do_call);
+EXPORT_SYMBOL(rpc_call_setup);
+EXPORT_SYMBOL(rpc_delay);
+EXPORT_SYMBOL(rpc_restart_call);
+
+/* Client transport */
+EXPORT_SYMBOL(xprt_create_proto);
+EXPORT_SYMBOL(xprt_destroy);
+EXPORT_SYMBOL(xprt_set_timeout);
+
+/* Client credential cache */
+EXPORT_SYMBOL(rpcauth_register);
+EXPORT_SYMBOL(rpcauth_unregister);
+EXPORT_SYMBOL(rpcauth_init_credcache);
+EXPORT_SYMBOL(rpcauth_free_credcache);
+EXPORT_SYMBOL(rpcauth_insert_credcache);
+EXPORT_SYMBOL(rpcauth_lookupcred);
+EXPORT_SYMBOL(rpcauth_matchcred);
+EXPORT_SYMBOL(rpcauth_releasecred);
+
+/* RPC server stuff */
+EXPORT_SYMBOL(svc_create);
+EXPORT_SYMBOL(svc_create_socket);
+EXPORT_SYMBOL(svc_create_thread);
+EXPORT_SYMBOL(svc_exit_thread);
+EXPORT_SYMBOL(svc_destroy);
+EXPORT_SYMBOL(svc_drop);
+EXPORT_SYMBOL(svc_process);
+EXPORT_SYMBOL(svc_recv);
+EXPORT_SYMBOL(svc_wake_up);
+
+/* RPC statistics */
+#ifdef CONFIG_PROC_FS
+EXPORT_SYMBOL(rpc_proc_register);
+EXPORT_SYMBOL(rpc_proc_unregister);
+EXPORT_SYMBOL(rpc_proc_read);
+EXPORT_SYMBOL(svc_proc_register);
+EXPORT_SYMBOL(svc_proc_unregister);
+EXPORT_SYMBOL(svc_proc_read);
+#endif
+
+/* Generic XDR */
+EXPORT_SYMBOL(xdr_encode_string);
+EXPORT_SYMBOL(xdr_decode_string);
+EXPORT_SYMBOL(xdr_decode_netobj);
+EXPORT_SYMBOL(xdr_encode_netobj);
+EXPORT_SYMBOL(xdr_zero);
+EXPORT_SYMBOL(xdr_one);
+
+/* RPC errors */
+EXPORT_SYMBOL(rpc_success);
+EXPORT_SYMBOL(rpc_garbage_args);
+EXPORT_SYMBOL(rpc_system_err);
+
+/* Debugging symbols */
+EXPORT_SYMBOL(rpc_debug);
+EXPORT_SYMBOL(nfs_debug);
+EXPORT_SYMBOL(nfsd_debug);
+EXPORT_SYMBOL(nlm_debug);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
new file mode 100644
index 000000000..55282366f
--- /dev/null
+++ b/net/sunrpc/svc.c
@@ -0,0 +1,357 @@
+/*
+ * linux/net/sunrpc/svc.c
+ *
+ * High-level RPC service routines
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+#include <linux/linkage.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/unistd.h>
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/clnt.h>
+
+#define RPCDBG_FACILITY RPCDBG_SVCDSP
+
+/*
+ * Create an RPC service
+ */
+struct svc_serv *
+svc_create(struct svc_program *prog, unsigned int bufsize, unsigned int xdrsize)
+{
+ struct svc_serv *serv;
+
+ xdr_init();
+
+ if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
+ return NULL;
+
+ memset(serv, 0, sizeof(*serv));
+ serv->sv_program = prog;
+ serv->sv_nrthreads = 1;
+ serv->sv_stats = prog->pg_stats;
+ serv->sv_bufsz = bufsize? bufsize : 4096;
+ serv->sv_xdrsize = xdrsize;
+
+ serv->sv_name = prog->pg_name;
+
+ /* Remove any stale portmap registrations */
+ svc_register(serv, 0, 0);
+
+ return serv;
+}
+
+/*
+ * Destroy an RPC service
+ */
+void
+svc_destroy(struct svc_serv *serv)
+{
+ struct svc_sock *svsk;
+
+ dprintk("RPC: svc_destroy(%s, %d)\n",
+ serv->sv_program->pg_name,
+ serv->sv_nrthreads);
+
+ if (--(serv->sv_nrthreads) != 0)
+ return;
+ while ((svsk = serv->sv_allsocks) != NULL)
+ svc_delete_socket(svsk);
+
+ /* Unregister service with the portmapper */
+ svc_register(serv, 0, 0);
+ kfree(serv);
+}
+
+/*
+ * Allocate an RPC server buffer
+ * Later versions may do nifty things by allocating multiple pages
+ * of memory directly and putting them into the bufp->iov.
+ */
+int
+svc_init_buffer(struct svc_buf *bufp, unsigned int size)
+{
+ if (!(bufp->area = (u32 *) kmalloc(size, GFP_KERNEL)))
+ return 0;
+ bufp->base = bufp->area;
+ bufp->buf = bufp->area;
+ bufp->len = 0;
+ bufp->buflen = size >> 2;
+
+ bufp->iov[0].iov_base = bufp->area;
+ bufp->iov[0].iov_len = size;
+ bufp->nriov = 1;
+
+ return 1;
+}
+
+/*
+ * Release an RPC server buffer
+ */
+void
+svc_release_buffer(struct svc_buf *bufp)
+{
+ kfree(bufp->area);
+ bufp->area = 0;
+}
+
+/*
+ * Create a server thread
+ */
+int
+svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
+{
+ struct svc_rqst *rqstp = 0;
+ int error;
+
+ if (!(rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL)))
+ return -ENOMEM;
+
+ memset(rqstp, 0, sizeof(*rqstp));
+ if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
+ || !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
+ || !svc_init_buffer(&rqstp->rq_defbuf, serv->sv_bufsz)) {
+ error = -ENOMEM;
+ goto failure;
+ }
+
+ serv->sv_nrthreads++;
+ if ((error = kernel_thread((int (*)(void *)) func, rqstp, 0)) < 0)
+ goto failure;
+
+ rqstp->rq_server = serv;
+ return 0;
+
+failure:
+ svc_exit_thread(rqstp);
+ return error;
+}
+
+/*
+ * Destroy an RPC server thread
+ */
+void
+svc_exit_thread(struct svc_rqst *rqstp)
+{
+ struct svc_serv *serv = rqstp->rq_server;
+
+ svc_release_buffer(&rqstp->rq_defbuf);
+ if (rqstp->rq_resp)
+ kfree(rqstp->rq_resp);
+ if (rqstp->rq_argp)
+ kfree(rqstp->rq_argp);
+ kfree(rqstp);
+
+ /* Release the server */
+ svc_destroy(serv);
+}
+
+/*
+ * Register an RPC service with the local portmapper. To
+ * unregister a service, call this routine with proto and port == 0.
+ */
+int
+svc_register(struct svc_serv *serv, int proto, unsigned short port)
+{
+ struct svc_program *progp;
+ unsigned long oldsigs = 0;
+ int i, error = 0, dummy;
+
+ progp = serv->sv_program;
+
+ dprintk("RPC: svc_register(%s, %s, %d)\n",
+ progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port);
+
+ if (!port) {
+ oldsigs = current->signal;
+ current->signal = 0;
+ }
+
+ for (i = 0; i < progp->pg_nvers; i++) {
+ if (progp->pg_vers[i] == NULL)
+ continue;
+ error = rpc_register(progp->pg_prog, i, proto, port, &dummy);
+ if (error < 0)
+ return error;
+ if (port && !dummy) {
+ error = -EACCES;
+ break;
+ }
+ }
+ current->signal |= oldsigs;
+ return error;
+}
+
+/*
+ * Process the RPC request.
+ */
+int
+svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+ struct svc_program *progp;
+ struct svc_version *versp = NULL; /* compiler food */
+ struct svc_procedure *procp = NULL;
+ struct svc_buf * argp = &rqstp->rq_argbuf;
+ struct svc_buf * resp = &rqstp->rq_resbuf;
+ kxdrproc_t xdr;
+ u32 *bufp, *statp;
+ u32 dir, prog, vers, proc,
+ auth_stat, rpc_stat;
+
+ rpc_stat = rpc_success;
+ bufp = argp->buf;
+
+ if (argp->len < 5)
+ goto dropit;
+
+ dir = ntohl(*bufp++);
+ vers = ntohl(*bufp++);
+
+ /* First words of reply: */
+ svc_putlong(resp, xdr_one); /* REPLY */
+ svc_putlong(resp, xdr_zero); /* ACCEPT */
+
+ if (dir != 0) { /* direction != CALL */
+ serv->sv_stats->rpcbadfmt++;
+ goto dropit; /* drop request */
+ }
+ if (vers != 2) { /* RPC version number */
+ serv->sv_stats->rpcbadfmt++;
+ resp->buf[-1] = xdr_one; /* REJECT */
+ svc_putlong(resp, xdr_zero); /* RPC_MISMATCH */
+ svc_putlong(resp, xdr_two); /* Only RPCv2 supported */
+ svc_putlong(resp, xdr_two);
+ goto error;
+ }
+
+ rqstp->rq_prog = prog = ntohl(*bufp++); /* program number */
+ rqstp->rq_vers = vers = ntohl(*bufp++); /* version number */
+ rqstp->rq_proc = proc = ntohl(*bufp++); /* procedure number */
+
+ argp->buf += 5;
+ argp->len -= 5;
+
+ /*
+ * Decode auth data, and add verifier to reply buffer.
+ * We do this before anything else in order to get a decent
+ * auth verifier.
+ */
+ svc_authenticate(rqstp, &rpc_stat, &auth_stat);
+
+ if (rpc_stat != rpc_success) {
+ serv->sv_stats->rpcbadfmt++;
+ svc_putlong(resp, rpc_garbage_args);
+ goto error;
+ }
+
+ if (auth_stat != rpc_auth_ok) {
+ dprintk("svc: authentication failed (%ld)\n", ntohl(auth_stat));
+ serv->sv_stats->rpcbadauth++;
+ resp->buf[-1] = xdr_one; /* REJECT */
+ svc_putlong(resp, xdr_one); /* AUTH_ERROR */
+ svc_putlong(resp, auth_stat); /* status */
+ goto error;
+ return svc_send(rqstp);
+ }
+
+ progp = serv->sv_program;
+ if (prog != progp->pg_prog) {
+ dprintk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog);
+ serv->sv_stats->rpcbadfmt++;
+ svc_putlong(resp, rpc_prog_unavail);
+ goto error;
+ return svc_send(rqstp);
+ }
+
+ versp = progp->pg_vers[vers];
+ if (!versp || vers >= progp->pg_nvers) {
+ dprintk("svc: unknown version (%d)\n", vers);
+ serv->sv_stats->rpcbadfmt++;
+ svc_putlong(resp, rpc_prog_mismatch);
+ svc_putlong(resp, htonl(progp->pg_lovers));
+ svc_putlong(resp, htonl(progp->pg_hivers));
+ goto error;
+ return svc_send(rqstp);
+ }
+
+ procp = versp->vs_proc + proc;
+ if (proc >= versp->vs_nproc || !procp->pc_func) {
+ dprintk("svc: unknown procedure (%d)\n", proc);
+ serv->sv_stats->rpcbadfmt++;
+ svc_putlong(resp, rpc_proc_unavail);
+ goto error;
+ return svc_send(rqstp);
+ }
+ rqstp->rq_server = serv;
+ rqstp->rq_procinfo = procp;
+
+ /* Syntactic check complete */
+ serv->sv_stats->rpccnt++;
+
+ /* Build the reply header. */
+ statp = resp->buf;
+ svc_putlong(resp, rpc_success); /* RPC_SUCCESS */
+
+ /* Bump per-procedure stats counter */
+ procp->pc_count++;
+
+ /* Initialize storage for argp and resp */
+ memset(rqstp->rq_argp, 0, procp->pc_argsize);
+ memset(rqstp->rq_resp, 0, procp->pc_ressize);
+
+ /* Call the function that processes the request. */
+ if (!versp->vs_dispatch) {
+ /* Decode arguments */
+ xdr = procp->pc_decode;
+ if (xdr && !xdr(rqstp, rqstp->rq_argbuf.buf, rqstp->rq_argp)) {
+ dprintk("svc: failed to decode args\n");
+ serv->sv_stats->rpcbadfmt++;
+ svc_putlong(resp, rpc_garbage_args);
+ goto error;
+ }
+
+ *statp = procp->pc_func(rqstp,
+ rqstp->rq_argp,
+ rqstp->rq_resp);
+
+ /* Encode reply */
+ if (*statp == rpc_success && (xdr = procp->pc_encode)
+ && !xdr(rqstp, rqstp->rq_resbuf.buf, rqstp->rq_resp)) {
+ dprintk("svc: failed to encode reply\n");
+ /* serv->sv_stats->rpcsystemerr++; */
+ *statp = rpc_system_err;
+ }
+ } else {
+ dprintk("svc: calling dispatcher\n");
+ if (!versp->vs_dispatch(rqstp, statp))
+ goto dropit;
+ }
+
+ /* Check RPC status result */
+ if (*statp != rpc_success)
+ resp->len = statp + 1 - resp->base;
+
+ /* Release reply info */
+ if (procp->pc_release)
+ procp->pc_release(rqstp, NULL, rqstp->rq_resp);
+
+ if (procp->pc_encode != NULL)
+ return svc_send(rqstp);
+
+dropit:
+ dprintk("svc: svc_process dropit\n");
+ svc_drop(rqstp);
+ return 0;
+
+error:
+ return svc_send(rqstp);
+}
diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c
new file mode 100644
index 000000000..660dbbb95
--- /dev/null
+++ b/net/sunrpc/svcauth.c
@@ -0,0 +1,163 @@
+/*
+ * linux/net/sunrpc/svcauth.c
+ *
+ * The generic interface for RPC authentication on the server side.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY RPCDBG_AUTH
+
+/*
+ * Type of authenticator function
+ */
+typedef void (*auth_fn_t)(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+
+/*
+ * Builtin auth flavors
+ */
+static void svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+static void svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp);
+
+/*
+ * Max number of authentication flavors we support
+ */
+#define RPC_SVCAUTH_MAX 8
+
+/*
+ * Table of authenticators
+ */
+static auth_fn_t authtab[RPC_SVCAUTH_MAX] = {
+ svcauth_null,
+ svcauth_unix,
+ NULL,
+};
+
+void
+svc_authenticate(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+ u32 flavor;
+ auth_fn_t func;
+
+ *statp = rpc_success;
+ *authp = rpc_auth_ok;
+
+ svc_getlong(&rqstp->rq_argbuf, flavor);
+ flavor = ntohl(flavor);
+
+ dprintk("svc: svc_authenticate (%d)\n", flavor);
+ if (flavor >= RPC_SVCAUTH_MAX || !(func = authtab[flavor])) {
+ *authp = rpc_autherr_badcred;
+ return;
+ }
+
+ rqstp->rq_cred.cr_flavor = flavor;
+ func(rqstp, statp, authp);
+}
+
+int
+svc_auth_register(u32 flavor, auth_fn_t func)
+{
+ if (flavor >= RPC_SVCAUTH_MAX || authtab[flavor])
+ return -EINVAL;
+ authtab[flavor] = func;
+ return 0;
+}
+
+void
+svc_auth_unregister(u32 flavor)
+{
+ if (flavor < RPC_SVCAUTH_MAX)
+ authtab[flavor] = NULL;
+}
+
+static void
+svcauth_null(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+ struct svc_buf *argp = &rqstp->rq_argbuf;
+ struct svc_buf *resp = &rqstp->rq_resbuf;
+
+ if ((argp->len -= 3) < 0) {
+ *statp = rpc_garbage_args;
+ return;
+ }
+ if (*(argp->buf)++ != 0) { /* we already skipped the flavor */
+ dprintk("svc: bad null cred\n");
+ *authp = rpc_autherr_badcred;
+ return;
+ }
+ if (*(argp->buf)++ != RPC_AUTH_NULL || *(argp->buf)++ != 0) {
+ dprintk("svc: bad null verf\n");
+ *authp = rpc_autherr_badverf;
+ return;
+ }
+
+ /* Signal that mapping to nobody uid/gid is required */
+ rqstp->rq_cred.cr_uid = (uid_t) -1;
+ rqstp->rq_cred.cr_gid = (gid_t) -1;
+ rqstp->rq_cred.cr_groups[0] = NOGROUP;
+
+ /* Put NULL verifier */
+ rqstp->rq_verfed = 1;
+ svc_putlong(resp, RPC_AUTH_NULL);
+ svc_putlong(resp, 0);
+}
+
+static void
+svcauth_unix(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+ struct svc_buf *argp = &rqstp->rq_argbuf;
+ struct svc_buf *resp = &rqstp->rq_resbuf;
+ struct svc_cred *cred = &rqstp->rq_cred;
+ u32 *bufp = argp->buf;
+ int len = argp->len, slen, i;
+
+ if ((len -= 3) < 0) {
+ *statp = rpc_garbage_args;
+ return;
+ }
+
+ bufp++; /* length */
+ bufp++; /* time stamp */
+ slen = (ntohl(*bufp++) + 3) >> 2; /* machname length */
+ if (slen > 64 || (len -= slen) < 0)
+ goto badcred;
+ bufp += slen; /* skip machname */
+
+ cred->cr_uid = ntohl(*bufp++); /* uid */
+ cred->cr_gid = ntohl(*bufp++); /* gid */
+
+ slen = ntohl(*bufp++); /* gids length */
+ if (slen > 16 || (len -= slen + 2) < 0)
+ goto badcred;
+ for (i = 0; i < NGROUPS && i < slen; i++)
+ cred->cr_groups[i] = ntohl(*bufp++);
+ if (i < NGROUPS)
+ cred->cr_groups[i] = NOGROUP;
+ bufp += (slen - i);
+
+ if (*bufp++ != RPC_AUTH_NULL || *bufp++ != 0) {
+ *authp = rpc_autherr_badverf;
+ return;
+ }
+
+ argp->buf = bufp;
+ argp->len = len;
+
+ /* Put NULL verifier */
+ rqstp->rq_verfed = 1;
+ svc_putlong(resp, RPC_AUTH_NULL);
+ svc_putlong(resp, 0);
+
+ return;
+
+badcred:
+ *authp = rpc_autherr_badcred;
+}
diff --git a/net/sunrpc/svcauth_des.c b/net/sunrpc/svcauth_des.c
new file mode 100644
index 000000000..5af0ec05a
--- /dev/null
+++ b/net/sunrpc/svcauth_des.c
@@ -0,0 +1,215 @@
+/*
+ * linux/net/sunrpc/svcauth_des.c
+ *
+ * Server-side AUTH_DES handling.
+ *
+ * Copyright (C) 1996, 1997 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcauth.h>
+#include <linux/sunrpc/svcsock.h>
+
+#define RPCDBG_FACILITY RPCDBG_AUTH
+
+/*
+ * DES cedential cache.
+ * The cache is indexed by fullname/key to allow for multiple sessions
+ * by the same user from different hosts.
+ * It would be tempting to use the client's IP address rather than the
+ * conversation key as an index, but that could become problematic for
+ * multi-homed hosts that distribute traffic across their interfaces.
+ */
+struct des_cred {
+ struct des_cred * dc_next;
+ char * dc_fullname;
+ u32 dc_nickname;
+ des_cblock dc_key; /* conversation key */
+ des_cblock dc_xkey; /* encrypted conv. key */
+ des_key_schedule dc_keysched;
+};
+
+#define ADN_FULLNAME 0
+#define ADN_NICKNAME 1
+
+/*
+ * The default slack allowed when checking for replayed credentials
+ * (in milliseconds).
+ */
+#define DES_REPLAY_SLACK 2000
+
+/*
+ * Make sure we don't place more than one call to the key server at
+ * a time.
+ */
+static int in_keycall = 0;
+
+#define FAIL(err) \
+ { if (data) put_cred(data); \
+ *authp = rpc_autherr_##err; \
+ return; \
+ }
+
+void
+svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
+{
+ struct svc_buf *argp = &rqstp->rq_argbuf;
+ struct svc_buf *resp = &rqstp->rq_resbuf;
+ struct svc_cred *cred = &rqstp->rq_cred;
+ struct des_cred *data = NULL;
+ u32 cryptkey[2];
+ u32 cryptbuf[4];
+ u32 *p = argp->buf;
+ int len = argp->len, slen, i;
+
+ *authp = rpc_auth_ok;
+
+ if ((argp->len -= 3) < 0) {
+ *statp = rpc_garbage_args;
+ return;
+ }
+
+ p++; /* skip length field */
+ namekind = ntohl(*p++); /* fullname/nickname */
+
+ /* Get the credentials */
+ if (namekind == ADN_NICKNAME) {
+ /* If we can't find the cached session key, initiate a
+ * new session. */
+ if (!(data = get_cred_bynick(*p++)))
+ FAIL(rejectedcred);
+ } else if (namekind == ADN_FULLNAME) {
+ p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN);
+ if (p == NULL)
+ FAIL(badcred);
+ cryptkey[0] = *p++; /* get the encrypted key */
+ cryptkey[1] = *p++;
+ cryptbuf[2] = *p++; /* get the encrypted window */
+ } else {
+ FAIL(badcred);
+ }
+
+ /* If we're just updating the key, silently discard the request. */
+ if (data && data->dc_locked) {
+ *authp = rpc_autherr_dropit;
+ _put_cred(data); /* release but don't unlock */
+ return;
+ }
+
+ /* Get the verifier flavor and length */
+ if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12)
+ FAIL(badverf);
+
+ cryptbuf[0] = *p++; /* encrypted time stamp */
+ cryptbuf[1] = *p++;
+ cryptbuf[3] = *p++; /* 0 or window - 1 */
+
+ if (namekind == ADN_NICKNAME) {
+ status = des_ecb_encrypt((des_block *) cryptbuf,
+ (des_block *) cryptbuf,
+ data->dc_keysched, DES_DECRYPT);
+ } else {
+ /* We first have to decrypt the new session key and
+ * fill in the UNIX creds. */
+ if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey)))
+ return;
+ status = des_cbc_encrypt((des_cblock *) cryptbuf,
+ (des_cblock *) cryptbuf, 16,
+ data->dc_keysched,
+ (des_cblock *) &ivec,
+ DES_DECRYPT);
+ }
+ if (status) {
+ printk("svcauth_des: DES decryption failed (status %d)\n",
+ status);
+ FAIL(badverf);
+ }
+
+ /* Now check the whole lot */
+ if (namekind == ADN_FULLNAME) {
+ unsigned long winverf;
+
+ data->dc_window = ntohl(cryptbuf[2]);
+ winverf = ntohl(cryptbuf[2]);
+ if (window != winverf - 1) {
+ printk("svcauth_des: bad window verifier!\n");
+ FAIL(badverf);
+ }
+ }
+
+ /* XDR the decrypted timestamp */
+ cryptbuf[0] = ntohl(cryptbuf[0]);
+ cryptbuf[1] = ntohl(cryptbuf[1]);
+ if (cryptbuf[1] > 1000000) {
+ dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]);
+ if (namekind == ADN_NICKNAME)
+ FAIL(rejectedverf);
+ FAIL(badverf);
+ }
+
+ /*
+ * Check for replayed credentials. We must allow for reordering
+ * of requests by the network, and the OS scheduler, hence we
+ * cannot expect timestamps to be increasing monotonically.
+ * This opens a small security hole, therefore the replay_slack
+ * value shouldn't be too large.
+ */
+ if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) {
+ switch (delta) {
+ case -1:
+ delta = -1000000;
+ case 0:
+ delta += cryptbuf[1] - data->dc_timestamp[1];
+ break;
+ default:
+ delta = -1000000;
+ }
+ if (delta < DES_REPLAY_SLACK)
+ FAIL(rejectedverf);
+#ifdef STRICT_REPLAY_CHECKS
+ /* TODO: compare time stamp to last five timestamps cached
+ * and reject (drop?) request if a match is found. */
+#endif
+ }
+
+ now = xtime;
+ now.tv_secs -= data->dc_window;
+ if (now.tv_secs < cryptbuf[0] ||
+ (now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1]))
+ FAIL(rejectedverf);
+
+ /* Okay, we're done. Update the lot */
+ if (namekind == ADN_FULLNAME)
+ data->dc_valid = 1;
+ data->dc_timestamp[0] = cryptbuf[0];
+ data->dc_timestamp[1] = cryptbuf[1];
+
+ put_cred(data);
+ return;
+garbage:
+ *statp = rpc_garbage_args;
+ return;
+}
+
+/*
+ * Call the keyserver to obtain the decrypted conversation key and
+ * UNIX creds. We use a Linux-specific keycall extension that does
+ * both things in one go.
+ */
+static struct des_cred *
+get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey)
+{
+ static int in_keycall = 0;
+ struct des_cred *cred;
+
+ if (in_keycall) {
+ *authp = rpc_autherr_dropit;
+ return NULL;
+ }
+ in_keycall = 1;
+ in_keycall = 0;
+ return cred;
+}
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
new file mode 100644
index 000000000..62d2d7b40
--- /dev/null
+++ b/net/sunrpc/svcsock.c
@@ -0,0 +1,967 @@
+/*
+ * linux/net/sunrpc/svcsock.c
+ *
+ * These are the RPC server socket internals.
+ *
+ * The server scheduling algorithm does not always distribute the load
+ * evenly when servicing a single client. May need to modify the
+ * svc_sock_enqueue procedure...
+ *
+ * TCP support is largely untested and may be a little slow. The problem
+ * is that we currently do two separate recvfrom's, one for the 4-byte
+ * record length, and the second for the actual record. This could possibly
+ * be improved by always reading a minimum size of around 100 bytes and
+ * tucking any superfluous bytes away in a temporary store. Still, that
+ * leaves write requests out in the rain. An alternative may be to peek at
+ * the first skb in the queue, and if it matches the next TCP sequence
+ * number, to extract the record marker. Yuck.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/udp.h>
+#include <linux/version.h>
+#include <linux/unistd.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#endif
+
+#include <linux/sunrpc/types.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/svcsock.h>
+#include <linux/sunrpc/stats.h>
+
+
+#define RPCDBG_FACILITY RPCDBG_SVCSOCK
+
+
+static struct svc_sock *svc_setup_socket(struct svc_serv *, struct socket *,
+ int *errp, int pmap_reg);
+static void svc_udp_data_ready(struct sock *, int);
+static int svc_udp_recvfrom(struct svc_rqst *);
+static int svc_udp_sendto(struct svc_rqst *);
+
+
+/*
+ * Queue up an idle server thread.
+ */
+static inline void
+svc_serv_enqueue(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+ rpc_append_list(&serv->sv_threads, rqstp);
+}
+
+/*
+ * Dequeue an nfsd thread.
+ */
+static inline void
+svc_serv_dequeue(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+ rpc_remove_list(&serv->sv_threads, rqstp);
+}
+
+/*
+ * Release an skbuff after use
+ */
+static inline void
+svc_release_skb(struct svc_rqst *rqstp)
+{
+ if (!rqstp->rq_skbuff)
+ return;
+
+ dprintk("svc: releasing skb %p\n", rqstp->rq_skbuff);
+ skb_free_datagram(rqstp->rq_sock->sk_sk, rqstp->rq_skbuff);
+ rqstp->rq_skbuff = NULL;
+}
+
+/*
+ * Queue up a socket with data pending. If there are idle nfsd
+ * processes, wake'em up.
+ * When calling this function, you should make sure it can't be interrupted
+ * by the network bottom half.
+ */
+static inline void
+svc_sock_enqueue(struct svc_sock *svsk)
+{
+ struct svc_rqst *rqstp;
+ struct svc_serv *serv;
+
+ if (svsk->sk_busy) {
+ /* Don't enqueue socket while daemon is receiving */
+ dprintk("svc: socket %p not enqueued: busy\n", svsk->sk_sk);
+ return;
+ }
+
+ /* Mark socket as busy. It will remain in this state until the
+ * server has processed all pending data and put the socket back
+ * on the idle list
+ */
+ svsk->sk_busy = 1;
+
+ serv = svsk->sk_server;
+ if ((rqstp = serv->sv_threads) != NULL) {
+ dprintk("svc: socket %p served by daemon %p\n",
+ svsk->sk_sk, rqstp);
+ svc_serv_dequeue(serv, rqstp);
+ rqstp->rq_sock = svsk;
+ svsk->sk_inuse++;
+ wake_up(&rqstp->rq_wait);
+ } else {
+ dprintk("svc: socket %p put into queue\n", svsk->sk_sk);
+ rpc_append_list(&serv->sv_sockets, svsk);
+ svsk->sk_qued = 1;
+ }
+}
+
+/*
+ * Dequeue the first socket.
+ */
+static inline struct svc_sock *
+svc_sock_dequeue(struct svc_serv *serv)
+{
+ struct svc_sock *svsk;
+
+ disable_bh(NET_BH);
+ if ((svsk = serv->sv_sockets) != NULL)
+ rpc_remove_list(&serv->sv_sockets, svsk);
+ enable_bh(NET_BH);
+
+ if (svsk) {
+ dprintk("svc: socket %p dequeued\n", svsk->sk_sk);
+ svsk->sk_qued = 0;
+ }
+
+ return svsk;
+}
+
+/*
+ * Having read count bytes from a socket, check whether it
+ * needs to be re-enqueued.
+ */
+static inline void
+svc_sock_received(struct svc_sock *svsk, int count)
+{
+ disable_bh(NET_BH);
+ if ((svsk->sk_data -= count) < 0) {
+ printk(KERN_NOTICE "svc: sk_data negative!\n");
+ svsk->sk_data = 0;
+ }
+ svsk->sk_rqstp = NULL; /* XXX */
+ svsk->sk_busy = 0;
+ if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
+ dprintk("svc: socket %p re-enqueued after receive\n",
+ svsk->sk_sk);
+ svc_sock_enqueue(svsk);
+ }
+ enable_bh(NET_BH);
+}
+
+/*
+ * Dequeue a new connection.
+ */
+static inline void
+svc_sock_accepted(struct svc_sock *svsk)
+{
+ disable_bh(NET_BH);
+ svsk->sk_busy = 0;
+ svsk->sk_conn--;
+ if (svsk->sk_conn || svsk->sk_data || svsk->sk_close) {
+ dprintk("svc: socket %p re-enqueued after accept\n",
+ svsk->sk_sk);
+ svc_sock_enqueue(svsk);
+ }
+ enable_bh(NET_BH);
+}
+
+/*
+ * Release a socket after use.
+ */
+static inline void
+svc_sock_release(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk = rqstp->rq_sock;
+
+ if (!svsk)
+ return;
+ svc_release_skb(rqstp);
+ rqstp->rq_sock = NULL;
+ if (!--(svsk->sk_inuse) && svsk->sk_dead) {
+ dprintk("svc: releasing dead socket\n");
+ sock_release(svsk->sk_sock);
+ kfree(svsk);
+ }
+}
+
+/*
+ * External function to wake up a server waiting for data
+ */
+void
+svc_wake_up(struct svc_serv *serv)
+{
+ struct svc_rqst *rqstp;
+
+ if ((rqstp = serv->sv_threads) != NULL) {
+ dprintk("svc: daemon %p woken up.\n", rqstp);
+ /*
+ svc_serv_dequeue(serv, rqstp);
+ rqstp->rq_sock = NULL;
+ */
+ wake_up(&rqstp->rq_wait);
+ }
+}
+
+/*
+ * Generic sendto routine
+ */
+static int
+svc_sendto(struct svc_rqst *rqstp, struct iovec *iov, int nr)
+{
+ unsigned long oldfs;
+ struct socket *sock = rqstp->rq_sock->sk_sock;
+ struct msghdr msg;
+ int i, buflen, len;
+
+ for (i = buflen = 0; i < nr; i++)
+ buflen += iov[i].iov_len;
+
+ msg.msg_name = &rqstp->rq_addr;
+ msg.msg_namelen = sizeof(rqstp->rq_addr);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_control = 0;
+
+#if LINUX_VERSION_CODE >= 0x020100
+ msg.msg_flags = MSG_DONTWAIT;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_sendmsg(sock, &msg, buflen);
+ set_fs(oldfs);
+#else
+ msg.msg_flags = 0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock->ops->sendmsg(sock, &msg, buflen, 1, 0);
+ set_fs(oldfs);
+#endif
+
+ dprintk("svc: socket %p sendto([%p %d... ], %d, %d) = %d\n",
+ rqstp->rq_sock,
+ iov[0].iov_base, iov[0].iov_len, nr,
+ buflen, len);
+
+ return len;
+}
+
+/*
+ * Check input queue length
+ */
+static int
+svc_recv_available(struct svc_sock *svsk)
+{
+ unsigned long oldfs;
+ struct socket *sock = svsk->sk_sock;
+ int avail, err;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ err = sock->ops->ioctl(sock, TIOCINQ, (unsigned long) &avail);
+ set_fs(oldfs);
+
+ return (err >= 0)? avail : err;
+}
+
+/*
+ * Generic recvfrom routine.
+ */
+static int
+svc_recvfrom(struct svc_rqst *rqstp, struct iovec *iov, int nr, int buflen)
+{
+ unsigned long oldfs;
+ struct msghdr msg;
+ struct socket *sock;
+ int len;
+
+ rqstp->rq_addrlen = sizeof(rqstp->rq_addr);
+ sock = rqstp->rq_sock->sk_sock;
+
+ msg.msg_name = &rqstp->rq_addr;
+ msg.msg_namelen = sizeof(rqstp->rq_addr);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_control = 0;
+
+#if LINUX_VERSION_CODE >= 0x020100
+ msg.msg_flags = MSG_DONTWAIT;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock_recvmsg(sock, &msg, buflen, MSG_DONTWAIT);
+ set_fs(oldfs);
+#else
+ msg.msg_flags = 0;
+
+ oldfs = get_fs(); set_fs(KERNEL_DS);
+ len = sock->ops->recvmsg(sock, &msg, buflen, 0, 1, &rqstp->rq_addrlen);
+ set_fs(oldfs);
+#endif
+
+ dprintk("svc: socket %p recvfrom(%p, %d) = %d\n", rqstp->rq_sock,
+ iov[0].iov_base, iov[0].iov_len, len);
+
+ return len;
+}
+
+/*
+ * INET callback when data has been received on the socket.
+ */
+static void
+svc_udp_data_ready(struct sock *sk, int count)
+{
+ struct svc_sock *svsk;
+
+ dprintk("svc: socket %p data ready (inet %p)\n", sk->user_data, sk);
+
+ svsk = (struct svc_sock *)(sk->user_data);
+ if (!svsk)
+ return;
+ svsk->sk_data = 1;
+ svc_sock_enqueue(svsk);
+}
+
+/*
+ * Receive a datagram from a UDP socket.
+ */
+static int
+svc_udp_recvfrom(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk = rqstp->rq_sock;
+ struct svc_serv *serv = svsk->sk_server;
+ struct sk_buff *skb;
+ u32 *data;
+ int err, len;
+
+ svsk->sk_data = 0;
+ while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) {
+ svc_sock_received(svsk, 0);
+ if (err == -EAGAIN)
+ return err;
+ /* possibly an icmp error */
+ dprintk("svc: recvfrom returned error %d\n", -err);
+ }
+
+ /* There may be more data */
+ svsk->sk_data = 1;
+
+ len = skb->len - sizeof(struct udphdr);
+ data = (u32 *) (skb->h.raw + sizeof(struct udphdr));
+
+ rqstp->rq_skbuff = skb;
+ rqstp->rq_argbuf.base = data;
+ rqstp->rq_argbuf.buf = data;
+ rqstp->rq_argbuf.len = (len >> 2);
+ /* rqstp->rq_resbuf = rqstp->rq_defbuf; */
+ rqstp->rq_prot = IPPROTO_UDP;
+
+ /* Get sender address */
+ rqstp->rq_addr.sin_family = AF_INET;
+ rqstp->rq_addr.sin_port = skb->h.uh->source;
+#if LINUX_VERSION_CODE >= 0x020100
+ rqstp->rq_addr.sin_addr.s_addr = skb->nh.iph->saddr;
+#else
+ rqstp->rq_addr.sin_addr.s_addr = skb->saddr;
+#endif
+
+ if (serv->sv_stats)
+ serv->sv_stats->netudpcnt++;
+
+ /* One down, maybe more to go... */
+ svsk->sk_sk->stamp = skb->stamp;
+ svc_sock_received(svsk, 0);
+
+ return len;
+}
+
+static int
+svc_udp_sendto(struct svc_rqst *rqstp)
+{
+ struct svc_buf *bufp = &rqstp->rq_resbuf;
+ int error;
+
+ /* Set up the first element of the reply iovec.
+ * Any other iovecs that may be in use have been taken
+ * care of by the server implementation itself.
+ */
+ /* bufp->base = bufp->area; */
+ bufp->iov[0].iov_base = bufp->base;
+ bufp->iov[0].iov_len = bufp->len << 2;
+
+ error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
+ if (error == -ECONNREFUSED)
+ /* ICMP error on earlier request. */
+ error = svc_sendto(rqstp, bufp->iov, bufp->nriov);
+ else if (error == -EAGAIN)
+ /* Ignore and wait for re-xmit */
+ error = 0;
+
+ return error;
+}
+
+static int
+svc_udp_init(struct svc_sock *svsk)
+{
+ svsk->sk_sk->data_ready = svc_udp_data_ready;
+ svsk->sk_recvfrom = svc_udp_recvfrom;
+ svsk->sk_sendto = svc_udp_sendto;
+
+ return 0;
+}
+
+/*
+ * A state change on a listening socket means there's a connection
+ * pending.
+ */
+static void
+svc_tcp_state_change1(struct sock *sk)
+{
+ struct svc_sock *svsk;
+
+ dprintk("svc: socket %p TCP (listen) state change %d\n",
+ sk, sk->state);
+
+ if (sk->state != TCP_ESTABLISHED) {
+ /* Aborted connection, SYN_RECV or whatever... */
+ return;
+ }
+ if (!(svsk = (struct svc_sock *) sk->user_data)) {
+ printk("svc: socket %p: no user data\n", sk);
+ return;
+ }
+ svsk->sk_conn++;
+ svc_sock_enqueue(svsk);
+}
+
+/*
+ * A state change on a connected socket means it's dying or dead.
+ */
+static void
+svc_tcp_state_change2(struct sock *sk)
+{
+ struct svc_sock *svsk;
+
+ dprintk("svc: socket %p TCP (connected) state change %d (svsk %p)\n",
+ sk, sk->state, sk->user_data);
+
+ if (!(svsk = (struct svc_sock *) sk->user_data)) {
+ printk("svc: socket %p: no user data\n", sk);
+ return;
+ }
+ svsk->sk_close = 1;
+ svc_sock_enqueue(svsk);
+}
+
+static void
+svc_tcp_data_ready(struct sock *sk, int count)
+{
+ struct svc_sock * svsk;
+
+ /* Disconnect signalled through data_ready?!? */
+ if (sk->state != TCP_ESTABLISHED) {
+ svc_tcp_state_change2(sk);
+ return;
+ }
+
+ dprintk("svc: socket %p TCP data ready (svsk %p)\n",
+ sk, sk->user_data);
+ if (!(svsk = (struct svc_sock *)(sk->user_data)))
+ return;
+ svsk->sk_data++;
+ svc_sock_enqueue(svsk);
+}
+
+/*
+ * Accept a TCP connection
+ */
+static void
+svc_tcp_accept(struct svc_sock *svsk)
+{
+ struct sockaddr_in sin;
+ struct svc_serv *serv = svsk->sk_server;
+ struct socket *sock = svsk->sk_sock;
+ struct socket *newsock;
+ struct proto_ops *ops;
+ struct svc_sock *newsvsk;
+ int err, slen;
+
+ dprintk("svc: tcp_accept %p sock %p\n", svsk, sock);
+ if (!sock)
+ return;
+
+ if (!(newsock = sock_alloc())) {
+ printk(KERN_WARNING "%s: no more sockets!\n", serv->sv_name);
+ return;
+ }
+ dprintk("svc: tcp_accept %p allocated\n", newsock);
+
+ newsock->type = sock->type;
+ if ((err = sock->ops->dup(newsock, sock)) < 0) {
+ printk(KERN_WARNING "%s: socket dup failed (err %d)!\n",
+ serv->sv_name, -err);
+ goto failed;
+ }
+
+ ops = newsock->ops;
+ if ((err = ops->accept(sock, newsock, O_NONBLOCK)) < 0) {
+ printk(KERN_WARNING "%s: accept failed (err %d)!\n",
+ serv->sv_name, -err);
+ goto failed; /* aborted connection or whatever */
+ }
+
+ slen = sizeof(sin);
+ err = ops->getname(newsock, (struct sockaddr *) &sin, &slen, 1);
+ if (err < 0) {
+ printk(KERN_WARNING "%s: peername failed (err %d)!\n",
+ serv->sv_name, -err);
+ goto failed; /* aborted connection or whatever */
+ }
+
+ /* Ideally, we would want to reject connections from unauthorized
+ * hosts here, but we have no generic client tables. For now,
+ * we just punt connects from unprivileged ports. */
+ if (ntohs(sin.sin_port) >= 1024) {
+ printk(KERN_WARNING
+ "%s: connect from unprivileged port: %08lx:%d",
+ serv->sv_name,
+ ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+ goto failed;
+ }
+
+ dprintk("%s: connect from %08lx:%04x\n", serv->sv_name,
+ ntohl(sin.sin_addr.s_addr), ntohs(sin.sin_port));
+
+ if (!(newsvsk = svc_setup_socket(serv, newsock, &err, 0)))
+ goto failed;
+
+ /* Precharge. Data may have arrived on the socket before we
+ * installed the data_ready callback.
+ */
+ newsvsk->sk_data = 1;
+ newsvsk->sk_temp = 1;
+ svc_sock_enqueue(newsvsk);
+
+ if (serv->sv_stats)
+ serv->sv_stats->nettcpconn++;
+
+ return;
+
+failed:
+ sock_release(newsock);
+ return;
+}
+
+/*
+ * Receive data from a TCP socket.
+ */
+static int
+svc_tcp_recvfrom(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk = rqstp->rq_sock;
+ struct svc_serv *serv = svsk->sk_server;
+ struct svc_buf *bufp = &rqstp->rq_argbuf;
+ int len, ready;
+
+ dprintk("svc: tcp_recv %p data %d conn %d close %d\n",
+ svsk, svsk->sk_data, svsk->sk_conn, svsk->sk_close);
+
+ if (svsk->sk_close) {
+ svc_delete_socket(svsk);
+ return 0;
+ }
+
+ if (svsk->sk_conn) {
+ svc_tcp_accept(svsk);
+ svc_sock_accepted(svsk);
+ return 0;
+ }
+
+ ready = svsk->sk_data;
+
+ /* Receive data. If we haven't got the record length yet, get
+ * the next four bytes. Otherwise try to gobble up as much as
+ * possible up to the complete record length.
+ */
+ if (svsk->sk_tcplen < 4) {
+ unsigned long want = 4 - svsk->sk_tcplen;
+ struct iovec iov;
+
+ iov.iov_base = ((u32 *) &svsk->sk_reclen) + svsk->sk_tcplen;
+ iov.iov_len = want;
+ if ((len = svc_recvfrom(rqstp, &iov, 1, want)) < 0)
+ goto error;
+ svsk->sk_tcplen += len;
+
+ svsk->sk_reclen = ntohl(svsk->sk_reclen);
+ if (!(svsk->sk_reclen & 0x80000000)) {
+ /* FIXME: shutdown socket */
+ printk(KERN_NOTICE "RPC: bad TCP reclen %08lx",
+ (unsigned long) svsk->sk_reclen);
+ return -EIO;
+ }
+ svsk->sk_reclen &= 0x7fffffff;
+ dprintk("svc: TCP record, %ld bytes\n", svsk->sk_reclen);
+ }
+
+ /* Check whether enough data is available */
+ len = svc_recv_available(svsk);
+ if (len < 0)
+ goto error;
+
+ if (len < svsk->sk_reclen) {
+ dprintk("svc: incomplete TCP record (%d of %ld)\n",
+ len, svsk->sk_reclen);
+ svc_sock_received(svsk, ready);
+ len = -EAGAIN; /* record not complete */
+ }
+
+ /* Frob argbuf */
+ bufp->iov[0].iov_base += 4;
+ bufp->iov[0].iov_len -= 4;
+
+ /* Now receive data */
+ len = svc_recvfrom(rqstp, bufp->iov, bufp->nriov, svsk->sk_reclen);
+ if (len < 0)
+ goto error;
+
+ dprintk("svc: TCP complete record (%d bytes)\n", len);
+
+ /* Position reply write pointer immediately after
+ * record length */
+ rqstp->rq_resbuf.buf += 1;
+ rqstp->rq_resbuf.len = 1;
+
+ rqstp->rq_skbuff = 0;
+ rqstp->rq_argbuf.buf += 1;
+ rqstp->rq_argbuf.len = (len >> 2);
+ rqstp->rq_prot = IPPROTO_TCP;
+
+ /* Reset TCP read info */
+ svsk->sk_reclen = 0;
+ svsk->sk_tcplen = 0;
+
+ svc_sock_received(svsk, 1);
+ if (serv->sv_stats)
+ serv->sv_stats->nettcpcnt++;
+
+ return len;
+
+error:
+ if (len == -EAGAIN) {
+ dprintk("RPC: TCP recvfrom got EAGAIN\n");
+ svc_sock_received(svsk, ready); /* Clear data ready */
+ } else {
+ printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
+ svsk->sk_server->sv_name, -len);
+ svc_sock_received(svsk, 0);
+ }
+
+ return len;
+}
+
+/*
+ * Send out data on TCP socket.
+ * FIXME: Make the sendto call non-blocking in order not to hang
+ * a daemon on a a dead client. Requires write queue maintenance.
+ */
+static int
+svc_tcp_sendto(struct svc_rqst *rqstp)
+{
+ struct svc_buf *bufp = &rqstp->rq_resbuf;
+
+ /* Set up the first element of the reply iovec.
+ * Any other iovecs that may be in use have been taken
+ * care of by the server implementation itself.
+ */
+ bufp->iov[0].iov_base = bufp->base;
+ bufp->iov[0].iov_len = bufp->len << 2;
+ bufp->base[0] = htonl(0x80000000|((bufp->len << 2) - 4));
+
+ return svc_sendto(rqstp, bufp->iov, bufp->nriov);
+}
+
+static int
+svc_tcp_init(struct svc_sock *svsk)
+{
+ struct sock *sk = svsk->sk_sk;
+
+ svsk->sk_recvfrom = svc_tcp_recvfrom;
+ svsk->sk_sendto = svc_tcp_sendto;
+
+ if (sk->state == TCP_LISTEN) {
+ dprintk("setting up TCP socket for listening\n");
+ sk->state_change = svc_tcp_state_change1;
+ } else {
+ dprintk("setting up TCP socket for reading\n");
+ sk->state_change = svc_tcp_state_change2;
+ sk->data_ready = svc_tcp_data_ready;
+
+ svsk->sk_reclen = 0;
+ svsk->sk_tcplen = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive the next request on any socket.
+ */
+int
+svc_recv(struct svc_serv *serv, struct svc_rqst *rqstp)
+{
+ struct wait_queue wait = { current, NULL };
+ struct svc_sock *svsk;
+ int len;
+
+ dprintk("svc: server %p waiting for data (to = %ld)\n",
+ rqstp, current->timeout);
+
+again:
+ /* Initialize the buffers */
+ rqstp->rq_argbuf = rqstp->rq_defbuf;
+ rqstp->rq_resbuf = rqstp->rq_defbuf;
+
+ if (signalled())
+ return -EINTR;
+
+ disable_bh(NET_BH);
+ if ((svsk = svc_sock_dequeue(serv)) != NULL) {
+ enable_bh(NET_BH);
+ rqstp->rq_sock = svsk;
+ svsk->sk_inuse++;
+ } else {
+ /* No data pending. Go to sleep */
+ rqstp->rq_sock = NULL;
+ rqstp->rq_wait = NULL;
+ svc_serv_enqueue(serv, rqstp);
+
+ current->state = TASK_INTERRUPTIBLE;
+ add_wait_queue(&rqstp->rq_wait, &wait);
+ enable_bh(NET_BH);
+ schedule();
+
+ if (!(svsk = rqstp->rq_sock)) {
+ svc_serv_dequeue(serv, rqstp);
+ if (!(svsk = rqstp->rq_sock))
+ return signalled()? -EINTR : -EAGAIN;
+ }
+ }
+
+ dprintk("svc: server %p servicing socket %p\n", rqstp, svsk);
+ len = svsk->sk_recvfrom(rqstp);
+
+ /* No data, incomplete (TCP) read, or accept() */
+ if (len == 0 || len == -EAGAIN) {
+ svc_sock_release(rqstp);
+ goto again;
+ }
+
+ rqstp->rq_secure = ntohs(rqstp->rq_addr.sin_port) < 1024;
+ rqstp->rq_userset = 0;
+ rqstp->rq_verfed = 0;
+
+ svc_getlong(&rqstp->rq_argbuf, rqstp->rq_xid);
+ svc_putlong(&rqstp->rq_resbuf, rqstp->rq_xid);
+
+ /* Assume that the reply consists of a single buffer. */
+ rqstp->rq_resbuf.nriov = 1;
+
+ if (serv->sv_stats)
+ serv->sv_stats->netcnt++;
+ return len;
+}
+
+/*
+ * Drop request
+ */
+void
+svc_drop(struct svc_rqst *rqstp)
+{
+ dprintk("svc: socket %p dropped request\n", rqstp->rq_sock);
+ svc_sock_release(rqstp);
+}
+
+/*
+ * Return reply to client.
+ */
+int
+svc_send(struct svc_rqst *rqstp)
+{
+ struct svc_sock *svsk;
+ int len;
+
+ if ((svsk = rqstp->rq_sock) == NULL) {
+ printk(KERN_WARNING "NULL socket pointer in %s:%d\n",
+ __FILE__, __LINE__);
+ return -EFAULT;
+ }
+
+ /* release the receive skb before sending the reply */
+ svc_release_skb(rqstp);
+
+ len = svsk->sk_sendto(rqstp);
+ svc_sock_release(rqstp);
+
+ if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN)
+ return 0;
+ return len;
+}
+
+/*
+ * Initialize socket for RPC use and create svc_sock struct
+ * XXX: May want to setsockopt SO_SNDBUF and SO_RCVBUF.
+ */
+static struct svc_sock *
+svc_setup_socket(struct svc_serv *serv, struct socket *sock,
+ int *errp, int pmap_register)
+{
+ struct svc_sock *svsk;
+ struct sock *inet;
+
+ dprintk("svc: svc_setup_socket %p\n", sock);
+ if (!(svsk = kmalloc(sizeof(*svsk), GFP_KERNEL))) {
+ *errp = -ENOMEM;
+ return NULL;
+ }
+ memset(svsk, 0, sizeof(*svsk));
+
+#if LINUX_VERSION_CODE >= 0x020100
+ inet = sock->sk;
+#else
+ inet = (struct sock *) sock->data;
+#endif
+ inet->user_data = svsk;
+ svsk->sk_sock = sock;
+ svsk->sk_sk = inet;
+ svsk->sk_ostate = inet->state_change;
+ svsk->sk_odata = inet->data_ready;
+ svsk->sk_server = serv;
+
+ /* Initialize the socket */
+ if (sock->type == SOCK_DGRAM)
+ *errp = svc_udp_init(svsk);
+ else
+ *errp = svc_tcp_init(svsk);
+if (svsk->sk_sk == NULL)
+ printk(KERN_WARNING "svsk->sk_sk == NULL after svc_prot_init!\n");
+
+ /* Register socket with portmapper */
+ if (*errp >= 0 && pmap_register)
+ *errp = svc_register(serv, inet->protocol,
+ ntohs(inet->dummy_th.source));
+
+ if (*errp < 0) {
+ inet->user_data = NULL;
+ kfree(svsk);
+ return NULL;
+ }
+
+ svsk->sk_list = serv->sv_allsocks;
+ serv->sv_allsocks = svsk;
+
+ dprintk("svc: svc_setup_socket created %p (inet %p)\n",
+ svsk, svsk->sk_sk);
+ return svsk;
+}
+
+/*
+ * Create socket for RPC service.
+ */
+int
+svc_create_socket(struct svc_serv *serv, int protocol, struct sockaddr_in *sin)
+{
+ struct svc_sock *svsk;
+ struct socket *sock;
+ int error;
+ int type;
+
+ dprintk("svc: svc_create_socket(%s, %d, %08lx:%d)\n",
+ serv->sv_program->pg_name, protocol,
+ ntohl(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+
+ if (protocol != IPPROTO_UDP && protocol != IPPROTO_TCP) {
+ printk(KERN_WARNING "svc: only UDP and TCP "
+ "sockets supported\n");
+ return -EINVAL;
+ }
+ type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+
+ if ((error = sock_create(AF_INET, type, protocol, &sock)) < 0)
+ return error;
+
+ if (sin != NULL) {
+ error = sock->ops->bind(sock, (struct sockaddr *) sin,
+ sizeof(*sin));
+ if (error < 0)
+ goto bummer;
+ }
+
+ if (protocol == IPPROTO_TCP) {
+ if ((error = sock->ops->listen(sock, 5)) < 0)
+ goto bummer;
+ sock->flags |= SO_ACCEPTCON;
+ }
+
+ if ((svsk = svc_setup_socket(serv, sock, &error, 1)) != NULL)
+ return 0;
+
+bummer:
+ dprintk("svc: svc_create_socket error = %d\n", -error);
+ sock_release(sock);
+ return error;
+}
+
+/*
+ * Remove a dead socket
+ */
+void
+svc_delete_socket(struct svc_sock *svsk)
+{
+ struct svc_sock **rsk;
+ struct svc_serv *serv;
+ struct sock *sk;
+
+ dprintk("svc: svc_delete_socket(%p)\n", svsk);
+
+ serv = svsk->sk_server;
+ sk = svsk->sk_sk;
+
+ sk->state_change = svsk->sk_ostate;
+ sk->data_ready = svsk->sk_odata;
+
+ for (rsk = &serv->sv_allsocks; *rsk; rsk = &(*rsk)->sk_list) {
+ if (*rsk == svsk)
+ break;
+ }
+ if (!*rsk)
+ return;
+ *rsk = svsk->sk_list;
+
+ if (svsk->sk_qued)
+ rpc_remove_list(&serv->sv_sockets, svsk);
+ svsk->sk_dead = 1;
+
+ if (!svsk->sk_inuse) {
+ sock_release(svsk->sk_sock);
+ kfree(svsk);
+ } else {
+ printk(KERN_NOTICE "svc: server socket destroy delayed\n");
+ /* svsk->sk_server = NULL; */
+ }
+}
diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c
new file mode 100644
index 000000000..859d55853
--- /dev/null
+++ b/net/sunrpc/sysctl.c
@@ -0,0 +1,134 @@
+/*
+ * linux/net/sunrpc/sysctl.c
+ *
+ * Sysctl interface to sunrpc module. This is for debugging only now.
+ *
+ * I would prefer to register the sunrpc table below sys/net, but that's
+ * impossible at the moment.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/linkage.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/sysctl.h>
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#else
+# include <linux/mm.h>
+# define copy_from_user memcpy_fromfs
+# define copy_to_user memcpy_tofs
+# define access_ok !verify_area
+#endif
+#include <linux/sunrpc/types.h>
+
+/*
+ * Declare the debug flags here
+ */
+unsigned int rpc_debug = 0;
+unsigned int nfs_debug = 0;
+unsigned int nfsd_debug = 0;
+unsigned int nlm_debug = 0;
+
+#ifdef RPC_DEBUG
+
+static struct ctl_table_header *sunrpc_table_header = NULL;
+static ctl_table sunrpc_table[];
+
+void
+rpc_register_sysctl(void)
+{
+ if (sunrpc_table_header)
+ return;
+ sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
+}
+
+void
+rpc_unregister_sysctl(void)
+{
+ if (!sunrpc_table_header)
+ return;
+ unregister_sysctl_table(sunrpc_table_header);
+}
+
+int
+proc_dodebug(ctl_table *table, int write, struct file *file,
+ void *buffer, size_t *lenp)
+{
+ char tmpbuf[20], *p, c;
+ unsigned int value;
+ int left, len;
+
+ if ((file->f_pos && !write) || !*lenp) {
+ *lenp = 0;
+ return 0;
+ }
+
+ left = *lenp;
+
+ if (write) {
+ if (!access_ok(VERIFY_READ, buffer, left))
+ return -EFAULT;
+ p = (char *) buffer;
+#if LINUX_VERSION_CODE >= 0x020100
+ while (left && __get_user(c, p) >= 0 && isspace(c))
+ left--, p++;
+#else
+ while (left && (c = get_fs_byte(p)) >= 0 && isspace(c))
+ left--, p++;
+#endif
+ if (!left)
+ goto done;
+
+ if (left > sizeof(tmpbuf) - 1)
+ return -EINVAL;
+ copy_from_user(tmpbuf, p, left);
+ tmpbuf[left] = '\0';
+
+ for (p = tmpbuf, value = 0; '0' <= *p && *p <= '9'; p++, left--)
+ value = 10 * value + (*p - '0');
+ if (*p && !isspace(*p))
+ return -EINVAL;
+ while (left && isspace(*p))
+ left--, p++;
+ *(unsigned int *) table->data = value;
+ } else {
+ if (!access_ok(VERIFY_WRITE, buffer, left))
+ return -EFAULT;
+ len = sprintf(tmpbuf, "%d", *(unsigned int *) table->data);
+ if (len > left)
+ len = left;
+ copy_to_user(buffer, tmpbuf, len);
+ if ((left -= len) > 0) {
+ put_user('\n', (char *)buffer + len);
+ left--;
+ }
+ }
+
+done:
+ *lenp -= left;
+ file->f_pos += *lenp;
+ return 0;
+}
+
+#define DIRENTRY(nam1, nam2, child) \
+ {CTL_##nam1, #nam2, NULL, 0, 0555, child }
+#define DBGENTRY(nam1, nam2) \
+ {CTL_##nam1##DEBUG, #nam2 "_debug", &nam2##_debug, sizeof(int),\
+ 0644, NULL, &proc_dodebug}
+
+static ctl_table debug_table[] = {
+ DBGENTRY(RPC, rpc),
+ DBGENTRY(NFS, nfs),
+ DBGENTRY(NFSD, nfsd),
+ DBGENTRY(NLM, nlm),
+ {0}
+};
+
+static ctl_table sunrpc_table[] = {
+ DIRENTRY(SUNRPC, sunrpc, debug_table),
+ {0}
+};
+
+#endif
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
new file mode 100644
index 000000000..6ebd94079
--- /dev/null
+++ b/net/sunrpc/xdr.c
@@ -0,0 +1,118 @@
+/*
+ * linux/net/sunrpc/xdr.c
+ *
+ * Generic XDR support.
+ *
+ * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/sunrpc/xdr.h>
+#include <linux/sunrpc/msg_prot.h>
+
+u32 rpc_success, rpc_prog_unavail, rpc_prog_mismatch, rpc_proc_unavail,
+ rpc_garbage_args, rpc_system_err;
+u32 rpc_auth_ok, rpc_autherr_badcred, rpc_autherr_rejectedcred,
+ rpc_autherr_badverf, rpc_autherr_rejectedverf, rpc_autherr_tooweak;
+u32 xdr_zero, xdr_one, xdr_two;
+
+void
+xdr_init(void)
+{
+ static int inited = 0;
+
+ if (inited)
+ return;
+
+ xdr_zero = htonl(0);
+ xdr_one = htonl(1);
+ xdr_two = htonl(2);
+
+ rpc_success = htonl(RPC_SUCCESS);
+ rpc_prog_unavail = htonl(RPC_PROG_UNAVAIL);
+ rpc_prog_mismatch = htonl(RPC_PROG_MISMATCH);
+ rpc_proc_unavail = htonl(RPC_PROC_UNAVAIL);
+ rpc_garbage_args = htonl(RPC_GARBAGE_ARGS);
+ rpc_system_err = htonl(RPC_SYSTEM_ERR);
+
+ rpc_auth_ok = htonl(RPC_AUTH_OK);
+ rpc_autherr_badcred = htonl(RPC_AUTH_BADCRED);
+ rpc_autherr_rejectedcred = htonl(RPC_AUTH_REJECTEDCRED);
+ rpc_autherr_badverf = htonl(RPC_AUTH_BADVERF);
+ rpc_autherr_rejectedverf = htonl(RPC_AUTH_REJECTEDVERF);
+ rpc_autherr_tooweak = htonl(RPC_AUTH_TOOWEAK);
+
+ inited = 1;
+}
+
+/*
+ * XDR functions for basic NFS types
+ */
+u32 *
+xdr_encode_netobj(u32 *p, const struct xdr_netobj *obj)
+{
+ unsigned int quadlen = XDR_QUADLEN(obj->len);
+
+ *p++ = htonl(obj->len);
+ p[quadlen-1] = 0; /* zero trailing bytes */
+ memcpy(p, obj->data, obj->len);
+ return p + XDR_QUADLEN(obj->len);
+}
+
+u32 *
+xdr_decode_netobj_fixed(u32 *p, void *obj, unsigned int len)
+{
+ if (ntohl(*p++) != len)
+ return NULL;
+ memcpy(obj, p, len);
+ return p + XDR_QUADLEN(len);
+}
+
+u32 *
+xdr_decode_netobj(u32 *p, struct xdr_netobj *obj)
+{
+ unsigned int len;
+
+ if ((len = ntohl(*p++)) > XDR_MAX_NETOBJ)
+ return NULL;
+ obj->len = len;
+ obj->data = (u8 *) p;
+ return p + XDR_QUADLEN(len);
+}
+
+u32 *
+xdr_encode_string(u32 *p, const char *string)
+{
+ int len = strlen(string);
+ int quadlen = XDR_QUADLEN(len);
+
+ p[quadlen] = 0;
+ *p++ = htonl(len);
+ memcpy(p, string, len);
+ return p + quadlen;
+}
+
+u32 *
+xdr_decode_string(u32 *p, char **sp, int *lenp, int maxlen)
+{
+ unsigned int len;
+ char *string;
+
+ if ((len = ntohl(*p++)) > maxlen)
+ return NULL;
+ if (lenp)
+ *lenp = len;
+ if ((len % 4) != 0) {
+ string = (char *) p;
+ } else {
+ string = (char *) (p - 1);
+ memmove(string, p, len);
+ }
+ string[len] = '\0';
+ *sp = string;
+ return p + XDR_QUADLEN(len);
+}
+
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
new file mode 100644
index 000000000..dc4fd0515
--- /dev/null
+++ b/net/sunrpc/xprt.c
@@ -0,0 +1,1367 @@
+/*
+ * linux/net/sunrpc/xprt.c
+ *
+ * This is a generic RPC call interface supporting congestion avoidance,
+ * and asynchronous calls.
+ *
+ * The interface works like this:
+ *
+ * - When a process places a call, it allocates a request slot if
+ * one is available. Otherwise, it sleeps on the backlog queue
+ * (xprt_reserve).
+ * - Next, the caller puts together the RPC message, stuffs it into
+ * the request struct, and calls xprt_call().
+ * - xprt_call transmits the message and installs the caller on the
+ * socket's wait list. At the same time, it installs a timer that
+ * is run after the packet's timeout has expired.
+ * - When a packet arrives, the data_ready handler walks the list of
+ * pending requests for that socket. If a matching XID is found, the
+ * caller is woken up, and the timer removed.
+ * - When no reply arrives within the timeout interval, the timer is
+ * fired by the kernel and runs xprt_timer(). It either adjusts the
+ * timeout values (minor timeout) or wakes up the caller with a status
+ * of -ETIMEDOUT.
+ * - When the caller receives a notification from RPC that a reply arrived,
+ * it should release the RPC slot, and process the reply.
+ * If the call timed out, it may choose to retry the operation by
+ * adjusting the initial timeout value, and simply calling rpc_call
+ * again.
+ *
+ * Support for async RPC is done through a set of RPC-specific scheduling
+ * primitives that `transparently' work for processes as well as async
+ * tasks that rely on callbacks.
+ *
+ * Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/udp.h>
+#include <linux/unistd.h>
+#include <linux/sunrpc/clnt.h>
+#include <net/sock.h>
+
+#if LINUX_VERSION_CODE >= 0x020100
+#include <asm/uaccess.h>
+#endif
+
+#define SOCK_HAS_USER_DATA
+
+/*
+ * Local variables
+ */
+#ifndef SOCK_HAS_USER_DATA
+static struct rpc_xprt * sock_list = NULL;
+#endif
+
+#ifdef RPC_DEBUG
+# undef RPC_DEBUG_DATA
+# define RPCDBG_FACILITY RPCDBG_XPRT
+#endif
+
+#ifndef MAX
+# define MAX(a, b) ((a) > (b)? (a) : (b))
+# define MIN(a, b) ((a) < (b)? (a) : (b))
+#endif
+
+/*
+ * Local functions
+ */
+static void xprt_request_init(struct rpc_task *, struct rpc_xprt *);
+static void xprt_transmit_status(struct rpc_task *task);
+static void xprt_receive_status(struct rpc_task *task);
+static void xprt_reserve_status(struct rpc_task *task);
+static void xprt_reconn_timeout(struct rpc_task *task);
+static void xprt_reconn_status(struct rpc_task *task);
+static struct socket *xprt_create_socket(int, struct sockaddr_in *,
+ struct rpc_timeout *);
+
+#ifdef RPC_DEBUG_DATA
+/*
+ * Print the buffer contents (first 128 bytes only--just enough for
+ * diropres return).
+ */
+static void
+xprt_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+ u8 *buf = (u8 *) packet;
+ int j;
+
+ dprintk("RPC: %s\n", msg);
+ for (j = 0; j < count && j < 128; j += 4) {
+ if (!(j & 31)) {
+ if (j)
+ dprintk("\n");
+ dprintk("0x%04x ", j);
+ }
+ dprintk("%02x%02x%02x%02x ",
+ buf[j], buf[j+1], buf[j+2], buf[j+3]);
+ }
+ dprintk("\n");
+}
+#else
+static inline void
+xprt_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+ /* NOP */
+}
+#endif
+
+/*
+ * Look up RPC transport given an INET socket
+ */
+static inline struct rpc_xprt *
+xprt_from_sock(struct sock *sk)
+{
+#ifndef SOCK_HAS_USER_DATA
+ struct rpc_xprt *xprt;
+
+ for (xprt = sock_list; xprt && sk != xprt->inet; xprt = xprt->link)
+ ;
+ return xprt;
+#else
+ return (struct rpc_xprt *) sk->user_data;
+#endif
+}
+
+/*
+ * Write data to socket.
+ */
+static inline int
+xprt_sendmsg(struct rpc_xprt *xprt)
+{
+ struct socket *sock = xprt->sock;
+ struct msghdr msg;
+ unsigned long oldfs;
+ int result;
+
+ xprt_pktdump("packet data:",
+ xprt->snd_buf.io_vec->iov_base,
+ xprt->snd_buf.io_vec->iov_len);
+
+#if LINUX_VERSION_CODE >= 0x020100
+ msg.msg_flags = MSG_DONTWAIT;
+ msg.msg_iov = xprt->snd_buf.io_vec;
+ msg.msg_iovlen = xprt->snd_buf.io_nr;
+ msg.msg_name = (struct sockaddr *) &xprt->addr;
+ msg.msg_namelen = sizeof(xprt->addr);
+ msg.msg_control = NULL;
+
+ oldfs = get_fs(); set_fs(get_ds());
+ result = sock_sendmsg(sock, &msg, xprt->snd_buf.io_len);
+ set_fs(oldfs);
+#else
+ msg.msg_flags = 0;
+ msg.msg_iov = xprt->snd_buf.io_vec;
+ msg.msg_iovlen = xprt->snd_buf.io_nr;
+ msg.msg_name = (struct sockaddr *) &xprt->addr;
+ msg.msg_namelen = sizeof(xprt->addr);
+ msg.msg_control = NULL;
+
+ oldfs = get_fs(); set_fs(get_ds());
+ result = sock->ops->sendmsg(sock, &msg, xprt->snd_buf.io_len, 1, 0);
+ set_fs(oldfs);
+#endif
+
+ dprintk("RPC: xprt_sendmsg(%d) = %d\n",
+ xprt->snd_buf.io_len, result);
+
+ if (result >= 0) {
+ xprt->snd_buf.io_len -= result;
+ return result;
+ }
+
+ switch (result) {
+ case -ECONNREFUSED:
+ /* When the server has died, an ICMP port unreachable message
+ * prompts ECONNREFUSED.
+ */
+ break;
+ case -ENOTCONN: case -EPIPE:
+ /* connection broken */
+ break;
+ default:
+ printk(KERN_NOTICE "RPC: sendmsg returned error %d\n", -result);
+ result = 0;
+ }
+ return result;
+}
+
+/*
+ * Read data from socket
+ */
+static inline int
+xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, int len)
+{
+ struct socket *sock = xprt->sock;
+ struct sockaddr_in sin;
+ struct msghdr msg;
+ unsigned long oldfs;
+ int result;
+
+#if LINUX_VERSION_CODE >= 0x020100
+ msg.msg_flags = MSG_DONTWAIT;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_name = &sin;
+ msg.msg_namelen = sizeof(sin);
+ msg.msg_control = NULL;
+
+ oldfs = get_fs(); set_fs(get_ds());
+ result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
+ set_fs(oldfs);
+#else
+ int alen = sizeof(sin);
+ msg.msg_flags = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = nr;
+ msg.msg_name = &sin;
+ msg.msg_namelen = sizeof(sin);
+ msg.msg_control = NULL;
+
+ oldfs = get_fs(); set_fs(get_ds());
+ result = sock->ops->recvmsg(sock, &msg, len, 1, 0, &alen);
+ set_fs(oldfs);
+#endif
+
+ if (!result && len)
+ result = -EAGAIN;
+
+ dprintk("RPC: xprt_recvmsg(iov %p, len %d) = %d\n",
+ iov, len, result);
+ return result;
+}
+
+
+/*
+ * Adjust RPC congestion window
+ * We use a time-smoothed congestion estimator to avoid heavy oscillation.
+ */
+static void
+xprt_adjust_cwnd(struct rpc_xprt *xprt, int result)
+{
+ unsigned long cwnd = xprt->cwnd;
+
+ if (xprt->nocong)
+ return;
+ if (result >= 0) {
+ if (xprt->cong < cwnd || jiffies < xprt->congtime)
+ return;
+ /* The (cwnd >> 1) term makes sure
+ * the result gets rounded properly. */
+ cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE + (cwnd >> 1)) / cwnd;
+ if (cwnd > RPC_MAXCWND)
+ cwnd = RPC_MAXCWND;
+ else
+ pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
+ xprt->congtime = jiffies + ((cwnd * HZ) << 2) / RPC_CWNDSCALE;
+ dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx, "
+ "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
+ (xprt->congtime-jiffies)*1000/HZ);
+ } else if (result == -ETIMEDOUT) {
+ if ((cwnd >>= 1) < RPC_CWNDSCALE)
+ cwnd = RPC_CWNDSCALE;
+ xprt->congtime = jiffies + ((cwnd * HZ) << 3) / RPC_CWNDSCALE;
+ dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx, "
+ "time %ld ms\n", xprt->cong, xprt->cwnd, cwnd,
+ (xprt->congtime-jiffies)*1000/HZ);
+ pprintk("RPC: %lu %ld cwnd\n", jiffies, cwnd);
+ }
+
+ xprt->cwnd = cwnd;
+}
+
+/*
+ * Adjust timeout values etc for next retransmit
+ */
+int
+xprt_adjust_timeout(struct rpc_timeout *to)
+{
+ if (to->to_exponential)
+ to->to_current <<= 1;
+ else
+ to->to_current += to->to_increment;
+ if (to->to_maxval && to->to_current >= to->to_maxval) {
+ to->to_current = to->to_maxval;
+ to->to_retries = 0;
+ }
+ if (!to->to_current) {
+ printk(KERN_WARNING "xprt_adjust_timeout: to_current = 0!\n");
+ to->to_current = 5 * HZ;
+ }
+ pprintk("RPC: %lu %s\n", jiffies,
+ to->to_retries? "retrans" : "timeout");
+ return (to->to_retries)--;
+}
+
+/*
+ * Close down a transport socket
+ */
+static void
+xprt_close(struct rpc_xprt *xprt)
+{
+ struct sock *sk = xprt->inet;
+
+#ifdef SOCK_HAS_USER_DATA
+ sk->user_data = NULL;
+#endif
+ sk->data_ready = xprt->old_data_ready;
+ sk->state_change = xprt->old_state_change;
+ sk->write_space = xprt->old_write_space;
+
+ if (xprt->file)
+ close_fp(xprt->file);
+ else
+ sock_release(xprt->sock);
+}
+
+/*
+ * Mark a transport as disconnected
+ */
+static void
+xprt_disconnect(struct rpc_xprt *xprt)
+{
+ dprintk("RPC: disconnected transport %p\n", xprt);
+ rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+ rpc_wake_up_status(&xprt->sending, -ENOTCONN);
+ xprt->connected = 0;
+}
+
+/*
+ * Reconnect a broken TCP connection.
+ */
+void
+xprt_reconnect(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+ struct socket *sock;
+ struct sock *inet;
+ int status;
+
+ dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
+ task->tk_pid, xprt, xprt->connected);
+ task->tk_status = 0;
+
+ if (xprt->connecting) {
+ task->tk_timeout = xprt->timeout.to_maxval;
+ rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
+ return;
+ }
+ xprt->connecting = 1;
+
+ /* Create an unconnected socket */
+ if (!(sock = xprt_create_socket(xprt->prot, NULL, &xprt->timeout)))
+ goto defer;
+
+#if LINUX_VERSION_CODE >= 0x020100
+ inet = sock->sk;
+#else
+ inet = (struct sock *) sock->data;
+#endif
+ inet->data_ready = xprt->inet->data_ready;
+ inet->state_change = xprt->inet->state_change;
+ inet->write_space = xprt->inet->write_space;
+#ifdef SOCK_HAS_USER_DATA
+ inet->user_data = xprt;
+#endif
+
+ dprintk("RPC: %4d closing old socket\n", task->tk_pid);
+ xprt_disconnect(xprt);
+ xprt_close(xprt);
+
+ /* Reset to new socket and default congestion */
+ xprt->sock = sock;
+ xprt->inet = inet;
+ xprt->cwnd = RPC_INITCWND;
+
+ /* Now connect it asynchronously. */
+ dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
+ status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
+ sizeof(xprt->addr), O_NONBLOCK);
+ if (status < 0) {
+ if (status != -EINPROGRESS && status != -EALREADY) {
+ printk("RPC: TCP connect error %d!\n", -status);
+ goto defer;
+ }
+
+ dprintk("RPC: %4d connect status %d connected %d\n",
+ task->tk_pid, status, xprt->connected);
+ task->tk_timeout = 60 * HZ;
+
+ disable_bh(NET_BH);
+ if (!xprt->connected) {
+ rpc_sleep_on(&xprt->reconn, task,
+ xprt_reconn_status, xprt_reconn_timeout);
+ enable_bh(NET_BH);
+ return;
+ }
+ enable_bh(NET_BH);
+ }
+
+ xprt->connecting = 0;
+ rpc_wake_up(&xprt->reconn);
+ return;
+
+defer:
+ task->tk_timeout = 30 * HZ;
+ rpc_sleep_on(&xprt->reconn, task, NULL, NULL);
+ xprt->connecting = 0;
+}
+
+/*
+ * Reconnect status
+ */
+static void
+xprt_reconn_status(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+
+ dprintk("RPC: %4d xprt_reconn_status %d\n",
+ task->tk_pid, task->tk_status);
+ if (!xprt->connected && task->tk_status != -ETIMEDOUT) {
+ task->tk_timeout = 30 * HZ;
+ rpc_sleep_on(&xprt->reconn, task, NULL, xprt_reconn_timeout);
+ }
+}
+
+/*
+ * Reconnect timeout. We just mark the transport as not being in the
+ * process of reconnecting, and leave the rest to the upper layers.
+ */
+static void
+xprt_reconn_timeout(struct rpc_task *task)
+{
+ dprintk("RPC: %4d xprt_reconn_timeout %d\n",
+ task->tk_pid, task->tk_status);
+ task->tk_status = -ENOTCONN;
+ task->tk_xprt->connecting = 0;
+ task->tk_timeout = 0;
+ rpc_wake_up_task(task);
+}
+
+/*
+ * Look up the RPC request corresponding to a reply.
+ */
+static inline struct rpc_rqst *
+xprt_lookup_rqst(struct rpc_xprt *xprt, u32 xid)
+{
+ struct rpc_task *head, *task;
+ struct rpc_rqst *req;
+ int safe = 0;
+
+ if ((head = xprt->pending.task) != NULL) {
+ task = head;
+ do {
+ if ((req = task->tk_rqstp) && req->rq_xid == xid)
+ return req;
+ task = task->tk_next;
+ if (++safe > 100) {
+ printk("xprt_lookup_rqst: loop in Q!\n");
+ return NULL;
+ }
+ } while (task != head);
+ }
+ dprintk("RPC: unknown XID %08x in reply.\n", xid);
+ return NULL;
+}
+
+/*
+ * Complete reply received.
+ * The TCP code relies on us to remove the request from xprt->pending.
+ */
+static inline void
+xprt_complete_rqst(struct rpc_xprt *xprt, struct rpc_rqst *req, int copied)
+{
+ struct rpc_task *task = req->rq_task;
+
+ req->rq_rlen = copied;
+ req->rq_gotit = 1;
+
+ /* Adjust congestion window */
+ xprt_adjust_cwnd(xprt, copied);
+
+#ifdef RPC_PROFILE
+ /* Profile only reads for now */
+ if (copied > 1024) {
+ static unsigned long nextstat = 0;
+ static unsigned long pkt_rtt = 0, pkt_len = 0, pkt_cnt = 0;
+
+ pkt_cnt++;
+ pkt_len += req->rq_slen + copied;
+ pkt_rtt += jiffies - req->rq_xtime;
+ if (nextstat < jiffies) {
+ printk("RPC: %lu %ld cwnd\n", jiffies, xprt->cwnd);
+ printk("RPC: %ld %ld %ld %ld stat\n",
+ jiffies, pkt_cnt, pkt_len, pkt_rtt);
+ pkt_rtt = pkt_len = pkt_cnt = 0;
+ nextstat = jiffies + 5 * HZ;
+ }
+ }
+#endif
+
+ /* ... and wake up the process. */
+ dprintk("RPC: %4d has input (%d bytes)\n", task->tk_pid, copied);
+ task->tk_status = copied;
+
+ rpc_wake_up_task(task);
+ return;
+}
+
+/*
+ * Input handler for RPC replies. Called from a bottom half and hence
+ * atomic.
+ */
+static inline void
+udp_data_ready(struct sock *sk, int len)
+{
+ struct rpc_task *task;
+ struct rpc_xprt *xprt;
+ struct rpc_rqst *rovr;
+ struct sk_buff *skb;
+ struct iovec iov[MAX_IOVEC];
+ unsigned long oldfs;
+ int err, repsize, copied;
+
+ dprintk("RPC: udp_data_ready...\n");
+ if (!(xprt = xprt_from_sock(sk)))
+ return;
+ dprintk("RPC: udp_data_ready client %p\n", xprt);
+
+ if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL)
+ return;
+ repsize = skb->len - 8; /* don't account for UDP header */
+
+ if (repsize < 4) {
+ printk("RPC: impossible RPC reply size %d!\n", repsize);
+ goto dropit;
+ }
+
+ /* Look up the request corresponding to the given XID */
+ if (!(rovr = xprt_lookup_rqst(xprt, *(u32 *) (skb->h.raw + 8))))
+ goto dropit;
+ task = rovr->rq_task;
+
+ dprintk("RPC: %4d received reply\n", task->tk_pid);
+ xprt_pktdump("packet data:", (u32 *) (skb->h.raw+8), repsize);
+
+ if ((copied = rovr->rq_rlen) > repsize)
+ copied = repsize;
+
+ /* Okay, we have it. Copy datagram... */
+ memcpy(iov, rovr->rq_rvec, rovr->rq_rnr * sizeof(iov[0]));
+ oldfs = get_fs(); set_fs(get_ds());
+ skb_copy_datagram_iovec(skb, 8, iov, copied);
+ set_fs(oldfs);
+
+ xprt_complete_rqst(xprt, rovr, copied);
+
+dropit:
+ skb_free_datagram(sk, skb);
+ return;
+}
+
+/*
+ * TCP record receive routine
+ * This is not the most efficient code since we call recvfrom twice--
+ * first receiving the record marker and XID, then the data.
+ *
+ * The optimal solution would be a RPC support in the TCP layer, which
+ * would gather all data up to the next record marker and then pass us
+ * the list of all TCP segments ready to be copied.
+ */
+static inline int
+tcp_input_record(struct rpc_xprt *xprt)
+{
+ struct rpc_rqst *req;
+ struct iovec *iov;
+ struct iovec riov;
+ u32 offset;
+ int result, maxcpy, reclen, avail, want;
+
+ dprintk("RPC: tcp_input_record\n");
+ offset = xprt->tcp_offset;
+ result = -EAGAIN;
+ if (offset < 4 || (!xprt->tcp_more && offset < 8)) {
+ want = (xprt->tcp_more? 4 : 8) - offset;
+ dprintk("RPC: reading header (%d bytes)\n", want);
+ riov.iov_base = xprt->tcp_recm.data + offset;
+ riov.iov_len = want;
+ result = xprt_recvmsg(xprt, &riov, 1, want);
+ if (result < 0)
+ goto done;
+ offset += result;
+ if (result < want) {
+ result = -EAGAIN;
+ goto done;
+ }
+
+ /* Get the record length and mask out the more_fragments bit */
+ reclen = ntohl(xprt->tcp_reclen);
+ dprintk("RPC: reclen %08x\n", reclen);
+ xprt->tcp_more = (reclen & 0x80000000)? 0 : 1;
+ if (!(reclen &= 0x7fffffff)) {
+ printk(KERN_NOTICE "RPC: empty TCP record.\n");
+ return -ENOTCONN; /* break connection */
+ }
+ xprt->tcp_total += reclen;
+ xprt->tcp_reclen = reclen;
+
+ dprintk("RPC: got xid %08x reclen %d morefrags %d\n",
+ xprt->tcp_xid, xprt->tcp_reclen, xprt->tcp_more);
+ if (!xprt->tcp_copied
+ && (req = xprt_lookup_rqst(xprt, xprt->tcp_xid))) {
+ iov = xprt->tcp_iovec;
+ memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
+#if 0
+*(u32 *)iov->iov_base = req->rq_xid;
+#endif
+ iov->iov_base += 4;
+ iov->iov_len -= 4;
+ xprt->tcp_copied = 4;
+ xprt->tcp_rqstp = req;
+ }
+ } else {
+ reclen = xprt->tcp_reclen;
+ }
+
+ avail = reclen - (offset - 4);
+ if ((req = xprt->tcp_rqstp) && req->rq_xid == xprt->tcp_xid
+ && req->rq_task->tk_rpcwait == &xprt->pending) {
+ want = MIN(req->rq_rlen - xprt->tcp_copied, avail);
+
+ dprintk("RPC: %4d TCP receiving %d bytes\n",
+ req->rq_task->tk_pid, want);
+ result = xprt_recvmsg(xprt, xprt->tcp_iovec, req->rq_rnr, want);
+ if (result < 0)
+ goto done;
+ xprt->tcp_copied += result;
+ offset += result;
+ avail -= result;
+ if (result < want) {
+ result = -EAGAIN;
+ goto done;
+ }
+
+ maxcpy = MIN(req->rq_rlen, xprt->tcp_total);
+ if (xprt->tcp_copied == maxcpy && !xprt->tcp_more) {
+ dprintk("RPC: %4d received reply complete\n",
+ req->rq_task->tk_pid);
+ xprt_complete_rqst(xprt, req, xprt->tcp_total);
+ xprt->tcp_copied = 0;
+ xprt->tcp_rqstp = NULL;
+ }
+ /* Request must be re-encoded before retransmit */
+ req->rq_damaged = 1;
+ }
+
+ /* Skip over any trailing bytes on short reads */
+ while (avail) {
+ static u8 dummy[64];
+
+ want = MIN(avail, sizeof(dummy));
+ riov.iov_base = dummy;
+ riov.iov_len = want;
+ dprintk("RPC: TCP skipping %d bytes\n", want);
+ result = xprt_recvmsg(xprt, &riov, 1, want);
+ if (result < 0)
+ goto done;
+ offset += result;
+ avail -= result;
+ if (result < want) {
+ result = -EAGAIN;
+ goto done;
+ }
+ }
+ if (!xprt->tcp_more)
+ xprt->tcp_total = 0;
+ offset = 0;
+
+done:
+ dprintk("RPC: tcp_input_record done (off %d total %d copied %d)\n",
+ offset, xprt->tcp_total, xprt->tcp_copied);
+ xprt->tcp_offset = offset;
+ return result;
+}
+
+/*
+ * data_ready callback for TCP.
+ */
+static void
+tcp_data_ready(struct sock *sk, int len)
+{
+ struct rpc_xprt *xprt;
+ int result, safe_retry = 0;
+
+ dprintk("RPC: tcp_data_ready...\n");
+ if (!(xprt = xprt_from_sock(sk)))
+ return;
+ dprintk("RPC: tcp_data_ready client %p\n", xprt);
+ dprintk("RPC: state %x conn %d dead %d zapped %d\n",
+ sk->state, xprt->connected,
+ sk->dead, sk->zapped);
+
+ do {
+ if (safe_retry++ > 20)
+ return;
+ result = tcp_input_record(xprt);
+ } while (result >= 0);
+
+ switch (result) {
+ case -EAGAIN:
+ return;
+ case -ENOTCONN:
+ case -EPIPE:
+ xprt_disconnect(xprt);
+ return;
+ default:
+ printk("RPC: unexpected error %d from tcp_input_record\n",
+ result);
+ }
+}
+
+static void
+tcp_state_change(struct sock *sk)
+{
+ struct rpc_xprt *xprt;
+
+ if (!(xprt = xprt_from_sock(sk)))
+ return;
+ dprintk("RPC: tcp_state_change client %p...\n", xprt);
+ dprintk("RPC: state %x conn %d dead %d zapped %d\n",
+ sk->state, xprt->connected,
+ sk->dead, sk->zapped);
+
+ if (sk->state == TCP_ESTABLISHED && !xprt->connected) {
+ xprt->connected = 1;
+ xprt->connecting = 0;
+ rpc_wake_up(&xprt->reconn);
+ } else if (sk->zapped) {
+ rpc_wake_up_status(&xprt->pending, -ENOTCONN);
+ rpc_wake_up_status(&xprt->sending, -ENOTCONN);
+ rpc_wake_up_status(&xprt->reconn, -ENOTCONN);
+ }
+}
+
+static void
+tcp_write_space(struct sock *sk)
+{
+ struct rpc_xprt *xprt;
+
+ if (!(xprt = xprt_from_sock(sk)))
+ return;
+ xprt->write_space = 1;
+ if (xprt->snd_task && !RPC_IS_RUNNING(xprt->snd_task))
+ rpc_wake_up_task(xprt->snd_task);
+}
+
+/*
+ * RPC receive timeout handler.
+ */
+static void
+xprt_timer(struct rpc_task *task)
+{
+ if (task->tk_rqstp)
+ xprt_adjust_cwnd(task->tk_xprt, -ETIMEDOUT);
+
+ dprintk("RPC: %4d xprt_timer (%s request)\n", task->tk_pid,
+ task->tk_rqstp? "pending" : "backlogged");
+
+ task->tk_status = -ETIMEDOUT;
+ task->tk_timeout = 0;
+ rpc_wake_up_task(task);
+}
+
+/*
+ * (Partly) transmit the RPC packet
+ * Note that task->tk_status is either 0 or negative on return.
+ * Only when the reply is received will the status be set to a
+ * positive value.
+ */
+static inline int
+xprt_transmit_some(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+ struct rpc_rqst *req = task->tk_rqstp;
+ int result;
+
+ task->tk_status = 0;
+ if ((result = xprt_sendmsg(xprt)) >= 0) {
+ if (!xprt->snd_buf.io_len || !xprt->stream) {
+ rpc_wake_up_next(&xprt->sending);
+ return req->rq_slen;
+ }
+ result = -EAGAIN;
+ } else if (xprt->stream) {
+ if (result == -ENOTCONN || result == -EPIPE) {
+ xprt_disconnect(xprt);
+ result = -ENOTCONN;
+ }
+ }
+ return task->tk_status = result;
+}
+
+/*
+ * Place the actual RPC call.
+ * We have to copy the iovec because sendmsg fiddles with its contents.
+ */
+void
+xprt_transmit(struct rpc_task *task)
+{
+ struct rpc_timeout *timeo;
+ struct rpc_rqst *req = task->tk_rqstp;
+ struct rpc_xprt *xprt = req->rq_xprt;
+
+ dprintk("RPC: %4d xprt_transmit(%x)\n", task->tk_pid,
+ *(u32 *)(req->rq_svec[0].iov_base));
+
+ if (xprt->shutdown) {
+ task->tk_status = -EIO;
+ return;
+ }
+
+ /* If we're not already in the process of transmitting our call,
+ * set up everything as needed. */
+ if (xprt->snd_task != task) {
+ /* Write the record marker */
+ if (xprt->stream) {
+ u32 marker;
+
+ if (!xprt->connected) {
+ task->tk_status = -ENOTCONN;
+ return;
+ }
+ marker = htonl(0x80000000|(req->rq_slen-4));
+ *((u32 *) req->rq_svec[0].iov_base) = marker;
+ }
+
+ /* Reset timeout parameters */
+ timeo = &req->rq_timeout;
+ if (timeo->to_retries < 0) {
+ dprintk("RPC: %4d xprt_transmit reset timeo\n",
+ task->tk_pid);
+ timeo->to_retries = xprt->timeout.to_retries;
+ timeo->to_current = timeo->to_initval;
+ }
+
+#ifdef RPC_PROFILE
+ req->rq_xtime = jiffies;
+#endif
+ req->rq_gotit = 0;
+
+ if (xprt->snd_task) {
+ dprintk("RPC: %4d TCP write queue full (task %d)\n",
+ task->tk_pid, xprt->snd_task->tk_pid);
+ rpc_sleep_on(&xprt->sending, task,
+ xprt_transmit_status, NULL);
+ return;
+ }
+ xprt->snd_buf = req->rq_snd_buf;
+ xprt->snd_task = task;
+ }
+
+ /* For fast networks/servers we have to put the request on
+ * the pending list now:
+ */
+ disable_bh(NET_BH);
+ rpc_add_wait_queue(&xprt->pending, task);
+ task->tk_callback = NULL;
+ enable_bh(NET_BH);
+
+ /* Continue transmitting the packet/record. We must be careful
+ * to cope with writespace callbacks arriving _after_ we have
+ * called xprt_sendmsg().
+ */
+ while (1) {
+ xprt->write_space = 0;
+ if (xprt_transmit_some(xprt, task) != -EAGAIN) {
+ dprintk("RPC: %4d xmit complete\n", task->tk_pid);
+ xprt->snd_task = NULL;
+ return;
+ }
+
+ dprintk("RPC: %4d xmit incomplete (%d left of %d)\n",
+ task->tk_pid, xprt->snd_buf.io_len,
+ req->rq_slen);
+ task->tk_status = 0;
+ disable_bh(NET_BH);
+ if (!xprt->write_space) {
+ /* Remove from pending */
+ rpc_remove_wait_queue(task);
+ rpc_sleep_on(&xprt->sending, task,
+ xprt_transmit_status, NULL);
+ enable_bh(NET_BH);
+ return;
+ }
+ enable_bh(NET_BH);
+ }
+}
+
+/*
+ * This callback is invoked when the sending task is forced to sleep
+ * because the TCP write buffers are full
+ */
+static void
+xprt_transmit_status(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_client->cl_xprt;
+
+ dprintk("RPC: %4d transmit_status %d\n", task->tk_pid, task->tk_status);
+ if (xprt->snd_task == task) {
+ if (task->tk_status < 0)
+ xprt->snd_task = NULL;
+ xprt_disconnect(xprt);
+ }
+}
+
+/*
+ * Wait for the reply to our call.
+ * When the callback is invoked, the congestion window should have
+ * been updated already.
+ */
+void
+xprt_receive(struct rpc_task *task)
+{
+ struct rpc_rqst *req = task->tk_rqstp;
+ struct rpc_xprt *xprt = req->rq_xprt;
+
+ dprintk("RPC: %4d xprt_receive\n", task->tk_pid);
+ if (xprt->connected == 0) {
+ task->tk_status = -ENOTCONN;
+ return;
+ }
+
+ /*
+ * Wait until rq_gotit goes non-null, or timeout elapsed.
+ */
+ task->tk_timeout = req->rq_timeout.to_current;
+
+ disable_bh(NET_BH);
+ if (!req->rq_gotit) {
+ rpc_sleep_on(&xprt->pending, task,
+ xprt_receive_status, xprt_timer);
+ }
+ enable_bh(NET_BH);
+
+ dprintk("RPC: %4d xprt_receive returns %d\n",
+ task->tk_pid, task->tk_status);
+}
+
+static void
+xprt_receive_status(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+
+ if (xprt->stream && xprt->tcp_rqstp == task->tk_rqstp)
+ xprt->tcp_rqstp = NULL;
+}
+
+/*
+ * Reserve an RPC call slot.
+ */
+int
+xprt_reserve(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+
+ /* We already have an initialized request. */
+ if (task->tk_rqstp)
+ return 0;
+
+ dprintk("RPC: %4d xprt_reserve cong = %ld cwnd = %ld\n",
+ task->tk_pid, xprt->cong, xprt->cwnd);
+ if ((!RPCXPRT_CONGESTED(xprt) && xprt->free)) {
+ xprt_reserve_status(task);
+ task->tk_timeout = 0;
+ } else if (!task->tk_timeout) {
+ task->tk_status = -ENOBUFS;
+ } else {
+ dprintk("RPC: xprt_reserve waiting on backlog\n");
+ rpc_sleep_on(&xprt->backlog, task, xprt_reserve_status, NULL);
+ }
+ dprintk("RPC: %4d xprt_reserve returns %d\n",
+ task->tk_pid, task->tk_status);
+ return task->tk_status;
+}
+
+/*
+ * Reservation callback
+ */
+static void
+xprt_reserve_status(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+ struct rpc_rqst *req;
+
+ if (xprt->shutdown) {
+ task->tk_status = -EIO;
+ } else if (task->tk_status < 0) {
+ /* NOP */
+ } else if (task->tk_rqstp) {
+ /* We've already been given a request slot: NOP */
+ } else if (!RPCXPRT_CONGESTED(xprt)) {
+ /* OK: There's room for us. Grab a free slot and bump
+ * congestion value */
+ if (!(req = xprt->free)) {
+ /* printk("RPC: inconsistent free list!\n"); */
+ rpc_debug = ~0;
+ dprintk("RPC: %4d inconsistent free list "
+ "(cong %ld cwnd %ld)\n",
+ task->tk_pid, xprt->cong, xprt->cwnd);
+ goto bummer;
+ }
+ if (req->rq_xid) {
+ printk("RPC: used rqst slot %p on free list!\n", req);
+ goto bummer;
+ }
+ xprt->free = req->rq_next;
+ xprt->cong += RPC_CWNDSCALE;
+ task->tk_rqstp = req;
+ req->rq_next = NULL;
+ xprt_request_init(task, xprt);
+ } else {
+ task->tk_status = -EAGAIN;
+ }
+
+ if (xprt->free && !RPCXPRT_CONGESTED(xprt))
+ rpc_wake_up_next(&xprt->backlog);
+
+ return;
+
+bummer:
+ task->tk_status = -EIO;
+ xprt->free = NULL;
+ return;
+}
+
+/*
+ * Initialize RPC request
+ */
+static void
+xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
+{
+ struct rpc_rqst *req = task->tk_rqstp;
+ static u32 xid = 0;
+
+ if (!xid)
+ xid = jiffies;
+
+ dprintk("RPC: %4d reserved req %p xid %08x\n", task->tk_pid, req, xid);
+ task->tk_status = 0;
+ req->rq_gotit = 0;
+ req->rq_timeout = xprt->timeout;
+ req->rq_task = task;
+ req->rq_xprt = xprt;
+ req->rq_xid = xid++;
+}
+
+/*
+ * Release an RPC call slot
+ */
+void
+xprt_release(struct rpc_task *task)
+{
+ struct rpc_xprt *xprt = task->tk_xprt;
+ struct rpc_rqst *req;
+
+ if (!(req = task->tk_rqstp))
+ return;
+ memset(req, 0, sizeof(*req)); /* mark unused */
+
+ dprintk("RPC: %4d release request %p\n", task->tk_pid, req);
+
+ /* remove slot from queue of pending */
+ disable_bh(NET_BH);
+ if (task->tk_rpcwait) {
+ printk("RPC: task of released request still queued!\n");
+#ifdef RPC_DEBUG
+ printk("RPC: (task is on %s)\n", rpc_qname(task->tk_rpcwait));
+#endif
+ rpc_del_timer(task);
+ rpc_remove_wait_queue(task);
+ }
+ enable_bh(NET_BH);
+
+ /* Decrease congestion value. If congestion threshold is not yet
+ * reached, pass on the request slot.
+ * This looks kind of kludgy, but it guarantees backlogged requests
+ * are served in order.
+ */
+ xprt->cong -= RPC_CWNDSCALE;
+ if (!RPCXPRT_CONGESTED(xprt)) {
+ struct rpc_task *next = rpc_wake_up_next(&xprt->backlog);
+
+ if (next && next->tk_rqstp == 0) {
+ xprt->cong += RPC_CWNDSCALE;
+ next->tk_rqstp = req;
+ xprt_request_init(next, xprt);
+ return;
+ }
+ }
+
+ req->rq_next = xprt->free;
+ xprt->free = req;
+}
+
+/*
+ * Set default timeout parameters
+ */
+void
+xprt_default_timeout(struct rpc_timeout *to, int proto)
+{
+ if (proto == IPPROTO_UDP)
+ xprt_set_timeout(to, 5, 5 * HZ);
+ else
+ xprt_set_timeout(to, 5, 15 * HZ);
+}
+
+/*
+ * Set constant timeout
+ */
+void
+xprt_set_timeout(struct rpc_timeout *to, unsigned int retr, unsigned long incr)
+{
+ to->to_current =
+ to->to_initval =
+ to->to_increment = incr;
+ to->to_maxval = incr * retr;
+ to->to_resrvval = incr * retr;
+ to->to_retries = retr;
+ to->to_exponential = 0;
+}
+
+/*
+ * Initialize an RPC client
+ */
+static struct rpc_xprt *
+xprt_setup(struct socket *sock, int proto,
+ struct sockaddr_in *ap, struct rpc_timeout *to)
+{
+ struct rpc_xprt *xprt;
+ struct rpc_rqst *req;
+ struct sock *inet;
+ int i;
+
+ dprintk("RPC: setting up %s transport...\n",
+ proto == IPPROTO_UDP? "UDP" : "TCP");
+
+#if LINUX_VERSION_CODE >= 0x020100
+ inet = sock->sk;
+#else
+ inet = (struct sock *) sock->data;
+#endif
+
+ if ((xprt = kmalloc(sizeof(struct rpc_xprt), GFP_KERNEL)) == NULL)
+ return NULL;
+ memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */
+
+ xprt->file = NULL;
+ xprt->sock = sock;
+ xprt->inet = inet;
+ xprt->addr = *ap;
+ xprt->prot = proto;
+ xprt->stream = (proto == IPPROTO_TCP)? 1 : 0;
+ xprt->cwnd = RPC_INITCWND;
+#ifdef SOCK_HAS_USER_DATA
+ inet->user_data = xprt;
+#else
+ xprt->link = sock_list;
+ sock_list = xprt;
+#endif
+ xprt->old_data_ready = inet->data_ready;
+ xprt->old_state_change = inet->state_change;
+ xprt->old_write_space = inet->write_space;
+ if (proto == IPPROTO_UDP) {
+ inet->data_ready = udp_data_ready;
+ } else {
+ inet->data_ready = tcp_data_ready;
+ inet->state_change = tcp_state_change;
+ inet->write_space = tcp_write_space;
+ xprt->nocong = 1;
+ }
+ xprt->connected = 1;
+
+ /* Set timeout parameters */
+ if (to) {
+ xprt->timeout = *to;
+ xprt->timeout.to_current = to->to_initval;
+ xprt->timeout.to_resrvval = to->to_maxval << 1;
+ } else {
+ xprt_default_timeout(&xprt->timeout, xprt->prot);
+ }
+
+ xprt->pending = RPC_INIT_WAITQ("xprt_pending");
+ xprt->sending = RPC_INIT_WAITQ("xprt_sending");
+ xprt->backlog = RPC_INIT_WAITQ("xprt_backlog");
+ xprt->reconn = RPC_INIT_WAITQ("xprt_reconn");
+
+ /* initialize free list */
+ for (i = 0, req = xprt->slot; i < RPC_MAXREQS-1; i++, req++)
+ req->rq_next = req + 1;
+ req->rq_next = NULL;
+ xprt->free = xprt->slot;
+
+ dprintk("RPC: created transport %p\n", xprt);
+ return xprt;
+}
+
+/*
+ * Create and initialize an RPC client given an open file.
+ * This is obsolete now.
+ */
+#if 0
+struct rpc_xprt *
+xprt_create(struct file *file, struct sockaddr_in *ap, struct rpc_timeout *to)
+{
+ struct rpc_xprt *xprt;
+ struct socket *sock;
+ int proto;
+
+ if (!file) {
+ printk("RPC: file == NULL in xprt_create!\n");
+ return NULL;
+ }
+
+ sock = &file->f_inode->u.socket_i;
+ if (sock->ops->family != AF_INET) {
+ printk(KERN_WARNING "RPC: only INET sockets supported\n");
+ return NULL;
+ }
+
+ proto = (sock->type == SOCK_DGRAM)? IPPROTO_UDP : IPPROTO_TCP;
+ if ((xprt = xprt_setup(sock, proto, ap, to)) != NULL) {
+ xprt->file = file;
+ file->f_count++;
+ }
+
+ return xprt;
+}
+#endif
+
+/*
+ * Bind to a reserved port
+ */
+static inline int
+xprt_bindresvport(struct socket *sock)
+{
+ struct sockaddr_in myaddr;
+ int err, port;
+
+ memset(&myaddr, 0, sizeof(myaddr));
+ myaddr.sin_family = AF_INET;
+ port = 800;
+ do {
+ myaddr.sin_port = htons(port);
+ err = sock->ops->bind(sock, (struct sockaddr *) &myaddr,
+ sizeof(myaddr));
+ } while (err == -EADDRINUSE && --port > 0);
+
+ if (err < 0)
+ printk("RPC: Can't bind to reserved port (%d).\n", -err);
+
+ return err;
+}
+
+/*
+ * Create a client socket given the protocol and peer address.
+ */
+static struct socket *
+xprt_create_socket(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+{
+ struct socket *sock;
+ int type, err;
+
+ dprintk("RPC: xprt_create_socket(%08lx, %s %d)\n",
+ sap? ntohl(sap->sin_addr.s_addr) : 0,
+ (proto == IPPROTO_UDP)? "udp" : "tcp", proto);
+
+ type = (proto == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+ if ((err = sock_create(AF_INET, type, proto, &sock)) < 0) {
+ printk("RPC: can't create socket (%d).\n", -err);
+ goto failed;
+ }
+
+ /* If the caller has root privs, bind to a reserved port */
+ if (!current->fsuid && xprt_bindresvport(sock) < 0)
+ goto failed;
+
+ if (type == SOCK_STREAM && sap) {
+ err = sock->ops->connect(sock, (struct sockaddr *) sap,
+ sizeof(*sap), 0);
+ if (err < 0) {
+ printk("RPC: TCP connect failed (%d).\n", -err);
+ goto failed;
+ }
+ }
+
+ return sock;
+
+failed:
+ sock_release(sock);
+ return NULL;
+}
+
+/*
+ * Create an RPC client transport given the protocol and peer address.
+ */
+struct rpc_xprt *
+xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to)
+{
+ struct socket *sock;
+ struct rpc_xprt *xprt;
+
+ dprintk("RPC: xprt_create_proto called\n");
+
+ if (!(sock = xprt_create_socket(proto, sap, to)))
+ return NULL;
+
+ if (!(xprt = xprt_setup(sock, proto, sap, to)))
+ sock_release(sock);
+
+ return xprt;
+}
+
+/*
+ * Prepare for transport shutdown.
+ */
+void
+xprt_shutdown(struct rpc_xprt *xprt)
+{
+ xprt->shutdown = 1;
+ rpc_wake_up(&xprt->sending);
+ rpc_wake_up(&xprt->pending);
+ rpc_wake_up(&xprt->backlog);
+ rpc_wake_up(&xprt->reconn);
+}
+
+/*
+ * Destroy an RPC transport, killing off all requests.
+ */
+int
+xprt_destroy(struct rpc_xprt *xprt)
+{
+#ifndef SOCK_HAS_USER_DATA
+ struct rpc_xprt **q;
+
+ for (q = &sock_list; *q && *q != xprt; q = &((*q)->link))
+ ;
+ if (!*q) {
+ printk(KERN_WARNING "xprt_destroy: unknown socket!\n");
+ return -EIO; /* why is there no EBUGGYSOFTWARE */
+ }
+ *q = xprt->link;
+#endif
+
+ dprintk("RPC: destroying transport %p\n", xprt);
+ xprt_close(xprt);
+ kfree(xprt);
+
+ return 0;
+}
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index 8bdb4f224..eb23ea698 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -42,6 +42,10 @@ extern ctl_table bridge_table[];
extern ctl_table ipv6_table[];
#endif
+#ifdef CONFIG_TR
+extern ctl_table tr_table[];
+#endif
+
ctl_table net_table[] = {
{NET_CORE, "core", NULL, 0, 0555, core_table},
{NET_UNIX, "unix", NULL, 0, 0555, unix_table},
@@ -64,5 +68,8 @@ ctl_table net_table[] = {
#ifdef CONFIG_IPV6
{NET_IPV6, "ipv6", NULL, 0, 0555, ipv6_table},
#endif
+#ifdef CONFIG_TR
+ {NET_TR, "token-ring", NULL, 0, 0555, tr_table},
+#endif
{0}
};
diff --git a/net/unix/Makefile b/net/unix/Makefile
index 9116cc054..afce06790 100644
--- a/net/unix/Makefile
+++ b/net/unix/Makefile
@@ -8,7 +8,11 @@
# Note 2! The CFLAGS definition is now in the main makefile...
O_TARGET := unix.o
-O_OBJS := af_unix.o garbage.o sysctl_net_unix.o
+O_OBJS := af_unix.o garbage.o
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_unix.o
+endif
include $(TOPDIR)/Rules.make
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index aeb752d96..7c4d4679d 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1,12 +1,7 @@
/*
* NET3: Implementation of BSD Unix domain sockets.
*
- * Authors: Alan Cox, <alan@cymru.net>
- *
- * Currently this contains all but the file descriptor passing code.
- * Before that goes in the odd bugs in the iovec handlers need
- * fixing, and this bit testing. BSD fd passing is not a trivial part
- * of the exercise it turns out. Anyone like writing garbage collectors.
+ * Authors: Alan Cox, <alan.cox@linux.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -45,6 +40,19 @@
* socketpair(...SOCK_RAW..) doesn't panic the kernel.
* BSD af_unix apparently has connect forgetting to block properly.
* (need to check this with the POSIX spec in detail)
+ *
+ * Differences from 2.0.0-11-... (ANK)
+ * Bug fixes and improvements.
+ * - client shutdown killed server socket.
+ * - removed all useless cli/sti pairs.
+ *
+ * Semantic changes/extensions.
+ * - generic control message passing.
+ * - SCM_CREDENTIALS control message.
+ * - "Abstract" (not FS based) socket bindings.
+ * Abstract names are sequences of bytes (not zero terminated)
+ * started by 0, so that this name space does not intersect
+ * with BSD names.
*/
#include <linux/config.h>
@@ -72,71 +80,144 @@
#include <net/tcp.h>
#include <net/af_unix.h>
#include <linux/proc_fs.h>
+#include <net/scm.h>
-unix_socket *unix_socket_list=NULL;
+#include <asm/checksum.h>
#define min(a,b) (((a)<(b))?(a):(b))
-/*
- * Make sure the unix name is null-terminated.
- */
-
-static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len)
+
+unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
+
+#define unix_sockets_unbound (unix_socket_table[UNIX_HASH_SIZE])
+
+#define UNIX_ABSTRACT(sk) ((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
+
+extern __inline__ unsigned unix_hash_fold(unsigned hash)
+{
+ hash ^= hash>>16;
+ hash ^= hash>>8;
+ hash ^= hash>>4;
+ return hash;
+}
+
+#define unix_peer(sk) ((sk)->pair)
+
+extern __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk)
+{
+ return unix_peer(osk) == sk;
+}
+
+extern __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk)
+{
+ return (sk->type==osk->type);
+}
+
+extern __inline__ void unix_lock(unix_socket *sk)
+{
+ sk->sock_readers++;
+}
+
+extern __inline__ int unix_unlock(unix_socket *sk)
{
- if (len >= sizeof(*sunaddr))
- len = sizeof(*sunaddr)-1;
- ((char *)sunaddr)[len]=0;
+ return sk->sock_readers--;
+}
+
+extern __inline__ int unix_locked(unix_socket *sk)
+{
+ return sk->sock_readers;
+}
+
+extern __inline__ void unix_release_addr(struct unix_address *addr)
+{
+ if (addr)
+ {
+ if (atomic_dec_and_test(&addr->refcnt))
+ kfree(addr);
+ }
}
+
/*
- * Note: Sockets may not be removed _during_ an interrupt or net_bh
- * handler using this technique. They can be added although we do not
- * use this facility.
+ * Check unix socket name:
+ * - should be not zero length.
+ * - if started by not zero, should be NULL terminated (FS object)
+ * - if started by zero, it is abstract name.
*/
-static void unix_remove_socket(unix_socket *sk)
+static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
{
- unix_socket **s;
-
- cli();
- s=&unix_socket_list;
-
- while(*s!=NULL)
+ if (len <= sizeof(short) || len > sizeof(*sunaddr))
+ return -EINVAL;
+ if (!sunaddr || sunaddr->sun_family != AF_UNIX)
+ return -EINVAL;
+ if (sunaddr->sun_path[0])
{
- if(*s==sk)
- {
- *s=sk->next;
- sti();
- return;
- }
- s=&((*s)->next);
+ if (len >= sizeof(*sunaddr))
+ len = sizeof(*sunaddr)-1;
+ ((char *)sunaddr)[len]=0;
+ len = strlen(sunaddr->sun_path)+1+sizeof(short);
+ return len;
}
- sti();
+
+ *hashp = unix_hash_fold(csum_partial((char*)sunaddr, len, 0));
+ return len;
+}
+
+static void unix_remove_socket(unix_socket *sk)
+{
+ unix_socket **list = sk->protinfo.af_unix.list;
+ if (sk->next)
+ sk->next->prev = sk->prev;
+ if (sk->prev)
+ sk->prev->next = sk->next;
+ if (*list == sk)
+ *list = sk->next;
+ sk->protinfo.af_unix.list = NULL;
+ sk->prev = NULL;
+ sk->next = NULL;
}
static void unix_insert_socket(unix_socket *sk)
{
- cli();
- sk->next=unix_socket_list;
- unix_socket_list=sk;
- sti();
+ unix_socket **list = sk->protinfo.af_unix.list;
+ sk->prev = NULL;
+ sk->next = *list;
+ if (*list)
+ (*list)->prev = sk;
+ *list=sk;
}
-static unix_socket *unix_find_socket(struct inode *i)
+static unix_socket *unix_find_socket_byname(struct sockaddr_un *sunname,
+ int len, int type, unsigned hash)
{
unix_socket *s;
- cli();
- s=unix_socket_list;
- while(s)
+
+ for (s=unix_socket_table[(hash^type)&0xF]; s; s=s->next)
+ {
+ if(s->protinfo.af_unix.addr->len==len &&
+ memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0 &&
+ s->type == type)
+ {
+ unix_lock(s);
+ return(s);
+ }
+ }
+ return(NULL);
+}
+
+static unix_socket *unix_find_socket_byinode(struct inode *i)
+{
+ unix_socket *s;
+
+ for (s=unix_socket_table[i->i_ino & 0xF]; s; s=s->next)
{
if(s->protinfo.af_unix.inode==i)
{
- sti();
+ unix_lock(s);
return(s);
}
- s=s->next;
}
- sti();
return(NULL);
}
@@ -147,10 +228,9 @@ static unix_socket *unix_find_socket(struct inode *i)
static void unix_destroy_timer(unsigned long data)
{
unix_socket *sk=(unix_socket *)data;
- if(sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+ if(!unix_locked(sk) && atomic_read(&sk->wmem_alloc) == 0)
{
- if(sk->protinfo.af_unix.name)
- kfree(sk->protinfo.af_unix.name);
+ unix_release_addr(sk->protinfo.af_unix.addr);
sk_free(sk);
return;
}
@@ -201,10 +281,9 @@ static void unix_destroy_socket(unix_socket *sk)
sk->protinfo.af_unix.inode=NULL;
}
- if(--sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+ if(!unix_unlock(sk) && atomic_read(&sk->wmem_alloc) == 0)
{
- if(sk->protinfo.af_unix.name)
- kfree(sk->protinfo.af_unix.name);
+ unix_release_addr(sk->protinfo.af_unix.addr);
sk_free(sk);
}
else
@@ -214,82 +293,40 @@ static void unix_destroy_socket(unix_socket *sk)
}
}
-/*
- * Fixme: We need async I/O on AF_UNIX doing next.
- */
-
-static int unix_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- return -EINVAL;
-}
-
-/*
- * Yes socket options work with the new unix domain socketry!!!!!!!
- */
-
-static int unix_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
- unix_socket *sk=sock->data;
- if(level!=SOL_SOCKET)
- return -EOPNOTSUPP;
- return sock_setsockopt(sk,level,optname,optval,optlen);
-}
-
-static int unix_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
-{
- unix_socket *sk=sock->data;
- if(level!=SOL_SOCKET)
- return -EOPNOTSUPP;
- return sock_getsockopt(sk,level,optname,optval,optlen);
-}
-
static int unix_listen(struct socket *sock, int backlog)
{
- unix_socket *sk=sock->data;
- if(sk->type!=SOCK_STREAM)
+ struct sock *sk = sock->sk;
+
+ if (sock->state != SS_UNCONNECTED)
+ return(-EINVAL);
+ if (sock->type!=SOCK_STREAM)
return -EOPNOTSUPP; /* Only stream sockets accept */
- if(sk->protinfo.af_unix.name==NULL)
+ if (!sk->protinfo.af_unix.addr)
return -EINVAL; /* No listens on an unbound socket */
sk->max_ack_backlog=backlog;
+ if (sk->ack_backlog < backlog)
+ sk->state_change(sk);
sk->state=TCP_LISTEN;
+ sock->flags |= SO_ACCEPTCON;
return 0;
}
-static void def_callback1(struct sock *sk)
-{
- if(!sk->dead)
- wake_up_interruptible(sk->sleep);
-}
+extern struct proto_ops unix_stream_ops;
+extern struct proto_ops unix_dgram_ops;
-static void def_callback2(struct sock *sk, int len)
+static int unix_create(struct socket *sock, int protocol)
{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 1);
- }
-}
+ struct sock *sk;
-static void def_callback3(struct sock *sk)
-{
- if(!sk->dead)
- {
- wake_up_interruptible(sk->sleep);
- sock_wake_async(sk->socket, 2);
- }
-}
+ sock->state = SS_UNCONNECTED;
-static int unix_create(struct socket *sock, int protocol)
-{
- unix_socket *sk;
- if(protocol && protocol != PF_UNIX)
+ if (protocol && protocol != PF_UNIX)
return -EPROTONOSUPPORT;
- sk=(unix_socket *)sk_alloc(GFP_KERNEL);
- if(sk==NULL)
- return -ENOMEM;
- switch(sock->type)
+
+ switch (sock->type)
{
case SOCK_STREAM:
+ sock->ops = &unix_stream_ops;
break;
/*
* Believe it or not BSD has AF_UNIX, SOCK_RAW though
@@ -298,63 +335,56 @@ static int unix_create(struct socket *sock, int protocol)
case SOCK_RAW:
sock->type=SOCK_DGRAM;
case SOCK_DGRAM:
+ sock->ops = &unix_dgram_ops;
break;
default:
- sk_free(sk);
return -ESOCKTNOSUPPORT;
}
- sk->type=sock->type;
- init_timer(&sk->timer);
- skb_queue_head_init(&sk->write_queue);
- skb_queue_head_init(&sk->receive_queue);
- skb_queue_head_init(&sk->back_log);
+ sk = sk_alloc(GFP_KERNEL);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock,sk);
+
sk->protinfo.af_unix.family=AF_UNIX;
sk->protinfo.af_unix.inode=NULL;
- sk->protinfo.af_unix.locks=1; /* Us */
+ sk->sock_readers=1; /* Us */
sk->protinfo.af_unix.readsem=MUTEX; /* single task reading lock */
- sk->rcvbuf=SK_RMEM_MAX;
- sk->sndbuf=SK_WMEM_MAX;
- sk->allocation=GFP_KERNEL;
- sk->state=TCP_CLOSE;
- sk->priority=SOPRI_NORMAL;
- sk->state_change=def_callback1;
- sk->data_ready=def_callback2;
- sk->write_space=def_callback3;
- sk->error_report=def_callback1;
sk->mtu=4096;
- sk->socket=sock;
- sock->data=(void *)sk;
- sk->sleep=sock->wait;
+ sk->protinfo.af_unix.list=&unix_sockets_unbound;
unix_insert_socket(sk);
return 0;
}
static int unix_dup(struct socket *newsock, struct socket *oldsock)
{
- return unix_create(newsock,0);
+ return unix_create(newsock, 0);
}
static int unix_release(struct socket *sock, struct socket *peer)
{
- unix_socket *sk=sock->data;
+ unix_socket *sk = sock->sk;
unix_socket *skpair;
-
- /* May not have data attached */
-
- if(sk==NULL)
+
+ if (!sk)
return 0;
-
+
+ if (sock->state != SS_UNCONNECTED)
+ sock->state = SS_DISCONNECTING;
+
sk->state_change(sk);
sk->dead=1;
- skpair=(unix_socket *)sk->protinfo.af_unix.other; /* Person we send to (default) */
- if(sk->type==SOCK_STREAM && skpair!=NULL && skpair->state!=TCP_LISTEN)
+ skpair=unix_peer(sk);
+ if (sock->type==SOCK_STREAM && skpair)
{
- skpair->shutdown=SHUTDOWN_MASK; /* No more writes */
- skpair->state_change(skpair); /* Wake any blocked writes */
+ if (unix_our_peer(sk, skpair))
+ skpair->shutdown=SHUTDOWN_MASK; /* No more writes */
+ if (skpair->state!=TCP_LISTEN)
+ skpair->state_change(skpair); /* Wake any blocked writes */
}
- if(skpair!=NULL)
- skpair->protinfo.af_unix.locks--; /* It may now die */
- sk->protinfo.af_unix.other=NULL; /* No pair */
+ if (skpair!=NULL)
+ unix_unlock(skpair); /* It may now die */
+ unix_peer(sk)=NULL; /* No pair */
unix_destroy_socket(sk); /* Try to flush out this socket. Throw out buffers at least */
unix_gc(); /* Garbage collect fds */
@@ -362,33 +392,85 @@ static int unix_release(struct socket *sock, struct socket *peer)
* FIXME: BSD difference: In BSD all sockets connected to use get ECONNRESET and we die on the spot. In
* Linux we behave like files and pipes do and wait for the last dereference.
*/
+ if (sk->socket)
+ {
+ sk->socket = NULL;
+ sock->sk = NULL;
+ }
- sock->data = NULL;
- sk->socket = NULL;
-
return 0;
}
+static int unix_autobind(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+ static u32 ordernum = 1;
+ struct unix_address * addr;
+ unix_socket *osk;
+
+ addr = kmalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);
+ if (!addr)
+ return -ENOBUFS;
+ if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+ {
+ kfree(addr);
+ return -EINVAL;
+ }
+ memset(addr, 0, sizeof(*addr) + sizeof(short) + 16);
+ addr->name->sun_family = AF_UNIX;
+ atomic_set(&addr->refcnt, 1);
+
+retry:
+ addr->len = sprintf(addr->name->sun_path+1, "%08x", ordernum) + 1 + sizeof(short);
+ addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
+ ordernum++;
-static unix_socket *unix_find_other(char *path, int *error)
+ if ((osk=unix_find_socket_byname(addr->name, addr->len, sock->type,
+ addr->hash)) != NULL)
+ {
+ unix_unlock(osk);
+ goto retry;
+ }
+
+ sk->protinfo.af_unix.addr = addr;
+ unix_remove_socket(sk);
+ sk->protinfo.af_unix.list = &unix_socket_table[(addr->hash ^ sk->type)&0xF];
+ unix_insert_socket(sk);
+ return 0;
+}
+
+static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
+ int type, unsigned hash, int *error)
{
int old_fs;
int err;
struct inode *inode;
unix_socket *u;
- old_fs=get_fs();
- set_fs(get_ds());
- err = open_namei(path, 2, S_IFSOCK, &inode, NULL);
- set_fs(old_fs);
- if(err<0)
+ if (sunname->sun_path[0])
{
- *error=err;
- return NULL;
+ old_fs=get_fs();
+ set_fs(get_ds());
+ err = open_namei(sunname->sun_path, 2, S_IFSOCK, &inode, NULL);
+ set_fs(old_fs);
+ if(err<0)
+ {
+ *error=err;
+ return NULL;
+ }
+ u=unix_find_socket_byinode(inode);
+ iput(inode);
+ if (u && u->type != type)
+ {
+ *error=-EPROTOTYPE;
+ unix_unlock(u);
+ return NULL;
+ }
}
- u=unix_find_socket(inode);
- iput(inode);
- if(u==NULL)
+ else
+ u=unix_find_socket_byname(sunname, len, type, hash);
+
+ if (u==NULL)
{
*error=-ECONNREFUSED;
return NULL;
@@ -399,173 +481,230 @@ static unix_socket *unix_find_other(char *path, int *error)
static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
+ struct sock *sk = sock->sk;
struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
- unix_socket *sk=sock->data;
+ struct inode * inode;
int old_fs;
int err;
+ unsigned hash;
+ struct unix_address *addr;
- if(sk->protinfo.af_unix.name)
- return -EINVAL; /* Already bound */
-
- if(addr_len>sizeof(struct sockaddr_un) || addr_len<3 || sunaddr->sun_family!=AF_UNIX)
+ if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode ||
+ sunaddr->sun_family != AF_UNIX)
return -EINVAL;
- unix_mkname(sunaddr, addr_len);
- /*
- * Put ourselves in the filesystem
- */
- if(sk->protinfo.af_unix.inode!=NULL)
- return -EINVAL;
-
- sk->protinfo.af_unix.name=kmalloc(addr_len+1, GFP_KERNEL);
- if(sk->protinfo.af_unix.name==NULL)
+
+ if (addr_len==sizeof(short))
+ return unix_autobind(sock);
+
+ addr_len = unix_mkname(sunaddr, addr_len, &hash);
+ if (addr_len < 0)
+ return addr_len;
+
+ addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
+ if (!addr)
return -ENOBUFS;
- memcpy(sk->protinfo.af_unix.name, sunaddr->sun_path, addr_len+1);
+
+ /* We slept; recheck ... */
+
+ if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+ {
+ kfree(addr);
+ return -EINVAL; /* Already bound */
+ }
+
+ memcpy(addr->name, sunaddr, addr_len);
+ addr->len = addr_len;
+ addr->hash = hash;
+ atomic_set(&addr->refcnt, 1);
+
+ if (!sunaddr->sun_path[0])
+ {
+ unix_socket *osk = unix_find_socket_byname(sunaddr, addr_len,
+ sk->type, hash);
+ if (osk)
+ {
+ unix_unlock(osk);
+ kfree(addr);
+ return -EADDRINUSE;
+ }
+ unix_remove_socket(sk);
+ sk->protinfo.af_unix.addr = addr;
+ sk->protinfo.af_unix.list = &unix_socket_table[(hash^sk->type)&0xF];
+ unix_insert_socket(sk);
+ return 0;
+ }
+
+ addr->hash = UNIX_HASH_SIZE;
+ sk->protinfo.af_unix.addr = addr;
old_fs=get_fs();
set_fs(get_ds());
-
- err=do_mknod(sk->protinfo.af_unix.name,S_IFSOCK|S_IRWXUGO,0);
- if(err==0)
- err=open_namei(sk->protinfo.af_unix.name, 2, S_IFSOCK, &sk->protinfo.af_unix.inode, NULL);
+
+ err=do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
+ if (!err)
+ err=open_namei(sunaddr->sun_path, 2, S_IFSOCK, &inode, NULL);
set_fs(old_fs);
if(err<0)
{
- kfree_s(sk->protinfo.af_unix.name,addr_len+1);
- sk->protinfo.af_unix.name=NULL;
- if(err==-EEXIST)
+ unix_release_addr(addr);
+ sk->protinfo.af_unix.addr = NULL;
+ if (err==-EEXIST)
return -EADDRINUSE;
else
return err;
}
-
+ unix_remove_socket(sk);
+ sk->protinfo.af_unix.list = &unix_socket_table[inode->i_ino & 0xF];
+ sk->protinfo.af_unix.inode = inode;
+ unix_insert_socket(sk);
+
return 0;
-
}
-static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
+ int alen, int flags)
{
- unix_socket *sk=sock->data;
- struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
- unix_socket *other;
- struct sk_buff *skb;
+ struct sock *sk = sock->sk;
+ struct sockaddr_un *sunaddr=(struct sockaddr_un*)addr;
+ struct sock *other;
+ unsigned hash;
int err;
-
+
/*
* 1003.1g breaking connected state with AF_UNSPEC
*/
- if(sunaddr->sun_family==AF_UNSPEC)
+ if(addr->sa_family==AF_UNSPEC)
{
- if(sk->protinfo.af_unix.other)
+ if(unix_peer(sk))
{
- sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
- sk->protinfo.af_unix.other=NULL;
+ unix_unlock(unix_peer(sk));
+ unix_peer(sk) = NULL;
sock->state=SS_UNCONNECTED;
}
return 0;
}
- if(sk->type==SOCK_STREAM && sk->protinfo.af_unix.other)
+ alen = unix_mkname(sunaddr, alen, &hash);
+ if (alen < 0)
+ return alen;
+
+ other=unix_find_other(sunaddr, alen, sock->type, hash, &err);
+ if (!other)
+ return err;
+ if (!unix_may_send(sk, other))
+ {
+ unix_unlock(other);
+ return -EINVAL;
+ }
+
+ /*
+ * If it was connected, reconnect.
+ */
+ if (unix_peer(sk))
{
- if(sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED)
+ unix_unlock(unix_peer(sk));
+ unix_peer(sk)=NULL;
+ }
+ unix_peer(sk)=other;
+ if (sock->passcred && !sk->protinfo.af_unix.addr)
+ unix_autobind(sock);
+ return 0;
+}
+
+static int unix_stream_connect1(struct socket *sock, struct msghdr *msg,
+ int len, struct unix_skb_parms *cmsg, int nonblock)
+{
+ struct sockaddr_un *sunaddr=(struct sockaddr_un *)msg->msg_name;
+ struct sock *sk = sock->sk;
+ unix_socket *other;
+ struct sk_buff *skb;
+ int err;
+ unsigned hash;
+ int addr_len;
+
+ addr_len = unix_mkname(sunaddr, msg->msg_namelen, &hash);
+ if (addr_len < 0)
+ return addr_len;
+
+ switch (sock->state)
+ {
+ case SS_UNCONNECTED:
+ /* This is ok... continue with connect */
+ break;
+ case SS_CONNECTED:
+ /* Socket is already connected */
+ return -EISCONN;
+ case SS_CONNECTING:
+ /* Not yet connected... we will check this. */
+ break;
+ default:
+ return(-EINVAL);
+ }
+
+
+ if (unix_peer(sk))
+ {
+ if (sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED)
{
sock->state=SS_CONNECTED;
+ if (!sk->protinfo.af_unix.addr)
+ unix_autobind(sock);
return 0;
}
- if(sock->state==SS_CONNECTING && sk->state == TCP_CLOSE)
+ if (sock->state==SS_CONNECTING && sk->state == TCP_CLOSE)
{
sock->state=SS_UNCONNECTED;
return -ECONNREFUSED;
}
- if(sock->state!=SS_CONNECTING)
+ if (sock->state!=SS_CONNECTING)
return -EISCONN;
- if(flags&O_NONBLOCK)
+ if (nonblock)
return -EALREADY;
/*
* Drop through the connect up logic to the wait.
*/
}
-
- if(addr_len < sizeof(sunaddr->sun_family)+1 || sunaddr->sun_family!=AF_UNIX)
- return -EINVAL;
-
- unix_mkname(sunaddr, addr_len);
-
- if(sk->type==SOCK_DGRAM)
- {
- if(sk->protinfo.af_unix.other)
- {
- sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
- sk->protinfo.af_unix.other=NULL;
- sock->state=SS_UNCONNECTED;
- }
- other=unix_find_other(sunaddr->sun_path, &err);
- if(other==NULL)
- return err;
- if(other->type!=sk->type)
- return -EPROTOTYPE;
- other->protinfo.af_unix.locks++;
- sk->protinfo.af_unix.other=other;
- sock->state=SS_CONNECTED;
- sk->state=TCP_ESTABLISHED;
- return 0; /* Done */
- }
-
- if(sock->state==SS_UNCONNECTED)
+ if (sock->state==SS_UNCONNECTED)
{
/*
* Now ready to connect
*/
- skb=sock_alloc_send_skb(sk, 0, 0, 0, &err); /* Marker object */
+ skb=sock_alloc_send_skb(sk, len, 0, nonblock, &err); /* Marker object */
if(skb==NULL)
return err;
- skb->sk=sk; /* So they know it is us */
- skb->free=1;
- skb->h.filp=NULL;
+ memcpy(&UNIXCB(skb), cmsg, sizeof(*cmsg));
+ if (len)
+ memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
sk->state=TCP_CLOSE;
- unix_mkname(sunaddr, addr_len);
- other=unix_find_other(sunaddr->sun_path, &err);
+ other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
if(other==NULL)
{
kfree_skb(skb, FREE_WRITE);
return err;
}
- if(other->type!=sk->type)
- {
- kfree_skb(skb, FREE_WRITE);
- return -EPROTOTYPE;
- }
- other->protinfo.af_unix.locks++; /* Lock the other socket so it doesn't run off for a moment */
other->ack_backlog++;
- sk->protinfo.af_unix.other=other;
+ unix_peer(sk)=other;
skb_queue_tail(&other->receive_queue,skb);
sk->state=TCP_SYN_SENT;
sock->state=SS_CONNECTING;
- sti();
other->data_ready(other,0); /* Wake up ! */
}
/* Wait for an accept */
- cli();
while(sk->state==TCP_SYN_SENT)
{
- if(flags&O_NONBLOCK)
- {
- sti();
+ if(nonblock)
return -EINPROGRESS;
- }
interruptible_sleep_on(sk->sleep);
if(current->signal & ~current->blocked)
- {
- sti();
return -ERESTARTSYS;
- }
}
/*
@@ -574,10 +713,9 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
if(sk->state==TCP_CLOSE)
{
- sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
- sk->protinfo.af_unix.other=NULL;
+ unix_unlock(unix_peer(sk));
+ unix_peer(sk)=NULL;
sock->state=SS_UNCONNECTED;
- sti();
return -ECONNREFUSED;
}
@@ -586,315 +724,296 @@ static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
*/
sock->state=SS_CONNECTED;
- sti();
+ if (!sk->protinfo.af_unix.addr)
+ unix_autobind(sock);
return 0;
-
}
-static int unix_socketpair(struct socket *a, struct socket *b)
+
+static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
{
- unix_socket *ska,*skb;
-
- ska=a->data;
- skb=b->data;
+ struct msghdr msg;
+ struct unix_skb_parms cmsg;
+
+ msg.msg_name = uaddr;
+ msg.msg_namelen = addr_len;
+ cmsg.fp = NULL;
+ cmsg.attr = MSG_SYN;
+ cmsg.creds.pid = current->pid;
+ cmsg.creds.uid = current->euid;
+ cmsg.creds.gid = current->egid;
+
+ return unix_stream_connect1(sock, &msg, 0, &cmsg, flags&O_NONBLOCK);
+}
+
+static int unix_socketpair(struct socket *socka, struct socket *sockb)
+{
+ struct sock *ska=socka->sk, *skb = sockb->sk;
/* Join our sockets back to back */
- ska->protinfo.af_unix.locks++;
- skb->protinfo.af_unix.locks++;
- ska->protinfo.af_unix.other=skb;
- skb->protinfo.af_unix.other=ska;
- ska->state=TCP_ESTABLISHED;
- skb->state=TCP_ESTABLISHED;
+ unix_lock(ska);
+ unix_lock(skb);
+ unix_peer(ska)=skb;
+ unix_peer(skb)=ska;
+
+ if (ska->type != SOCK_DGRAM)
+ {
+ ska->state=TCP_ESTABLISHED;
+ skb->state=TCP_ESTABLISHED;
+ socka->state=SS_CONNECTED;
+ sockb->state=SS_CONNECTED;
+ }
return 0;
}
static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
{
- unix_socket *sk=sock->data;
- unix_socket *newsk, *tsk;
+ unix_socket *sk = sock->sk;
+ unix_socket *newsk = newsock->sk;
+ unix_socket *tsk;
struct sk_buff *skb;
- if(sk->type!=SOCK_STREAM)
- {
+ if (sock->state != SS_UNCONNECTED)
+ return(-EINVAL);
+ if (!(sock->flags & SO_ACCEPTCON))
+ return(-EINVAL);
+
+ if (sock->type!=SOCK_STREAM)
return -EOPNOTSUPP;
- }
- if(sk->state!=TCP_LISTEN)
- {
+ if (sk->state!=TCP_LISTEN)
return -EINVAL;
- }
- newsk=newsock->data;
- if(sk->protinfo.af_unix.name!=NULL)
+ if (sk->protinfo.af_unix.addr)
{
- newsk->protinfo.af_unix.name=kmalloc(strlen(sk->protinfo.af_unix.name)+1, GFP_KERNEL);
- if(newsk->protinfo.af_unix.name==NULL)
- return -ENOMEM;
- strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name);
+ atomic_inc(&sk->protinfo.af_unix.addr->refcnt);
+ newsk->protinfo.af_unix.addr=sk->protinfo.af_unix.addr;
+ }
+ if (sk->protinfo.af_unix.inode)
+ {
+ sk->protinfo.af_unix.inode->i_count++;
+ newsk->protinfo.af_unix.inode=sk->protinfo.af_unix.inode;
}
- do
+ for (;;)
{
- cli();
skb=skb_dequeue(&sk->receive_queue);
if(skb==NULL)
{
if(flags&O_NONBLOCK)
- {
- sti();
return -EAGAIN;
- }
interruptible_sleep_on(sk->sleep);
if(current->signal & ~current->blocked)
- {
- sti();
return -ERESTARTSYS;
- }
- sti();
+ continue;
+ }
+ if (!(UNIXCB(skb).attr & MSG_SYN))
+ {
+ tsk=skb->sk;
+ tsk->state_change(tsk);
+ kfree_skb(skb, FREE_WRITE);
+ continue;
}
+ break;
}
- while(skb==NULL);
+
tsk=skb->sk;
- kfree_skb(skb, FREE_WRITE); /* The buffer is just used as a tag */
sk->ack_backlog--;
- newsk->protinfo.af_unix.other=tsk;
- tsk->protinfo.af_unix.other=newsk;
+ unix_peer(newsk)=tsk;
+ unix_peer(tsk)=newsk;
tsk->state=TCP_ESTABLISHED;
newsk->state=TCP_ESTABLISHED;
- newsk->protinfo.af_unix.locks++; /* Swap lock over */
- sk->protinfo.af_unix.locks--; /* Locked to child socket not master */
- tsk->protinfo.af_unix.locks++; /* Back lock */
- sti();
+ memcpy(&newsk->peercred, UNIXCREDS(skb), sizeof(struct ucred));
+ tsk->peercred.pid = current->pid;
+ tsk->peercred.uid = current->euid;
+ tsk->peercred.gid = current->egid;
+ unix_lock(newsk); /* Swap lock over */
+ unix_unlock(sk); /* Locked to child socket not master */
+ unix_lock(tsk); /* Back lock */
+ kfree_skb(skb, FREE_WRITE); /* The buffer is just used as a tag */
tsk->state_change(tsk); /* Wake up any sleeping connect */
sock_wake_async(tsk->socket, 0);
return 0;
}
+
static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
- unix_socket *sk=sock->data;
+ struct sock *sk = sock->sk;
struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
- if(peer)
+ if (peer)
{
- if(sk->protinfo.af_unix.other==NULL)
+ if (!unix_peer(sk))
return -ENOTCONN;
- sk=sk->protinfo.af_unix.other;
+ sk=unix_peer(sk);
}
- sunaddr->sun_family=AF_UNIX;
- if(sk->protinfo.af_unix.name==NULL)
+ if (!sk->protinfo.af_unix.addr)
{
- *sunaddr->sun_path=0;
- *uaddr_len=sizeof(sunaddr->sun_family)+1;
+ sunaddr->sun_family = AF_UNIX;
+ sunaddr->sun_path[0] = 0;
+ *uaddr_len = sizeof(short);
return 0; /* Not bound */
}
- *uaddr_len=sizeof(sunaddr->sun_family)+strlen(sk->protinfo.af_unix.name)+1;
- strcpy(sunaddr->sun_path,sk->protinfo.af_unix.name); /* 108 byte limited */
+ *uaddr_len = sk->protinfo.af_unix.addr->len;
+ memcpy(sunaddr, sk->protinfo.af_unix.addr->name, *uaddr_len);
return 0;
}
-/*
- * Copy file descriptors into system space.
- * Return number copied or negative error code
- */
-
-static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp)
+static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
- int num=cmsg->cmsg_len-sizeof(struct cmsghdr);
int i;
- int *fdp=(int *)cmsg->cmsg_data;
- num /= sizeof(int); /* Odd bytes are forgotten in BSD not errored */
- if (num >= UNIX_MAX_FD)
- return -EINVAL;
-
- /*
- * Verify the descriptors.
- */
-
- for(i=0; i< num; i++)
- {
- int fd;
-
- fd = fdp[i];
- if (fd < 0 || fd >= NR_OPEN)
- return -EBADF;
- if (current->files->fd[fd]==NULL)
- return -EBADF;
- }
-
- /* add another reference to these files */
- for(i=0; i< num; i++)
- {
- fp[i]=current->files->fd[fdp[i]];
- fp[i]->f_count++;
- unix_inflight(fp[i]);
- }
-
- return num;
+ scm->fp = UNIXCB(skb).fp;
+ skb->destructor = sock_wfree;
+ UNIXCB(skb).fp = NULL;
+
+ for (i=scm->fp->count-1; i>=0; i--)
+ unix_notinflight(scm->fp->fp[i]);
}
-/*
- * Free the descriptors in the array
- */
+static void unix_destruct_fds(struct sk_buff *skb)
+{
+ struct scm_cookie scm;
+ memset(&scm, 0, sizeof(scm));
+ unix_detach_fds(&scm, skb);
+ scm_destroy(&scm);
+ sock_wfree(skb);
+}
-static void unix_fd_free(struct sock *sk, struct file **fp, int num)
+static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
{
int i;
- for(i=0;i<num;i++)
- {
- close_fp(fp[i]);
- unix_notinflight(fp[i]);
- }
+ for (i=scm->fp->count-1; i>=0; i--)
+ unix_inflight(scm->fp->fp[i]);
+ UNIXCB(skb).fp = scm->fp;
+ skb->destructor = unix_destruct_fds;
+ scm->fp = NULL;
}
/*
- * Perform the AF_UNIX file descriptor pass out functionality. This
- * is nasty and messy as is the whole design of BSD file passing.
+ * Send AF_UNIX data.
*/
-static void unix_detach_fds(struct sk_buff *skb, struct cmsghdr *cmsg)
+static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
{
- int i;
- /* count of space in parent for fds */
- int cmnum;
- struct file **fp;
- int *cmfptr;
- int fdnum;
-
- cmfptr = NULL;
- cmnum = 0;
- if (cmsg)
- {
- cmnum = (cmsg->cmsg_len-sizeof(struct cmsghdr)) / sizeof(int);
- cmfptr = (int *)&cmsg->cmsg_data;
+ struct sock *sk = sock->sk;
+ unix_socket *other;
+ struct sockaddr_un *sunaddr=msg->msg_name;
+ int namelen = 0; /* fake GCC */
+ int err;
+ unsigned hash;
+ struct sk_buff *skb;
+
+ if (msg->msg_flags&MSG_OOB)
+ return -EOPNOTSUPP;
+
+ if (msg->msg_flags&~MSG_DONTWAIT)
+ return -EINVAL;
+
+ if (msg->msg_namelen) {
+ namelen = unix_mkname(sunaddr, msg->msg_namelen, &hash);
+ if (namelen < 0)
+ return namelen;
+ } else {
+ sunaddr = NULL;
+ if (!unix_peer(sk))
+ return -ENOTCONN;
}
-
- fdnum = *(int *)skb->h.filp;
- fp = (struct file **)(skb->h.filp+sizeof(long));
- if (cmnum > fdnum)
- cmnum = fdnum;
+ if (sock->passcred && !sk->protinfo.af_unix.addr)
+ unix_autobind(sock);
- /*
- * Copy those that fit
- */
- for (i = 0 ; i < cmnum ; i++)
+ skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err);
+
+ if (skb==NULL)
+ return err;
+
+ memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
+ UNIXCB(skb).attr = msg->msg_flags;
+ if (scm->fp)
+ unix_attach_fds(scm, skb);
+
+ skb->h.raw = skb->data;
+ memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+
+ other = unix_peer(sk);
+ if (other && other->dead)
{
- int new_fd = get_unused_fd();
- if (new_fd < 0)
- break;
- current->files->fd[new_fd]=fp[i];
- *cmfptr++ = new_fd;
- unix_notinflight(fp[i]);
+ /*
+ * Check with 1003.1g - what should
+ * datagram error
+ */
+ unix_unlock(other);
+ unix_peer(sk)=NULL;
+ other = NULL;
+ if (sunaddr == NULL) {
+ kfree_skb(skb, FREE_WRITE);
+ return -ECONNRESET;
+ }
}
- /*
- * Dump those that don't
- */
- for( ; i < fdnum ; i++)
+ if (!other)
{
- close_fp(fp[i]);
- unix_notinflight(fp[i]);
+ other = unix_find_other(sunaddr, namelen, sk->type, hash, &err);
+
+ if (other==NULL)
+ {
+ kfree_skb(skb, FREE_WRITE);
+ return err;
+ }
+ if (!unix_may_send(sk, other))
+ {
+ unix_unlock(other);
+ kfree_skb(skb, FREE_WRITE);
+ return -EINVAL;
+ }
}
- kfree(skb->h.filp);
- skb->h.filp=NULL;
- /* no need to use destructor */
- skb->destructor = NULL;
-}
-
-static void unix_destruct_fds(struct sk_buff *skb)
-{
- unix_detach_fds(skb,NULL);
-}
+ skb_queue_tail(&other->receive_queue, skb);
+ other->data_ready(other,len);
-/*
- * Attach the file descriptor array to an sk_buff
- */
-static void unix_attach_fds(int fpnum,struct file **fp,struct sk_buff *skb)
-{
-
- skb->h.filp = kmalloc(sizeof(long)+fpnum*sizeof(struct file *),
- GFP_KERNEL);
- /* number of descriptors starts block */
- *(int *)skb->h.filp = fpnum;
- /* actual descriptors */
- memcpy(skb->h.filp+sizeof(long),fp,fpnum*sizeof(struct file *));
- skb->destructor = unix_destruct_fds;
+ if (!unix_peer(sk))
+ unix_unlock(other);
+ return len;
}
-/*
- * Send AF_UNIX data.
- */
-static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+ struct scm_cookie *scm)
{
- unix_socket *sk=sock->data;
+ struct sock *sk = sock->sk;
unix_socket *other;
struct sockaddr_un *sunaddr=msg->msg_name;
int err,size;
struct sk_buff *skb;
int limit=0;
int sent=0;
- struct file *fp[UNIX_MAX_FD];
- /* number of fds waiting to be passed, 0 means either
- * no fds to pass or they've already been passed
- */
- int fpnum=0;
- if(sk->err)
- return sock_error(sk);
+ if (sock->flags & SO_ACCEPTCON)
+ return(-EINVAL);
- if(flags&MSG_OOB)
+ if (msg->msg_flags&MSG_OOB)
return -EOPNOTSUPP;
-
- if(flags) /* For now */ {
+
+ if (msg->msg_flags&~MSG_DONTWAIT)
return -EINVAL;
- }
-
- if(sk->shutdown&SEND_SHUTDOWN)
- {
- send_sig(SIGPIPE,current,0);
- return -EPIPE;
- }
-
- if(sunaddr!=NULL)
- {
- if(sock->type==SOCK_STREAM)
- {
- if(sk->state==TCP_ESTABLISHED)
- return -EISCONN;
- else
- return -EOPNOTSUPP;
- }
- }
- if(sunaddr==NULL)
- {
- if(sk->protinfo.af_unix.other==NULL)
+ if (msg->msg_namelen) {
+ if (sk->state==TCP_ESTABLISHED)
+ return -EISCONN;
+ else
+ return -EOPNOTSUPP;
+ } else {
+ sunaddr = NULL;
+ if (!unix_peer(sk))
return -ENOTCONN;
}
- /*
- * A control message has been attached.
- */
- if(msg->msg_control)
- {
- struct cmsghdr *cm = msg->msg_control;
-
- if(cm==NULL || msg->msg_controllen<sizeof(struct cmsghdr) ||
- cm->cmsg_type!=SCM_RIGHTS ||
- cm->cmsg_level!=SOL_SOCKET ||
- msg->msg_controllen!=cm->cmsg_len)
- {
- return -EINVAL;
- }
-
- fpnum = unix_fd_copy(sk, cm, fp);
-
- if(fpnum<0) {
- return fpnum;
- }
+ if (sk->shutdown&SEND_SHUTDOWN) {
+ send_sig(SIGPIPE,current,0);
+ return -EPIPE;
}
while(sent < len)
@@ -906,23 +1025,17 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
size=len-sent;
- if(size>(sk->sndbuf-sizeof(struct sk_buff))/2) /* Keep two messages in the pipe so it schedules better */
- {
- if(sock->type==SOCK_DGRAM)
- {
- unix_fd_free(sk,fp,fpnum);
- return -EMSGSIZE;
- }
+ if (size>(sk->sndbuf-sizeof(struct sk_buff))/2) /* Keep two messages in the pipe so it schedules better */
size=(sk->sndbuf-sizeof(struct sk_buff))/2;
- }
+
/*
* Keep to page sized kmalloc()'s as various people
* have suggested. Big mallocs stress the vm too
* much.
*/
-#define MAX_ALLOC (PAGE_SIZE*7/8)
- if(size > MAX_ALLOC && sock->type!=SOCK_DGRAM)
- limit = MAX_ALLOC; /* Fall back to 4K if we can't grab a big buffer this instant */
+
+ if (size > 3500)
+ limit = 3500; /* Fall back to a page if we can't grab a big buffer this instant */
else
limit = 0; /* Otherwise just grab and wait */
@@ -930,16 +1043,12 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
* Grab a buffer
*/
- skb=sock_alloc_send_skb(sk,size,limit,nonblock, &err);
+ skb=sock_alloc_send_skb(sk,size,limit,msg->msg_flags&MSG_DONTWAIT, &err);
- if(skb==NULL)
+ if (skb==NULL)
{
- unix_fd_free(sk,fp,fpnum);
- if(sent)
- {
- sk->err=-err;
+ if (sent)
return sent;
- }
return err;
}
@@ -952,70 +1061,25 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
*/
size = min(size, skb_tailroom(skb));
- skb->sk=sk;
- skb->free=1;
-
- if(fpnum)
- {
- unix_attach_fds(fpnum,fp,skb);
- fpnum=0;
- }
- else
- skb->h.filp=NULL;
+ memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
+ UNIXCB(skb).attr = msg->msg_flags;
+ if (scm->fp)
+ unix_attach_fds(scm, skb);
- memcpy_fromiovec(skb_put(skb,size),msg->msg_iov, size);
+ memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size);
- cli();
- if(sunaddr==NULL)
+ other=unix_peer(sk);
+
+ if (other->dead || (sk->shutdown & SEND_SHUTDOWN))
{
- other=sk->protinfo.af_unix.other;
- if(sock->type==SOCK_DGRAM && other->dead)
- {
- other->protinfo.af_unix.locks--;
- sk->protinfo.af_unix.other=NULL;
- sock->state=SS_UNCONNECTED;
- sti();
- kfree_skb(skb, FREE_WRITE);
- /*
- * Check with 1003.1g - what should
- * datagram error
- */
- if (!sent)
- sent = -ECONNRESET;
- return sent;
- }
- /*
- * Stream sockets SIGPIPE
- */
- if(sock->type==SOCK_STREAM && other->dead)
- {
- kfree_skb(skb, FREE_WRITE);
- sti();
- if(!sent)
- {
- send_sig(SIGPIPE,current,0);
- sent = -EPIPE;
- }
+ kfree_skb(skb, FREE_WRITE);
+ if(sent)
return sent;
- }
- }
- else
- {
- unix_mkname(sunaddr, msg->msg_namelen);
- other=unix_find_other(sunaddr->sun_path, &err);
- if(other==NULL)
- {
- sti();
- kfree_skb(skb, FREE_WRITE);
- if(sent)
- return sent;
- else
- return err;
- }
+ send_sig(SIGPIPE,current,0);
+ return -EPIPE;
}
+
skb_queue_tail(&other->receive_queue, skb);
- sti();
- /* if we sent an fd, only do it once */
other->data_ready(other,size);
sent+=size;
}
@@ -1028,173 +1092,229 @@ static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int no
static void unix_data_wait(unix_socket * sk)
{
- /*
- * AF_UNIX sockets get no messages during interrupts, so this
- * is safe without cli/sti.
- */
- if (!skb_peek(&sk->receive_queue)) {
+ if (!skb_peek(&sk->receive_queue))
+ {
sk->socket->flags |= SO_WAITDATA;
interruptible_sleep_on(sk->sleep);
sk->socket->flags &= ~SO_WAITDATA;
}
}
-static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
{
- unix_socket *sk=sock->data;
- struct sockaddr_un *sunaddr=msg->msg_name;
+ struct sock *sk = sock->sk;
+ int noblock = flags & MSG_DONTWAIT;
struct sk_buff *skb;
- int copied=0;
- unsigned char *sp;
- int len;
- int num;
- struct iovec *iov=msg->msg_iov;
- struct cmsghdr *cm=NULL;
- int ct=msg->msg_iovlen;
- int err = 0;
+ int err;
+
+ if (flags&MSG_OOB)
+ return -EOPNOTSUPP;
+
+ msg->msg_namelen = 0;
+
+ skb=skb_recv_datagram(sk, flags, noblock, &err);
+ if(skb==NULL)
+ return err;
+
+ if (msg->msg_name)
+ {
+ if (skb->sk->protinfo.af_unix.addr)
+ {
+ memcpy(msg->msg_name, skb->sk->protinfo.af_unix.addr->name,
+ skb->sk->protinfo.af_unix.addr->len);
+ msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
+ }
+ else
+ msg->msg_namelen=sizeof(short);
+ }
+
+ if (size > skb->len)
+ size = skb->len;
+ else if (size < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+
+ if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size))
+ return -EFAULT;
+
+ scm->creds = *UNIXCREDS(skb);
+
+ if (!(flags & MSG_PEEK))
+ {
+ if (UNIXCB(skb).fp)
+ unix_detach_fds(scm, skb);
+ }
+ else
+ {
+ /* It is questionable: on PEEK we could:
+ - do not return fds - good, but too simple 8)
+ - return fds, and do not return them on read (old strategy,
+ apparently wrong)
+ - clone fds (I choosed it for now, it is the most universal
+ solution)
+
+ POSIX 1003.1g does not actually define this clearly
+ at all. POSIX 1003.1g doesn't define a lot of things
+ clearly however!
+
+ */
+ if (UNIXCB(skb).fp)
+ scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+ }
+ skb_free_datagram(sk,skb);
+ return size;
+}
+
+
+static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+ int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ int noblock = flags & MSG_DONTWAIT;
+ struct sockaddr_un *sunaddr=msg->msg_name;
+ int copied = 0;
+ int check_creds = 0;
int target = 1;
- if(flags&MSG_OOB)
+ if (sock->flags & SO_ACCEPTCON)
+ return(-EINVAL);
+
+ if (flags&MSG_OOB)
return -EOPNOTSUPP;
if(flags&MSG_WAITALL)
target = size;
- if(addr_len)
- *addr_len=0;
-
- if(msg->msg_control)
+ msg->msg_namelen = 0;
+
+ /* Lock the socket to prevent queue disordering
+ * while sleeps in memcpy_tomsg
+ */
+
+ down(&sk->protinfo.af_unix.readsem);
+
+ do
{
- cm=msg->msg_control;
-
- if(msg->msg_controllen<sizeof(struct cmsghdr)
-#if 0
-/* investigate this further -- Stevens example doesn't seem to care */
- ||
- cm->cmsg_type!=SCM_RIGHTS ||
- cm->cmsg_level!=SOL_SOCKET ||
- msg->msg_controllen!=cm->cmsg_len
-#endif
- )
+ int chunk;
+ struct sk_buff *skb;
+
+ skb=skb_dequeue(&sk->receive_queue);
+ if (skb==NULL)
{
- printk(KERN_DEBUG "unix_recvmsg: Bad msg_control\n");
- return -EINVAL;
+ if (copied >= target)
+ break;
+
+ if (sk->err)
+ return sock_error(sk);
+
+ if (sk->shutdown & RCV_SHUTDOWN)
+ break;
+ up(&sk->protinfo.af_unix.readsem);
+ if (noblock)
+ return -EAGAIN;
+ unix_data_wait(sk);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ down(&sk->protinfo.af_unix.readsem);
+ continue;
}
- }
-
- down(&sk->protinfo.af_unix.readsem); /* Lock the socket */
- while(ct--)
- {
- int done=0;
- sp=iov->iov_base;
- len=iov->iov_len;
- iov++;
-
- while(done<len)
+
+ /* Never glue messages from different writers */
+ if (check_creds &&
+ memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0)
{
- if (copied && (flags & MSG_PEEK))
- goto out;
- if (copied == size)
- goto out;
- skb=skb_dequeue(&sk->receive_queue);
- if(skb==NULL)
- {
- up(&sk->protinfo.af_unix.readsem);
-
- if(copied >= target)
- return copied;
-
- /*
- * POSIX checking order...
- */
-
- if(sk->err)
- return sock_error(sk);
- if(sk->shutdown & RCV_SHUTDOWN)
- return copied;
-
- if(current->signal & ~current->blocked)
- return -ERESTARTSYS;
- if(noblock)
- return -EAGAIN;
-
- unix_data_wait(sk);
- down(&sk->protinfo.af_unix.readsem);
- continue;
- }
- if(msg->msg_name!=NULL)
- {
- sunaddr->sun_family=AF_UNIX;
- if(skb->sk->protinfo.af_unix.name)
- {
- memcpy(sunaddr->sun_path, skb->sk->protinfo.af_unix.name, 108);
- if(addr_len)
- *addr_len=strlen(sunaddr->sun_path)+sizeof(short);
- }
- else
- if(addr_len)
- *addr_len=sizeof(short);
- }
+ skb_queue_head(&sk->receive_queue, skb);
+ break;
+ }
- num=skb->len;
- if(num>len-done)
+ /* Copy address just once */
+ if (sunaddr)
+ {
+ if (skb->sk->protinfo.af_unix.addr)
{
- num=len-done;
- msg->msg_flags|=MSG_TRUNC;
+ memcpy(sunaddr, skb->sk->protinfo.af_unix.addr->name,
+ skb->sk->protinfo.af_unix.addr->len);
+ msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
}
- err = copy_to_user(sp, skb->data, num);
+ else
+ msg->msg_namelen=sizeof(short);
+ sunaddr = NULL;
+ }
+
+ chunk = min(skb->len, size);
+ memcpy_toiovec(msg->msg_iov, skb->data, chunk);
+ copied += chunk;
+ size -= chunk;
+
+ /* Copy credentials */
+ scm->creds = *UNIXCREDS(skb);
+ check_creds = 1;
+
+ /* Mark read part of skb as used */
+ if (!(flags & MSG_PEEK))
+ {
+ skb_pull(skb, chunk);
+
+ if (UNIXCB(skb).fp)
+ unix_detach_fds(scm, skb);
- if (err)
- {
- goto out;
- }
-
- if (skb->h.filp!=NULL)
- unix_detach_fds(skb,cm);
-
- copied+=num;
- done+=num;
- sp+=num;
- if (!(flags & MSG_PEEK))
- skb_pull(skb, num);
/* put the skb back if we didn't use it up.. */
- if (skb->len) {
+ if (skb->len)
+ {
skb_queue_head(&sk->receive_queue, skb);
- continue;
+ break;
}
+
kfree_skb(skb, FREE_WRITE);
- if(sock->type==SOCK_DGRAM || cm)
- goto out;
+
+ if (scm->fp)
+ break;
}
- }
-out:
- up(&sk->protinfo.af_unix.readsem);
+ else
+ {
+ /* It is questionable, see note in unix_dgram_recvmsg.
+
+ */
+ if (UNIXCB(skb).fp)
+ scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+
+ /* put message back and return */
+ skb_queue_head(&sk->receive_queue, skb);
+ break;
+ }
+ } while (size);
- return err ? -EFAULT : copied;
+ up(&sk->protinfo.af_unix.readsem);
+ return copied;
}
static int unix_shutdown(struct socket *sock, int mode)
{
- unix_socket *sk=(unix_socket *)sock->data;
- unix_socket *other=sk->protinfo.af_unix.other;
- if(mode&SEND_SHUTDOWN)
+ struct sock *sk = sock->sk;
+ unix_socket *other=unix_peer(sk);
+
+ mode++;
+
+ if (mode&SEND_SHUTDOWN)
{
sk->shutdown|=SEND_SHUTDOWN;
sk->state_change(sk);
- if(other)
+ if(other && sk->type == SOCK_STREAM && other->state != TCP_LISTEN)
{
- other->shutdown|=RCV_SHUTDOWN;
+ if (unix_our_peer(sk, other))
+ other->shutdown|=RCV_SHUTDOWN;
other->state_change(other);
}
}
- other=sk->protinfo.af_unix.other;
+ other=unix_peer(sk);
if(mode&RCV_SHUTDOWN)
{
sk->shutdown|=RCV_SHUTDOWN;
sk->state_change(sk);
- if(other)
+ if(other && sk->type != SOCK_DGRAM && other->state != TCP_LISTEN)
{
- other->shutdown|=SEND_SHUTDOWN;
+ if (unix_our_peer(sk, other))
+ other->shutdown|=SEND_SHUTDOWN;
other->state_change(other);
}
}
@@ -1202,21 +1322,16 @@ static int unix_shutdown(struct socket *sock, int mode)
}
-static int unix_select(struct socket *sock, int sel_type, select_table *wait)
-{
- return datagram_select(sock->data,sel_type,wait);
-}
-
static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- unix_socket *sk=sock->data;
+ struct sock *sk = sock->sk;
long amount=0;
switch(cmd)
{
case TIOCOUTQ:
- amount=sk->sndbuf-sk->wmem_alloc;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if(amount<0)
amount=0;
return put_user(amount, (int *)arg);
@@ -1226,7 +1341,7 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
if(sk->state==TCP_LISTEN)
return -EINVAL;
/*
- * These two are safe on a single CPU system as
+ * These two are safe on current systems as
* only user tasks fiddle here
*/
if((skb=skb_peek(&sk->receive_queue))!=NULL)
@@ -1242,30 +1357,41 @@ static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
#ifdef CONFIG_PROC_FS
-static int unix_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+static int unix_read_proc(char *buffer, char **start, off_t offset,
+ int length, int *eof, void *data)
{
off_t pos=0;
off_t begin=0;
int len=0;
- unix_socket *s=unix_socket_list;
+ int i;
+ unix_socket *s;
len+= sprintf(buffer,"Num RefCount Protocol Flags Type St "
"Inode Path\n");
- while(s!=NULL)
+ forall_unix_sockets (i,s)
{
len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld",
s,
- s->protinfo.af_unix.locks,
+ s->sock_readers,
0,
- s->socket->flags,
- s->socket->type,
- s->socket->state,
- s->socket->inode ? s->socket->inode->i_ino : 0);
- if(s->protinfo.af_unix.name!=NULL)
- len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name);
- else
- buffer[len++]='\n';
+ s->socket ? s->socket->flags : 0,
+ s->type,
+ s->socket ? s->socket->state : 0,
+ s->socket ? s->socket->inode->i_ino : 0);
+
+ if (s->protinfo.af_unix.addr)
+ {
+ buffer[len++] = ' ';
+ memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path,
+ s->protinfo.af_unix.addr->len-sizeof(short));
+ if (!UNIX_ABSTRACT(s))
+ len--;
+ else
+ buffer[len] = '@';
+ len += s->protinfo.af_unix.addr->len - sizeof(short);
+ }
+ buffer[len++]='\n';
pos=begin+len;
if(pos<offset)
@@ -1274,9 +1400,10 @@ static int unix_get_info(char *buffer, char **start, off_t offset, int length, i
begin=pos;
}
if(pos>offset+length)
- break;
- s=s->next;
+ goto done;
}
+ *eof = 1;
+done:
*start=buffer+(offset-begin);
len-=(offset-begin);
if(len>length)
@@ -1285,43 +1412,68 @@ static int unix_get_info(char *buffer, char **start, off_t offset, int length, i
}
#endif
-struct proto_ops unix_proto_ops = {
+struct proto_ops unix_stream_ops = {
AF_UNIX,
- unix_create,
unix_dup,
unix_release,
unix_bind,
- unix_connect,
+ unix_stream_connect,
unix_socketpair,
unix_accept,
unix_getname,
- unix_select,
+ datagram_poll,
unix_ioctl,
unix_listen,
unix_shutdown,
- unix_setsockopt,
- unix_getsockopt,
- unix_fcntl,
- unix_sendmsg,
- unix_recvmsg
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
+ unix_stream_sendmsg,
+ unix_stream_recvmsg
};
-#ifdef CONFIG_PROC_FS
-static struct proc_dir_entry proc_net_unix = {
- PROC_NET_UNIX, 4, "unix",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- unix_get_info
+struct proto_ops unix_dgram_ops = {
+ AF_UNIX,
+
+ unix_dup,
+ unix_release,
+ unix_bind,
+ unix_dgram_connect,
+ unix_socketpair,
+ NULL,
+ unix_getname,
+ datagram_poll,
+ unix_ioctl,
+ sock_no_listen,
+ unix_shutdown,
+ sock_no_setsockopt,
+ sock_no_getsockopt,
+ sock_no_fcntl,
+ unix_dgram_sendmsg,
+ unix_dgram_recvmsg
+};
+
+struct net_proto_family unix_family_ops = {
+ AF_UNIX,
+ unix_create
};
-#endif
void unix_proto_init(struct net_proto *pro)
{
- printk(KERN_INFO "NET3: Unix domain sockets 0.14 for Linux NET3.037.\n");
- sock_register(unix_proto_ops.family, &unix_proto_ops);
+ struct sk_buff *dummy_skb;
+ struct proc_dir_entry *ent;
+
+ printk(KERN_INFO "NET3: Unix domain sockets 0.16 for Linux NET3.038.\n");
+ if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
+ {
+ printk(KERN_CRIT "unix_proto_init: panic\n");
+ return;
+ }
+ sock_register(&unix_family_ops);
#ifdef CONFIG_PROC_FS
- proc_net_register(&proc_net_unix);
+ ent = create_proc_entry("net/unix", 0, 0);
+ ent->read_proc = unix_read_proc;
#endif
}
/*
diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index c53c4d4e6..2a10304df 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -56,6 +56,7 @@
#include <net/tcp.h>
#include <net/af_unix.h>
#include <linux/proc_fs.h>
+#include <net/scm.h>
/* Internal data structures and random procedures: */
@@ -73,13 +74,14 @@ extern inline unix_socket *unix_get_socket(struct file *filp)
* Socket ?
*/
if (inode && inode->i_sock) {
- struct socket * s = &inode->u.socket_i;
+ struct socket * sock = &inode->u.socket_i;
+ struct sock * s = sock->sk;
/*
* AF_UNIX ?
*/
- if (s->ops == &unix_proto_ops)
- u_sock = s->data;
+ if (s && sock->ops && sock->ops->family == AF_UNIX)
+ u_sock = s;
}
return u_sock;
}
@@ -141,6 +143,7 @@ extern inline void maybe_mark_and_push(unix_socket *x)
void unix_gc(void)
{
static int in_unix_gc=0;
+ int i;
unix_socket *s;
unix_socket *next;
@@ -171,7 +174,7 @@ void unix_gc(void)
* Push root set
*/
- for(s=unix_socket_list;s!=NULL;s=s->next)
+ forall_unix_sockets(i, s)
{
/*
* If all instances of the descriptor are not
@@ -202,13 +205,13 @@ tail:
/*
* Do we have file descriptors ?
*/
- if(skb->h.filp)
+ if(UNIXCB(skb).fp)
{
/*
* Process the descriptors of this socket
*/
- int nfd=*(int *)skb->h.filp;
- struct file **fp=(struct file **)(skb->h.filp+sizeof(int));
+ int nfd=UNIXCB(skb).fp->count;
+ struct file **fp = UNIXCB(skb).fp->fp;
while(nfd--)
{
/*
@@ -250,7 +253,7 @@ tail:
* Sweep phase. NOTE: this part dominates the time complexity
*/
- for(s=unix_socket_list;s!=NULL;s=next)
+ forall_unix_sockets(i, s)
{
next=s->next;
if (!(s->protinfo.af_unix.marksweep&MARKED))
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index b436aabb3..5af2df443 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -1,8 +1,14 @@
-/* -*- linux-c -*-
- * sysctl_net_unix.c: sysctl interface to net af_unix subsystem.
+/*
+ * NET3: Sysctl interface to net af_unix subsystem.
*
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/unix directory entry (empty =) ). [MS]
+ * Authors: Mike Shaver.
+ *
+ * Added /proc/sys/net/unix directory entry (empty =) ).
+ *
+ * This program 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 of the License, or (at your option) any later version.
*/
#include <linux/mm.h>
diff --git a/net/wanrouter/Makefile b/net/wanrouter/Makefile
new file mode 100644
index 000000000..12afaee5d
--- /dev/null
+++ b/net/wanrouter/Makefile
@@ -0,0 +1,17 @@
+#
+# Makefile for the Linux WAN router layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := wanrouter.o
+O_OBJS := wanmain.o wanproc.o
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/wanrouter/patchlevel b/net/wanrouter/patchlevel
new file mode 100644
index 000000000..3eefcb9dd
--- /dev/null
+++ b/net/wanrouter/patchlevel
@@ -0,0 +1 @@
+1.0.0
diff --git a/net/wanrouter/wanmain.c b/net/wanrouter/wanmain.c
new file mode 100644
index 000000000..948bf81fa
--- /dev/null
+++ b/net/wanrouter/wanmain.c
@@ -0,0 +1,676 @@
+/*****************************************************************************
+* wanmain.c WAN Multiprotocol Router Module. Main code.
+*
+* This module is completely hardware-independent and provides
+* the following common services for the WAN Link Drivers:
+* o WAN device managenment (registering, unregistering)
+* o Network interface management
+* o Physical connection management (dial-up, incomming calls)
+* o Logical connection management (switched virtual circuits)
+* o Protocol encapsulation/decapsulation
+*
+* Author: Gene Kozin <genek@compuserve.com>
+*
+* Copyright: (c) 1995-1996 Sangoma Technologies Inc.
+*
+* This program 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 of the License, or (at your option) any later version.
+* ============================================================================
+* Dec 27, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
+* Jan 31, 1997 Alan Cox Hacked it about a bit for 2.1
+*****************************************************************************/
+
+#include <linux/stddef.h> /* offsetof(), etc. */
+#include <linux/errno.h> /* return codes */
+#include <linux/config.h> /* OS configuration options */
+#include <linux/kernel.h>
+#include <linux/module.h> /* support for loadable modules */
+#include <linux/malloc.h> /* kmalloc(), kfree() */
+#include <linux/mm.h> /* verify_area(), etc. */
+#include <linux/string.h> /* inline mem*, str* functions */
+#include <asm/segment.h> /* kernel <-> user copy */
+#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h> /* copy_to/from_user */
+#include <linux/wanrouter.h> /* WAN router API definitions */
+
+/****** Defines and Macros **************************************************/
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/****** Function Prototypes *************************************************/
+
+/*
+ * Kernel loadable module interface.
+ */
+
+#ifdef MODULE
+int init_module (void);
+void cleanup_module (void);
+#endif
+
+/*
+ * WAN device IOCTL handlers
+ */
+
+static int device_setup (wan_device_t* wandev, wandev_conf_t* u_conf);
+static int device_stat (wan_device_t* wandev, wandev_stat_t* u_stat);
+static int device_shutdown (wan_device_t* wandev);
+static int device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf);
+static int device_del_if (wan_device_t* wandev, char* u_name);
+
+/*
+ * Miscellaneous
+ */
+
+static wan_device_t* find_device (char* name);
+static int delete_interface (wan_device_t* wandev, char* name, int forse);
+
+/*
+ * Global Data
+ */
+
+static char fullname[] = "WAN Router";
+static char copyright[] = "(c) 1995-1996 Sangoma Technologies Inc.";
+static char modname[] = ROUTER_NAME; /* short module name */
+static wan_device_t* devlist = NULL; /* list of registered devices */
+static int devcnt = 0;
+
+/*
+ * Organizationally Unique Identifiers for encapsulation/decapsulation
+ */
+
+static unsigned char oui_ether[] = { 0x00, 0x00, 0x00 };
+static unsigned char oui_802_2[] = { 0x00, 0x80, 0xC2 };
+
+#ifdef MODULE
+
+/*
+ * Kernel Loadable Module Entry Points
+ */
+
+/*
+ * Module 'insert' entry point.
+ * o print announcement
+ * o initialize static data
+ * o create /proc/net/router directory and static entries
+ *
+ * Return: 0 Ok
+ * < 0 error.
+ * Context: process
+ */
+
+int init_module (void)
+{
+ int err;
+
+ printk(KERN_INFO "%s v%u.%u %s\n",
+ fullname, ROUTER_VERSION, ROUTER_RELEASE, copyright);
+ err = wanrouter_proc_init();
+ if (err) printk(KERN_ERR
+ "%s: can't create entry in proc filesystem!\n", modname);
+ return err;
+}
+
+/*
+ * Module 'remove' entry point.
+ * o delete /proc/net/router directory and static entries.
+ */
+
+void cleanup_module (void)
+{
+ wanrouter_proc_cleanup();
+}
+
+#else
+
+void wanrouter_init(void)
+{
+ int err = wanrouter_proc_init();
+ if (err) printk(KERN_ERR
+ "%s: can't create entry in proc filesystem!\n", modname);
+}
+#endif
+
+/*
+ * Kernel APIs
+ */
+
+/*
+ * Register WAN device.
+ * o verify device credentials
+ * o create an entry for the device in the /proc/net/router directory
+ * o initialize internally maintained fields of the wan_device structure
+ * o link device data space to a singly-linked list
+ * o if it's the first device, then start kernel 'thread'
+ * o increment module use count
+ *
+ * Return:
+ * 0 Ok
+ * < 0 error.
+ *
+ * Context: process
+ */
+
+int register_wan_device(wan_device_t* wandev)
+{
+ int err, namelen;
+
+ if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC) ||
+ (wandev->name == NULL))
+ return -EINVAL;
+
+ namelen = strlen(wandev->name);
+ if (!namelen || (namelen > WAN_DRVNAME_SZ))
+ return -EINVAL;
+
+ if (find_device(wandev->name) != NULL)
+ return -EEXIST;
+
+#ifdef WANDEBUG
+ printk(KERN_INFO "%s: registering WAN device %s\n",
+ modname, wandev->name);
+#endif
+
+ /*
+ * Register /proc directory entry
+ */
+ err = wanrouter_proc_add(wandev);
+ if (err)
+ {
+ printk(KERN_ERR
+ "%s: can't create /proc/net/router/%s entry!\n",
+ modname, wandev->name)
+ ;
+ return err;
+ }
+
+ /*
+ * Initialize fields of the wan_device structure maintained by the
+ * router and update local data.
+ */
+
+ wandev->ndev = 0;
+ wandev->dev = NULL;
+ wandev->next = devlist;
+ devlist = wandev;
+ ++devcnt;
+ MOD_INC_USE_COUNT; /* prevent module from unloading */
+ return 0;
+}
+
+/*
+ * Unregister WAN device.
+ * o shut down device
+ * o unlink device data space from the linked list
+ * o delete device entry in the /proc/net/router directory
+ * o decrement module use count
+ *
+ * Return: 0 Ok
+ * <0 error.
+ * Context: process
+ */
+
+int unregister_wan_device(char* name)
+{
+ wan_device_t *wandev, *prev;
+
+ if (name == NULL)
+ return -EINVAL;
+
+ for (wandev = devlist, prev = NULL;
+ wandev && strcmp(wandev->name, name);
+ prev = wandev, wandev = wandev->next)
+ ;
+ if (wandev == NULL)
+ return -ENODEV;
+
+#ifdef WANDEBUG
+ printk(KERN_INFO "%s: unregistering WAN device %s\n", modname, name);
+#endif
+
+ if (wandev->state != WAN_UNCONFIGURED)
+ {
+ while(wandev->dev)
+ delete_interface(wandev, wandev->dev->name, 1);
+ if (wandev->shutdown)
+ wandev->shutdown(wandev);
+ }
+ if (prev)
+ prev->next = wandev->next;
+ else
+ devlist = wandev->next;
+ --devcnt;
+ wanrouter_proc_delete(wandev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Encapsulate packet.
+ *
+ * Return: encapsulation header size
+ * < 0 - unsupported Ethertype
+ *
+ * Notes:
+ * 1. This function may be called on interrupt context.
+ */
+
+int wanrouter_encapsulate (struct sk_buff* skb, struct device* dev)
+{
+ int hdr_len = 0;
+
+ switch (skb->protocol)
+ {
+ case ETH_P_IP: /* IP datagram encapsulation */
+ hdr_len += 1;
+ skb_push(skb, 1);
+ skb->data[0] = NLPID_IP;
+ break;
+
+ case ETH_P_IPX: /* SNAP encapsulation */
+ case ETH_P_ARP:
+ hdr_len += 6;
+ skb_push(skb, 6);
+ skb->data[0] = NLPID_SNAP;
+ memcpy(&skb->data[1], oui_ether, sizeof(oui_ether));
+ *((unsigned short*)&skb->data[4]) = htons(skb->protocol);
+ break;
+
+ default: /* Unknown packet type */
+ printk(KERN_INFO
+ "%s: unsupported Ethertype 0x%04X on interface %s!\n",
+ modname, skb->protocol, dev->name);
+ hdr_len = -EINVAL;
+ }
+ return hdr_len;
+}
+
+/*
+ * Decapsulate packet.
+ *
+ * Return: Ethertype (in network order)
+ * 0 unknown encapsulation
+ *
+ * Notes:
+ * 1. This function may be called on interrupt context.
+ */
+
+unsigned short wanrouter_type_trans (struct sk_buff* skb, struct device* dev)
+{
+ int cnt = skb->data[0] ? 0 : 1; /* there may be a pad present */
+ unsigned short ethertype;
+
+ switch (skb->data[cnt])
+ {
+ case NLPID_IP: /* IP datagramm */
+ ethertype = htons(ETH_P_IP);
+ cnt += 1;
+ break;
+
+ case NLPID_SNAP: /* SNAP encapsulation */
+ if (memcmp(&skb->data[cnt + 1], oui_ether, sizeof(oui_ether)))
+ {
+ printk(KERN_INFO
+ "%s: unsupported SNAP OUI %02X-%02X-%02X "
+ "on interface %s!\n", modname,
+ skb->data[cnt+1], skb->data[cnt+2],
+ skb->data[cnt+3], dev->name);
+ ;
+ return 0;
+ }
+ ethertype = *((unsigned short*)&skb->data[cnt+4]);
+ cnt += 6;
+ break;
+
+ /* add other protocols, e.g. CLNP, ESIS, ISIS, if needed */
+
+ default:
+ printk(KERN_INFO
+ "%s: unsupported NLPID 0x%02X on interface %s!\n",
+ modname, skb->data[cnt], dev->name)
+ ;
+ return 0;
+ }
+ skb->protocol = ethertype;
+ skb->pkt_type = PACKET_HOST; /* Physically point to point */
+ skb->mac.raw = skb->data;
+ skb_pull(skb, cnt);
+ return ethertype;
+}
+
+/*
+ * WAN device IOCTL.
+ * o find WAN device associated with this node
+ * o execute requested action or pass command to the device driver
+ */
+
+int wanrouter_ioctl(struct inode* inode, struct file* file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct proc_dir_entry* dent;
+ wan_device_t* wandev;
+
+ if (!suser())
+ return -EPERM;
+
+ if ((cmd >> 8) != ROUTER_IOCTL)
+ return -EINVAL;
+
+ dent = inode->u.generic_ip;
+ if ((dent == NULL) || (dent->data == NULL))
+ return -EINVAL;
+
+ wandev = dent->data;
+ if (wandev->magic != ROUTER_MAGIC)
+ return -EINVAL;
+
+ switch (cmd)
+ {
+ case ROUTER_SETUP:
+ err = device_setup(wandev, (void*)arg);
+ break;
+
+ case ROUTER_DOWN:
+ err = device_shutdown(wandev);
+ break;
+
+ case ROUTER_STAT:
+ err = device_stat(wandev, (void*)arg);
+ break;
+
+ case ROUTER_IFNEW:
+ err = device_new_if(wandev, (void*)arg);
+ break;
+
+ case ROUTER_IFDEL:
+ err = device_del_if(wandev, (void*)arg);
+ break;
+
+ case ROUTER_IFSTAT:
+ break;
+
+ default:
+ if ((cmd >= ROUTER_USER) &&
+ (cmd <= ROUTER_USER_MAX) &&
+ wandev->ioctl)
+ err = wandev->ioctl(wandev, cmd, arg)
+ ;
+ else err = -EINVAL;
+ }
+ return err;
+}
+
+/*
+ * WAN Driver IOCTL Handlers
+ */
+
+/*
+ * Setup WAN link device.
+ * o verify user address space
+ * o allocate kernel memory and copy configuration data to kernel space
+ * o if configuration data includes extension, copy it to kernel space too
+ * o call driver's setup() entry point
+ */
+
+static int device_setup (wan_device_t* wandev, wandev_conf_t* u_conf)
+{
+ void* data;
+ wandev_conf_t* conf;
+ int err= -EINVAL;
+
+ if (wandev->setup == NULL) /* Nothing to do ? */
+ return 0;
+
+ conf = kmalloc(sizeof(wandev_conf_t), GFP_KERNEL);
+ if (conf == NULL)
+ return -ENOBUFS;
+
+ if(copy_from_user(conf, u_conf, sizeof(wandev_conf_t)))
+ {
+ kfree(conf);
+ return -EFAULT;
+ }
+
+ if (conf->magic != ROUTER_MAGIC)
+ goto bail;
+
+ if (conf->data_size && conf->data)
+ {
+ if(conf->data_size > 1024 || conf->data_size < 0)
+ goto bail;
+ data = kmalloc(conf->data_size, GFP_KERNEL);
+ if (data)
+ {
+ if(!copy_from_user(data, conf->data, conf->data_size))
+ {
+ conf->data=data;
+ wandev->setup(wandev,conf);
+ }
+ else
+ err = -ENOBUFS;
+ }
+ if (data)
+ kfree(data);
+ }
+bail:
+ kfree(conf);
+ return err;
+}
+
+/*
+ * Shutdown WAN device.
+ * o delete all not opened logical channels for this device
+ * o call driver's shutdown() entry point
+ */
+
+static int device_shutdown (wan_device_t* wandev)
+{
+ struct device* dev;
+
+ if (wandev->state == WAN_UNCONFIGURED)
+ return 0;
+
+ for (dev = wandev->dev; dev;)
+ {
+ if (delete_interface(wandev, dev->name, 0))
+ dev = dev->slave;
+ }
+ if (wandev->ndev)
+ return -EBUSY; /* there are opened interfaces */
+
+ if (wandev->shutdown)
+ return wandev->shutdown(wandev);
+ return 0;
+}
+
+/*
+ * Get WAN device status & statistics.
+ */
+
+static int device_stat (wan_device_t* wandev, wandev_stat_t* u_stat)
+{
+ wandev_stat_t stat;
+
+ memset(&stat, 0, sizeof(stat));
+
+ /* Ask device driver to update device statistics */
+ if ((wandev->state != WAN_UNCONFIGURED) && wandev->update)
+ wandev->update(wandev);
+
+ /* Fill out structure */
+ stat.ndev = wandev->ndev;
+ stat.state = wandev->state;
+
+ if(copy_to_user(u_stat, &stat, sizeof(stat)))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * Create new WAN interface.
+ * o verify user address space
+ * o copy configuration data to kernel address space
+ * o allocate network interface data space
+ * o call driver's new_if() entry point
+ * o make sure there is no interface name conflict
+ * o register network interface
+ */
+
+static int device_new_if (wan_device_t* wandev, wanif_conf_t* u_conf)
+{
+ wanif_conf_t conf;
+ struct device* dev;
+ int err;
+
+ if ((wandev->state == WAN_UNCONFIGURED) || (wandev->new_if == NULL))
+ return -ENODEV;
+
+ if(copy_from_user(&conf, u_conf, sizeof(wanif_conf_t)))
+ return -EFAULT;
+
+ if (conf.magic != ROUTER_MAGIC)
+ return -EINVAL;
+
+ dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+ if (dev == NULL)
+ return -ENOBUFS;
+
+ memset(dev, 0, sizeof(struct device));
+ err = wandev->new_if(wandev, dev, &conf);
+ if (!err)
+ {
+ /* Register network interface. This will invoke init()
+ * function supplied by the driver. If device registered
+ * successfully, add it to the interface list.
+ */
+ if (dev->name == NULL)
+ err = -EINVAL;
+
+ else if (dev_get(dev->name))
+ err = -EEXIST; /* name already exists */
+ else
+ {
+#ifdef WANDEBUG
+ printk(KERN_INFO "%s: registering interface %s...\n",
+ modname, dev->name);
+#endif
+ err = register_netdev(dev);
+ if (!err)
+ {
+ cli(); /***** critical section start *****/
+ dev->slave = wandev->dev;
+ wandev->dev = dev;
+ ++wandev->ndev;
+ sti(); /****** critical section end ******/
+ return 0; /* done !!! */
+ }
+ }
+ if (wandev->del_if)
+ wandev->del_if(wandev, dev);
+ }
+ kfree(dev);
+ return err;
+}
+
+/*
+ * Delete WAN logical channel.
+ * o verify user address space
+ * o copy configuration data to kernel address space
+ */
+
+static int device_del_if (wan_device_t* wandev, char* u_name)
+{
+ char name[WAN_IFNAME_SZ + 1];
+
+ if (wandev->state == WAN_UNCONFIGURED)
+ return -ENODEV;
+
+ memset(name, 0, sizeof(name));
+ if(copy_from_user(name, u_name, WAN_IFNAME_SZ))
+ return -EFAULT;
+ return delete_interface(wandev, name, 0);
+}
+
+/*
+ * Miscellaneous Functions
+ */
+
+/*
+ * Find WAN device by name.
+ * Return pointer to the WAN device data space or NULL if device not found.
+ */
+
+static wan_device_t* find_device (char* name)
+{
+ wan_device_t* wandev;
+
+ for (wandev = devlist;wandev && strcmp(wandev->name, name);
+ wandev = wandev->next);
+ return wandev;
+}
+
+/*
+ * Delete WAN logical channel identified by its name.
+ * o find logical channel by its name
+ * o call driver's del_if() entry point
+ * o unregister network interface
+ * o unlink channel data space from linked list of channels
+ * o release channel data space
+ *
+ * Return: 0 success
+ * -ENODEV channel not found.
+ * -EBUSY interface is open
+ *
+ * Note: If (force != 0), then device will be destroyed even if interface
+ * associated with it is open. It's caller's responsibility to make
+ * sure that opened interfaces are not removed!
+ */
+
+static int delete_interface (wan_device_t* wandev, char* name, int force)
+{
+ struct device *dev, *prev;
+
+ for (dev = wandev->dev, prev = NULL;
+ dev && strcmp(name, dev->name);
+ prev = dev, dev = dev->slave);
+
+ if (dev == NULL)
+ return -ENODEV; /* interface not found */
+
+ if (dev->start)
+ {
+ if (force)
+ {
+ printk(KERN_WARNING
+ "%s: deleting opened interface %s!\n",modname, name);
+ }
+ else
+ return -EBUSY; /* interface in use */
+ }
+ if (wandev->del_if)
+ wandev->del_if(wandev, dev);
+
+ cli(); /***** critical section start *****/
+ if (prev)
+ prev->slave = dev->slave;
+ else
+ wandev->dev = dev->slave;
+ --wandev->ndev;
+ sti(); /****** critical section end ******/
+
+ unregister_netdev(dev);
+ kfree(dev);
+ return 0;
+}
+
+/*
+ * End
+ */
diff --git a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c
new file mode 100644
index 000000000..ce7140db0
--- /dev/null
+++ b/net/wanrouter/wanproc.c
@@ -0,0 +1,460 @@
+/*****************************************************************************
+* wanproc.c WAN Multiprotocol Router Module. proc filesystem interface.
+*
+* This module is completely hardware-independent and provides
+* access to the router using Linux /proc filesystem.
+*
+* Author: Gene Kozin <genek@compuserve.com>
+*
+* Copyright: (c) 1995-1996 Sangoma Technologies Inc.
+*
+* This program 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 of the License, or (at your option) any later version.
+* ============================================================================
+* Dec 13, 1996 Gene Kozin Initial version (based on Sangoma's WANPIPE)
+* Jan 30, 1997 Alan Cox Hacked around for 2.1
+*****************************************************************************/
+
+#include <linux/stddef.h> /* offsetof(), etc. */
+#include <linux/errno.h> /* return codes */
+#include <linux/kernel.h>
+#include <linux/malloc.h> /* kmalloc(), kfree() */
+#include <linux/mm.h> /* verify_area(), etc. */
+#include <linux/string.h> /* inline mem*, str* functions */
+#include <asm/segment.h> /* kernel <-> user copy */
+#include <asm/byteorder.h> /* htons(), etc. */
+#include <asm/uaccess.h> /* copy_to_user */
+#include <linux/wanrouter.h> /* WAN router API definitions */
+
+
+/****** Defines and Macros **************************************************/
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define ROUTER_PAGE_SZ 4000 /* buffer size for printing proc info */
+
+/****** Data Types **********************************************************/
+
+typedef struct wan_stat_entry
+{
+ struct wan_stat_entry * next;
+ char *description; /* description string */
+ void *data; /* -> data */
+ unsigned data_type; /* data type */
+} wan_stat_entry_t;
+
+/****** Function Prototypes *************************************************/
+
+/* Proc filesystem interface */
+static int router_proc_perms (struct inode*, int);
+static long router_proc_read(struct inode* inode, struct file* file, char* buf,
+ unsigned long count);
+
+/* Methods for preparing data for reading proc entries */
+
+static int about_get_info(char* buf, char** start, off_t offs, int len, int dummy);
+static int config_get_info(char* buf, char** start, off_t offs, int len, int dummy);
+static int status_get_info(char* buf, char** start, off_t offs, int len, int dummy);
+static int wandev_get_info(char* buf, char** start, off_t offs, int len, int dummy);
+
+/* Miscellaneous */
+
+/*
+ * Global Data
+ */
+
+/*
+ * Names of the proc directory entries
+ */
+
+static char name_root[] = ROUTER_NAME;
+static char name_info[] = "about";
+static char name_conf[] = "config";
+static char name_stat[] = "status";
+
+/*
+ * Structures for interfacing with the /proc filesystem.
+ * Router creates its own directory /proc/net/router with the folowing
+ * entries:
+ * About general information (version, copyright, etc.)
+ * Conf device configuration
+ * Stat global device statistics
+ * <device> entry for each WAN device
+ */
+
+/*
+ * Generic /proc/net/router/<file> file and inode operations
+ */
+
+static struct file_operations router_fops =
+{
+ NULL, /* lseek */
+ router_proc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+static struct inode_operations router_inode =
+{
+ &router_fops,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ router_proc_perms
+};
+
+/*
+ * /proc/net/router/<device> file and inode operations
+ */
+
+static struct file_operations wandev_fops =
+{
+ NULL, /* lseek */
+ router_proc_read, /* read */
+ NULL, /* write */
+ NULL, /* readdir */
+ NULL, /* select */
+ wanrouter_ioctl, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+static struct inode_operations wandev_inode =
+{
+ &wandev_fops,
+ NULL, /* create */
+ NULL, /* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* readpage */
+ NULL, /* writepage */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ router_proc_perms
+};
+
+/*
+ * Proc filesystem derectory entries.
+ */
+
+/*
+ * /proc/net/router
+ */
+
+static struct proc_dir_entry proc_router =
+{
+ 0, /* .low_ino */
+ sizeof(name_root) - 1, /* .namelen */
+ name_root, /* .name */
+ 0555 | S_IFDIR, /* .mode */
+ 2, /* .nlink */
+ 0, /* .uid */
+ 0, /* .gid */
+ 0, /* .size */
+ &proc_dir_inode_operations, /* .ops */
+ NULL, /* .get_info */
+ NULL, /* .fill_node */
+ NULL, /* .next */
+ NULL, /* .parent */
+ NULL, /* .subdir */
+ NULL, /* .data */
+};
+
+/*
+ * /proc/net/router/about
+ */
+
+static struct proc_dir_entry proc_router_info =
+{
+ 0, /* .low_ino */
+ sizeof(name_info) - 1, /* .namelen */
+ name_info, /* .name */
+ 0444 | S_IFREG, /* .mode */
+ 1, /* .nlink */
+ 0, /* .uid */
+ 0, /* .gid */
+ 0, /* .size */
+ &router_inode, /* .ops */
+ &about_get_info, /* .get_info */
+ NULL, /* .fill_node */
+ NULL, /* .next */
+ NULL, /* .parent */
+ NULL, /* .subdir */
+ NULL, /* .data */
+};
+
+/*
+ * /proc/net/router/config
+ */
+
+static struct proc_dir_entry proc_router_conf =
+{
+ 0, /* .low_ino */
+ sizeof(name_conf) - 1, /* .namelen */
+ name_conf, /* .name */
+ 0444 | S_IFREG, /* .mode */
+ 1, /* .nlink */
+ 0, /* .uid */
+ 0, /* .gid */
+ 0, /* .size */
+ &router_inode, /* .ops */
+ &config_get_info, /* .get_info */
+ NULL, /* .fill_node */
+ NULL, /* .next */
+ NULL, /* .parent */
+ NULL, /* .subdir */
+ NULL, /* .data */
+};
+
+/*
+ * /proc/net/router/status
+ */
+
+static struct proc_dir_entry proc_router_stat =
+{
+ 0, /* .low_ino */
+ sizeof(name_stat) - 1, /* .namelen */
+ name_stat, /* .name */
+ 0444 | S_IFREG, /* .mode */
+ 1, /* .nlink */
+ 0, /* .uid */
+ 0, /* .gid */
+ 0, /* .size */
+ &router_inode, /* .ops */
+ status_get_info, /* .get_info */
+ NULL, /* .fill_node */
+ NULL, /* .next */
+ NULL, /* .parent */
+ NULL, /* .subdir */
+ NULL, /* .data */
+};
+
+/*
+ * Interface functions
+ */
+
+/*
+ * Initialize router proc interface.
+ */
+
+int wanrouter_proc_init (void)
+{
+ int err = proc_register(&proc_net, &proc_router);
+
+ if (!err)
+ {
+ proc_register(&proc_router, &proc_router_info);
+ proc_register(&proc_router, &proc_router_conf);
+ proc_register(&proc_router, &proc_router_stat);
+ }
+ return err;
+}
+
+/*
+ * Clean up router proc interface.
+ */
+
+void wanrouter_proc_cleanup (void)
+{
+ proc_unregister(&proc_router, proc_router_info.low_ino);
+ proc_unregister(&proc_router, proc_router_conf.low_ino);
+ proc_unregister(&proc_router, proc_router_stat.low_ino);
+ proc_unregister(&proc_net, proc_router.low_ino);
+}
+
+/*
+ * Add directory entry for WAN device.
+ */
+
+int wanrouter_proc_add (wan_device_t* wandev)
+{
+ if (wandev->magic != ROUTER_MAGIC)
+ return -EINVAL;
+
+ memset(&wandev->dent, 0, sizeof(wandev->dent));
+ wandev->dent.namelen = strlen(wandev->name);
+ wandev->dent.name = wandev->name;
+ wandev->dent.mode = 0444 | S_IFREG;
+ wandev->dent.nlink = 1;
+ wandev->dent.ops = &wandev_inode;
+ wandev->dent.get_info = &wandev_get_info;
+ wandev->dent.data = wandev;
+ return proc_register(&proc_router, &wandev->dent);
+}
+
+/*
+ * Delete directory entry for WAN device.
+ */
+
+int wanrouter_proc_delete(wan_device_t* wandev)
+{
+ if (wandev->magic != ROUTER_MAGIC)
+ return -EINVAL;
+ proc_unregister(&proc_router, wandev->dent.low_ino);
+ return 0;
+}
+
+/****** Proc filesystem entry points ****************************************/
+
+/*
+ * Verify access rights.
+ */
+
+static int router_proc_perms (struct inode* inode, int op)
+{
+ return 0;
+}
+
+/*
+ * Read router proc directory entry.
+ * This is universal routine for reading all entries in /proc/net/router
+ * directory. Each directory entry contains a pointer to the 'method' for
+ * preparing data for that entry.
+ * o verify arguments
+ * o allocate kernel buffer
+ * o call get_info() to prepare data
+ * o copy data to user space
+ * o release kernel buffer
+ *
+ * Return: number of bytes copied to user space (0, if no data)
+ * <0 error
+ */
+
+static long router_proc_read(struct inode* inode, struct file* file,
+ char* buf, unsigned long count)
+{
+ struct proc_dir_entry* dent;
+ char* page;
+ int pos, offs, len;
+
+ if (count <= 0)
+ return 0;
+
+ dent = inode->u.generic_ip;
+ if ((dent == NULL) || (dent->get_info == NULL))
+ return 0;
+
+ page = kmalloc(ROUTER_PAGE_SZ, GFP_KERNEL);
+ if (page == NULL)
+ return -ENOBUFS;
+
+ pos = dent->get_info(page, dent->data, 0, 0, 0);
+ offs = file->f_pos;
+ if (offs < pos)
+ {
+ len = min(pos - offs, count);
+ if(copy_to_user(buf, (page + offs), len))
+ return -EFAULT;
+ file->f_pos += len;
+ }
+ else
+ len = 0;
+ kfree(page);
+ return len;
+}
+
+/*
+ * Prepare data for reading 'About' entry.
+ * Return length of data.
+ */
+
+static int about_get_info(char* buf, char** start, off_t offs, int len,
+ int dummy)
+{
+ int cnt = 0;
+
+ cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
+ "version", ROUTER_VERSION, ROUTER_RELEASE);
+ return cnt;
+}
+
+/*
+ * Prepare data for reading 'Config' entry.
+ * Return length of data.
+ * NOT YET IMPLEMENTED
+ */
+
+static int config_get_info(char* buf, char** start, off_t offs, int len,
+ int dummy)
+{
+ int cnt = 0;
+
+ cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
+ "version", ROUTER_VERSION, ROUTER_RELEASE);
+ return cnt;
+}
+
+/*
+ * Prepare data for reading 'Status' entry.
+ * Return length of data.
+ * NOT YET IMPLEMENTED
+ */
+
+static int status_get_info(char* buf, char** start, off_t offs, int len,
+ int dummy)
+{
+ int cnt = 0;
+
+ cnt += sprintf(&buf[cnt], "%12s : %u.%u\n",
+ "version", ROUTER_VERSION, ROUTER_RELEASE);
+ return cnt;
+}
+
+/*
+ * Prepare data for reading <device> entry.
+ * Return length of data.
+ *
+ * On entry, the 'start' argument will contain a pointer to WAN device
+ * data space.
+ */
+
+static int wandev_get_info(char* buf, char** start, off_t offs, int len,
+ int dummy)
+{
+ wan_device_t* wandev = (void*)start;
+ int cnt = 0;
+
+ if ((wandev == NULL) || (wandev->magic != ROUTER_MAGIC))
+ return 0;
+ cnt += sprintf(&buf[cnt], "%12s : %s\n", "name", wandev->name);
+ return cnt;
+}
+
+/*
+ * End
+ */
+
diff --git a/net/x25/Makefile b/net/x25/Makefile
new file mode 100644
index 000000000..a215a8ea2
--- /dev/null
+++ b/net/x25/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for the Linux X.25 Packet layer.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definition is now in the main makefile...
+
+O_TARGET := x25.o
+O_OBJS := af_x25.o x25_dev.o x25_in.o x25_link.o x25_out.o x25_route.o x25_subr.o x25_timer.o
+M_OBJS := $(O_TARGET)
+
+ifeq ($(CONFIG_SYSCTL),y)
+O_OBJS += sysctl_net_x25.o
+endif
+
+include $(TOPDIR)/Rules.make
+
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
new file mode 100644
index 000000000..971ae497d
--- /dev/null
+++ b/net/x25/af_x25.c
@@ -0,0 +1,1356 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <net/x25.h>
+
+int sysctl_x25_restart_request_timeout = X25_DEFAULT_T20;
+int sysctl_x25_call_request_timeout = X25_DEFAULT_T21;
+int sysctl_x25_reset_request_timeout = X25_DEFAULT_T22;
+int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23;
+int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2;
+
+static unsigned int lci = 1;
+
+static struct sock *volatile x25_list = NULL;
+
+static struct proto_ops x25_proto_ops;
+
+int x25_addr_ntoa(unsigned char *p, x25_address *called_addr, x25_address *calling_addr)
+{
+ int called_len, calling_len;
+ char *called, *calling;
+ int i;
+
+ called_len = (*p >> 0) & 0x0F;
+ calling_len = (*p >> 4) & 0x0F;
+
+ called = called_addr->x25_addr;
+ calling = calling_addr->x25_addr;
+ p++;
+
+ for (i = 0; i < (called_len + calling_len); i++) {
+ if (i < called_len) {
+ if (i % 2 != 0) {
+ *called++ = ((*p >> 0) & 0x0F) + '0';
+ p++;
+ } else {
+ *called++ = ((*p >> 4) & 0x0F) + '0';
+ }
+ } else {
+ if (i % 2 != 0) {
+ *calling++ = ((*p >> 0) & 0x0F) + '0';
+ p++;
+ } else {
+ *calling++ = ((*p >> 4) & 0x0F) + '0';
+ }
+ }
+ }
+
+ *called = '\0';
+ *calling = '\0';
+
+ return 1 + (called_len + calling_len + 1) / 2;
+}
+
+int x25_addr_aton(unsigned char *p, x25_address *called_addr, x25_address *calling_addr)
+{
+ unsigned int called_len, calling_len;
+ char *called, *calling;
+ int i;
+
+ called = called_addr->x25_addr;
+ calling = calling_addr->x25_addr;
+
+ called_len = strlen(called);
+ calling_len = strlen(calling);
+
+ *p++ = (calling_len << 4) | (called_len << 0);
+
+ for (i = 0; i < (called_len + calling_len); i++) {
+ if (i < called_len) {
+ if (i % 2 != 0) {
+ *p |= (*called++ - '0') << 0;
+ p++;
+ *p = 0x00;
+ } else {
+ *p |= (*called++ - '0') << 4;
+ }
+ } else {
+ if (i % 2 != 0) {
+ *p |= (*calling++ - '0') << 0;
+ p++;
+ *p = 0x00;
+ } else {
+ *p |= (*calling++ - '0') << 4;
+ }
+ }
+ }
+
+ return 1 + (called_len + calling_len + 1) / 2;
+}
+
+/*
+ * Socket removal during an interrupt is now safe.
+ */
+static void x25_remove_socket(struct sock *sk)
+{
+ struct sock *s;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if ((s = x25_list) == sk) {
+ x25_list = s->next;
+ restore_flags(flags);
+ return;
+ }
+
+ while (s != NULL && s->next != NULL) {
+ if (s->next == sk) {
+ s->next = sk->next;
+ restore_flags(flags);
+ return;
+ }
+
+ s = s->next;
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Kill all bound sockets on a dropped device.
+ */
+static void x25_kill_by_device(struct device *dev)
+{
+ struct sock *s;
+
+ for (s = x25_list; s != NULL; s = s->next) {
+ if (s->protinfo.x25->neighbour->dev == dev) {
+ s->protinfo.x25->state = X25_STATE_0;
+ s->state = TCP_CLOSE;
+ s->err = ENETUNREACH;
+ s->shutdown |= SEND_SHUTDOWN;
+ s->state_change(s);
+ s->dead = 1;
+ }
+ }
+}
+
+/*
+ * Handle device status changes.
+ */
+static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ struct device *dev = (struct device *)ptr;
+
+ if (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ || dev->type == ARPHRD_ETHER
+#endif
+ ) {
+ switch (event) {
+ case NETDEV_UP:
+ x25_link_device_up(dev);
+ break;
+ case NETDEV_DOWN:
+ x25_kill_by_device(dev);
+ x25_route_device_down(dev);
+ x25_link_device_down(dev);
+ break;
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * Add a socket to the bound sockets list.
+ */
+static void x25_insert_socket(struct sock *sk)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ sk->next = x25_list;
+ x25_list = sk;
+
+ restore_flags(flags);
+}
+
+/*
+ * Find a socket that wants to accept the Call Request we just
+ * received.
+ */
+static struct sock *x25_find_listener(x25_address *addr)
+{
+ unsigned long flags;
+ struct sock *s;
+
+ save_flags(flags);
+ cli();
+
+ for (s = x25_list; s != NULL; s = s->next) {
+ if (strcmp(s->protinfo.x25->source_addr.x25_addr, addr->x25_addr) == 0 && s->state == TCP_LISTEN) {
+ restore_flags(flags);
+ return s;
+ }
+ }
+
+ restore_flags(flags);
+ return NULL;
+}
+
+/*
+ * Find a connected X.25 socket given my LCI.
+ */
+struct sock *x25_find_socket(unsigned int lci)
+{
+ struct sock *s;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ for (s = x25_list; s != NULL; s = s->next) {
+ if (s->protinfo.x25->lci == lci) {
+ restore_flags(flags);
+ return s;
+ }
+ }
+
+ restore_flags(flags);
+ return NULL;
+}
+
+/*
+ * Find a unique LCI for a given device.
+ */
+unsigned int x25_new_lci(void)
+{
+ lci++;
+ if (lci > 4095) lci = 1;
+
+ while (x25_find_socket(lci) != NULL) {
+ lci++;
+ if (lci > 4095) lci = 1;
+ }
+
+ return lci;
+}
+
+/*
+ * Deferred destroy.
+ */
+void x25_destroy_socket(struct sock *);
+
+/*
+ * handler for deferred kills.
+ */
+static void x25_destroy_timer(unsigned long data)
+{
+ x25_destroy_socket((struct sock *)data);
+}
+
+/*
+ * This is called from user mode and the timers. Thus it protects itself against
+ * interrupt users but doesn't worry about being called during work.
+ * Once it is removed from the queue no interrupt or bottom half will
+ * touch it and we are (fairly 8-) ) safe.
+ */
+void x25_destroy_socket(struct sock *sk) /* Not static as it's used by the timer */
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ del_timer(&sk->timer);
+
+ x25_remove_socket(sk);
+ x25_clear_queues(sk); /* Flush the queues */
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ if (skb->sk != sk) { /* A pending connection */
+ skb->sk->dead = 1; /* Queue the unaccepted socket for death */
+ x25_set_timer(skb->sk);
+ skb->sk->protinfo.x25->state = X25_STATE_0;
+ }
+
+ kfree_skb(skb, FREE_READ);
+ }
+
+ if (atomic_read(&sk->wmem_alloc) != 0 || atomic_read(&sk->rmem_alloc) != 0) {
+ /* Defer: outstanding buffers */
+ init_timer(&sk->timer);
+ sk->timer.expires = jiffies + 10 * HZ;
+ sk->timer.function = x25_destroy_timer;
+ sk->timer.data = (unsigned long)sk;
+ add_timer(&sk->timer);
+ } else {
+ kfree_s(sk->protinfo.x25, sizeof(*sk->protinfo.x25));
+ sk_free(sk);
+ MOD_DEC_USE_COUNT;
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Handling for system calls applied via the various interfaces to a
+ * X.25 socket object.
+ */
+
+static int x25_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ struct sock *sk = sock->sk;
+ int opt;
+
+ if (level != SOL_X25)
+ return -ENOPROTOOPT;
+
+ if (optlen < sizeof(int))
+ return-EINVAL;
+
+ if (get_user(opt, (int *)optval))
+ return -EFAULT;
+
+ switch (optname) {
+ case X25_QBITINCL:
+ sk->protinfo.x25->qbitincl = opt ? 1 : 0;
+ return 0;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+}
+
+static int x25_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ struct sock *sk = sock->sk;
+ int val = 0;
+ int len;
+
+ if (level != SOL_X25)
+ return -ENOPROTOOPT;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ switch (optname) {
+ case X25_QBITINCL:
+ val = sk->protinfo.x25->qbitincl;
+ break;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ len = min(len, sizeof(int));
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int x25_listen(struct socket *sock, int backlog)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->state != TCP_LISTEN) {
+ memset(&sk->protinfo.x25->dest_addr, '\0', X25_ADDR_LEN);
+ sk->max_ack_backlog = backlog;
+ sk->state = TCP_LISTEN;
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static struct sock *x25_alloc_socket(void)
+{
+ struct sock *sk;
+ x25_cb *x25;
+
+ if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ if ((x25 = (x25_cb *)kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ memset(x25, 0x00, sizeof(*x25));
+
+ x25->sk = sk;
+ sk->protinfo.x25 = x25;
+
+ MOD_INC_USE_COUNT;
+
+ sock_init_data(NULL, sk);
+
+ skb_queue_head_init(&x25->fragment_queue);
+ skb_queue_head_init(&x25->interrupt_in_queue);
+ skb_queue_head_init(&x25->interrupt_out_queue);
+
+ return sk;
+}
+
+static int x25_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+ x25_cb *x25;
+
+ if (sock->type != SOCK_SEQPACKET || protocol != 0)
+ return -ESOCKTNOSUPPORT;
+
+ if ((sk = x25_alloc_socket()) == NULL)
+ return -ENOMEM;
+
+ x25 = sk->protinfo.x25;
+
+ sock_init_data(sock, sk);
+
+ sock->ops = &x25_proto_ops;
+ sk->protocol = protocol;
+ sk->mtu = X25_DEFAULT_PACKET_SIZE; /* X25_PS128 */
+
+ x25->t21 = sysctl_x25_call_request_timeout;
+ x25->t22 = sysctl_x25_reset_request_timeout;
+ x25->t23 = sysctl_x25_clear_request_timeout;
+ x25->t2 = sysctl_x25_ack_holdback_timeout;
+ x25->state = X25_STATE_0;
+
+ x25->facilities.winsize_in = X25_DEFAULT_WINDOW_SIZE;
+ x25->facilities.winsize_out = X25_DEFAULT_WINDOW_SIZE;
+ x25->facilities.pacsize_in = X25_DEFAULT_PACKET_SIZE;
+ x25->facilities.pacsize_out = X25_DEFAULT_PACKET_SIZE;
+ x25->facilities.throughput = X25_DEFAULT_THROUGHPUT;
+ x25->facilities.reverse = X25_DEFAULT_REVERSE;
+
+ return 0;
+}
+
+static struct sock *x25_make_new(struct sock *osk)
+{
+ struct sock *sk;
+ x25_cb *x25;
+
+ if (osk->type != SOCK_SEQPACKET)
+ return NULL;
+
+ if ((sk = x25_alloc_socket()) == NULL)
+ return NULL;
+
+ x25 = sk->protinfo.x25;
+
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->mtu = osk->mtu;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
+
+ x25->t21 = osk->protinfo.x25->t21;
+ x25->t22 = osk->protinfo.x25->t22;
+ x25->t23 = osk->protinfo.x25->t23;
+ x25->t2 = osk->protinfo.x25->t2;
+
+ x25->facilities = osk->protinfo.x25->facilities;
+
+ x25->qbitincl = osk->protinfo.x25->qbitincl;
+
+ return sk;
+}
+
+static int x25_dup(struct socket *newsock, struct socket *oldsock)
+{
+ struct sock *sk = oldsock->sk;
+
+ if (sk == NULL || newsock == NULL)
+ return -EINVAL;
+
+ return x25_create(newsock, sk->protocol);
+}
+
+static int x25_release(struct socket *sock, struct socket *peer)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk == NULL) return 0;
+
+ switch (sk->protinfo.x25->state) {
+
+ case X25_STATE_0:
+ sk->state = TCP_CLOSE;
+ sk->shutdown |= SEND_SHUTDOWN;
+ sk->state_change(sk);
+ sk->dead = 1;
+ x25_destroy_socket(sk);
+ break;
+
+ case X25_STATE_2:
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->shutdown |= SEND_SHUTDOWN;
+ sk->state_change(sk);
+ sk->dead = 1;
+ x25_destroy_socket(sk);
+ break;
+
+ case X25_STATE_1:
+ case X25_STATE_3:
+ case X25_STATE_4:
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_CLEAR_REQUEST);
+ sk->protinfo.x25->timer = sk->protinfo.x25->t23;
+ sk->protinfo.x25->state = X25_STATE_2;
+ sk->state = TCP_CLOSE;
+ sk->shutdown |= SEND_SHUTDOWN;
+ sk->state_change(sk);
+ sk->dead = 1;
+ sk->destroy = 1;
+ break;
+
+ default:
+ break;
+ }
+
+ sock->sk = NULL;
+ sk->socket = NULL; /* Not used, but we should do this */
+
+ return 0;
+}
+
+static int x25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
+
+ if (sk->zapped == 0)
+ return -EINVAL;
+
+ if (addr_len != sizeof(struct sockaddr_x25))
+ return -EINVAL;
+
+ if (addr->sx25_family != AF_X25)
+ return -EINVAL;
+
+ sk->protinfo.x25->source_addr = addr->sx25_addr;
+
+ x25_insert_socket(sk);
+
+ sk->zapped = 0;
+ SOCK_DEBUG(sk, "x25_bind: socket is bound\n");
+ return 0;
+}
+
+static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_x25 *addr = (struct sockaddr_x25 *)uaddr;
+ struct device *dev;
+
+ if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
+ sock->state = SS_CONNECTED;
+ return 0; /* Connect completed during a ERESTARTSYS event */
+ }
+
+ if (sk->state == TCP_CLOSE && sock->state == SS_CONNECTING) {
+ sock->state = SS_UNCONNECTED;
+ return -ECONNREFUSED;
+ }
+
+ if (sk->state == TCP_ESTABLISHED)
+ return -EISCONN; /* No reconnect on a seqpacket socket */
+
+ sk->state = TCP_CLOSE;
+ sock->state = SS_UNCONNECTED;
+
+ if (addr_len != sizeof(struct sockaddr_x25))
+ return -EINVAL;
+
+ if (addr->sx25_family != AF_X25)
+ return -EINVAL;
+
+ if ((dev = x25_get_route(&addr->sx25_addr)) == NULL)
+ return -ENETUNREACH;
+
+ if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL)
+ return -ENETUNREACH;
+
+ if (sk->zapped) /* Must bind first - autobinding does not work */
+ return -EINVAL;
+
+ sk->protinfo.x25->dest_addr = addr->sx25_addr;
+ sk->protinfo.x25->lci = x25_new_lci();
+
+ /* Move to connecting socket, start sending Connect Requests */
+ sock->state = SS_CONNECTING;
+ sk->state = TCP_SYN_SENT;
+
+ sk->protinfo.x25->state = X25_STATE_1;
+ sk->protinfo.x25->timer = sk->protinfo.x25->t21;
+ x25_write_internal(sk, X25_CALL_REQUEST);
+
+ x25_set_timer(sk);
+
+ /* Now the loop */
+ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
+ return -EINPROGRESS;
+
+ cli(); /* To avoid races on the sleep */
+
+ /*
+ * A Connect Ack with Choke or timeout or failed routing will go to closed.
+ */
+ while (sk->state == TCP_SYN_SENT) {
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ return -ERESTARTSYS;
+ }
+ }
+
+ if (sk->state != TCP_ESTABLISHED) {
+ sti();
+ sock->state = SS_UNCONNECTED;
+ return sock_error(sk); /* Always set at this point */
+ }
+
+ sock->state = SS_CONNECTED;
+
+ sti();
+
+ return 0;
+}
+
+static int x25_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ return -EOPNOTSUPP;
+}
+
+static int x25_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+ struct sock *sk;
+ struct sock *newsk;
+ struct sk_buff *skb;
+
+ if (newsock->sk != NULL)
+ x25_destroy_socket(newsock->sk);
+
+ newsock->sk = NULL;
+
+ if ((sk = sock->sk) == NULL)
+ return -EINVAL;
+
+ if (sk->type != SOCK_SEQPACKET)
+ return -EOPNOTSUPP;
+
+ if (sk->state != TCP_LISTEN)
+ return -EINVAL;
+
+ /*
+ * The write queue this time is holding sockets ready to use
+ * hooked into the CALL INDICATION we saved
+ */
+ do {
+ cli();
+ if ((skb = skb_dequeue(&sk->receive_queue)) == NULL) {
+ if (flags & O_NONBLOCK) {
+ sti();
+ return -EWOULDBLOCK;
+ }
+ interruptible_sleep_on(sk->sleep);
+ if (current->signal & ~current->blocked) {
+ sti();
+ return -ERESTARTSYS;
+ }
+ }
+ } while (skb == NULL);
+
+ newsk = skb->sk;
+ newsk->pair = NULL;
+ sti();
+
+ /* Now attach up the new socket */
+ skb->sk = NULL;
+ kfree_skb(skb, FREE_READ);
+ sk->ack_backlog--;
+ newsock->sk = newsk;
+
+ return 0;
+}
+
+static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
+{
+ struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr;
+ struct sock *sk = sock->sk;
+
+ if (peer != 0) {
+ if (sk->state != TCP_ESTABLISHED)
+ return -ENOTCONN;
+ sx25->sx25_addr = sk->protinfo.x25->dest_addr;
+ } else {
+ sx25->sx25_addr = sk->protinfo.x25->source_addr;
+ }
+
+ sx25->sx25_family = AF_X25;
+ *uaddr_len = sizeof(struct sockaddr_x25);
+
+ return 0;
+}
+
+int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned int lci)
+{
+ struct sock *sk;
+ struct sock *make;
+ x25_address source_addr, dest_addr;
+ struct x25_facilities facilities;
+
+ /*
+ * Remove the LCI and frame type.
+ */
+ skb_pull(skb, X25_STD_MIN_LEN);
+
+ /*
+ * Extract the X.25 addresses and convert them to ASCII strings,
+ * and remove them.
+ */
+ skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
+
+ /*
+ * Find a listener for the particular address.
+ */
+ sk = x25_find_listener(&source_addr);
+
+ /*
+ * We can't accept the Call Request.
+ */
+ if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = x25_make_new(sk)) == NULL) {
+ x25_transmit_clear_request(neigh, lci, 0x01);
+ return 0;
+ }
+
+ /*
+ * Parse the facilities, and remove them, leaving any Call User
+ * Data.
+ */
+ skb_pull(skb, x25_parse_facilities(skb, &facilities));
+
+ skb->sk = make;
+ make->state = TCP_ESTABLISHED;
+
+ make->protinfo.x25->lci = lci;
+ make->protinfo.x25->dest_addr = dest_addr;
+ make->protinfo.x25->source_addr = source_addr;
+ make->protinfo.x25->neighbour = neigh;
+
+ /*
+ * This implies that we accept all the incoming facilities
+ * values (if any). This needs fixing. XXX
+ */
+ make->protinfo.x25->facilities = facilities;
+
+ x25_write_internal(make, X25_CALL_ACCEPTED);
+
+ /*
+ * Incoming Call User Data.
+ */
+ if (skb->len >= 0) {
+ memcpy(make->protinfo.x25->calluserdata.cuddata, skb->data, skb->len);
+ make->protinfo.x25->calluserdata.cudlength = skb->len;
+ }
+
+ make->protinfo.x25->state = X25_STATE_3;
+
+ sk->ack_backlog++;
+ make->pair = sk;
+
+ x25_insert_socket(make);
+
+ skb_queue_head(&sk->receive_queue, skb);
+
+ x25_set_timer(make);
+
+ if (!sk->dead)
+ sk->data_ready(sk, skb->len);
+
+ return 1;
+}
+
+static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_x25 *usx25 = (struct sockaddr_x25 *)msg->msg_name;
+ int err;
+ struct sockaddr_x25 sx25;
+ struct sk_buff *skb;
+ unsigned char *asmptr;
+ int size, qbit = 0;
+
+ if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_OOB))
+ return -EINVAL;
+
+ if (sk->zapped)
+ return -EADDRNOTAVAIL;
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+ }
+
+ if (sk->protinfo.x25->neighbour == NULL)
+ return -ENETUNREACH;
+
+ if (usx25 != NULL) {
+ if (msg->msg_namelen < sizeof(sx25))
+ return -EINVAL;
+ sx25 = *usx25;
+ if (strcmp(sk->protinfo.x25->dest_addr.x25_addr, sx25.sx25_addr.x25_addr) != 0)
+ return -EISCONN;
+ if (sx25.sx25_family != AF_X25)
+ return -EINVAL;
+ } else {
+ /*
+ * FIXME 1003.1g - if the socket is like this because
+ * it has become closed (not started closed) we ought
+ * to SIGPIPE, EPIPE;
+ */
+ if (sk->state != TCP_ESTABLISHED)
+ return -ENOTCONN;
+
+ sx25.sx25_family = AF_X25;
+ sx25.sx25_addr = sk->protinfo.x25->dest_addr;
+ }
+ SOCK_DEBUG(sk, "x25_sendmsg: sendto: Addresses built.\n");
+
+ /* Build a packet */
+ SOCK_DEBUG(sk, "x25_sendmsg: sendto: building packet.\n");
+ if ((msg->msg_flags & MSG_OOB) && len > 32)
+ len = 32;
+
+ size = len + X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
+
+ if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
+ return err;
+
+ skb_reserve(skb, X25_MAX_L2_LEN + X25_EXT_MIN_LEN);
+
+ /*
+ * Put the data on the end
+ */
+ SOCK_DEBUG(sk, "x25_sendmsg: Copying user data\n");
+ skb->h.raw = skb_put(skb, len);
+ asmptr = skb->h.raw;
+
+ memcpy_fromiovec(asmptr, msg->msg_iov, len);
+
+ /*
+ * If the Q BIT Include socket option is in force, the first
+ * byte of the user data is the logical value of the Q Bit.
+ */
+ if (sk->protinfo.x25->qbitincl) {
+ qbit = skb->data[0];
+ skb_pull(skb, 1);
+ }
+
+ /*
+ * Push down the X.25 header
+ */
+ SOCK_DEBUG(sk, "x25_sendmsg: Building X.25 Header.\n");
+ if (msg->msg_flags & MSG_OOB) {
+ if (sk->protinfo.x25->neighbour->extended) {
+ asmptr = skb_push(skb, X25_STD_MIN_LEN);
+ *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
+ *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+ *asmptr++ = X25_INTERRUPT;
+ } else {
+ asmptr = skb_push(skb, X25_STD_MIN_LEN);
+ *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
+ *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+ *asmptr++ = X25_INTERRUPT;
+ }
+ } else {
+ if (sk->protinfo.x25->neighbour->extended) {
+ /* Build an Extended X.25 header */
+ asmptr = skb_push(skb, X25_EXT_MIN_LEN);
+ *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_EXTSEQ;
+ *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+ *asmptr++ = X25_DATA;
+ *asmptr++ = X25_DATA;
+ } else {
+ /* Build an Standard X.25 header */
+ asmptr = skb_push(skb, X25_STD_MIN_LEN);
+ *asmptr++ = ((sk->protinfo.x25->lci >> 8) & 0x0F) | X25_GFI_STDSEQ;
+ *asmptr++ = (sk->protinfo.x25->lci >> 0) & 0xFF;
+ *asmptr++ = X25_DATA;
+ }
+
+ if (qbit)
+ skb->data[0] |= X25_Q_BIT;
+ }
+ SOCK_DEBUG(sk, "x25_sendmsg: Built header.\n");
+ SOCK_DEBUG(sk, "x25_sendmsg: Transmitting buffer\n");
+
+ if (sk->state != TCP_ESTABLISHED) {
+ kfree_skb(skb, FREE_WRITE);
+ return -ENOTCONN;
+ }
+
+ if (msg->msg_flags & MSG_OOB) {
+ skb_queue_tail(&sk->protinfo.x25->interrupt_out_queue, skb);
+ } else {
+ x25_output(sk, skb);
+ }
+
+ if (sk->protinfo.x25->state == X25_STATE_3)
+ x25_kick(sk);
+
+ return len;
+}
+
+
+static int x25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
+{
+ struct sock *sk = sock->sk;
+ struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)msg->msg_name;
+ int copied, qbit;
+ struct sk_buff *skb;
+ unsigned char *asmptr;
+ int er;
+
+ /*
+ * This works for seqpacket too. The receiver has ordered the queue for
+ * us! We do one quick check first though
+ */
+ if (sk->state != TCP_ESTABLISHED)
+ return -ENOTCONN;
+
+ if (flags & MSG_OOB) {
+ if (sk->urginline || skb_peek(&sk->protinfo.x25->interrupt_in_queue) == NULL)
+ return -EINVAL;
+
+ skb = skb_dequeue(&sk->protinfo.x25->interrupt_in_queue);
+
+ skb_pull(skb, X25_STD_MIN_LEN);
+
+ /*
+ * No Q bit information on Interrupt data.
+ */
+ if (sk->protinfo.x25->qbitincl) {
+ asmptr = skb_push(skb, 1);
+ *asmptr = 0x00;
+ }
+
+ msg->msg_flags |= MSG_OOB;
+ } else {
+ /* Now we can treat all alike */
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
+ return er;
+
+ qbit = (skb->data[0] & X25_Q_BIT) == X25_Q_BIT;
+
+ skb_pull(skb, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
+
+ if (sk->protinfo.x25->qbitincl) {
+ asmptr = skb_push(skb, 1);
+ *asmptr = qbit;
+ }
+ }
+
+ skb->h.raw = skb->data;
+
+ copied = skb->len;
+
+ if (copied > size) {
+ copied = size;
+ msg->msg_flags |= MSG_TRUNC;
+ }
+
+ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+ if (sx25 != NULL) {
+ sx25->sx25_family = AF_X25;
+ sx25->sx25_addr = sk->protinfo.x25->dest_addr;
+ }
+
+ msg->msg_namelen = sizeof(struct sockaddr_x25);
+
+ skb_free_datagram(sk, skb);
+
+ return copied;
+}
+
+static int x25_shutdown(struct socket *sk, int how)
+{
+ return -EOPNOTSUPP;
+}
+
+static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ struct x25_facilities facilities;
+ struct x25_calluserdata calluserdata;
+ struct sock *sk = sock->sk;
+ int err;
+ long amount = 0;
+
+ switch (cmd) {
+ case TIOCOUTQ:
+ if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+ return err;
+ amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
+ if (amount < 0)
+ amount = 0;
+ put_user(amount, (unsigned long *)arg);
+ return 0;
+
+ case TIOCINQ: {
+ struct sk_buff *skb;
+ /* These two are safe on a single CPU system as only user tasks fiddle here */
+ if ((skb = skb_peek(&sk->receive_queue)) != NULL)
+ amount = skb->len - 20;
+ if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
+ return err;
+ put_user(amount, (unsigned long *)arg);
+ return 0;
+ }
+
+ case SIOCGSTAMP:
+ if (sk != NULL) {
+ if (sk->stamp.tv_sec==0)
+ return -ENOENT;
+ if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
+ return err;
+ copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+ return 0;
+ }
+ return -EINVAL;
+
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCSIFMETRIC:
+ return -EINVAL;
+
+ case SIOCADDRT:
+ case SIOCDELRT:
+ if (!suser()) return -EPERM;
+ return x25_route_ioctl(cmd, (void *)arg);
+
+ case SIOCX25GSUBSCRIP:
+ return x25_subscr_ioctl(cmd, (void *)arg);
+
+ case SIOCX25SSUBSCRIP:
+ if (!suser()) return -EPERM;
+ return x25_subscr_ioctl(cmd, (void *)arg);
+
+ case SIOCX25GFACILITIES:
+ if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(facilities))) != 0)
+ return err;
+ facilities = sk->protinfo.x25->facilities;
+ copy_to_user((void *)arg, &facilities, sizeof(facilities));
+ return 0;
+
+ case SIOCX25SFACILITIES:
+ if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(facilities))) != 0)
+ return err;
+ copy_from_user(&facilities, (void *)arg, sizeof(facilities));
+ if (sk->state != TCP_LISTEN)
+ return -EINVAL;
+ if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096)
+ return -EINVAL;
+ if (facilities.pacsize_out < X25_PS16 || facilities.pacsize_out > X25_PS4096)
+ return -EINVAL;
+ if (sk->protinfo.x25->neighbour->extended) {
+ if (facilities.winsize_in < 1 || facilities.winsize_in > 127)
+ return -EINVAL;
+ if (facilities.winsize_out < 1 || facilities.winsize_out > 127)
+ return -EINVAL;
+ } else {
+ if (facilities.winsize_in < 1 || facilities.winsize_in > 7)
+ return -EINVAL;
+ if (facilities.winsize_out < 1 || facilities.winsize_out > 7)
+ return -EINVAL;
+ }
+ if (facilities.throughput < 0x03 || facilities.throughput > 0x2C)
+ return -EINVAL;
+ if (facilities.reverse != 0 && facilities.reverse != 1)
+ return -EINVAL;
+ sk->protinfo.x25->facilities = facilities;
+ return 0;
+
+ case SIOCX25GCALLUSERDATA:
+ if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(calluserdata))) != 0)
+ return err;
+ calluserdata = sk->protinfo.x25->calluserdata;
+ copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata));
+ return 0;
+
+ case SIOCX25SCALLUSERDATA:
+ if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(calluserdata))) != 0)
+ return err;
+ copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata));
+ if (calluserdata.cudlength > X25_MAX_CUD_LEN)
+ return -EINVAL;
+ sk->protinfo.x25->calluserdata = calluserdata;
+ return 0;
+
+ default:
+ return dev_ioctl(cmd, (void *)arg);
+ }
+
+ /*NOTREACHED*/
+ return 0;
+}
+
+static int x25_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct sock *s;
+ struct device *dev;
+ const char *devname;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ cli();
+
+ len += sprintf(buffer, "dest_addr src_addr dev lci st vs vr va t t2 t21 t22 t23 Snd-Q Rcv-Q\n");
+
+ for (s = x25_list; s != NULL; s = s->next) {
+ if (s->protinfo.x25->neighbour == NULL || (dev = s->protinfo.x25->neighbour->dev) == NULL)
+ devname = "???";
+ else
+ devname = s->protinfo.x25->neighbour->dev->name;
+
+ len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n",
+ (s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr,
+ (s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr,
+ devname, s->protinfo.x25->lci & 0x0FFF,
+ s->protinfo.x25->state,
+ s->protinfo.x25->vs, s->protinfo.x25->vr, s->protinfo.x25->va,
+ s->protinfo.x25->timer / X25_SLOWHZ,
+ s->protinfo.x25->t2 / X25_SLOWHZ,
+ s->protinfo.x25->t21 / X25_SLOWHZ,
+ s->protinfo.x25->t22 / X25_SLOWHZ,
+ s->protinfo.x25->t23 / X25_SLOWHZ,
+ atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return(len);
+}
+
+struct net_proto_family x25_family_ops = {
+ AF_X25,
+ x25_create
+};
+
+static struct proto_ops x25_proto_ops = {
+ AF_X25,
+
+ x25_dup,
+ x25_release,
+ x25_bind,
+ x25_connect,
+ x25_socketpair,
+ x25_accept,
+ x25_getname,
+ datagram_poll,
+ x25_ioctl,
+ x25_listen,
+ x25_shutdown,
+ x25_setsockopt,
+ x25_getsockopt,
+ sock_no_fcntl,
+ x25_sendmsg,
+ x25_recvmsg
+};
+
+static struct packet_type x25_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_X25),*/
+ 0, /* copy */
+ x25_lapb_receive_frame,
+ NULL,
+ NULL,
+};
+
+struct notifier_block x25_dev_notifier = {
+ x25_device_event,
+ 0
+};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_x25 = {
+ PROC_NET_X25, 3, "x25",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ x25_get_info
+};
+static struct proc_dir_entry proc_net_x25_links = {
+ PROC_NET_X25_LINKS, 9, "x25_links",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ x25_link_get_info
+};
+static struct proc_dir_entry proc_net_x25_routes = {
+ PROC_NET_X25_ROUTES, 10, "x25_routes",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ x25_routes_get_info
+};
+#endif
+
+void x25_proto_init(struct net_proto *pro)
+{
+ sock_register(&x25_family_ops);
+
+ x25_packet_type.type = htons(ETH_P_X25);
+ dev_add_pack(&x25_packet_type);
+
+ register_netdevice_notifier(&x25_dev_notifier);
+
+ printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n");
+
+#ifdef CONFIG_SYSCTL
+ x25_register_sysctl();
+#endif
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&proc_net_x25);
+ proc_net_register(&proc_net_x25_links);
+ proc_net_register(&proc_net_x25_routes);
+#endif
+}
+
+#ifdef MODULE
+EXPORT_NO_SYMBOLS;
+
+int init_module(void)
+{
+ struct device *dev;
+
+ x25_proto_init(NULL);
+
+ /*
+ * Register any pre existing devices.
+ */
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ || dev->type == ARPHRD_ETHER
+#endif
+ ))
+ x25_link_device_up(dev);
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+
+#ifdef CONFIG_PROC_FS
+ proc_net_unregister(PROC_NET_X25);
+ proc_net_unregister(PROC_NET_X25_LINKS);
+ proc_net_unregister(PROC_NET_X25_ROUTES);
+#endif
+
+ x25_link_free();
+ x25_route_free();
+
+#ifdef CONFIG_SYSCTL
+ x25_unregister_sysctl();
+#endif
+
+ unregister_netdevice_notifier(&x25_dev_notifier);
+
+ dev_remove_pack(&x25_packet_type);
+
+ sock_unregister(AF_X25);
+}
+
+#endif
+
+#endif
diff --git a/net/x25/sysctl_net_x25.c b/net/x25/sysctl_net_x25.c
new file mode 100644
index 000000000..892d817d7
--- /dev/null
+++ b/net/x25/sysctl_net_x25.c
@@ -0,0 +1,57 @@
+/* -*- linux-c -*-
+ * sysctl_net_x25.c: sysctl interface to net X.25 subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/x25 directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <net/x25.h>
+
+static int min_timer[] = {1 * X25_SLOWHZ};
+static int max_timer[] = {300 * X25_SLOWHZ};
+
+static struct ctl_table_header *x25_table_header;
+
+static ctl_table x25_table[] = {
+ {NET_X25_RESTART_REQUEST_TIMEOUT, "restart_request_timeout",
+ &sysctl_x25_restart_request_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+ {NET_X25_CALL_REQUEST_TIMEOUT, "call_request_timeout",
+ &sysctl_x25_call_request_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+ {NET_X25_RESET_REQUEST_TIMEOUT, "reset_request_timeout",
+ &sysctl_x25_reset_request_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+ {NET_X25_CLEAR_REQUEST_TIMEOUT, "clear_request_timeout",
+ &sysctl_x25_clear_request_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+ {NET_X25_ACK_HOLD_BACK_TIMEOUT, "acknowledgement_hold_back_timeout",
+ &sysctl_x25_ack_holdback_timeout, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, NULL, &min_timer, &max_timer},
+ {0}
+};
+
+static ctl_table x25_dir_table[] = {
+ {NET_X25, "x25", NULL, 0, 0555, x25_table},
+ {0}
+};
+
+static ctl_table x25_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, x25_dir_table},
+ {0}
+};
+
+void x25_register_sysctl(void)
+{
+ x25_table_header = register_sysctl_table(x25_root_table, 1);
+}
+
+void x25_unregister_sysctl(void)
+{
+ unregister_sysctl_table(x25_table_header);
+}
diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c
new file mode 100644
index 000000000..6b02a9441
--- /dev/null
+++ b/net/x25/x25_dev.c
@@ -0,0 +1,246 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/stat.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
+#include <linux/firewall.h>
+#include <net/x25.h>
+
+static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh)
+{
+ struct sock *sk;
+ unsigned short frametype;
+ unsigned int lci;
+
+ if (call_in_firewall(PF_X25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) {
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ frametype = skb->data[2];
+ lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF);
+
+ /*
+ * LCI of zero is always for us, and its always a link control
+ * frame.
+ */
+ if (lci == 0) {
+ x25_link_control(skb, neigh, frametype);
+ return 0;
+ }
+
+ /*
+ * Find an existing socket.
+ */
+ if ((sk = x25_find_socket(lci)) != NULL) {
+ skb->h.raw = skb->data;
+ return x25_process_rx_frame(sk, skb);
+ }
+
+ /*
+ * Is is a Call Request ? if so process it.
+ */
+ if (frametype == X25_CALL_REQUEST)
+ return x25_rx_call_request(skb, neigh, lci);
+
+ /*
+ * Its not a Call Request, nor is it a control frame, throw it awa
+ */
+/*
+ x25_transmit_clear_request(neigh, lci, 0x0D);
+*/
+ kfree_skb(skb, FREE_READ);
+
+ return 0;
+}
+
+int x25_lapb_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+ struct x25_neigh *neigh;
+
+ skb->sk = NULL;
+
+ /*
+ * Packet received from unrecognised device, throw it away.
+ */
+ if ((neigh = x25_get_neigh(dev)) == NULL) {
+ printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ switch (skb->data[0]) {
+ case 0x00:
+ skb_pull(skb, 1);
+ return x25_receive_data(skb, neigh);
+
+ case 0x01:
+ x25_link_established(neigh);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+
+ case 0x02:
+ x25_link_terminated(neigh);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+
+ case 0x03:
+ kfree_skb(skb, FREE_READ);
+ return 0;
+
+ default:
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+}
+
+int x25_llc_receive_frame(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
+{
+ struct x25_neigh *neigh;
+
+ skb->sk = NULL;
+
+ /*
+ * Packet received from unrecognised device, throw it away.
+ */
+ if ((neigh = x25_get_neigh(dev)) == NULL) {
+ printk(KERN_DEBUG "X.25: unknown_neighbour - %s\n", dev->name);
+ kfree_skb(skb, FREE_READ);
+ return 0;
+ }
+
+ return x25_receive_data(skb, neigh);
+}
+
+void x25_establish_link(struct x25_neigh *neigh)
+{
+ struct sk_buff *skb;
+ unsigned char *ptr;
+
+ switch (neigh->dev->type) {
+ case ARPHRD_X25:
+ if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
+ printk(KERN_ERR "x25_dev: out of memory\n");
+ return;
+ }
+ ptr = skb_put(skb, 1);
+ *ptr = 0x01;
+ break;
+
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ case ARPHRD_ETHER:
+ return;
+#endif
+ default:
+ return;
+ }
+
+ skb->protocol = htons(ETH_P_X25);
+ skb->priority = SOPRI_NORMAL;
+ skb->dev = neigh->dev;
+ skb->arp = 1;
+
+ dev_queue_xmit(skb);
+}
+
+void x25_terminate_link(struct x25_neigh *neigh)
+{
+ struct sk_buff *skb;
+ unsigned char *ptr;
+
+ switch (neigh->dev->type) {
+ case ARPHRD_X25:
+ if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
+ printk(KERN_ERR "x25_dev: out of memory\n");
+ return;
+ }
+ ptr = skb_put(skb, 1);
+ *ptr = 0x02;
+ break;
+
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ case ARPHRD_ETHER:
+ return;
+#endif
+ default:
+ return;
+ }
+
+ skb->protocol = htons(ETH_P_X25);
+ skb->priority = SOPRI_NORMAL;
+ skb->dev = neigh->dev;
+ skb->arp = 1;
+
+ dev_queue_xmit(skb);
+}
+
+void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh)
+{
+ unsigned char *dptr;
+
+ switch (neigh->dev->type) {
+ case ARPHRD_X25:
+ dptr = skb_push(skb, 1);
+ *dptr = 0x00;
+ break;
+
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ case ARPHRD_ETHER:
+ kfree_skb(skb, FREE_WRITE);
+ return;
+#endif
+ default:
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+ skb->protocol = htons(ETH_P_X25);
+ skb->priority = SOPRI_NORMAL;
+ skb->dev = neigh->dev;
+ skb->arp = 1;
+
+ dev_queue_xmit(skb);
+}
+
+#endif
diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c
new file mode 100644
index 000000000..684c841b7
--- /dev/null
+++ b/net/x25/x25_in.c
@@ -0,0 +1,376 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/ip.h> /* For ip_rcv */
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/x25.h>
+
+static int x25_queue_rx_frame(struct sock *sk, struct sk_buff *skb, int more)
+{
+ struct sk_buff *skbo, *skbn = skb;
+
+ if (more) {
+ sk->protinfo.x25->fraglen += skb->len;
+ skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb);
+ return 0;
+ }
+
+ if (!more && sk->protinfo.x25->fraglen > 0) { /* End of fragment */
+ sk->protinfo.x25->fraglen += skb->len;
+ skb_queue_tail(&sk->protinfo.x25->fragment_queue, skb);
+
+ if ((skbn = alloc_skb(sk->protinfo.x25->fraglen, GFP_ATOMIC)) == NULL)
+ return 1;
+
+ skb_set_owner_r(skbn, sk);
+ skbn->h.raw = skbn->data;
+
+ skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue);
+ memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+ kfree_skb(skbo, FREE_READ);
+
+ while ((skbo = skb_dequeue(&sk->protinfo.x25->fragment_queue)) != NULL) {
+ skb_pull(skbo, (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN);
+ memcpy(skb_put(skbn, skbo->len), skbo->data, skbo->len);
+ kfree_skb(skbo, FREE_READ);
+ }
+
+ sk->protinfo.x25->fraglen = 0;
+ }
+
+ return sock_queue_rcv_skb(sk, skbn);
+}
+
+/*
+ * State machine for state 1, Awaiting Call Accepted State.
+ * The handling of the timer(s) is in file x25_timer.c.
+ * Handling of state 0 and connection release is in af_x25.c.
+ */
+static int x25_state1_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+ x25_address source_addr, dest_addr;
+ struct x25_facilities facilities;
+
+ switch (frametype) {
+
+ case X25_CALL_ACCEPTED:
+ sk->protinfo.x25->condition = 0x00;
+ sk->protinfo.x25->timer = 0;
+ sk->protinfo.x25->vs = 0;
+ sk->protinfo.x25->va = 0;
+ sk->protinfo.x25->vr = 0;
+ sk->protinfo.x25->vl = 0;
+ sk->protinfo.x25->state = X25_STATE_3;
+ sk->state = TCP_ESTABLISHED;
+ /*
+ * Parse the data in the frame.
+ */
+ skb_pull(skb, X25_STD_MIN_LEN);
+ skb_pull(skb, x25_addr_ntoa(skb->data, &source_addr, &dest_addr));
+ skb_pull(skb, x25_parse_facilities(skb, &facilities));
+ /*
+ * Facilities XXX
+ * Copy any Call User Data.
+ */
+ if (skb->len >= 0) {
+ memcpy(sk->protinfo.x25->calluserdata.cuddata, skb->data, skb->len);
+ sk->protinfo.x25->calluserdata.cudlength = skb->len;
+ }
+ if (!sk->dead)
+ sk->state_change(sk);
+ break;
+
+ case X25_CLEAR_REQUEST:
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->err = ECONNREFUSED;
+ sk->shutdown |= SEND_SHUTDOWN;
+ if (!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+ break;
+
+ default:
+ printk(KERN_WARNING "x25: unknown %02X in state 1\n", frametype);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Clear Confirmation State.
+ * The handling of the timer(s) is in file x25_timer.c
+ * Handling of state 0 and connection release is in af_x25.c.
+ */
+static int x25_state2_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+ switch (frametype) {
+
+ case X25_CLEAR_REQUEST:
+ x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
+ case X25_CLEAR_CONFIRMATION:
+ x25_clear_queues(sk);
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->err = 0;
+ sk->shutdown |= SEND_SHUTDOWN;
+ if (!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+ break;
+
+ default:
+ printk(KERN_WARNING "x25: unknown %02X in state 2\n", frametype);
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file x25_timer.c
+ * Handling of state 0 and connection release is in af_x25.c.
+ */
+static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametype, int ns, int nr, int q, int d, int m)
+{
+ int queued = 0;
+ int modulus;
+
+ modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS;
+
+ switch (frametype) {
+
+ case X25_RESET_REQUEST:
+ x25_write_internal(sk, X25_RESET_CONFIRMATION);
+ sk->protinfo.x25->condition = 0x00;
+ sk->protinfo.x25->timer = 0;
+ sk->protinfo.x25->vs = 0;
+ sk->protinfo.x25->vr = 0;
+ sk->protinfo.x25->va = 0;
+ sk->protinfo.x25->vl = 0;
+ break;
+
+ case X25_CLEAR_REQUEST:
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->err = 0;
+ sk->shutdown |= SEND_SHUTDOWN;
+ if (!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+ break;
+
+ case X25_RR:
+ case X25_RNR:
+ if (frametype == X25_RNR) {
+ sk->protinfo.x25->condition |= X25_COND_PEER_RX_BUSY;
+ } else {
+ sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY;
+ }
+ if (!x25_validate_nr(sk, nr)) {
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_RESET_REQUEST);
+ sk->protinfo.x25->condition = 0x00;
+ sk->protinfo.x25->vs = 0;
+ sk->protinfo.x25->vr = 0;
+ sk->protinfo.x25->va = 0;
+ sk->protinfo.x25->vl = 0;
+ sk->protinfo.x25->state = X25_STATE_4;
+ sk->protinfo.x25->timer = sk->protinfo.x25->t22;
+ } else {
+ if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) {
+ sk->protinfo.x25->va = nr;
+ } else {
+ x25_check_iframes_acked(sk, nr);
+ }
+ }
+ break;
+
+ case X25_DATA: /* XXX */
+ sk->protinfo.x25->condition &= ~X25_COND_PEER_RX_BUSY;
+ if (!x25_validate_nr(sk, nr)) {
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_RESET_REQUEST);
+ sk->protinfo.x25->condition = 0x00;
+ sk->protinfo.x25->vs = 0;
+ sk->protinfo.x25->vr = 0;
+ sk->protinfo.x25->va = 0;
+ sk->protinfo.x25->vl = 0;
+ sk->protinfo.x25->state = X25_STATE_4;
+ sk->protinfo.x25->timer = sk->protinfo.x25->t22;
+ break;
+ }
+ if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) {
+ sk->protinfo.x25->va = nr;
+ } else {
+ x25_check_iframes_acked(sk, nr);
+ }
+ if (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY)
+ break;
+ if (ns == sk->protinfo.x25->vr) {
+ if (x25_queue_rx_frame(sk, skb, m) == 0) {
+ sk->protinfo.x25->vr = (sk->protinfo.x25->vr + 1) % modulus;
+ queued = 1;
+ } else {
+ sk->protinfo.x25->condition |= X25_COND_OWN_RX_BUSY;
+ }
+ }
+ /*
+ * If the window is full Ack it immediately, else
+ * start the holdback timer.
+ */
+ if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) {
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
+ sk->protinfo.x25->timer = 0;
+ x25_enquiry_response(sk);
+ } else {
+ sk->protinfo.x25->condition |= X25_COND_ACK_PENDING;
+ sk->protinfo.x25->timer = sk->protinfo.x25->t2;
+ }
+ break;
+
+ case X25_INTERRUPT_CONFIRMATION:
+ sk->protinfo.x25->intflag = 0;
+ break;
+
+ case X25_INTERRUPT:
+ if (sk->urginline) {
+ queued = (sock_queue_rcv_skb(sk, skb) == 0);
+ } else {
+ skb_set_owner_r(skb, sk);
+ skb_queue_tail(&sk->protinfo.x25->interrupt_in_queue, skb);
+ queued = 1;
+ }
+ if (sk->proc != 0) {
+ if (sk->proc > 0)
+ kill_proc(sk->proc, SIGURG, 1);
+ else
+ kill_pg(-sk->proc, SIGURG, 1);
+ }
+ x25_write_internal(sk, X25_INTERRUPT_CONFIRMATION);
+ break;
+
+ default:
+ printk(KERN_WARNING "x25: unknown %02X in state 3\n", frametype);
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Awaiting Reset Confirmation State.
+ * The handling of the timer(s) is in file x25_timer.c
+ * Handling of state 0 and connection release is in af_x25.c.
+ */
+static int x25_state4_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+ switch (frametype) {
+
+ case X25_RESET_REQUEST:
+ x25_write_internal(sk, X25_RESET_CONFIRMATION);
+ case X25_RESET_CONFIRMATION:
+ sk->protinfo.x25->timer = 0;
+ sk->protinfo.x25->condition = 0x00;
+ sk->protinfo.x25->va = 0;
+ sk->protinfo.x25->vr = 0;
+ sk->protinfo.x25->vs = 0;
+ sk->protinfo.x25->vl = 0;
+ sk->protinfo.x25->state = X25_STATE_3;
+ break;
+
+ case X25_CLEAR_REQUEST:
+ x25_clear_queues(sk);
+ x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
+ sk->protinfo.x25->timer = 0;
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->err = 0;
+ sk->shutdown |= SEND_SHUTDOWN;
+ if (!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+ break;
+
+ default:
+ printk(KERN_WARNING "x25: unknown %02X in state 4\n", frametype);
+ break;
+ }
+
+ return 0;
+}
+
+/* Higher level upcall for a LAPB frame */
+int x25_process_rx_frame(struct sock *sk, struct sk_buff *skb)
+{
+ int queued = 0, frametype, ns, nr, q, d, m;
+
+ if (sk->protinfo.x25->state == X25_STATE_0)
+ return 0;
+
+ del_timer(&sk->timer);
+
+ frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m);
+
+ switch (sk->protinfo.x25->state) {
+ case X25_STATE_1:
+ queued = x25_state1_machine(sk, skb, frametype);
+ break;
+ case X25_STATE_2:
+ queued = x25_state2_machine(sk, skb, frametype);
+ break;
+ case X25_STATE_3:
+ queued = x25_state3_machine(sk, skb, frametype, ns, nr, q, d, m);
+ break;
+ case X25_STATE_4:
+ queued = x25_state4_machine(sk, skb, frametype);
+ break;
+ }
+
+ x25_set_timer(sk);
+
+ return queued;
+}
+
+#endif
diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c
new file mode 100644
index 000000000..b3fce4696
--- /dev/null
+++ b/net/x25/x25_link.c
@@ -0,0 +1,482 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/firewall.h>
+#include <net/x25.h>
+
+static struct x25_neigh *x25_neigh_list = NULL;
+
+static void x25_link_timer(unsigned long);
+
+/*
+ * Linux set/reset timer routines
+ */
+static void x25_link_set_timer(struct x25_neigh *neigh)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ del_timer(&neigh->timer);
+ restore_flags(flags);
+
+ neigh->timer.data = (unsigned long)neigh;
+ neigh->timer.function = &x25_link_timer;
+ neigh->timer.expires = jiffies + (HZ / 1);
+
+ add_timer(&neigh->timer);
+}
+
+/*
+ * X.25 Link TIMER
+ *
+ * This routine is called every second. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+static void x25_link_timer(unsigned long param)
+{
+ struct x25_neigh *neigh = (struct x25_neigh *)param;
+
+ if (neigh->t20timer == 0 || --neigh->t20timer > 0) {
+ x25_link_set_timer(neigh);
+ return;
+ }
+
+ /*
+ * T20 for a link has expired.
+ */
+ x25_transmit_restart_request(neigh);
+
+ neigh->t20timer = neigh->t20;
+
+ x25_link_set_timer(neigh);
+}
+
+/*
+ * This handles all restart and diagnostic frames.
+ */
+void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, unsigned short frametype)
+{
+ struct sk_buff *skbn;
+
+ switch (frametype) {
+ case X25_RESTART_REQUEST:
+ neigh->t20timer = 0;
+ neigh->state = X25_LINK_STATE_3;
+ del_timer(&neigh->timer);
+ x25_transmit_restart_confirmation(neigh);
+ break;
+
+ case X25_RESTART_CONFIRMATION:
+ neigh->t20timer = 0;
+ neigh->state = X25_LINK_STATE_3;
+ del_timer(&neigh->timer);
+ break;
+
+ case X25_DIAGNOSTIC:
+ printk(KERN_WARNING "x25: diagnostic #%d\n", skb->data[3]);
+ break;
+
+ default:
+ printk(KERN_WARNING "x25: received unknown %02X with LCI 000\n", frametype);
+ break;
+ }
+
+ if (neigh->state == X25_LINK_STATE_3) {
+ while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
+ x25_send_frame(skbn, neigh);
+ }
+}
+
+/*
+ * This routine is called when a Restart Request is needed
+ */
+void x25_transmit_restart_request(struct x25_neigh *neigh)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ int len;
+
+ len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
+
+ if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, X25_MAX_L2_LEN);
+
+ dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
+
+ *dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
+ *dptr++ = 0x00;
+ *dptr++ = X25_RESTART_REQUEST;
+ *dptr++ = 0x00;
+ *dptr++ = 0;
+
+ skb->sk = NULL;
+
+ x25_send_frame(skb, neigh);
+}
+
+/*
+ * This routine is called when a Restart Confirmation is needed
+ */
+void x25_transmit_restart_confirmation(struct x25_neigh *neigh)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ int len;
+
+ len = X25_MAX_L2_LEN + X25_STD_MIN_LEN;
+
+ if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, X25_MAX_L2_LEN);
+
+ dptr = skb_put(skb, X25_STD_MIN_LEN);
+
+ *dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
+ *dptr++ = 0x00;
+ *dptr++ = X25_RESTART_CONFIRMATION;
+
+ skb->sk = NULL;
+
+ x25_send_frame(skb, neigh);
+}
+
+/*
+ * This routine is called when a Diagnostic is required.
+ */
+void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ int len;
+
+ len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 1;
+
+ if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, X25_MAX_L2_LEN);
+
+ dptr = skb_put(skb, X25_STD_MIN_LEN + 1);
+
+ *dptr++ = (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
+ *dptr++ = 0x00;
+ *dptr++ = X25_DIAGNOSTIC;
+ *dptr++ = diag;
+
+ skb->sk = NULL;
+
+ x25_send_frame(skb, neigh);
+}
+
+/*
+ * This routine is called when a Clear Request is needed outside of the context
+ * of a connected socket.
+ */
+void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsigned char cause)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ int len;
+
+ len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
+
+ if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, X25_MAX_L2_LEN);
+
+ dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
+
+ *dptr++ = ((lci >> 8) & 0x0F) | (neigh->extended) ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
+ *dptr++ = ((lci >> 0) & 0xFF);
+ *dptr++ = X25_CLEAR_REQUEST;
+ *dptr++ = cause;
+ *dptr++ = 0x00;
+
+ skb->sk = NULL;
+
+ x25_send_frame(skb, neigh);
+}
+
+void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
+{
+ if (call_fw_firewall(PF_X25, skb->dev, skb->data, NULL,&skb) != FW_ACCEPT)
+ return;
+
+ switch (neigh->state) {
+ case X25_LINK_STATE_0:
+ skb_queue_tail(&neigh->queue, skb);
+ neigh->state = X25_LINK_STATE_1;
+ x25_establish_link(neigh);
+ break;
+ case X25_LINK_STATE_1:
+ case X25_LINK_STATE_2:
+ skb_queue_tail(&neigh->queue, skb);
+ break;
+ case X25_LINK_STATE_3:
+ x25_send_frame(skb, neigh);
+ break;
+ }
+}
+
+/*
+ * Called when the link layer has become established.
+ */
+void x25_link_established(struct x25_neigh *neigh)
+{
+ switch (neigh->state) {
+ case X25_LINK_STATE_0:
+ neigh->state = X25_LINK_STATE_2;
+ break;
+ case X25_LINK_STATE_1:
+ x25_transmit_restart_request(neigh);
+ neigh->state = X25_LINK_STATE_2;
+ neigh->t20timer = neigh->t20;
+ x25_link_set_timer(neigh);
+ break;
+ }
+}
+
+/*
+ * Called when the link layer has terminated, or an establishment
+ * request has failed. XXX should tell sockets.
+ */
+void x25_link_terminated(struct x25_neigh *neigh)
+{
+ neigh->state = X25_LINK_STATE_0;
+}
+
+/*
+ * Add a new device.
+ */
+void x25_link_device_up(struct device *dev)
+{
+ struct x25_neigh *x25_neigh;
+ unsigned long flags;
+
+ if ((x25_neigh = (struct x25_neigh *)kmalloc(sizeof(*x25_neigh), GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_queue_head_init(&x25_neigh->queue);
+ init_timer(&x25_neigh->timer);
+
+ x25_neigh->dev = dev;
+ x25_neigh->state = X25_LINK_STATE_0;
+ x25_neigh->extended = 0;
+ x25_neigh->t20timer = 0;
+ x25_neigh->t20 = sysctl_x25_restart_request_timeout;
+
+ save_flags(flags); cli();
+ x25_neigh->next = x25_neigh_list;
+ x25_neigh_list = x25_neigh;
+ restore_flags(flags);
+}
+
+static void x25_remove_neigh(struct x25_neigh *x25_neigh)
+{
+ struct x25_neigh *s;
+ unsigned long flags;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&x25_neigh->queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+
+ del_timer(&x25_neigh->timer);
+
+ save_flags(flags);
+ cli();
+
+ if ((s = x25_neigh_list) == x25_neigh) {
+ x25_neigh_list = x25_neigh->next;
+ restore_flags(flags);
+ kfree_s(x25_neigh, sizeof(struct x25_neigh));
+ return;
+ }
+
+ while (s != NULL && s->next != NULL) {
+ if (s->next == x25_neigh) {
+ s->next = x25_neigh->next;
+ restore_flags(flags);
+ kfree_s(x25_neigh, sizeof(struct x25_neigh));
+ return;
+ }
+
+ s = s->next;
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * A device has been removed, remove its links.
+ */
+void x25_link_device_down(struct device *dev)
+{
+ struct x25_neigh *neigh, *x25_neigh = x25_neigh_list;
+
+ while (x25_neigh != NULL) {
+ neigh = x25_neigh;
+ x25_neigh = x25_neigh->next;
+
+ if (neigh->dev == dev)
+ x25_remove_neigh(neigh);
+ }
+}
+
+/*
+ * Given a device, return the neighbour address.
+ */
+struct x25_neigh *x25_get_neigh(struct device *dev)
+{
+ struct x25_neigh *x25_neigh;
+
+ for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next)
+ if (x25_neigh->dev == dev)
+ return x25_neigh;
+
+ return NULL;
+}
+
+/*
+ * Handle the ioctls that control the subscription functions.
+ */
+int x25_subscr_ioctl(unsigned int cmd, void *arg)
+{
+ struct x25_subscrip_struct x25_subscr;
+ struct x25_neigh *x25_neigh;
+ struct device *dev;
+ int err;
+
+ switch (cmd) {
+
+ case SIOCX25GSUBSCRIP:
+ if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct x25_subscrip_struct))) != 0)
+ return err;
+ if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
+ return -EINVAL;
+ if ((x25_neigh = x25_get_neigh(dev)) == NULL)
+ return -EINVAL;
+ x25_subscr.extended = x25_neigh->extended;
+ copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct));
+ break;
+
+ case SIOCX25SSUBSCRIP:
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_subscrip_struct))) != 0)
+ return err;
+ copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct));
+ if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
+ return -EINVAL;
+ if ((x25_neigh = x25_get_neigh(dev)) == NULL)
+ return -EINVAL;
+ if (x25_subscr.extended != 0 && x25_subscr.extended != 1)
+ return -EINVAL;
+ x25_neigh->extended = x25_subscr.extended;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int x25_link_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct x25_neigh *x25_neigh;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ cli();
+
+ len += sprintf(buffer, "device st t20 ext\n");
+
+ for (x25_neigh = x25_neigh_list; x25_neigh != NULL; x25_neigh = x25_neigh->next) {
+ len += sprintf(buffer + len, "%-6s %2d %3d/%03d %d\n",
+ x25_neigh->dev->name,
+ x25_neigh->state,
+ x25_neigh->t20timer / X25_SLOWHZ,
+ x25_neigh->t20 / X25_SLOWHZ,
+ x25_neigh->extended);
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+#ifdef MODULE
+
+/*
+ * Release all memory associated with X.25 neighbour structures.
+ */
+void x25_link_free(void)
+{
+ struct x25_neigh *neigh, *x25_neigh = x25_neigh_list;
+
+ while (x25_neigh != NULL) {
+ neigh = x25_neigh;
+ x25_neigh = x25_neigh->next;
+
+ x25_remove_neigh(neigh);
+ }
+}
+
+#endif
+
+#endif
diff --git a/net/x25/x25_out.c b/net/x25/x25_out.c
new file mode 100644
index 000000000..321baa5d6
--- /dev/null
+++ b/net/x25/x25_out.c
@@ -0,0 +1,205 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/x25.h>
+
+static int x25_pacsize_to_bytes(unsigned int pacsize)
+{
+ int bytes = 1;
+
+ if (pacsize == 0)
+ return 128;
+
+ while (pacsize-- > 0)
+ bytes *= 2;
+
+ return bytes;
+}
+
+/*
+ * This is where all X.25 information frames pass;
+ */
+void x25_output(struct sock *sk, struct sk_buff *skb)
+{
+ struct sk_buff *skbn;
+ unsigned char header[X25_EXT_MIN_LEN];
+ int err, frontlen, len, header_len, max_len;
+
+ header_len = (sk->protinfo.x25->neighbour->extended) ? X25_EXT_MIN_LEN : X25_STD_MIN_LEN;
+ max_len = x25_pacsize_to_bytes(sk->protinfo.x25->facilities.pacsize_out);
+
+ if (skb->len - header_len > max_len) {
+ /* Save a copy of the Header */
+ memcpy(header, skb->data, header_len);
+ skb_pull(skb, header_len);
+
+ frontlen = skb_headroom(skb);
+
+ while (skb->len > 0) {
+ if ((skbn = sock_alloc_send_skb(sk, frontlen + max_len, 0, 0, &err)) == NULL)
+ return;
+
+ skb_reserve(skbn, frontlen);
+
+ len = (max_len > skb->len) ? skb->len : max_len;
+
+ /* Copy the user data */
+ memcpy(skb_put(skbn, len), skb->data, len);
+ skb_pull(skb, len);
+
+ /* Duplicate the Header */
+ skb_push(skbn, header_len);
+ memcpy(skbn->data, header, header_len);
+
+ if (skb->len > 0) {
+ if (sk->protinfo.x25->neighbour->extended)
+ skbn->data[3] |= X25_EXT_M_BIT;
+ else
+ skbn->data[2] |= X25_STD_M_BIT;
+ }
+
+ skb_queue_tail(&sk->write_queue, skbn);
+ }
+
+ kfree_skb(skb, FREE_WRITE);
+ } else {
+ skb_queue_tail(&sk->write_queue, skb);
+ }
+}
+
+/*
+ * This procedure is passed a buffer descriptor for an iframe. It builds
+ * the rest of the control part of the frame and then writes it out.
+ */
+static void x25_send_iframe(struct sock *sk, struct sk_buff *skb)
+{
+ if (skb == NULL)
+ return;
+
+ if (sk->protinfo.x25->neighbour->extended) {
+ skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0xFE;
+ skb->data[3] |= (sk->protinfo.x25->vr << 1) & 0xFE;
+ } else {
+ skb->data[2] |= (sk->protinfo.x25->vs << 1) & 0x0E;
+ skb->data[2] |= (sk->protinfo.x25->vr << 5) & 0xE0;
+ }
+
+ x25_transmit_link(skb, sk->protinfo.x25->neighbour);
+}
+
+void x25_kick(struct sock *sk)
+{
+ struct sk_buff *skb;
+ unsigned short end;
+ int modulus;
+
+ del_timer(&sk->timer);
+
+ /*
+ * Transmit interrupt data.
+ */
+ if (!sk->protinfo.x25->intflag && skb_peek(&sk->protinfo.x25->interrupt_out_queue) != NULL) {
+ sk->protinfo.x25->intflag = 1;
+ skb = skb_dequeue(&sk->protinfo.x25->interrupt_out_queue);
+ x25_transmit_link(skb, sk->protinfo.x25->neighbour);
+ }
+
+ modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS;
+ end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus;
+
+ /*
+ * Transmit normal stream data.
+ */
+ if (!(sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) &&
+ sk->protinfo.x25->vs != end &&
+ skb_peek(&sk->write_queue) != NULL) {
+ /*
+ * Transmit data until either we're out of data to send or
+ * the window is full.
+ */
+
+ skb = skb_dequeue(&sk->write_queue);
+
+ do {
+ /*
+ * Transmit the frame.
+ */
+ x25_send_iframe(sk, skb);
+
+ sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus;
+
+ } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+
+ sk->protinfo.x25->vl = sk->protinfo.x25->vr;
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
+ sk->protinfo.x25->timer = 0;
+ }
+
+ x25_set_timer(sk);
+}
+
+/*
+ * The following routines are taken from page 170 of the 7th ARRL Computer
+ * Networking Conference paper, as is the whole state machine.
+ */
+
+void x25_enquiry_response(struct sock *sk)
+{
+ if (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY)
+ x25_write_internal(sk, X25_RNR);
+ else
+ x25_write_internal(sk, X25_RR);
+
+ sk->protinfo.x25->vl = sk->protinfo.x25->vr;
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
+ sk->protinfo.x25->timer = 0;
+}
+
+void x25_check_iframes_acked(struct sock *sk, unsigned short nr)
+{
+ if (sk->protinfo.x25->vs == nr) {
+ sk->protinfo.x25->va = nr;
+ } else {
+ if (sk->protinfo.x25->va != nr) {
+ sk->protinfo.x25->va = nr;
+ }
+ }
+}
+
+#endif
diff --git a/net/x25/x25_route.c b/net/x25/x25_route.c
new file mode 100644
index 000000000..fc1bfc54b
--- /dev/null
+++ b/net/x25/x25_route.c
@@ -0,0 +1,273 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/arp.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h> /* For TIOCINQ/OUTQ */
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <linux/firewall.h>
+#include <net/x25.h>
+
+static struct x25_route *x25_route_list = NULL;
+
+/*
+ * Add a new route.
+ */
+static int x25_add_route(x25_address *address, unsigned int sigdigits, struct device *dev)
+{
+ struct x25_route *x25_route;
+ unsigned long flags;
+
+ for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next)
+ if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits)
+ return -EINVAL;
+
+ if ((x25_route = (struct x25_route *)kmalloc(sizeof(*x25_route), GFP_ATOMIC)) == NULL)
+ return -ENOMEM;
+
+ strcpy(x25_route->address.x25_addr, "000000000000000");
+ memcpy(x25_route->address.x25_addr, address->x25_addr, sigdigits);
+
+ x25_route->sigdigits = sigdigits;
+ x25_route->dev = dev;
+
+ save_flags(flags); cli();
+ x25_route->next = x25_route_list;
+ x25_route_list = x25_route;
+ restore_flags(flags);
+
+ return 0;
+}
+
+static void x25_remove_route(struct x25_route *x25_route)
+{
+ struct x25_route *s;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if ((s = x25_route_list) == x25_route) {
+ x25_route_list = x25_route->next;
+ restore_flags(flags);
+ kfree_s(x25_route, sizeof(struct x25_route));
+ return;
+ }
+
+ while (s != NULL && s->next != NULL) {
+ if (s->next == x25_route) {
+ s->next = x25_route->next;
+ restore_flags(flags);
+ kfree_s(x25_route, sizeof(struct x25_route));
+ return;
+ }
+
+ s = s->next;
+ }
+
+ restore_flags(flags);
+}
+
+static int x25_del_route(x25_address *address, unsigned int sigdigits, struct device *dev)
+{
+ struct x25_route *x25_route;
+
+ for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
+ if (memcmp(&x25_route->address, address, sigdigits) == 0 && x25_route->sigdigits == sigdigits && x25_route->dev == dev) {
+ x25_remove_route(x25_route);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * A device has been removed, remove its routes.
+ */
+void x25_route_device_down(struct device *dev)
+{
+ struct x25_route *route, *x25_route = x25_route_list;
+
+ while (x25_route != NULL) {
+ route = x25_route;
+ x25_route = x25_route->next;
+
+ if (route->dev == dev)
+ x25_remove_route(route);
+ }
+}
+
+/*
+ * Check that the device given is a valid X.25 interface that is "up".
+ */
+struct device *x25_dev_get(char *devname)
+{
+ struct device *dev;
+
+ if ((dev = dev_get(devname)) == NULL)
+ return NULL;
+
+ if ((dev->flags & IFF_UP) && (dev->type == ARPHRD_X25
+#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
+ || dev->type == ARPHRD_ETHER
+#endif
+ ))
+ return dev;
+
+ return NULL;
+}
+
+/*
+ * Find a device given an X.25 address.
+ */
+struct device *x25_get_route(x25_address *addr)
+{
+ struct x25_route *route, *use = NULL;
+
+ for (route = x25_route_list; route != NULL; route = route->next) {
+ if (memcmp(&route->address, addr, route->sigdigits) == 0) {
+ if (use == NULL) {
+ use = route;
+ } else {
+ if (route->sigdigits > use->sigdigits)
+ use = route;
+ }
+ }
+ }
+
+ return (use != NULL) ? use->dev : NULL;
+}
+
+/*
+ * Handle the ioctls that control the routing functions.
+ */
+int x25_route_ioctl(unsigned int cmd, void *arg)
+{
+ struct x25_route_struct x25_route;
+ struct device *dev;
+ int err;
+
+ switch (cmd) {
+
+ case SIOCADDRT:
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0)
+ return err;
+ copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct));
+ if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
+ return -EINVAL;
+ if ((dev = x25_dev_get(x25_route.device)) == NULL)
+ return -EINVAL;
+ return x25_add_route(&x25_route.address, x25_route.sigdigits, dev);
+
+ case SIOCDELRT:
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0)
+ return err;
+ copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct));
+ if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
+ return -EINVAL;
+ if ((dev = x25_dev_get(x25_route.device)) == NULL)
+ return -EINVAL;
+ return x25_del_route(&x25_route.address, x25_route.sigdigits, dev);
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int x25_routes_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ struct x25_route *x25_route;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ cli();
+
+ len += sprintf(buffer, "address digits device\n");
+
+ for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
+ len += sprintf(buffer + len, "%-15s %-6d %-5s\n",
+ x25_route->address.x25_addr, x25_route->sigdigits,
+ (x25_route->dev != NULL) ? x25_route->dev->name : "???");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+ sti();
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+#ifdef MODULE
+
+/*
+ * Release all memory associated with X.25 routing structures.
+ */
+void x25_route_free(void)
+{
+ struct x25_route *route, *x25_route = x25_route_list;
+
+ while (x25_route != NULL) {
+ route = x25_route;
+ x25_route = x25_route->next;
+
+ x25_remove_route(route);
+ }
+}
+
+#endif
+
+#endif
diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c
new file mode 100644
index 000000000..245109ee9
--- /dev/null
+++ b/net/x25/x25_subr.c
@@ -0,0 +1,386 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/x25.h>
+
+/*
+ * This routine purges all of the queues of frames.
+ */
+void x25_clear_queues(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&sk->write_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+
+ while ((skb = skb_dequeue(&sk->protinfo.x25->interrupt_in_queue)) != NULL)
+ kfree_skb(skb, FREE_READ);
+
+ while ((skb = skb_dequeue(&sk->protinfo.x25->interrupt_out_queue)) != NULL)
+ kfree_skb(skb, FREE_WRITE);
+
+ while ((skb = skb_dequeue(&sk->protinfo.x25->fragment_queue)) != NULL)
+ kfree_skb(skb, FREE_READ);
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+int x25_validate_nr(struct sock *sk, unsigned short nr)
+{
+ unsigned short vc = sk->protinfo.x25->va;
+ int modulus;
+
+ modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS;
+
+ while (vc != sk->protinfo.x25->vs) {
+ if (nr == vc) return 1;
+ vc = (vc + 1) % modulus;
+ }
+
+ if (nr == sk->protinfo.x25->vs) return 1;
+
+ return 0;
+}
+
+/*
+ * This routine is called when the packet layer internally generates a
+ * control frame.
+ */
+void x25_write_internal(struct sock *sk, int frametype)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ unsigned char facilities[X25_MAX_FAC_LEN];
+ unsigned char addresses[1 + X25_ADDR_LEN];
+ unsigned char lci1, lci2;
+ int len;
+
+ /*
+ * Default safe frame size.
+ */
+ len = X25_MAX_L2_LEN + X25_EXT_MIN_LEN;
+
+ /*
+ * Adjust frame size.
+ */
+ switch (frametype) {
+ case X25_CALL_REQUEST:
+ len += 1 + X25_ADDR_LEN + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN;
+ break;
+ case X25_CALL_ACCEPTED:
+ len += 1 + X25_MAX_FAC_LEN + X25_MAX_CUD_LEN;
+ break;
+ case X25_CLEAR_REQUEST:
+ case X25_RESET_REQUEST:
+ len += 2;
+ break;
+ case X25_RR:
+ case X25_RNR:
+ case X25_REJ:
+ case X25_CLEAR_CONFIRMATION:
+ case X25_INTERRUPT_CONFIRMATION:
+ case X25_RESET_CONFIRMATION:
+ break;
+ default:
+ printk(KERN_ERR "X.25: invalid frame type %02X\n", frametype);
+ return;
+ }
+
+ if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+ return;
+
+ /*
+ * Space for Ethernet and 802.2 LLC headers.
+ */
+ skb_reserve(skb, X25_MAX_L2_LEN);
+
+ /*
+ * Make space for the GFI and LCI, and fill them in.
+ */
+ dptr = skb_put(skb, 2);
+
+ lci1 = (sk->protinfo.x25->lci >> 8) & 0x0F;
+ lci2 = (sk->protinfo.x25->lci >> 0) & 0xFF;
+
+ if (sk->protinfo.x25->neighbour->extended) {
+ *dptr++ = lci1 | X25_GFI_EXTSEQ;
+ *dptr++ = lci2;
+ } else {
+ *dptr++ = lci1 | X25_GFI_STDSEQ;
+ *dptr++ = lci2;
+ }
+
+ /*
+ * Now fill in the frame type specific information.
+ */
+ switch (frametype) {
+
+ case X25_CALL_REQUEST:
+ dptr = skb_put(skb, 1);
+ *dptr++ = X25_CALL_REQUEST;
+ len = x25_addr_aton(addresses, &sk->protinfo.x25->dest_addr, &sk->protinfo.x25->source_addr);
+ dptr = skb_put(skb, len);
+ memcpy(dptr, addresses, len);
+ len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities);
+ dptr = skb_put(skb, len);
+ memcpy(dptr, facilities, len);
+ dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength);
+ memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength);
+ sk->protinfo.x25->calluserdata.cudlength = 0;
+ break;
+
+ case X25_CALL_ACCEPTED:
+ dptr = skb_put(skb, 2);
+ *dptr++ = X25_CALL_ACCEPTED;
+ *dptr++ = 0x00; /* Address lengths */
+ len = x25_create_facilities(facilities, &sk->protinfo.x25->facilities);
+ dptr = skb_put(skb, len);
+ memcpy(dptr, facilities, len);
+ dptr = skb_put(skb, sk->protinfo.x25->calluserdata.cudlength);
+ memcpy(dptr, sk->protinfo.x25->calluserdata.cuddata, sk->protinfo.x25->calluserdata.cudlength);
+ sk->protinfo.x25->calluserdata.cudlength = 0;
+ break;
+
+ case X25_CLEAR_REQUEST:
+ case X25_RESET_REQUEST:
+ dptr = skb_put(skb, 3);
+ *dptr++ = frametype;
+ *dptr++ = 0x00; /* XXX */
+ *dptr++ = 0x00; /* XXX */
+ break;
+
+ case X25_RR:
+ case X25_RNR:
+ case X25_REJ:
+ if (sk->protinfo.x25->neighbour->extended) {
+ dptr = skb_put(skb, 2);
+ *dptr++ = frametype;
+ *dptr++ = (sk->protinfo.x25->vr << 1) & 0xFE;
+ } else {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr++ |= (sk->protinfo.x25->vr << 5) & 0xE0;
+ }
+ break;
+
+ case X25_CLEAR_CONFIRMATION:
+ case X25_INTERRUPT_CONFIRMATION:
+ case X25_RESET_CONFIRMATION:
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ break;
+ }
+
+ x25_transmit_link(skb, sk->protinfo.x25->neighbour);
+}
+
+/*
+ * Unpick the contents of the passed X.25 Packet Layer frame.
+ */
+int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q, int *d, int *m)
+{
+ unsigned char *frame;
+
+ frame = skb->data;
+
+ *ns = *nr = *q = *d = *m = 0;
+
+ switch (frame[2]) {
+ case X25_CALL_REQUEST:
+ case X25_CALL_ACCEPTED:
+ case X25_CLEAR_REQUEST:
+ case X25_CLEAR_CONFIRMATION:
+ case X25_INTERRUPT:
+ case X25_INTERRUPT_CONFIRMATION:
+ case X25_RESET_REQUEST:
+ case X25_RESET_CONFIRMATION:
+ case X25_RESTART_REQUEST:
+ case X25_RESTART_CONFIRMATION:
+ case X25_REGISTRATION_REQUEST:
+ case X25_REGISTRATION_CONFIRMATION:
+ case X25_DIAGNOSTIC:
+ return frame[2];
+ }
+
+ if (sk->protinfo.x25->neighbour->extended) {
+ if (frame[2] == X25_RR ||
+ frame[2] == X25_RNR ||
+ frame[2] == X25_REJ) {
+ *nr = (frame[3] >> 1) & 0x7F;
+ return frame[2];
+ }
+ } else {
+ if ((frame[2] & 0x1F) == X25_RR ||
+ (frame[2] & 0x1F) == X25_RNR ||
+ (frame[2] & 0x1F) == X25_REJ) {
+ *nr = (frame[2] >> 5) & 0x07;
+ return frame[2] & 0x1F;
+ }
+ }
+
+ if (sk->protinfo.x25->neighbour->extended) {
+ if ((frame[2] & 0x01) == X25_DATA) {
+ *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT;
+ *d = (frame[0] & X25_D_BIT) == X25_D_BIT;
+ *m = (frame[3] & X25_EXT_M_BIT) == X25_EXT_M_BIT;
+ *nr = (frame[3] >> 1) & 0x7F;
+ *ns = (frame[2] >> 1) & 0x7F;
+ return X25_DATA;
+ }
+ } else {
+ if ((frame[2] & 0x01) == X25_DATA) {
+ *q = (frame[0] & X25_Q_BIT) == X25_Q_BIT;
+ *d = (frame[0] & X25_D_BIT) == X25_D_BIT;
+ *m = (frame[2] & X25_STD_M_BIT) == X25_STD_M_BIT;
+ *nr = (frame[2] >> 5) & 0x07;
+ *ns = (frame[2] >> 1) & 0x07;
+ return X25_DATA;
+ }
+ }
+
+ printk(KERN_DEBUG "X.25: invalid PLP frame %02X %02X %02X\n", frame[0], frame[1], frame[2]);
+
+ return X25_ILLEGAL;
+}
+
+/*
+ * Parse a set of facilities into the facilities structure. Unrecognised
+ * facilities are written to the debug log file.
+ */
+int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities)
+{
+ unsigned int len;
+ unsigned char *p = skb->data;
+
+ memset(facilities, 0x00, sizeof(struct x25_facilities));
+
+ len = *p++;
+
+ while (len > 0) {
+ switch (*p & X25_FAC_CLASS_MASK) {
+ case X25_FAC_CLASS_A:
+ switch (*p) {
+ case X25_FAC_REVERSE:
+ facilities->reverse = (p[1] & 0x01);
+ break;
+ case X25_FAC_THROUGHPUT:
+ facilities->throughput = p[1];
+ break;
+ default:
+ printk(KERN_DEBUG "X.25: unknown facility %02X, value %02X\n", p[0], p[1]);
+ break;
+ }
+ p += 2;
+ len -= 2;
+ break;
+
+ case X25_FAC_CLASS_B:
+ switch (*p) {
+ case X25_FAC_PACKET_SIZE:
+ facilities->pacsize_in = p[1];
+ facilities->pacsize_out = p[2];
+ break;
+ case X25_FAC_WINDOW_SIZE:
+ facilities->winsize_in = p[1];
+ facilities->winsize_out = p[2];
+ break;
+ default:
+ printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X\n", p[0], p[1], p[2]);
+ break;
+ }
+ p += 3;
+ len -= 3;
+ break;
+
+ case X25_FAC_CLASS_C:
+ printk(KERN_DEBUG "X.25: unknown facility %02X, values %02X, %02X, %02X\n", p[0], p[1], p[2], p[3]);
+ p += 4;
+ len -= 4;
+ break;
+
+ case X25_FAC_CLASS_D:
+ printk(KERN_DEBUG "X.25: unknown facility %02X, length %d, values %02X, %02X, %02X, %02X\n", p[0], p[1], p[2], p[3], p[4], p[5]);
+ p += p[1] + 2;
+ len -= p[1] + 2;
+ break;
+ }
+ }
+
+ return p - skb->data;
+}
+
+/*
+ * Create a set of facilities.
+ */
+int x25_create_facilities(unsigned char *buffer, struct x25_facilities *facilities)
+{
+ unsigned char *p = buffer + 1;
+ int len;
+
+ if (facilities->reverse != 0) {
+ *p++ = X25_FAC_REVERSE;
+ *p++ = (facilities->reverse) ? 0x01 : 0x00;
+ }
+
+ if (facilities->throughput != 0) {
+ *p++ = X25_FAC_THROUGHPUT;
+ *p++ = facilities->throughput;
+ }
+
+ if (facilities->pacsize_in != 0 || facilities->pacsize_out != 0) {
+ *p++ = X25_FAC_PACKET_SIZE;
+ *p++ = (facilities->pacsize_in == 0) ? facilities->pacsize_out : facilities->pacsize_in;
+ *p++ = (facilities->pacsize_out == 0) ? facilities->pacsize_in : facilities->pacsize_out;
+ }
+
+ if (facilities->winsize_in != 0 || facilities->winsize_out != 0) {
+ *p++ = X25_FAC_WINDOW_SIZE;
+ *p++ = (facilities->winsize_in == 0) ? facilities->winsize_out : facilities->winsize_in;
+ *p++ = (facilities->winsize_out == 0) ? facilities->winsize_in : facilities->winsize_out;
+ }
+
+ len = p - buffer;
+ buffer[0] = len - 1;
+
+ return len;
+}
+
+#endif
diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c
new file mode 100644
index 000000000..e625c41ed
--- /dev/null
+++ b/net/x25/x25_timer.c
@@ -0,0 +1,145 @@
+/*
+ * X.25 Packet Layer release 001
+ *
+ * This is ALPHA test software. This code may break your machine, randomly fail to work with new
+ * releases, misbehave and/or generally screw up. It might even work.
+ *
+ * This code REQUIRES 2.1.15 or higher
+ *
+ * This module:
+ * This module 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 of the License, or (at your option) any later version.
+ *
+ * History
+ * X.25 001 Jonathan Naylor Started coding.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/x25.h>
+
+static void x25_timer(unsigned long);
+
+/*
+ * Linux set timer
+ */
+void x25_set_timer(struct sock *sk)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ del_timer(&sk->timer);
+ restore_flags(flags);
+
+ sk->timer.data = (unsigned long)sk;
+ sk->timer.function = &x25_timer;
+ sk->timer.expires = jiffies + (HZ / 1);
+
+ add_timer(&sk->timer);
+}
+
+/*
+ * X.25 TIMER
+ *
+ * This routine is called every second. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+static void x25_timer(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
+
+ switch (sk->protinfo.x25->state) {
+ case X25_STATE_0:
+ /* Magic here: If we listen() and a new link dies before it
+ is accepted() it isn't 'dead' so doesn't get removed. */
+ if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
+ del_timer(&sk->timer);
+ x25_destroy_socket(sk);
+ return;
+ }
+ break;
+
+ case X25_STATE_3:
+ /*
+ * Check for the state of the receive buffer.
+ */
+ if (atomic_read(&sk->rmem_alloc) < (sk->rcvbuf / 2) &&
+ (sk->protinfo.x25->condition & X25_COND_OWN_RX_BUSY)) {
+ sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY;
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
+ sk->protinfo.x25->vl = sk->protinfo.x25->vr;
+ sk->protinfo.x25->timer = 0;
+ x25_write_internal(sk, X25_RR);
+ break;
+ }
+ /*
+ * Check for frames to transmit.
+ */
+ x25_kick(sk);
+ break;
+
+ default:
+ break;
+ }
+
+ if (sk->protinfo.x25->timer == 0 || --sk->protinfo.x25->timer > 0) {
+ x25_set_timer(sk);
+ return;
+ }
+
+ /*
+ * Timer has expired, it may have been T2, T21, T22, or T23. We can tell
+ * by the state machine state.
+ */
+ switch (sk->protinfo.x25->state) {
+ case X25_STATE_3: /* T2 */
+ if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) {
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
+ x25_enquiry_response(sk);
+ }
+ break;
+
+ case X25_STATE_1: /* T21 */
+ case X25_STATE_4: /* T22 */
+ x25_write_internal(sk, X25_CLEAR_REQUEST);
+ sk->protinfo.x25->state = X25_STATE_2;
+ sk->protinfo.x25->timer = sk->protinfo.x25->t23;
+ break;
+
+ case X25_STATE_2: /* T23 */
+ x25_clear_queues(sk);
+ sk->protinfo.x25->state = X25_STATE_0;
+ sk->state = TCP_CLOSE;
+ sk->err = ETIMEDOUT;
+ sk->shutdown |= SEND_SHUTDOWN;
+ if (!sk->dead)
+ sk->state_change(sk);
+ sk->dead = 1;
+ break;
+ }
+
+ x25_set_timer(sk);
+}
+
+#endif