summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-03-23 23:44:19 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-03-23 23:44:19 -0500
commit1ebbe2b20091d306453a5cf480a87e6cd28ae76f (patch)
treef5cd7a0fa69b8b1938cb5a0faed2e7b0628072a5 /net
parentac58c9059da8886b5e8cde012a80266b18ca146e (diff)
parent674a396c6d2ba0341ebdd7c1c9950f32f018e2dd (diff)
downloadlinux-sh-1ebbe2b20091d306453a5cf480a87e6cd28ae76f.tar.gz
Merge branch 'linus'
Diffstat (limited to 'net')
-rw-r--r--net/802/psnap.c4
-rw-r--r--net/8021q/vlan.c43
-rw-r--r--net/8021q/vlan_dev.c6
-rw-r--r--net/atm/clip.c2
-rw-r--r--net/atm/common.c4
-rw-r--r--net/atm/ioctl.c15
-rw-r--r--net/atm/resources.c32
-rw-r--r--net/atm/resources.h3
-rw-r--r--net/bluetooth/bnep/core.c4
-rw-r--r--net/bluetooth/rfcomm/core.c8
-rw-r--r--net/bridge/Kconfig1
-rw-r--r--net/bridge/br.c12
-rw-r--r--net/bridge/br_device.c3
-rw-r--r--net/bridge/br_fdb.c6
-rw-r--r--net/bridge/br_if.c9
-rw-r--r--net/bridge/br_input.c43
-rw-r--r--net/bridge/br_netfilter.c225
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/bridge/br_stp_bpdu.c196
-rw-r--r--net/bridge/br_stp_timer.c47
-rw-r--r--net/bridge/br_sysfs_br.c49
-rw-r--r--net/bridge/netfilter/ebtables.c101
-rw-r--r--net/compat.c114
-rw-r--r--net/core/dev.c32
-rw-r--r--net/core/flow.c7
-rw-r--r--net/core/link_watch.c44
-rw-r--r--net/core/neighbour.c12
-rw-r--r--net/core/net-sysfs.c41
-rw-r--r--net/core/netpoll.c6
-rw-r--r--net/core/pktgen.c2980
-rw-r--r--net/core/rtnetlink.c176
-rw-r--r--net/core/skbuff.c42
-rw-r--r--net/core/sock.c30
-rw-r--r--net/core/sysctl_net_core.c23
-rw-r--r--net/core/wireless.c911
-rw-r--r--net/dccp/Kconfig13
-rw-r--r--net/dccp/Makefile9
-rw-r--r--net/dccp/ackvec.c296
-rw-r--r--net/dccp/ackvec.h53
-rw-r--r--net/dccp/ccid.c189
-rw-r--r--net/dccp/ccid.h129
-rw-r--r--net/dccp/ccids/Kconfig43
-rw-r--r--net/dccp/ccids/Makefile4
-rw-r--r--net/dccp/ccids/ccid2.c779
-rw-r--r--net/dccp/ccids/ccid2.h85
-rw-r--r--net/dccp/ccids/ccid3.c112
-rw-r--r--net/dccp/ccids/ccid3.h5
-rw-r--r--net/dccp/dccp.h133
-rw-r--r--net/dccp/diag.c2
-rw-r--r--net/dccp/feat.c586
-rw-r--r--net/dccp/feat.h29
-rw-r--r--net/dccp/input.c28
-rw-r--r--net/dccp/ipv4.c333
-rw-r--r--net/dccp/ipv6.c371
-rw-r--r--net/dccp/minisocks.c37
-rw-r--r--net/dccp/options.c291
-rw-r--r--net/dccp/output.c88
-rw-r--r--net/dccp/proto.c440
-rw-r--r--net/dccp/sysctl.c124
-rw-r--r--net/dccp/timer.c14
-rw-r--r--net/decnet/af_decnet.c18
-rw-r--r--net/decnet/dn_dev.c34
-rw-r--r--net/decnet/dn_fib.c8
-rw-r--r--net/decnet/dn_neigh.c24
-rw-r--r--net/decnet/dn_nsp_in.c28
-rw-r--r--net/decnet/dn_nsp_out.c38
-rw-r--r--net/decnet/dn_route.c60
-rw-r--r--net/decnet/dn_rules.c115
-rw-r--r--net/decnet/dn_table.c12
-rw-r--r--net/decnet/sysctl_net_decnet.c12
-rw-r--r--net/ieee80211/Kconfig1
-rw-r--r--net/ieee80211/Makefile1
-rw-r--r--net/ieee80211/ieee80211_rx.c74
-rw-r--r--net/ieee80211/softmac/Kconfig10
-rw-r--r--net/ieee80211/softmac/Makefile9
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_assoc.c396
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_auth.c364
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_event.c159
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_io.c474
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_module.c457
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_priv.h230
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_scan.c244
-rw-r--r--net/ieee80211/softmac/ieee80211softmac_wx.c412
-rw-r--r--net/ipv4/af_inet.c120
-rw-r--r--net/ipv4/ah4.c1
-rw-r--r--net/ipv4/arp.c20
-rw-r--r--net/ipv4/devinet.c8
-rw-r--r--net/ipv4/esp4.c1
-rw-r--r--net/ipv4/fib_rules.c140
-rw-r--r--net/ipv4/fib_trie.c24
-rw-r--r--net/ipv4/igmp.c26
-rw-r--r--net/ipv4/inet_connection_sock.c49
-rw-r--r--net/ipv4/ip_output.c6
-rw-r--r--net/ipv4/ip_sockglue.c170
-rw-r--r--net/ipv4/ipcomp.c17
-rw-r--r--net/ipv4/ipconfig.c10
-rw-r--r--net/ipv4/ipmr.c4
-rw-r--r--net/ipv4/ipvs/ip_vs_app.c19
-rw-r--r--net/ipv4/netfilter/Kconfig36
-rw-r--r--net/ipv4/netfilter/Makefile6
-rw-r--r--net/ipv4/netfilter/arp_tables.c27
-rw-r--r--net/ipv4/netfilter/arpt_mangle.c23
-rw-r--r--net/ipv4/netfilter/ip_conntrack_core.c4
-rw-r--r--net/ipv4/netfilter/ip_conntrack_helper_h323.c1731
-rw-r--r--net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.c870
-rw-r--r--net/ipv4/netfilter/ip_conntrack_helper_h323_asn1.h98
-rw-r--r--net/ipv4/netfilter/ip_conntrack_helper_h323_types.c1926
-rw-r--r--net/ipv4/netfilter/ip_conntrack_helper_h323_types.h938
-rw-r--r--net/ipv4/netfilter/ip_conntrack_netlink.c79
-rw-r--r--net/ipv4/netfilter/ip_nat_helper_h323.c605
-rw-r--r--net/ipv4/netfilter/ip_nat_helper_pptp.c8
-rw-r--r--net/ipv4/netfilter/ip_nat_rule.c45
-rw-r--r--net/ipv4/netfilter/ip_nat_snmp_basic.c5
-rw-r--r--net/ipv4/netfilter/ip_queue.c11
-rw-r--r--net/ipv4/netfilter/ip_tables.c84
-rw-r--r--net/ipv4/netfilter/ipt_CLUSTERIP.c27
-rw-r--r--net/ipv4/netfilter/ipt_DSCP.c17
-rw-r--r--net/ipv4/netfilter/ipt_ECN.c18
-rw-r--r--net/ipv4/netfilter/ipt_LOG.c11
-rw-r--r--net/ipv4/netfilter/ipt_MASQUERADE.c18
-rw-r--r--net/ipv4/netfilter/ipt_NETMAP.c19
-rw-r--r--net/ipv4/netfilter/ipt_REDIRECT.c17
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c28
-rw-r--r--net/ipv4/netfilter/ipt_SAME.c19
-rw-r--r--net/ipv4/netfilter/ipt_TCPMSS.c16
-rw-r--r--net/ipv4/netfilter/ipt_TOS.c17
-rw-r--r--net/ipv4/netfilter/ipt_TTL.c25
-rw-r--r--net/ipv4/netfilter/ipt_ULOG.c12
-rw-r--r--net/ipv4/netfilter/ipt_addrtype.c20
-rw-r--r--net/ipv4/netfilter/ipt_ah.c25
-rw-r--r--net/ipv4/netfilter/ipt_dscp.c19
-rw-r--r--net/ipv4/netfilter/ipt_ecn.c14
-rw-r--r--net/ipv4/netfilter/ipt_esp.c25
-rw-r--r--net/ipv4/netfilter/ipt_hashlimit.c21
-rw-r--r--net/ipv4/netfilter/ipt_iprange.c28
-rw-r--r--net/ipv4/netfilter/ipt_multiport.c31
-rw-r--r--net/ipv4/netfilter/ipt_owner.c21
-rw-r--r--net/ipv4/netfilter/ipt_policy.c176
-rw-r--r--net/ipv4/netfilter/ipt_recent.c22
-rw-r--r--net/ipv4/netfilter/ipt_tos.c18
-rw-r--r--net/ipv4/netfilter/ipt_ttl.c19
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c23
-rw-r--r--net/ipv4/raw.c80
-rw-r--r--net/ipv4/sysctl_net_ipv4.c25
-rw-r--r--net/ipv4/tcp.c63
-rw-r--r--net/ipv4/tcp_htcp.c66
-rw-r--r--net/ipv4/tcp_input.c49
-rw-r--r--net/ipv4/tcp_ipv4.c44
-rw-r--r--net/ipv4/tcp_output.c259
-rw-r--r--net/ipv4/tcp_timer.c36
-rw-r--r--net/ipv4/udp.c83
-rw-r--r--net/ipv4/xfrm4_tunnel.c11
-rw-r--r--net/ipv6/Kconfig26
-rw-r--r--net/ipv6/addrconf.c346
-rw-r--r--net/ipv6/af_inet6.c120
-rw-r--r--net/ipv6/ah6.c5
-rw-r--r--net/ipv6/anycast.c7
-rw-r--r--net/ipv6/esp6.c5
-rw-r--r--net/ipv6/ip6_fib.c1
-rw-r--r--net/ipv6/ip6_flowlabel.c6
-rw-r--r--net/ipv6/ip6_output.c45
-rw-r--r--net/ipv6/ipcomp6.c22
-rw-r--r--net/ipv6/ipv6_sockglue.c163
-rw-r--r--net/ipv6/mcast.c17
-rw-r--r--net/ipv6/ndisc.c49
-rw-r--r--net/ipv6/netfilter/Kconfig10
-rw-r--r--net/ipv6/netfilter/Makefile1
-rw-r--r--net/ipv6/netfilter/ip6_queue.c11
-rw-r--r--net/ipv6/netfilter/ip6_tables.c102
-rw-r--r--net/ipv6/netfilter/ip6t_HL.c19
-rw-r--r--net/ipv6/netfilter/ip6t_LOG.c11
-rw-r--r--net/ipv6/netfilter/ip6t_REJECT.c25
-rw-r--r--net/ipv6/netfilter/ip6t_ah.c12
-rw-r--r--net/ipv6/netfilter/ip6t_dst.c13
-rw-r--r--net/ipv6/netfilter/ip6t_esp.c12
-rw-r--r--net/ipv6/netfilter/ip6t_eui64.c27
-rw-r--r--net/ipv6/netfilter/ip6t_frag.c13
-rw-r--r--net/ipv6/netfilter/ip6t_hbh.c13
-rw-r--r--net/ipv6/netfilter/ip6t_hl.c22
-rw-r--r--net/ipv6/netfilter/ip6t_ipv6header.c8
-rw-r--r--net/ipv6/netfilter/ip6t_multiport.c11
-rw-r--r--net/ipv6/netfilter/ip6t_owner.c18
-rw-r--r--net/ipv6/netfilter/ip6t_policy.c176
-rw-r--r--net/ipv6/netfilter/ip6t_rt.c12
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c40
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c8
-rw-r--r--net/ipv6/raw.c145
-rw-r--r--net/ipv6/reassembly.c35
-rw-r--r--net/ipv6/route.c680
-rw-r--r--net/ipv6/tcp_ipv6.c74
-rw-r--r--net/ipv6/udp.c84
-rw-r--r--net/ipv6/xfrm6_tunnel.c11
-rw-r--r--net/key/af_key.c6
-rw-r--r--net/llc/af_llc.c15
-rw-r--r--net/llc/llc_c_ac.c1
-rw-r--r--net/llc/llc_core.c1
-rw-r--r--net/llc/llc_output.c3
-rw-r--r--net/llc/llc_output.h20
-rw-r--r--net/llc/llc_s_ac.c2
-rw-r--r--net/netfilter/Kconfig16
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/nf_conntrack_core.c170
-rw-r--r--net/netfilter/nf_conntrack_ftp.c2
-rw-r--r--net/netfilter/nf_conntrack_netlink.c130
-rw-r--r--net/netfilter/nf_conntrack_standalone.c3
-rw-r--r--net/netfilter/nf_sockopt.c94
-rw-r--r--net/netfilter/nfnetlink.c6
-rw-r--r--net/netfilter/nfnetlink_log.c46
-rw-r--r--net/netfilter/nfnetlink_queue.c19
-rw-r--r--net/netfilter/x_tables.c88
-rw-r--r--net/netfilter/xt_CLASSIFY.c54
-rw-r--r--net/netfilter/xt_CONNMARK.c39
-rw-r--r--net/netfilter/xt_MARK.c58
-rw-r--r--net/netfilter/xt_NFQUEUE.c43
-rw-r--r--net/netfilter/xt_NOTRACK.c57
-rw-r--r--net/netfilter/xt_comment.c30
-rw-r--r--net/netfilter/xt_connbytes.c27
-rw-r--r--net/netfilter/xt_connmark.c57
-rw-r--r--net/netfilter/xt_conntrack.c41
-rw-r--r--net/netfilter/xt_dccp.c57
-rw-r--r--net/netfilter/xt_helper.c55
-rw-r--r--net/netfilter/xt_length.c36
-rw-r--r--net/netfilter/xt_limit.c19
-rw-r--r--net/netfilter/xt_mac.c46
-rw-r--r--net/netfilter/xt_mark.c28
-rw-r--r--net/netfilter/xt_physdev.c26
-rw-r--r--net/netfilter/xt_pkttype.c35
-rw-r--r--net/netfilter/xt_policy.c211
-rw-r--r--net/netfilter/xt_realm.c32
-rw-r--r--net/netfilter/xt_sctp.c78
-rw-r--r--net/netfilter/xt_state.c44
-rw-r--r--net/netfilter/xt_string.c22
-rw-r--r--net/netfilter/xt_tcpmss.c64
-rw-r--r--net/netfilter/xt_tcpudp.c138
-rw-r--r--net/netlink/af_netlink.c52
-rw-r--r--net/sched/Kconfig1
-rw-r--r--net/sched/act_ipt.c10
-rw-r--r--net/sched/cls_u32.c6
-rw-r--r--net/sched/sch_atm.c1
-rw-r--r--net/sched/sch_dsmark.c1
-rw-r--r--net/sched/sch_generic.c2
-rw-r--r--net/sched/sch_netem.c4
-rw-r--r--net/sched/sch_prio.c2
-rw-r--r--net/sched/sch_red.c179
-rw-r--r--net/sched/sch_sfq.c5
-rw-r--r--net/sched/sch_tbf.c9
-rw-r--r--net/sctp/ipv6.c92
-rw-r--r--net/sctp/protocol.c94
-rw-r--r--net/socket.c353
-rw-r--r--net/sunrpc/cache.c17
-rw-r--r--net/sunrpc/sched.c11
-rw-r--r--net/sunrpc/svcsock.c8
-rw-r--r--net/tipc/bcast.c58
-rw-r--r--net/tipc/bearer.c20
-rw-r--r--net/tipc/cluster.c22
-rw-r--r--net/tipc/cluster.h2
-rw-r--r--net/tipc/config.c4
-rw-r--r--net/tipc/dbg.c4
-rw-r--r--net/tipc/discover.c8
-rw-r--r--net/tipc/eth_media.c4
-rw-r--r--net/tipc/link.c89
-rw-r--r--net/tipc/name_distr.c6
-rw-r--r--net/tipc/name_table.c62
-rw-r--r--net/tipc/net.c7
-rw-r--r--net/tipc/node.c20
-rw-r--r--net/tipc/node.h2
-rw-r--r--net/tipc/node_subscr.c2
-rw-r--r--net/tipc/port.c57
-rw-r--r--net/tipc/ref.c8
-rw-r--r--net/tipc/ref.h4
-rw-r--r--net/tipc/socket.c28
-rw-r--r--net/tipc/subscr.c30
-rw-r--r--net/tipc/user_reg.c4
-rw-r--r--net/tipc/zone.c12
-rw-r--r--net/unix/af_unix.c32
-rw-r--r--net/unix/garbage.c7
-rw-r--r--net/x25/af_x25.c173
-rw-r--r--net/x25/x25_facilities.c82
-rw-r--r--net/x25/x25_in.c3
-rw-r--r--net/x25/x25_subr.c6
-rw-r--r--net/xfrm/xfrm_policy.c9
-rw-r--r--net/xfrm/xfrm_state.c108
-rw-r--r--net/xfrm/xfrm_user.c397
283 files changed, 21060 insertions, 6525 deletions
diff --git a/net/802/psnap.c b/net/802/psnap.c
index 34e42968b477..270b9d2cae65 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -59,10 +59,8 @@ static int snap_rcv(struct sk_buff *skb, struct net_device *dev,
proto = find_snap_client(skb->h.raw);
if (proto) {
/* Pass the frame on. */
- u8 *hdr = skb->data;
skb->h.raw += 5;
- skb_pull(skb, 5);
- skb_postpull_rcsum(skb, hdr, 5);
+ skb_pull_rcsum(skb, 5);
rc = proto->rcvfunc(skb, dev, &snap_packet_type, orig_dev);
} else {
skb->sk = NULL;
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index fa76220708ce..3948949a609a 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -69,7 +69,7 @@ static struct packet_type vlan_packet_type = {
/* Bits of netdev state that are propagated from real device to virtual */
#define VLAN_LINK_STATE_MASK \
- ((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER))
+ ((1<<__LINK_STATE_PRESENT)|(1<<__LINK_STATE_NOCARRIER)|(1<<__LINK_STATE_DORMANT))
/* End of global variables definitions. */
@@ -344,6 +344,26 @@ static void vlan_setup(struct net_device *new_dev)
new_dev->do_ioctl = vlan_dev_ioctl;
}
+static void vlan_transfer_operstate(const struct net_device *dev, struct net_device *vlandev)
+{
+ /* Have to respect userspace enforced dormant state
+ * of real device, also must allow supplicant running
+ * on VLAN device
+ */
+ if (dev->operstate == IF_OPER_DORMANT)
+ netif_dormant_on(vlandev);
+ else
+ netif_dormant_off(vlandev);
+
+ if (netif_carrier_ok(dev)) {
+ if (!netif_carrier_ok(vlandev))
+ netif_carrier_on(vlandev);
+ } else {
+ if (netif_carrier_ok(vlandev))
+ netif_carrier_off(vlandev);
+ }
+}
+
/* Attach a VLAN device to a mac address (ie Ethernet Card).
* Returns the device that was created, or NULL if there was
* an error of some kind.
@@ -450,7 +470,7 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
new_dev->flags = real_dev->flags;
new_dev->flags &= ~IFF_UP;
- new_dev->state = real_dev->state & VLAN_LINK_STATE_MASK;
+ new_dev->state = real_dev->state & ~(1<<__LINK_STATE_START);
/* need 4 bytes for extra VLAN header info,
* hope the underlying device can handle it.
@@ -498,6 +518,10 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
if (register_netdevice(new_dev))
goto out_free_newdev;
+ new_dev->iflink = real_dev->ifindex;
+ vlan_transfer_operstate(real_dev, new_dev);
+ linkwatch_fire_event(new_dev); /* _MUST_ call rfc2863_policy() */
+
/* So, got the sucker initialized, now lets place
* it into our local structure.
*/
@@ -573,25 +597,12 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
switch (event) {
case NETDEV_CHANGE:
/* Propagate real device state to vlan devices */
- flgs = dev->state & VLAN_LINK_STATE_MASK;
for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
vlandev = grp->vlan_devices[i];
if (!vlandev)
continue;
- if (netif_carrier_ok(dev)) {
- if (!netif_carrier_ok(vlandev))
- netif_carrier_on(vlandev);
- } else {
- if (netif_carrier_ok(vlandev))
- netif_carrier_off(vlandev);
- }
-
- if ((vlandev->state & VLAN_LINK_STATE_MASK) != flgs) {
- vlandev->state = (vlandev->state &~ VLAN_LINK_STATE_MASK)
- | flgs;
- netdev_state_change(vlandev);
- }
+ vlan_transfer_operstate(dev, vlandev);
}
break;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 0f604d227da2..da9cfe927158 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -163,10 +163,8 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
stats->rx_packets++;
stats->rx_bytes += skb->len;
- skb_pull(skb, VLAN_HLEN); /* take off the VLAN header (4 bytes currently) */
-
- /* Need to correct hardware checksum */
- skb_postpull_rcsum(skb, vhdr, VLAN_HLEN);
+ /* Take off the VLAN header (4 bytes currently) */
+ skb_pull_rcsum(skb, VLAN_HLEN);
/* Ok, lets check to make sure the device (dev) we
* came in on is what this VLAN is attached to.
diff --git a/net/atm/clip.c b/net/atm/clip.c
index 73370de97539..3ab4e7947bab 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -289,7 +289,6 @@ static void clip_neigh_error(struct neighbour *neigh,struct sk_buff *skb)
static struct neigh_ops clip_neigh_ops = {
.family = AF_INET,
- .destructor = clip_neigh_destroy,
.solicit = clip_neigh_solicit,
.error_report = clip_neigh_error,
.output = dev_queue_xmit,
@@ -347,6 +346,7 @@ static struct neigh_table clip_tbl = {
/* parameters are copied from ARP ... */
.parms = {
.tbl = &clip_tbl,
+ .neigh_destructor = clip_neigh_destroy,
.base_reachable_time = 30 * HZ,
.retrans_time = 1 * HZ,
.gc_staletime = 60 * HZ,
diff --git a/net/atm/common.c b/net/atm/common.c
index 6656b111cc05..ae002220fa99 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -451,12 +451,12 @@ int vcc_connect(struct socket *sock, int itf, short vpi, int vci)
dev = try_then_request_module(atm_dev_lookup(itf), "atm-device-%d", itf);
} else {
dev = NULL;
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
if (!list_empty(&atm_devs)) {
dev = list_entry(atm_devs.next, struct atm_dev, dev_list);
atm_dev_hold(dev);
}
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
}
if (!dev)
return -ENODEV;
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index eb109af7eb4a..851cfa6312af 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -18,6 +18,7 @@
#include <linux/atmmpc.h>
#include <net/atmclip.h>
#include <linux/atmlec.h>
+#include <linux/mutex.h>
#include <asm/ioctls.h>
#include "resources.h"
@@ -25,22 +26,22 @@
#include "common.h"
-static DECLARE_MUTEX(ioctl_mutex);
+static DEFINE_MUTEX(ioctl_mutex);
static LIST_HEAD(ioctl_list);
void register_atm_ioctl(struct atm_ioctl *ioctl)
{
- down(&ioctl_mutex);
+ mutex_lock(&ioctl_mutex);
list_add_tail(&ioctl->list, &ioctl_list);
- up(&ioctl_mutex);
+ mutex_unlock(&ioctl_mutex);
}
void deregister_atm_ioctl(struct atm_ioctl *ioctl)
{
- down(&ioctl_mutex);
+ mutex_lock(&ioctl_mutex);
list_del(&ioctl->list);
- up(&ioctl_mutex);
+ mutex_unlock(&ioctl_mutex);
}
EXPORT_SYMBOL(register_atm_ioctl);
@@ -137,7 +138,7 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
error = -ENOIOCTLCMD;
- down(&ioctl_mutex);
+ mutex_lock(&ioctl_mutex);
list_for_each(pos, &ioctl_list) {
struct atm_ioctl * ic = list_entry(pos, struct atm_ioctl, list);
if (try_module_get(ic->owner)) {
@@ -147,7 +148,7 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
break;
}
}
- up(&ioctl_mutex);
+ mutex_unlock(&ioctl_mutex);
if (error != -ENOIOCTLCMD)
goto done;
diff --git a/net/atm/resources.c b/net/atm/resources.c
index 224190537c90..18ac80698f83 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -18,6 +18,8 @@
#include <linux/bitops.h>
#include <linux/capability.h>
#include <linux/delay.h>
+#include <linux/mutex.h>
+
#include <net/sock.h> /* for struct sock */
#include "common.h"
@@ -26,7 +28,7 @@
LIST_HEAD(atm_devs);
-DECLARE_MUTEX(atm_dev_mutex);
+DEFINE_MUTEX(atm_dev_mutex);
static struct atm_dev *__alloc_atm_dev(const char *type)
{
@@ -65,9 +67,9 @@ struct atm_dev *atm_dev_lookup(int number)
{
struct atm_dev *dev;
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
dev = __atm_dev_lookup(number);
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
return dev;
}
@@ -83,11 +85,11 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
type);
return NULL;
}
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
if (number != -1) {
if ((inuse = __atm_dev_lookup(number))) {
atm_dev_put(inuse);
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
kfree(dev);
return NULL;
}
@@ -112,12 +114,12 @@ struct atm_dev *atm_dev_register(const char *type, const struct atmdev_ops *ops,
printk(KERN_ERR "atm_dev_register: "
"atm_proc_dev_register failed for dev %s\n",
type);
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
kfree(dev);
return NULL;
}
list_add_tail(&dev->dev_list, &atm_devs);
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
return dev;
}
@@ -133,9 +135,9 @@ void atm_dev_deregister(struct atm_dev *dev)
* with same number can appear, such we need deregister proc,
* release async all vccs and remove them from vccs list too
*/
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
list_del(&dev->dev_list);
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
atm_dev_release_vccs(dev);
atm_proc_dev_deregister(dev);
@@ -196,16 +198,16 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
return -EFAULT;
if (get_user(len, &iobuf->length))
return -EFAULT;
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
list_for_each(p, &atm_devs)
size += sizeof(int);
if (size > len) {
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
return -E2BIG;
}
tmp_buf = kmalloc(size, GFP_ATOMIC);
if (!tmp_buf) {
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
return -ENOMEM;
}
tmp_p = tmp_buf;
@@ -213,7 +215,7 @@ int atm_dev_ioctl(unsigned int cmd, void __user *arg)
dev = list_entry(p, struct atm_dev, dev_list);
*tmp_p++ = dev->number;
}
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
error = ((copy_to_user(buf, tmp_buf, size)) ||
put_user(size, &iobuf->length))
? -EFAULT : 0;
@@ -400,13 +402,13 @@ static __inline__ void *dev_get_idx(loff_t left)
void *atm_dev_seq_start(struct seq_file *seq, loff_t *pos)
{
- down(&atm_dev_mutex);
+ mutex_lock(&atm_dev_mutex);
return *pos ? dev_get_idx(*pos) : (void *) 1;
}
void atm_dev_seq_stop(struct seq_file *seq, void *v)
{
- up(&atm_dev_mutex);
+ mutex_unlock(&atm_dev_mutex);
}
void *atm_dev_seq_next(struct seq_file *seq, void *v, loff_t *pos)
diff --git a/net/atm/resources.h b/net/atm/resources.h
index b7fb82a93b42..ac7222fee7a8 100644
--- a/net/atm/resources.h
+++ b/net/atm/resources.h
@@ -8,10 +8,11 @@
#include <linux/config.h>
#include <linux/atmdev.h>
+#include <linux/mutex.h>
extern struct list_head atm_devs;
-extern struct semaphore atm_dev_mutex;
+extern struct mutex atm_dev_mutex;
int atm_dev_ioctl(unsigned int cmd, void __user *arg);
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index cbb20c32a6c8..d908d49dc9f8 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -532,8 +532,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
dev = alloc_netdev(sizeof(struct bnep_session),
(*req->device) ? req->device : "bnep%d",
bnep_net_setup);
- if (!dev)
- return ENOMEM;
+ if (!dev)
+ return -ENOMEM;
down_write(&bnep_session_sem);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 5b4253c61f62..e99010ce8bb2 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -37,6 +37,8 @@
#include <linux/wait.h>
#include <linux/device.h>
#include <linux/net.h>
+#include <linux/mutex.h>
+
#include <net/sock.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
@@ -57,9 +59,9 @@ static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU;
static struct task_struct *rfcomm_thread;
-static DECLARE_MUTEX(rfcomm_sem);
-#define rfcomm_lock() down(&rfcomm_sem);
-#define rfcomm_unlock() up(&rfcomm_sem);
+static DEFINE_MUTEX(rfcomm_mutex);
+#define rfcomm_lock() mutex_lock(&rfcomm_mutex)
+#define rfcomm_unlock() mutex_unlock(&rfcomm_mutex)
static unsigned long rfcomm_event;
diff --git a/net/bridge/Kconfig b/net/bridge/Kconfig
index db23d59746cf..12265aff7099 100644
--- a/net/bridge/Kconfig
+++ b/net/bridge/Kconfig
@@ -4,6 +4,7 @@
config BRIDGE
tristate "802.1d Ethernet Bridging"
+ select LLC
---help---
If you say Y here, then your Linux box will be able to act as an
Ethernet bridge, which means that the different Ethernet segments it
diff --git a/net/bridge/br.c b/net/bridge/br.c
index 188cc1ac49eb..22d806cf40ca 100644
--- a/net/bridge/br.c
+++ b/net/bridge/br.c
@@ -19,13 +19,23 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
+#include <linux/llc.h>
+#include <net/llc.h>
#include "br_private.h"
int (*br_should_route_hook) (struct sk_buff **pskb) = NULL;
+static struct llc_sap *br_stp_sap;
+
static int __init br_init(void)
{
+ br_stp_sap = llc_sap_open(LLC_SAP_BSPAN, br_stp_rcv);
+ if (!br_stp_sap) {
+ printk(KERN_ERR "bridge: can't register sap for STP\n");
+ return -EBUSY;
+ }
+
br_fdb_init();
#ifdef CONFIG_BRIDGE_NETFILTER
@@ -45,6 +55,8 @@ static int __init br_init(void)
static void __exit br_deinit(void)
{
+ llc_sap_close(br_stp_sap);
+
#ifdef CONFIG_BRIDGE_NETFILTER
br_netfilter_fini();
#endif
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 0b33a7b3a00c..0c88a2ac32c1 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -27,6 +27,7 @@ static struct net_device_stats *br_dev_get_stats(struct net_device *dev)
return &br->statistics;
}
+/* net device transmit always called with no BH (preempt_disabled) */
int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
@@ -39,7 +40,6 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
skb->mac.raw = skb->data;
skb_pull(skb, ETH_HLEN);
- rcu_read_lock();
if (dest[0] & 1)
br_flood_deliver(br, skb, 0);
else if ((dst = __br_fdb_get(br, dest)) != NULL)
@@ -47,7 +47,6 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
else
br_flood_deliver(br, skb, 0);
- rcu_read_unlock();
return 0;
}
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 1f08a59b51ea..3a73b8c94271 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -341,7 +341,6 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
if (hold_time(br) == 0)
return;
- rcu_read_lock();
fdb = fdb_find(head, addr);
if (likely(fdb)) {
/* attempt to update an entry for a local interface */
@@ -356,13 +355,12 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
fdb->ageing_timer = jiffies;
}
} else {
- spin_lock_bh(&br->hash_lock);
+ spin_lock(&br->hash_lock);
if (!fdb_find(head, addr))
fdb_create(head, source, addr, 0);
/* else we lose race and someone else inserts
* it first, don't bother updating
*/
- spin_unlock_bh(&br->hash_lock);
+ spin_unlock(&br->hash_lock);
}
- rcu_read_unlock();
}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f36b35edd60c..59eef42d4a42 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -210,7 +210,8 @@ static struct net_device *new_bridge_dev(const char *name)
br->bridge_id.prio[0] = 0x80;
br->bridge_id.prio[1] = 0x00;
- memset(br->bridge_id.addr, 0, ETH_ALEN);
+
+ memcpy(br->group_addr, br_group_address, ETH_ALEN);
br->feature_mask = dev->features;
br->stp_enabled = 0;
@@ -237,12 +238,11 @@ static int find_portno(struct net_bridge *br)
struct net_bridge_port *p;
unsigned long *inuse;
- inuse = kmalloc(BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long),
+ inuse = kcalloc(BITS_TO_LONGS(BR_MAX_PORTS), sizeof(unsigned long),
GFP_KERNEL);
if (!inuse)
return -ENOMEM;
- memset(inuse, 0, BITS_TO_LONGS(BR_MAX_PORTS)*sizeof(unsigned long));
set_bit(0, inuse); /* zero is reserved */
list_for_each_entry(p, &br->port_list, list) {
set_bit(p->port_no, inuse);
@@ -264,11 +264,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
if (index < 0)
return ERR_PTR(index);
- p = kmalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return ERR_PTR(-ENOMEM);
- memset(p, 0, sizeof(*p));
p->br = br;
dev_hold(dev);
p->dev = dev;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 4eef83755315..b7766562d72c 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -19,13 +19,8 @@
#include <linux/netfilter_bridge.h>
#include "br_private.h"
-const unsigned char bridge_ula[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
-
-static int br_pass_frame_up_finish(struct sk_buff *skb)
-{
- netif_receive_skb(skb);
- return 0;
-}
+/* Bridge group multicast address 802.1d (pg 51). */
+const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
{
@@ -38,7 +33,7 @@ static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
skb->dev = br->dev;
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
- br_pass_frame_up_finish);
+ netif_receive_skb);
}
/* note: already called with rcu_read_lock (preempt_disabled) */
@@ -100,6 +95,25 @@ drop:
goto out;
}
+/* note: already called with rcu_read_lock (preempt_disabled) */
+static int br_handle_local_finish(struct sk_buff *skb)
+{
+ struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+
+ if (p && p->state != BR_STATE_DISABLED)
+ br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
+
+ return 0; /* process further */
+}
+
+/* Does address match the link local multicast address.
+ * 01:80:c2:00:00:0X
+ */
+static inline int is_link_local(const unsigned char *dest)
+{
+ return memcmp(dest, br_group_address, 5) == 0 && (dest[5] & 0xf0) == 0;
+}
+
/*
* Called via br_handle_frame_hook.
* Return 0 if *pskb should be processed furthur
@@ -117,15 +131,10 @@ int br_handle_frame(struct net_bridge_port *p, struct sk_buff **pskb)
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto err;
- if (p->br->stp_enabled &&
- !memcmp(dest, bridge_ula, 5) &&
- !(dest[5] & 0xF0)) {
- if (!dest[5]) {
- NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
- NULL, br_stp_handle_bpdu);
- return 1;
- }
- goto err;
+ if (unlikely(is_link_local(dest))) {
+ skb->pkt_type = PACKET_HOST;
+ return NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, skb->dev,
+ NULL, br_handle_local_finish) != 0;
}
if (p->state == BR_STATE_FORWARDING || p->state == BR_STATE_LEARNING) {
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index e060aad8624d..f29450b788be 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -61,15 +61,25 @@ static int brnf_filter_vlan_tagged = 1;
#define brnf_filter_vlan_tagged 1
#endif
-#define IS_VLAN_IP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
- hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IP) && \
- brnf_filter_vlan_tagged)
-#define IS_VLAN_IPV6 (skb->protocol == __constant_htons(ETH_P_8021Q) && \
- hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_IPV6) && \
- brnf_filter_vlan_tagged)
-#define IS_VLAN_ARP (skb->protocol == __constant_htons(ETH_P_8021Q) && \
- hdr->h_vlan_encapsulated_proto == __constant_htons(ETH_P_ARP) && \
- brnf_filter_vlan_tagged)
+static __be16 inline vlan_proto(const struct sk_buff *skb)
+{
+ return vlan_eth_hdr(skb)->h_vlan_encapsulated_proto;
+}
+
+#define IS_VLAN_IP(skb) \
+ (skb->protocol == htons(ETH_P_8021Q) && \
+ vlan_proto(skb) == htons(ETH_P_IP) && \
+ brnf_filter_vlan_tagged)
+
+#define IS_VLAN_IPV6(skb) \
+ (skb->protocol == htons(ETH_P_8021Q) && \
+ vlan_proto(skb) == htons(ETH_P_IPV6) &&\
+ brnf_filter_vlan_tagged)
+
+#define IS_VLAN_ARP(skb) \
+ (skb->protocol == htons(ETH_P_8021Q) && \
+ vlan_proto(skb) == htons(ETH_P_ARP) && \
+ brnf_filter_vlan_tagged)
/* We need these fake structures to make netfilter happy --
* lots of places assume that skb->dst != NULL, which isn't
@@ -103,6 +113,25 @@ static inline struct net_device *bridge_parent(const struct net_device *dev)
return port ? port->br->dev : NULL;
}
+static inline struct nf_bridge_info *nf_bridge_alloc(struct sk_buff *skb)
+{
+ skb->nf_bridge = kzalloc(sizeof(struct nf_bridge_info), GFP_ATOMIC);
+ if (likely(skb->nf_bridge))
+ atomic_set(&(skb->nf_bridge->use), 1);
+
+ return skb->nf_bridge;
+}
+
+static inline void nf_bridge_save_header(struct sk_buff *skb)
+{
+ int header_size = 16;
+
+ if (skb->protocol == htons(ETH_P_8021Q))
+ header_size = 18;
+
+ memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
+}
+
/* PF_BRIDGE/PRE_ROUTING *********************************************/
/* Undo the changes made for ip6tables PREROUTING and continue the
* bridge PRE_ROUTING hook. */
@@ -120,7 +149,7 @@ static int br_nf_pre_routing_finish_ipv6(struct sk_buff *skb)
dst_hold(skb->dst);
skb->dev = nf_bridge->physindev;
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
@@ -136,7 +165,7 @@ static void __br_dnat_complain(void)
if (jiffies - last_complaint >= 5 * HZ) {
printk(KERN_WARNING "Performing cross-bridge DNAT requires IP "
- "forwarding to be enabled\n");
+ "forwarding to be enabled\n");
last_complaint = jiffies;
}
}
@@ -196,7 +225,7 @@ static int br_nf_pre_routing_finish_bridge(struct sk_buff *skb)
if (!skb->dev)
kfree_skb(skb);
else {
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
skb->nh.raw += VLAN_HLEN;
}
@@ -218,12 +247,17 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
nf_bridge->mask ^= BRNF_NF_BRIDGE_PREROUTING;
if (dnat_took_place(skb)) {
- if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos,
- dev)) {
+ if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev)) {
struct rtable *rt;
- struct flowi fl = { .nl_u =
- { .ip4_u = { .daddr = iph->daddr, .saddr = 0 ,
- .tos = RT_TOS(iph->tos)} }, .proto = 0};
+ struct flowi fl = {
+ .nl_u = {
+ .ip4_u = {
+ .daddr = iph->daddr,
+ .saddr = 0,
+ .tos = RT_TOS(iph->tos) },
+ },
+ .proto = 0,
+ };
if (!ip_route_output_key(&rt, &fl)) {
/* - Bridged-and-DNAT'ed traffic doesn't
@@ -247,7 +281,7 @@ bridged_dnat:
nf_bridge->mask |= BRNF_BRIDGED_DNAT;
skb->dev = nf_bridge->physindev;
if (skb->protocol ==
- __constant_htons(ETH_P_8021Q)) {
+ htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
@@ -257,8 +291,7 @@ bridged_dnat:
1);
return 0;
}
- memcpy(eth_hdr(skb)->h_dest, dev->dev_addr,
- ETH_ALEN);
+ memcpy(eth_hdr(skb)->h_dest, dev->dev_addr, ETH_ALEN);
skb->pkt_type = PACKET_HOST;
}
} else {
@@ -267,7 +300,7 @@ bridged_dnat:
}
skb->dev = nf_bridge->physindev;
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
@@ -297,10 +330,10 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb)
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff anyway */
static int check_hbh_len(struct sk_buff *skb)
{
- unsigned char *raw = (u8*)(skb->nh.ipv6h+1);
+ unsigned char *raw = (u8 *) (skb->nh.ipv6h + 1);
u32 pkt_len;
int off = raw - skb->nh.raw;
- int len = (raw[1]+1)<<3;
+ int len = (raw[1] + 1) << 3;
if ((raw + len) - skb->data > skb_headlen(skb))
goto bad;
@@ -309,7 +342,7 @@ static int check_hbh_len(struct sk_buff *skb)
len -= 2;
while (len > 0) {
- int optlen = skb->nh.raw[off+1]+2;
+ int optlen = skb->nh.raw[off + 1] + 2;
switch (skb->nh.raw[off]) {
case IPV6_TLV_PAD0:
@@ -320,16 +353,16 @@ static int check_hbh_len(struct sk_buff *skb)
break;
case IPV6_TLV_JUMBO:
- if (skb->nh.raw[off+1] != 4 || (off&3) != 2)
+ if (skb->nh.raw[off + 1] != 4 || (off & 3) != 2)
goto bad;
- pkt_len = ntohl(*(u32*)(skb->nh.raw+off+2));
+ pkt_len = ntohl(*(u32 *) (skb->nh.raw + off + 2));
if (pkt_len <= IPV6_MAXPLEN ||
skb->nh.ipv6h->payload_len)
goto bad;
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
goto bad;
if (pskb_trim_rcsum(skb,
- pkt_len+sizeof(struct ipv6hdr)))
+ pkt_len + sizeof(struct ipv6hdr)))
goto bad;
break;
default:
@@ -350,12 +383,13 @@ bad:
/* Replicate the checks that IPv6 does on packet reception and pass the packet
* to ip6tables, which doesn't support NAT, so things are fairly simple. */
static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
- struct sk_buff *skb, const struct net_device *in,
- const struct net_device *out, int (*okfn)(struct sk_buff *))
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct ipv6hdr *hdr;
u32 pkt_len;
- struct nf_bridge_info *nf_bridge;
if (skb->len < sizeof(struct ipv6hdr))
goto inhdr_error;
@@ -381,10 +415,10 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
}
}
if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
- goto inhdr_error;
+ goto inhdr_error;
- nf_bridge_put(skb->nf_bridge);
- if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+ nf_bridge_put(skb->nf_bridge);
+ if (!nf_bridge_alloc(skb))
return NF_DROP;
if (!setup_pre_routing(skb))
return NF_DROP;
@@ -412,10 +446,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
struct iphdr *iph;
__u32 len;
struct sk_buff *skb = *pskb;
- struct nf_bridge_info *nf_bridge;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(*pskb);
- if (skb->protocol == __constant_htons(ETH_P_IPV6) || IS_VLAN_IPV6) {
+ if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) {
#ifdef CONFIG_SYSCTL
if (!brnf_call_ip6tables)
return NF_ACCEPT;
@@ -423,10 +455,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
- u8 *vhdr = skb->data;
- skb_pull(skb, VLAN_HLEN);
- skb_postpull_rcsum(skb, vhdr, VLAN_HLEN);
+ if (skb->protocol == htons(ETH_P_8021Q)) {
+ skb_pull_rcsum(skb, VLAN_HLEN);
skb->nh.raw += VLAN_HLEN;
}
return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn);
@@ -436,16 +466,14 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT;
#endif
- if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP)
+ if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb))
return NF_ACCEPT;
if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
goto out;
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
- u8 *vhdr = skb->data;
- skb_pull(skb, VLAN_HLEN);
- skb_postpull_rcsum(skb, vhdr, VLAN_HLEN);
+ if (skb->protocol == htons(ETH_P_8021Q)) {
+ skb_pull_rcsum(skb, VLAN_HLEN);
skb->nh.raw += VLAN_HLEN;
}
@@ -456,15 +484,15 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
- if (!pskb_may_pull(skb, 4*iph->ihl))
+ if (!pskb_may_pull(skb, 4 * iph->ihl))
goto inhdr_error;
iph = skb->nh.iph;
- if (ip_fast_csum((__u8 *)iph, iph->ihl) != 0)
+ if (ip_fast_csum((__u8 *) iph, iph->ihl) != 0)
goto inhdr_error;
len = ntohs(iph->tot_len);
- if (skb->len < len || len < 4*iph->ihl)
+ if (skb->len < len || len < 4 * iph->ihl)
goto inhdr_error;
if (skb->len > len) {
@@ -473,8 +501,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
skb->ip_summed = CHECKSUM_NONE;
}
- nf_bridge_put(skb->nf_bridge);
- if ((nf_bridge = nf_bridge_alloc(skb)) == NULL)
+ nf_bridge_put(skb->nf_bridge);
+ if (!nf_bridge_alloc(skb))
return NF_DROP;
if (!setup_pre_routing(skb))
return NF_DROP;
@@ -486,7 +514,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
return NF_STOLEN;
inhdr_error:
-// IP_INC_STATS_BH(IpInHdrErrors);
+// IP_INC_STATS_BH(IpInHdrErrors);
out:
return NF_DROP;
}
@@ -500,8 +528,9 @@ out:
* register an IPv4 PRE_ROUTING 'sabotage' hook that will
* prevent this from happening. */
static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb = *pskb;
@@ -513,15 +542,13 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT;
}
-
/* PF_BRIDGE/FORWARD *************************************************/
static int br_nf_forward_finish(struct sk_buff *skb)
{
struct nf_bridge_info *nf_bridge = skb->nf_bridge;
struct net_device *in;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
- if (skb->protocol != __constant_htons(ETH_P_ARP) && !IS_VLAN_ARP) {
+ if (skb->protocol != htons(ETH_P_ARP) && !IS_VLAN_ARP(skb)) {
in = nf_bridge->physindev;
if (nf_bridge->mask & BRNF_PKT_TYPE) {
skb->pkt_type = PACKET_OTHERHOST;
@@ -530,12 +557,12 @@ static int br_nf_forward_finish(struct sk_buff *skb)
} else {
in = *((struct net_device **)(skb->cb));
}
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_FORWARD, skb, in,
- skb->dev, br_forward_finish, 1);
+ skb->dev, br_forward_finish, 1);
return 0;
}
@@ -545,12 +572,12 @@ static int br_nf_forward_finish(struct sk_buff *skb)
* because of the physdev module. For ARP, indev and outdev are the
* bridge ports. */
static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
struct net_device *parent;
int pf;
@@ -561,12 +588,12 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
if (!parent)
return NF_DROP;
- if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+ if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
pf = PF_INET;
else
pf = PF_INET6;
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
@@ -588,11 +615,11 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff **pskb,
}
static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb = *pskb;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
struct net_device **d = (struct net_device **)(skb->cb);
#ifdef CONFIG_SYSCTL
@@ -600,15 +627,15 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
return NF_ACCEPT;
#endif
- if (skb->protocol != __constant_htons(ETH_P_ARP)) {
- if (!IS_VLAN_ARP)
+ if (skb->protocol != htons(ETH_P_ARP)) {
+ if (!IS_VLAN_ARP(skb))
return NF_ACCEPT;
skb_pull(*pskb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
if (skb->nh.arph->ar_pln != 4) {
- if (IS_VLAN_ARP) {
+ if (IS_VLAN_ARP(skb)) {
skb_push(*pskb, VLAN_HLEN);
(*pskb)->nh.raw -= VLAN_HLEN;
}
@@ -621,17 +648,16 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb,
return NF_STOLEN;
}
-
/* PF_BRIDGE/LOCAL_OUT ***********************************************/
static int br_nf_local_out_finish(struct sk_buff *skb)
{
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
NF_HOOK_THRESH(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- br_forward_finish, NF_BR_PRI_FIRST + 1);
+ br_forward_finish, NF_BR_PRI_FIRST + 1);
return 0;
}
@@ -657,19 +683,19 @@ static int br_nf_local_out_finish(struct sk_buff *skb)
* even routed packets that didn't arrive on a bridge interface have their
* nf_bridge->physindev set. */
static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct net_device *realindev, *realoutdev;
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
int pf;
if (!skb->nf_bridge)
return NF_ACCEPT;
- if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+ if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
pf = PF_INET;
else
pf = PF_INET6;
@@ -695,7 +721,7 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->mask ^= BRNF_PKT_TYPE;
}
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_push(skb, VLAN_HLEN);
skb->nh.raw -= VLAN_HLEN;
}
@@ -713,14 +739,14 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb,
if (nf_bridge->netoutdev)
realoutdev = nf_bridge->netoutdev;
#endif
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
(*pskb)->nh.raw += VLAN_HLEN;
}
/* IP forwarded traffic has a physindev, locally
* generated traffic hasn't. */
if (realindev != NULL) {
- if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT) ) {
+ if (!(nf_bridge->mask & BRNF_DONT_TAKE_PARENT)) {
struct net_device *parent = bridge_parent(realindev);
if (parent)
realindev = parent;
@@ -742,12 +768,12 @@ out:
/* PF_BRIDGE/POST_ROUTING ********************************************/
static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb = *pskb;
struct nf_bridge_info *nf_bridge = (*pskb)->nf_bridge;
- struct vlan_ethhdr *hdr = vlan_eth_hdr(skb);
struct net_device *realoutdev = bridge_parent(skb->dev);
int pf;
@@ -756,7 +782,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
* keep the check just to be sure... */
if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) {
printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: "
- "bad mac.raw pointer.");
+ "bad mac.raw pointer.");
goto print_error;
}
#endif
@@ -767,7 +793,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
if (!realoutdev)
return NF_DROP;
- if (skb->protocol == __constant_htons(ETH_P_IP) || IS_VLAN_IP)
+ if (skb->protocol == htons(ETH_P_IP) || IS_VLAN_IP(skb))
pf = PF_INET;
else
pf = PF_INET6;
@@ -786,7 +812,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
nf_bridge->mask |= BRNF_PKT_TYPE;
}
- if (skb->protocol == __constant_htons(ETH_P_8021Q)) {
+ if (skb->protocol == htons(ETH_P_8021Q)) {
skb_pull(skb, VLAN_HLEN);
skb->nh.raw += VLAN_HLEN;
}
@@ -798,7 +824,7 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb,
realoutdev = nf_bridge->netoutdev;
#endif
NF_HOOK(pf, NF_IP_POST_ROUTING, skb, NULL, realoutdev,
- br_dev_queue_push_xmit);
+ br_dev_queue_push_xmit);
return NF_STOLEN;
@@ -810,18 +836,18 @@ print_error:
printk("[%s]", realoutdev->name);
}
printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw,
- skb->data);
+ skb->data);
return NF_ACCEPT;
#endif
}
-
/* IP/SABOTAGE *****************************************************/
/* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING
* for the second time. */
static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
if ((*pskb)->nf_bridge &&
!((*pskb)->nf_bridge->mask & BRNF_NF_BRIDGE_PREROUTING)) {
@@ -835,18 +861,18 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff **pskb,
* and PF_INET(6)/POST_ROUTING until we have done the forwarding
* decision in the bridge code and have determined nf_bridge->physoutdev. */
static unsigned int ip_sabotage_out(unsigned int hook, struct sk_buff **pskb,
- const struct net_device *in, const struct net_device *out,
- int (*okfn)(struct sk_buff *))
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
{
struct sk_buff *skb = *pskb;
if ((out->hard_start_xmit == br_dev_xmit &&
- okfn != br_nf_forward_finish &&
- okfn != br_nf_local_out_finish &&
- okfn != br_dev_queue_push_xmit)
+ okfn != br_nf_forward_finish &&
+ okfn != br_nf_local_out_finish && okfn != br_dev_queue_push_xmit)
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
|| ((out->priv_flags & IFF_802_1Q_VLAN) &&
- VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit)
+ VLAN_DEV_INFO(out)->real_dev->hard_start_xmit == br_dev_xmit)
#endif
) {
struct nf_bridge_info *nf_bridge;
@@ -971,8 +997,8 @@ static struct nf_hook_ops br_nf_ops[] = {
#ifdef CONFIG_SYSCTL
static
-int brnf_sysctl_call_tables(ctl_table *ctl, int write, struct file * filp,
- void __user *buffer, size_t *lenp, loff_t *ppos)
+int brnf_sysctl_call_tables(ctl_table * ctl, int write, struct file *filp,
+ void __user * buffer, size_t * lenp, loff_t * ppos)
{
int ret;
@@ -1059,7 +1085,8 @@ int br_netfilter_init(void)
#ifdef CONFIG_SYSCTL
brnf_sysctl_header = register_sysctl_table(brnf_net_table, 0);
if (brnf_sysctl_header == NULL) {
- printk(KERN_WARNING "br_netfilter: can't register to sysctl.\n");
+ printk(KERN_WARNING
+ "br_netfilter: can't register to sysctl.\n");
for (i = 0; i < ARRAY_SIZE(br_nf_ops); i++)
nf_unregister_hook(&br_nf_ops[i]);
return -EFAULT;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 8f10e09f251b..86ecea7ed372 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -109,6 +109,7 @@ struct net_bridge
unsigned long bridge_hello_time;
unsigned long bridge_forward_delay;
+ u8 group_addr[ETH_ALEN];
u16 root_port;
unsigned char stp_enabled;
unsigned char topology_change;
@@ -122,7 +123,7 @@ struct net_bridge
};
extern struct notifier_block br_device_notifier;
-extern const unsigned char bridge_ula[6];
+extern const u8 br_group_address[ETH_ALEN];
/* called under bridge lock */
static inline int br_is_root_bridge(const struct net_bridge *br)
@@ -217,7 +218,8 @@ extern void br_stp_set_path_cost(struct net_bridge_port *p,
extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id);
/* br_stp_bpdu.c */
-extern int br_stp_handle_bpdu(struct sk_buff *skb);
+extern int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev);
/* br_stp_timer.c */
extern void br_stp_timer_init(struct net_bridge *br);
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index 296f6a487c52..8934a54792be 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -15,158 +15,162 @@
#include <linux/kernel.h>
#include <linux/netfilter_bridge.h>
+#include <linux/etherdevice.h>
+#include <linux/llc.h>
+#include <net/llc.h>
+#include <net/llc_pdu.h>
#include "br_private.h"
#include "br_private_stp.h"
-#define JIFFIES_TO_TICKS(j) (((j) << 8) / HZ)
-#define TICKS_TO_JIFFIES(j) (((j) * HZ) >> 8)
+#define STP_HZ 256
-static void br_send_bpdu(struct net_bridge_port *p, unsigned char *data, int length)
+#define LLC_RESERVE sizeof(struct llc_pdu_un)
+
+static void br_send_bpdu(struct net_bridge_port *p,
+ const unsigned char *data, int length)
{
- struct net_device *dev;
struct sk_buff *skb;
- int size;
if (!p->br->stp_enabled)
return;
- size = length + 2*ETH_ALEN + 2;
- if (size < 60)
- size = 60;
-
- dev = p->dev;
-
- if ((skb = dev_alloc_skb(size)) == NULL) {
- printk(KERN_INFO "br: memory squeeze!\n");
+ skb = dev_alloc_skb(length+LLC_RESERVE);
+ if (!skb)
return;
- }
- skb->dev = dev;
+ skb->dev = p->dev;
skb->protocol = htons(ETH_P_802_2);
- skb->mac.raw = skb_put(skb, size);
- memcpy(skb->mac.raw, bridge_ula, ETH_ALEN);
- memcpy(skb->mac.raw+ETH_ALEN, dev->dev_addr, ETH_ALEN);
- skb->mac.raw[2*ETH_ALEN] = 0;
- skb->mac.raw[2*ETH_ALEN+1] = length;
- skb->nh.raw = skb->mac.raw + 2*ETH_ALEN + 2;
- memcpy(skb->nh.raw, data, length);
- memset(skb->nh.raw + length, 0xa5, size - length - 2*ETH_ALEN - 2);
+
+ skb_reserve(skb, LLC_RESERVE);
+ memcpy(__skb_put(skb, length), data, length);
+
+ llc_pdu_header_init(skb, LLC_PDU_TYPE_U, LLC_SAP_BSPAN,
+ LLC_SAP_BSPAN, LLC_PDU_CMD);
+ llc_pdu_init_as_ui_cmd(skb);
+
+ llc_mac_hdr_init(skb, p->dev->dev_addr, p->br->group_addr);
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
dev_queue_xmit);
}
-static __inline__ void br_set_ticks(unsigned char *dest, int jiff)
+static inline void br_set_ticks(unsigned char *dest, int j)
{
- __u16 ticks;
+ unsigned long ticks = (STP_HZ * j)/ HZ;
- ticks = JIFFIES_TO_TICKS(jiff);
- dest[0] = (ticks >> 8) & 0xFF;
- dest[1] = ticks & 0xFF;
+ *((__be16 *) dest) = htons(ticks);
}
-static __inline__ int br_get_ticks(unsigned char *dest)
+static inline int br_get_ticks(const unsigned char *src)
{
- return TICKS_TO_JIFFIES((dest[0] << 8) | dest[1]);
+ unsigned long ticks = ntohs(*(__be16 *)src);
+
+ return (ticks * HZ + STP_HZ - 1) / STP_HZ;
}
/* called under bridge lock */
void br_send_config_bpdu(struct net_bridge_port *p, struct br_config_bpdu *bpdu)
{
- unsigned char buf[38];
-
- buf[0] = 0x42;
- buf[1] = 0x42;
- buf[2] = 0x03;
- buf[3] = 0;
- buf[4] = 0;
- buf[5] = 0;
- buf[6] = BPDU_TYPE_CONFIG;
- buf[7] = (bpdu->topology_change ? 0x01 : 0) |
+ unsigned char buf[35];
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = BPDU_TYPE_CONFIG;
+ buf[4] = (bpdu->topology_change ? 0x01 : 0) |
(bpdu->topology_change_ack ? 0x80 : 0);
- buf[8] = bpdu->root.prio[0];
- buf[9] = bpdu->root.prio[1];
- buf[10] = bpdu->root.addr[0];
- buf[11] = bpdu->root.addr[1];
- buf[12] = bpdu->root.addr[2];
- buf[13] = bpdu->root.addr[3];
- buf[14] = bpdu->root.addr[4];
- buf[15] = bpdu->root.addr[5];
- buf[16] = (bpdu->root_path_cost >> 24) & 0xFF;
- buf[17] = (bpdu->root_path_cost >> 16) & 0xFF;
- buf[18] = (bpdu->root_path_cost >> 8) & 0xFF;
- buf[19] = bpdu->root_path_cost & 0xFF;
- buf[20] = bpdu->bridge_id.prio[0];
- buf[21] = bpdu->bridge_id.prio[1];
- buf[22] = bpdu->bridge_id.addr[0];
- buf[23] = bpdu->bridge_id.addr[1];
- buf[24] = bpdu->bridge_id.addr[2];
- buf[25] = bpdu->bridge_id.addr[3];
- buf[26] = bpdu->bridge_id.addr[4];
- buf[27] = bpdu->bridge_id.addr[5];
- buf[28] = (bpdu->port_id >> 8) & 0xFF;
- buf[29] = bpdu->port_id & 0xFF;
-
- br_set_ticks(buf+30, bpdu->message_age);
- br_set_ticks(buf+32, bpdu->max_age);
- br_set_ticks(buf+34, bpdu->hello_time);
- br_set_ticks(buf+36, bpdu->forward_delay);
-
- br_send_bpdu(p, buf, 38);
+ buf[5] = bpdu->root.prio[0];
+ buf[6] = bpdu->root.prio[1];
+ buf[7] = bpdu->root.addr[0];
+ buf[8] = bpdu->root.addr[1];
+ buf[9] = bpdu->root.addr[2];
+ buf[10] = bpdu->root.addr[3];
+ buf[11] = bpdu->root.addr[4];
+ buf[12] = bpdu->root.addr[5];
+ buf[13] = (bpdu->root_path_cost >> 24) & 0xFF;
+ buf[14] = (bpdu->root_path_cost >> 16) & 0xFF;
+ buf[15] = (bpdu->root_path_cost >> 8) & 0xFF;
+ buf[16] = bpdu->root_path_cost & 0xFF;
+ buf[17] = bpdu->bridge_id.prio[0];
+ buf[18] = bpdu->bridge_id.prio[1];
+ buf[19] = bpdu->bridge_id.addr[0];
+ buf[20] = bpdu->bridge_id.addr[1];
+ buf[21] = bpdu->bridge_id.addr[2];
+ buf[22] = bpdu->bridge_id.addr[3];
+ buf[23] = bpdu->bridge_id.addr[4];
+ buf[24] = bpdu->bridge_id.addr[5];
+ buf[25] = (bpdu->port_id >> 8) & 0xFF;
+ buf[26] = bpdu->port_id & 0xFF;
+
+ br_set_ticks(buf+27, bpdu->message_age);
+ br_set_ticks(buf+29, bpdu->max_age);
+ br_set_ticks(buf+31, bpdu->hello_time);
+ br_set_ticks(buf+33, bpdu->forward_delay);
+
+ br_send_bpdu(p, buf, 35);
}
/* called under bridge lock */
void br_send_tcn_bpdu(struct net_bridge_port *p)
{
- unsigned char buf[7];
-
- buf[0] = 0x42;
- buf[1] = 0x42;
- buf[2] = 0x03;
- buf[3] = 0;
- buf[4] = 0;
- buf[5] = 0;
- buf[6] = BPDU_TYPE_TCN;
+ unsigned char buf[4];
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = BPDU_TYPE_TCN;
br_send_bpdu(p, buf, 7);
}
-static const unsigned char header[6] = {0x42, 0x42, 0x03, 0x00, 0x00, 0x00};
-
-/* NO locks, but rcu_read_lock (preempt_disabled) */
-int br_stp_handle_bpdu(struct sk_buff *skb)
+/*
+ * Called from llc.
+ *
+ * NO locks, but rcu_read_lock (preempt_disabled)
+ */
+int br_stp_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
{
- struct net_bridge_port *p = rcu_dereference(skb->dev->br_port);
+ const struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
+ const unsigned char *dest = eth_hdr(skb)->h_dest;
+ struct net_bridge_port *p = rcu_dereference(dev->br_port);
struct net_bridge *br;
- unsigned char *buf;
+ const unsigned char *buf;
if (!p)
goto err;
- br = p->br;
- spin_lock(&br->lock);
+ if (pdu->ssap != LLC_SAP_BSPAN
+ || pdu->dsap != LLC_SAP_BSPAN
+ || pdu->ctrl_1 != LLC_PDU_TYPE_U)
+ goto err;
- if (p->state == BR_STATE_DISABLED || !(br->dev->flags & IFF_UP))
- goto out;
+ if (!pskb_may_pull(skb, 4))
+ goto err;
+
+ /* compare of protocol id and version */
+ buf = skb->data;
+ if (buf[0] != 0 || buf[1] != 0 || buf[2] != 0)
+ goto err;
- /* insert into forwarding database after filtering to avoid spoofing */
- br_fdb_update(br, p, eth_hdr(skb)->h_source);
+ br = p->br;
+ spin_lock(&br->lock);
- if (!br->stp_enabled)
+ if (p->state == BR_STATE_DISABLED
+ || !br->stp_enabled
+ || !(br->dev->flags & IFF_UP))
goto out;
- /* need at least the 802 and STP headers */
- if (!pskb_may_pull(skb, sizeof(header)+1) ||
- memcmp(skb->data, header, sizeof(header)))
+ if (compare_ether_addr(dest, br->group_addr) != 0)
goto out;
- buf = skb_pull(skb, sizeof(header));
+ buf = skb_pull(skb, 3);
if (buf[0] == BPDU_TYPE_CONFIG) {
struct br_config_bpdu bpdu;
if (!pskb_may_pull(skb, 32))
- goto out;
+ goto out;
buf = skb->data;
bpdu.topology_change = (buf[1] & 0x01) ? 1 : 0;
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c
index 9bef55f56425..d0fcde82c6fc 100644
--- a/net/bridge/br_stp_timer.c
+++ b/net/bridge/br_stp_timer.c
@@ -39,13 +39,13 @@ static void br_hello_timer_expired(unsigned long arg)
struct net_bridge *br = (struct net_bridge *)arg;
pr_debug("%s: hello timer expired\n", br->dev->name);
- spin_lock_bh(&br->lock);
+ spin_lock(&br->lock);
if (br->dev->flags & IFF_UP) {
br_config_bpdu_generation(br);
mod_timer(&br->hello_timer, jiffies + br->hello_time);
}
- spin_unlock_bh(&br->lock);
+ spin_unlock(&br->lock);
}
static void br_message_age_timer_expired(unsigned long arg)
@@ -71,7 +71,7 @@ static void br_message_age_timer_expired(unsigned long arg)
* running when we are the root bridge. So.. this was_root
* check is redundant. I'm leaving it in for now, though.
*/
- spin_lock_bh(&br->lock);
+ spin_lock(&br->lock);
if (p->state == BR_STATE_DISABLED)
goto unlock;
was_root = br_is_root_bridge(br);
@@ -82,7 +82,7 @@ static void br_message_age_timer_expired(unsigned long arg)
if (br_is_root_bridge(br) && !was_root)
br_become_root_bridge(br);
unlock:
- spin_unlock_bh(&br->lock);
+ spin_unlock(&br->lock);
}
static void br_forward_delay_timer_expired(unsigned long arg)
@@ -92,7 +92,7 @@ static void br_forward_delay_timer_expired(unsigned long arg)
pr_debug("%s: %d(%s) forward delay timer\n",
br->dev->name, p->port_no, p->dev->name);
- spin_lock_bh(&br->lock);
+ spin_lock(&br->lock);
if (p->state == BR_STATE_LISTENING) {
p->state = BR_STATE_LEARNING;
mod_timer(&p->forward_delay_timer,
@@ -103,7 +103,7 @@ static void br_forward_delay_timer_expired(unsigned long arg)
br_topology_change_detection(br);
}
br_log_state(p);
- spin_unlock_bh(&br->lock);
+ spin_unlock(&br->lock);
}
static void br_tcn_timer_expired(unsigned long arg)
@@ -111,13 +111,13 @@ static void br_tcn_timer_expired(unsigned long arg)
struct net_bridge *br = (struct net_bridge *) arg;
pr_debug("%s: tcn timer expired\n", br->dev->name);
- spin_lock_bh(&br->lock);
+ spin_lock(&br->lock);
if (br->dev->flags & IFF_UP) {
br_transmit_tcn(br);
mod_timer(&br->tcn_timer,jiffies + br->bridge_hello_time);
}
- spin_unlock_bh(&br->lock);
+ spin_unlock(&br->lock);
}
static void br_topology_change_timer_expired(unsigned long arg)
@@ -125,10 +125,10 @@ static void br_topology_change_timer_expired(unsigned long arg)
struct net_bridge *br = (struct net_bridge *) arg;
pr_debug("%s: topo change timer expired\n", br->dev->name);
- spin_lock_bh(&br->lock);
+ spin_lock(&br->lock);
br->topology_change_detected = 0;
br->topology_change = 0;
- spin_unlock_bh(&br->lock);
+ spin_unlock(&br->lock);
}
static void br_hold_timer_expired(unsigned long arg)
@@ -138,45 +138,36 @@ static void br_hold_timer_expired(unsigned long arg)
pr_debug("%s: %d(%s) hold timer expired\n",
p->br->dev->name, p->port_no, p->dev->name);
- spin_lock_bh(&p->br->lock);
+ spin_lock(&p->br->lock);
if (p->config_pending)
br_transmit_config(p);
- spin_unlock_bh(&p->br->lock);
-}
-
-static inline void br_timer_init(struct timer_list *timer,
- void (*_function)(unsigned long),
- unsigned long _data)
-{
- init_timer(timer);
- timer->function = _function;
- timer->data = _data;
+ spin_unlock(&p->br->lock);
}
void br_stp_timer_init(struct net_bridge *br)
{
- br_timer_init(&br->hello_timer, br_hello_timer_expired,
+ setup_timer(&br->hello_timer, br_hello_timer_expired,
(unsigned long) br);
- br_timer_init(&br->tcn_timer, br_tcn_timer_expired,
+ setup_timer(&br->tcn_timer, br_tcn_timer_expired,
(unsigned long) br);
- br_timer_init(&br->topology_change_timer,
+ setup_timer(&br->topology_change_timer,
br_topology_change_timer_expired,
(unsigned long) br);
- br_timer_init(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
+ setup_timer(&br->gc_timer, br_fdb_cleanup, (unsigned long) br);
}
void br_stp_port_timer_init(struct net_bridge_port *p)
{
- br_timer_init(&p->message_age_timer, br_message_age_timer_expired,
+ setup_timer(&p->message_age_timer, br_message_age_timer_expired,
(unsigned long) p);
- br_timer_init(&p->forward_delay_timer, br_forward_delay_timer_expired,
+ setup_timer(&p->forward_delay_timer, br_forward_delay_timer_expired,
(unsigned long) p);
- br_timer_init(&p->hold_timer, br_hold_timer_expired,
+ setup_timer(&p->hold_timer, br_hold_timer_expired,
(unsigned long) p);
}
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index 6f577f16c4c0..96bcb2ff59ab 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -242,6 +242,54 @@ static ssize_t show_gc_timer(struct class_device *cd, char *buf)
}
static CLASS_DEVICE_ATTR(gc_timer, S_IRUGO, show_gc_timer, NULL);
+static ssize_t show_group_addr(struct class_device *cd, char *buf)
+{
+ struct net_bridge *br = to_bridge(cd);
+ return sprintf(buf, "%x:%x:%x:%x:%x:%x\n",
+ br->group_addr[0], br->group_addr[1],
+ br->group_addr[2], br->group_addr[3],
+ br->group_addr[4], br->group_addr[5]);
+}
+
+static ssize_t store_group_addr(struct class_device *cd, const char *buf,
+ size_t len)
+{
+ struct net_bridge *br = to_bridge(cd);
+ unsigned new_addr[6];
+ int i;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+ &new_addr[0], &new_addr[1], &new_addr[2],
+ &new_addr[3], &new_addr[4], &new_addr[5]) != 6)
+ return -EINVAL;
+
+ /* Must be 01:80:c2:00:00:0X */
+ for (i = 0; i < 5; i++)
+ if (new_addr[i] != br_group_address[i])
+ return -EINVAL;
+
+ if (new_addr[5] & ~0xf)
+ return -EINVAL;
+
+ if (new_addr[5] == 1 /* 802.3x Pause address */
+ || new_addr[5] == 2 /* 802.3ad Slow protocols */
+ || new_addr[5] == 3) /* 802.1X PAE address */
+ return -EINVAL;
+
+ spin_lock_bh(&br->lock);
+ for (i = 0; i < 6; i++)
+ br->group_addr[i] = new_addr[i];
+ spin_unlock_bh(&br->lock);
+ return len;
+}
+
+static CLASS_DEVICE_ATTR(group_addr, S_IRUGO | S_IWUSR,
+ show_group_addr, store_group_addr);
+
+
static struct attribute *bridge_attrs[] = {
&class_device_attr_forward_delay.attr,
&class_device_attr_hello_time.attr,
@@ -259,6 +307,7 @@ static struct attribute *bridge_attrs[] = {
&class_device_attr_tcn_timer.attr,
&class_device_attr_topology_change_timer.attr,
&class_device_attr_gc_timer.attr,
+ &class_device_attr_group_addr.attr,
NULL
};
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index cbd4020cc84d..997953367204 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -35,6 +35,7 @@
#define ASSERT_READ_LOCK(x)
#define ASSERT_WRITE_LOCK(x)
#include <linux/netfilter_ipv4/listhelp.h>
+#include <linux/mutex.h>
#if 0
/* use this for remote debugging
@@ -81,7 +82,7 @@ static void print_string(char *str)
-static DECLARE_MUTEX(ebt_mutex);
+static DEFINE_MUTEX(ebt_mutex);
static LIST_HEAD(ebt_tables);
static LIST_HEAD(ebt_targets);
static LIST_HEAD(ebt_matches);
@@ -296,18 +297,18 @@ letscontinue:
/* If it succeeds, returns element and locks mutex */
static inline void *
find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
- struct semaphore *mutex)
+ struct mutex *mutex)
{
void *ret;
- *error = down_interruptible(mutex);
+ *error = mutex_lock_interruptible(mutex);
if (*error != 0)
return NULL;
ret = list_named_find(head, name);
if (!ret) {
*error = -ENOENT;
- up(mutex);
+ mutex_unlock(mutex);
}
return ret;
}
@@ -317,7 +318,7 @@ find_inlist_lock_noload(struct list_head *head, const char *name, int *error,
#else
static void *
find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
- int *error, struct semaphore *mutex)
+ int *error, struct mutex *mutex)
{
void *ret;
@@ -331,25 +332,25 @@ find_inlist_lock(struct list_head *head, const char *name, const char *prefix,
#endif
static inline struct ebt_table *
-find_table_lock(const char *name, int *error, struct semaphore *mutex)
+find_table_lock(const char *name, int *error, struct mutex *mutex)
{
return find_inlist_lock(&ebt_tables, name, "ebtable_", error, mutex);
}
static inline struct ebt_match *
-find_match_lock(const char *name, int *error, struct semaphore *mutex)
+find_match_lock(const char *name, int *error, struct mutex *mutex)
{
return find_inlist_lock(&ebt_matches, name, "ebt_", error, mutex);
}
static inline struct ebt_watcher *
-find_watcher_lock(const char *name, int *error, struct semaphore *mutex)
+find_watcher_lock(const char *name, int *error, struct mutex *mutex)
{
return find_inlist_lock(&ebt_watchers, name, "ebt_", error, mutex);
}
static inline struct ebt_target *
-find_target_lock(const char *name, int *error, struct semaphore *mutex)
+find_target_lock(const char *name, int *error, struct mutex *mutex)
{
return find_inlist_lock(&ebt_targets, name, "ebt_", error, mutex);
}
@@ -369,10 +370,10 @@ ebt_check_match(struct ebt_entry_match *m, struct ebt_entry *e,
return ret;
m->u.match = match;
if (!try_module_get(match->me)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return -ENOENT;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
if (match->check &&
match->check(name, hookmask, e, m->data, m->match_size) != 0) {
BUGPRINT("match->check failed\n");
@@ -398,10 +399,10 @@ ebt_check_watcher(struct ebt_entry_watcher *w, struct ebt_entry *e,
return ret;
w->u.watcher = watcher;
if (!try_module_get(watcher->me)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return -ENOENT;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
if (watcher->check &&
watcher->check(name, hookmask, e, w->data, w->watcher_size) != 0) {
BUGPRINT("watcher->check failed\n");
@@ -638,11 +639,11 @@ ebt_check_entry(struct ebt_entry *e, struct ebt_table_info *newinfo,
if (!target)
goto cleanup_watchers;
if (!try_module_get(target->me)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
ret = -ENOENT;
goto cleanup_watchers;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
t->u.target = target;
if (t->u.target == &ebt_standard_target) {
@@ -1015,7 +1016,7 @@ static int do_replace(void __user *user, unsigned int len)
t->private = newinfo;
write_unlock_bh(&t->lock);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
/* so, a user can change the chains while having messed up her counter
allocation. Only reason why this is done is because this way the lock
is held only once, while this doesn't bring the kernel into a
@@ -1045,7 +1046,7 @@ static int do_replace(void __user *user, unsigned int len)
return ret;
free_unlock:
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
free_iterate:
EBT_ENTRY_ITERATE(newinfo->entries, newinfo->entries_size,
ebt_cleanup_entry, NULL);
@@ -1068,69 +1069,69 @@ int ebt_register_target(struct ebt_target *target)
{
int ret;
- ret = down_interruptible(&ebt_mutex);
+ ret = mutex_lock_interruptible(&ebt_mutex);
if (ret != 0)
return ret;
if (!list_named_insert(&ebt_targets, target)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return -EEXIST;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return 0;
}
void ebt_unregister_target(struct ebt_target *target)
{
- down(&ebt_mutex);
+ mutex_lock(&ebt_mutex);
LIST_DELETE(&ebt_targets, target);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
}
int ebt_register_match(struct ebt_match *match)
{
int ret;
- ret = down_interruptible(&ebt_mutex);
+ ret = mutex_lock_interruptible(&ebt_mutex);
if (ret != 0)
return ret;
if (!list_named_insert(&ebt_matches, match)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return -EEXIST;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return 0;
}
void ebt_unregister_match(struct ebt_match *match)
{
- down(&ebt_mutex);
+ mutex_lock(&ebt_mutex);
LIST_DELETE(&ebt_matches, match);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
}
int ebt_register_watcher(struct ebt_watcher *watcher)
{
int ret;
- ret = down_interruptible(&ebt_mutex);
+ ret = mutex_lock_interruptible(&ebt_mutex);
if (ret != 0)
return ret;
if (!list_named_insert(&ebt_watchers, watcher)) {
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return -EEXIST;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return 0;
}
void ebt_unregister_watcher(struct ebt_watcher *watcher)
{
- down(&ebt_mutex);
+ mutex_lock(&ebt_mutex);
LIST_DELETE(&ebt_watchers, watcher);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
}
int ebt_register_table(struct ebt_table *table)
@@ -1178,7 +1179,7 @@ int ebt_register_table(struct ebt_table *table)
table->private = newinfo;
rwlock_init(&table->lock);
- ret = down_interruptible(&ebt_mutex);
+ ret = mutex_lock_interruptible(&ebt_mutex);
if (ret != 0)
goto free_chainstack;
@@ -1194,10 +1195,10 @@ int ebt_register_table(struct ebt_table *table)
goto free_unlock;
}
list_prepend(&ebt_tables, table);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
return 0;
free_unlock:
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
free_chainstack:
if (newinfo->chainstack) {
for_each_cpu(i)
@@ -1218,9 +1219,9 @@ void ebt_unregister_table(struct ebt_table *table)
BUGPRINT("Request to unregister NULL table!!!\n");
return;
}
- down(&ebt_mutex);
+ mutex_lock(&ebt_mutex);
LIST_DELETE(&ebt_tables, table);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
vfree(table->private->entries);
if (table->private->chainstack) {
for_each_cpu(i)
@@ -1281,7 +1282,7 @@ static int update_counters(void __user *user, unsigned int len)
write_unlock_bh(&t->lock);
ret = 0;
unlock_mutex:
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
free_tmp:
vfree(tmp);
return ret;
@@ -1328,7 +1329,7 @@ static inline int ebt_make_names(struct ebt_entry *e, char *base, char *ubase)
return 0;
}
-/* called with ebt_mutex down */
+/* called with ebt_mutex locked */
static int copy_everything_to_user(struct ebt_table *t, void __user *user,
int *len, int cmd)
{
@@ -1440,7 +1441,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
case EBT_SO_GET_INIT_INFO:
if (*len != sizeof(struct ebt_replace)){
ret = -EINVAL;
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
break;
}
if (cmd == EBT_SO_GET_INFO) {
@@ -1452,7 +1453,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
tmp.entries_size = t->table->entries_size;
tmp.valid_hooks = t->table->valid_hooks;
}
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
if (copy_to_user(user, &tmp, *len) != 0){
BUGPRINT("c2u Didn't work\n");
ret = -EFAULT;
@@ -1464,11 +1465,11 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
case EBT_SO_GET_ENTRIES:
case EBT_SO_GET_INIT_ENTRIES:
ret = copy_everything_to_user(t, user, len, cmd);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
break;
default:
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
ret = -EINVAL;
}
@@ -1476,17 +1477,23 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
}
static struct nf_sockopt_ops ebt_sockopts =
-{ { NULL, NULL }, PF_INET, EBT_BASE_CTL, EBT_SO_SET_MAX + 1, do_ebt_set_ctl,
- EBT_BASE_CTL, EBT_SO_GET_MAX + 1, do_ebt_get_ctl, 0, NULL
+{
+ .pf = PF_INET,
+ .set_optmin = EBT_BASE_CTL,
+ .set_optmax = EBT_SO_SET_MAX + 1,
+ .set = do_ebt_set_ctl,
+ .get_optmin = EBT_BASE_CTL,
+ .get_optmax = EBT_SO_GET_MAX + 1,
+ .get = do_ebt_get_ctl,
};
static int __init init(void)
{
int ret;
- down(&ebt_mutex);
+ mutex_lock(&ebt_mutex);
list_named_insert(&ebt_targets, &ebt_standard_target);
- up(&ebt_mutex);
+ mutex_unlock(&ebt_mutex);
if ((ret = nf_register_sockopt(&ebt_sockopts)) < 0)
return ret;
diff --git a/net/compat.c b/net/compat.c
index e593dace2fdb..8fd37cd7b501 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -416,7 +416,7 @@ struct compat_sock_fprog {
compat_uptr_t filter; /* struct sock_filter * */
};
-static int do_set_attach_filter(int fd, int level, int optname,
+static int do_set_attach_filter(struct socket *sock, int level, int optname,
char __user *optval, int optlen)
{
struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval;
@@ -432,11 +432,12 @@ static int do_set_attach_filter(int fd, int level, int optname,
__put_user(compat_ptr(ptr), &kfprog->filter))
return -EFAULT;
- return sys_setsockopt(fd, level, optname, (char __user *)kfprog,
+ return sock_setsockopt(sock, level, optname, (char __user *)kfprog,
sizeof(struct sock_fprog));
}
-static int do_set_sock_timeout(int fd, int level, int optname, char __user *optval, int optlen)
+static int do_set_sock_timeout(struct socket *sock, int level,
+ int optname, char __user *optval, int optlen)
{
struct compat_timeval __user *up = (struct compat_timeval __user *) optval;
struct timeval ktime;
@@ -451,30 +452,61 @@ static int do_set_sock_timeout(int fd, int level, int optname, char __user *optv
return -EFAULT;
old_fs = get_fs();
set_fs(KERNEL_DS);
- err = sys_setsockopt(fd, level, optname, (char *) &ktime, sizeof(ktime));
+ err = sock_setsockopt(sock, level, optname, (char *) &ktime, sizeof(ktime));
set_fs(old_fs);
return err;
}
+static int compat_sock_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int optlen)
+{
+ if (optname == SO_ATTACH_FILTER)
+ return do_set_attach_filter(sock, level, optname,
+ optval, optlen);
+ if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
+ return do_set_sock_timeout(sock, level, optname, optval, optlen);
+
+ return sock_setsockopt(sock, level, optname, optval, optlen);
+}
+
asmlinkage long compat_sys_setsockopt(int fd, int level, int optname,
char __user *optval, int optlen)
{
+ int err;
+ struct socket *sock;
+
/* SO_SET_REPLACE seems to be the same in all levels */
if (optname == IPT_SO_SET_REPLACE)
return do_netfilter_replace(fd, level, optname,
optval, optlen);
- if (level == SOL_SOCKET && optname == SO_ATTACH_FILTER)
- return do_set_attach_filter(fd, level, optname,
- optval, optlen);
- if (level == SOL_SOCKET &&
- (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
- return do_set_sock_timeout(fd, level, optname, optval, optlen);
- return sys_setsockopt(fd, level, optname, optval, optlen);
+ if (optlen < 0)
+ return -EINVAL;
+
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ err = security_socket_setsockopt(sock,level,optname);
+ if (err) {
+ sockfd_put(sock);
+ return err;
+ }
+
+ if (level == SOL_SOCKET)
+ err = compat_sock_setsockopt(sock, level,
+ optname, optval, optlen);
+ else if (sock->ops->compat_setsockopt)
+ err = sock->ops->compat_setsockopt(sock, level,
+ optname, optval, optlen);
+ else
+ err = sock->ops->setsockopt(sock, level,
+ optname, optval, optlen);
+ sockfd_put(sock);
+ }
+ return err;
}
-static int do_get_sock_timeout(int fd, int level, int optname,
+static int do_get_sock_timeout(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
struct compat_timeval __user *up;
@@ -490,7 +522,7 @@ static int do_get_sock_timeout(int fd, int level, int optname,
len = sizeof(ktime);
old_fs = get_fs();
set_fs(KERNEL_DS);
- err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len);
+ err = sock_getsockopt(sock, level, optname, (char *) &ktime, &len);
set_fs(old_fs);
if (!err) {
@@ -503,15 +535,61 @@ static int do_get_sock_timeout(int fd, int level, int optname,
return err;
}
-asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
+static int compat_sock_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
- if (level == SOL_SOCKET &&
- (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO))
- return do_get_sock_timeout(fd, level, optname, optval, optlen);
- return sys_getsockopt(fd, level, optname, optval, optlen);
+ if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO)
+ return do_get_sock_timeout(sock, level, optname, optval, optlen);
+ return sock_getsockopt(sock, level, optname, optval, optlen);
+}
+
+int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp)
+{
+ struct compat_timeval __user *ctv =
+ (struct compat_timeval __user*) userstamp;
+ int err = -ENOENT;
+
+ if (!sock_flag(sk, SOCK_TIMESTAMP))
+ sock_enable_timestamp(sk);
+ if (sk->sk_stamp.tv_sec == -1)
+ return err;
+ if (sk->sk_stamp.tv_sec == 0)
+ do_gettimeofday(&sk->sk_stamp);
+ if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) ||
+ put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec))
+ err = -EFAULT;
+ return err;
}
+EXPORT_SYMBOL(compat_sock_get_timestamp);
+
+asmlinkage long compat_sys_getsockopt(int fd, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ int err;
+ struct socket *sock;
+
+ if ((sock = sockfd_lookup(fd, &err))!=NULL)
+ {
+ err = security_socket_getsockopt(sock, level,
+ optname);
+ if (err) {
+ sockfd_put(sock);
+ return err;
+ }
+ if (level == SOL_SOCKET)
+ err = compat_sock_getsockopt(sock, level,
+ optname, optval, optlen);
+ else if (sock->ops->compat_getsockopt)
+ err = sock->ops->compat_getsockopt(sock, level,
+ optname, optval, optlen);
+ else
+ err = sock->ops->getsockopt(sock, level,
+ optname, optval, optlen);
+ sockfd_put(sock);
+ }
+ return err;
+}
/* Argument list sizes for compat_sys_socketcall */
#define AL(x) ((x) * sizeof(u32))
static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
diff --git a/net/core/dev.c b/net/core/dev.c
index ef56c035d44e..08dec6eb922b 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -81,6 +81,7 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
+#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/socket.h>
@@ -1759,8 +1760,7 @@ static void net_rx_action(struct softirq_action *h)
if (dev->quota <= 0 || dev->poll(dev, &budget)) {
netpoll_poll_unlock(have);
local_irq_disable();
- list_del(&dev->poll_list);
- list_add_tail(&dev->poll_list, &queue->poll_list);
+ list_move_tail(&dev->poll_list, &queue->poll_list);
if (dev->quota < 0)
dev->quota += dev->weight;
else
@@ -2174,12 +2174,20 @@ unsigned dev_get_flags(const struct net_device *dev)
flags = (dev->flags & ~(IFF_PROMISC |
IFF_ALLMULTI |
- IFF_RUNNING)) |
+ IFF_RUNNING |
+ IFF_LOWER_UP |
+ IFF_DORMANT)) |
(dev->gflags & (IFF_PROMISC |
IFF_ALLMULTI));
- if (netif_running(dev) && netif_carrier_ok(dev))
- flags |= IFF_RUNNING;
+ if (netif_running(dev)) {
+ if (netif_oper_up(dev))
+ flags |= IFF_RUNNING;
+ if (netif_carrier_ok(dev))
+ flags |= IFF_LOWER_UP;
+ if (netif_dormant(dev))
+ flags |= IFF_DORMANT;
+ }
return flags;
}
@@ -2458,9 +2466,9 @@ int dev_ioctl(unsigned int cmd, void __user *arg)
*/
if (cmd == SIOCGIFCONF) {
- rtnl_shlock();
+ rtnl_lock();
ret = dev_ifconf((char __user *) arg);
- rtnl_shunlock();
+ rtnl_unlock();
return ret;
}
if (cmd == SIOCGIFNAME)
@@ -2869,7 +2877,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
rebroadcast_time = warning_time = jiffies;
while (atomic_read(&dev->refcnt) != 0) {
if (time_after(jiffies, rebroadcast_time + 1 * HZ)) {
- rtnl_shlock();
+ rtnl_lock();
/* Rebroadcast unregister notification */
notifier_call_chain(&netdev_chain,
@@ -2886,7 +2894,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
linkwatch_run_queue();
}
- rtnl_shunlock();
+ __rtnl_unlock();
rebroadcast_time = jiffies;
}
@@ -2924,7 +2932,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
* 2) Since we run with the RTNL semaphore not held, we can sleep
* safely in order to wait for the netdev refcnt to drop to zero.
*/
-static DECLARE_MUTEX(net_todo_run_mutex);
+static DEFINE_MUTEX(net_todo_run_mutex);
void netdev_run_todo(void)
{
struct list_head list = LIST_HEAD_INIT(list);
@@ -2932,7 +2940,7 @@ void netdev_run_todo(void)
/* Need to guard against multiple cpu's getting out of order. */
- down(&net_todo_run_mutex);
+ mutex_lock(&net_todo_run_mutex);
/* Not safe to do outside the semaphore. We must not return
* until all unregister events invoked by the local processor
@@ -2989,7 +2997,7 @@ void netdev_run_todo(void)
}
out:
- up(&net_todo_run_mutex);
+ mutex_unlock(&net_todo_run_mutex);
}
/**
diff --git a/net/core/flow.c b/net/core/flow.c
index c4f25385029f..55789f832eda 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -20,6 +20,7 @@
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
+#include <linux/mutex.h>
#include <net/flow.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
@@ -287,11 +288,11 @@ static void flow_cache_flush_per_cpu(void *data)
void flow_cache_flush(void)
{
struct flow_flush_info info;
- static DECLARE_MUTEX(flow_flush_sem);
+ static DEFINE_MUTEX(flow_flush_sem);
/* Don't want cpus going down or up during this. */
lock_cpu_hotplug();
- down(&flow_flush_sem);
+ mutex_lock(&flow_flush_sem);
atomic_set(&info.cpuleft, num_online_cpus());
init_completion(&info.completion);
@@ -301,7 +302,7 @@ void flow_cache_flush(void)
local_bh_enable();
wait_for_completion(&info.completion);
- up(&flow_flush_sem);
+ mutex_unlock(&flow_flush_sem);
unlock_cpu_hotplug();
}
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index d43d1201275c..341de44c7ed1 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -49,6 +49,45 @@ struct lw_event {
/* Avoid kmalloc() for most systems */
static struct lw_event singleevent;
+static unsigned char default_operstate(const struct net_device *dev)
+{
+ if (!netif_carrier_ok(dev))
+ return (dev->ifindex != dev->iflink ?
+ IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
+
+ if (netif_dormant(dev))
+ return IF_OPER_DORMANT;
+
+ return IF_OPER_UP;
+}
+
+
+static void rfc2863_policy(struct net_device *dev)
+{
+ unsigned char operstate = default_operstate(dev);
+
+ if (operstate == dev->operstate)
+ return;
+
+ write_lock_bh(&dev_base_lock);
+
+ switch(dev->link_mode) {
+ case IF_LINK_MODE_DORMANT:
+ if (operstate == IF_OPER_UP)
+ operstate = IF_OPER_DORMANT;
+ break;
+
+ case IF_LINK_MODE_DEFAULT:
+ default:
+ break;
+ };
+
+ dev->operstate = operstate;
+
+ write_unlock_bh(&dev_base_lock);
+}
+
+
/* Must be called with the rtnl semaphore held */
void linkwatch_run_queue(void)
{
@@ -74,6 +113,7 @@ void linkwatch_run_queue(void)
*/
clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
+ rfc2863_policy(dev);
if (dev->flags & IFF_UP) {
if (netif_carrier_ok(dev)) {
WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
@@ -99,9 +139,9 @@ static void linkwatch_event(void *dummy)
linkwatch_nextevent = jiffies + HZ;
clear_bit(LW_RUNNING, &linkwatch_flags);
- rtnl_shlock();
+ rtnl_lock();
linkwatch_run_queue();
- rtnl_shunlock();
+ rtnl_unlock();
}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index e68700f950a5..0c8666872d10 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -586,8 +586,8 @@ void neigh_destroy(struct neighbour *neigh)
kfree(hh);
}
- if (neigh->ops && neigh->ops->destructor)
- (neigh->ops->destructor)(neigh);
+ if (neigh->parms->neigh_destructor)
+ (neigh->parms->neigh_destructor)(neigh);
skb_queue_purge(&neigh->arp_queue);
@@ -750,11 +750,13 @@ static void neigh_timer_handler(unsigned long arg)
neigh->used + neigh->parms->delay_probe_time)) {
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh->nud_state = NUD_DELAY;
+ neigh->updated = jiffies;
neigh_suspect(neigh);
next = now + neigh->parms->delay_probe_time;
} else {
NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
neigh->nud_state = NUD_STALE;
+ neigh->updated = jiffies;
neigh_suspect(neigh);
}
} else if (state & NUD_DELAY) {
@@ -762,11 +764,13 @@ static void neigh_timer_handler(unsigned long arg)
neigh->confirmed + neigh->parms->delay_probe_time)) {
NEIGH_PRINTK2("neigh %p is now reachable.\n", neigh);
neigh->nud_state = NUD_REACHABLE;
+ neigh->updated = jiffies;
neigh_connect(neigh);
next = neigh->confirmed + neigh->parms->reachable_time;
} else {
NEIGH_PRINTK2("neigh %p is probed.\n", neigh);
neigh->nud_state = NUD_PROBE;
+ neigh->updated = jiffies;
atomic_set(&neigh->probes, 0);
next = now + neigh->parms->retrans_time;
}
@@ -780,6 +784,7 @@ static void neigh_timer_handler(unsigned long arg)
struct sk_buff *skb;
neigh->nud_state = NUD_FAILED;
+ neigh->updated = jiffies;
notify = 1;
NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
@@ -843,10 +848,12 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
atomic_set(&neigh->probes, neigh->parms->ucast_probes);
neigh->nud_state = NUD_INCOMPLETE;
+ neigh->updated = jiffies;
neigh_hold(neigh);
neigh_add_timer(neigh, now + 1);
} else {
neigh->nud_state = NUD_FAILED;
+ neigh->updated = jiffies;
write_unlock_bh(&neigh->lock);
if (skb)
@@ -857,6 +864,7 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
NEIGH_PRINTK2("neigh %p is delayed.\n", neigh);
neigh_hold(neigh);
neigh->nud_state = NUD_DELAY;
+ neigh->updated = jiffies;
neigh_add_timer(neigh,
jiffies + neigh->parms->delay_probe_time);
}
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index e8b2acbc8ea2..21b68464cabb 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -91,6 +91,7 @@ NETDEVICE_SHOW(iflink, fmt_dec);
NETDEVICE_SHOW(ifindex, fmt_dec);
NETDEVICE_SHOW(features, fmt_long_hex);
NETDEVICE_SHOW(type, fmt_dec);
+NETDEVICE_SHOW(link_mode, fmt_dec);
/* use same locking rules as GIFHWADDR ioctl's */
static ssize_t format_addr(char *buf, const unsigned char *addr, int len)
@@ -133,6 +134,43 @@ static ssize_t show_carrier(struct class_device *dev, char *buf)
return -EINVAL;
}
+static ssize_t show_dormant(struct class_device *dev, char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+
+ if (netif_running(netdev))
+ return sprintf(buf, fmt_dec, !!netif_dormant(netdev));
+
+ return -EINVAL;
+}
+
+static const char *operstates[] = {
+ "unknown",
+ "notpresent", /* currently unused */
+ "down",
+ "lowerlayerdown",
+ "testing", /* currently unused */
+ "dormant",
+ "up"
+};
+
+static ssize_t show_operstate(struct class_device *dev, char *buf)
+{
+ const struct net_device *netdev = to_net_dev(dev);
+ unsigned char operstate;
+
+ read_lock(&dev_base_lock);
+ operstate = netdev->operstate;
+ if (!netif_running(netdev))
+ operstate = IF_OPER_DOWN;
+ read_unlock(&dev_base_lock);
+
+ if (operstate >= sizeof(operstates))
+ return -EINVAL; /* should not happen */
+
+ return sprintf(buf, "%s\n", operstates[operstate]);
+}
+
/* read-write attributes */
NETDEVICE_SHOW(mtu, fmt_dec);
@@ -190,9 +228,12 @@ static struct class_device_attribute net_class_attributes[] = {
__ATTR(ifindex, S_IRUGO, show_ifindex, NULL),
__ATTR(features, S_IRUGO, show_features, NULL),
__ATTR(type, S_IRUGO, show_type, NULL),
+ __ATTR(link_mode, S_IRUGO, show_link_mode, NULL),
__ATTR(address, S_IRUGO, show_address, NULL),
__ATTR(broadcast, S_IRUGO, show_broadcast, NULL),
__ATTR(carrier, S_IRUGO, show_carrier, NULL),
+ __ATTR(dormant, S_IRUGO, show_dormant, NULL),
+ __ATTR(operstate, S_IRUGO, show_operstate, NULL),
__ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu),
__ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags),
__ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len,
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index ea51f8d02eb8..e8e05cebd95a 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -669,14 +669,14 @@ int netpoll_setup(struct netpoll *np)
printk(KERN_INFO "%s: device %s not up yet, forcing it\n",
np->name, np->dev_name);
- rtnl_shlock();
+ rtnl_lock();
if (dev_change_flags(ndev, ndev->flags | IFF_UP) < 0) {
printk(KERN_ERR "%s: failed to open %s\n",
np->name, np->dev_name);
- rtnl_shunlock();
+ rtnl_unlock();
goto release;
}
- rtnl_shunlock();
+ rtnl_unlock();
atleast = jiffies + HZ/10;
atmost = jiffies + 4*HZ;
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index da16f8fd1494..c23e9c06ee23 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -106,6 +106,9 @@
*
* interruptible_sleep_on_timeout() replaced Nishanth Aravamudan <nacc@us.ibm.com>
* 050103
+ *
+ * MPLS support by Steven Whitehouse <steve@chygwyn.com>
+ *
*/
#include <linux/sys.h>
#include <linux/types.h>
@@ -113,6 +116,7 @@
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
@@ -125,6 +129,7 @@
#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/timer.h>
+#include <linux/list.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
@@ -149,38 +154,37 @@
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/uaccess.h>
-#include <asm/div64.h> /* do_div */
+#include <asm/div64.h> /* do_div */
#include <asm/timex.h>
-
-#define VERSION "pktgen v2.63: Packet Generator for packet performance testing.\n"
+#define VERSION "pktgen v2.67: Packet Generator for packet performance testing.\n"
/* #define PG_DEBUG(a) a */
-#define PG_DEBUG(a)
+#define PG_DEBUG(a)
/* The buckets are exponential in 'width' */
#define LAT_BUCKETS_MAX 32
#define IP_NAME_SZ 32
+#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
+#define MPLS_STACK_BOTTOM __constant_htonl(0x00000100)
/* Device flag bits */
-#define F_IPSRC_RND (1<<0) /* IP-Src Random */
-#define F_IPDST_RND (1<<1) /* IP-Dst Random */
-#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
-#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
-#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
-#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
-#define F_TXSIZE_RND (1<<6) /* Transmit size is random */
-#define F_IPV6 (1<<7) /* Interface in IPV6 Mode */
+#define F_IPSRC_RND (1<<0) /* IP-Src Random */
+#define F_IPDST_RND (1<<1) /* IP-Dst Random */
+#define F_UDPSRC_RND (1<<2) /* UDP-Src Random */
+#define F_UDPDST_RND (1<<3) /* UDP-Dst Random */
+#define F_MACSRC_RND (1<<4) /* MAC-Src Random */
+#define F_MACDST_RND (1<<5) /* MAC-Dst Random */
+#define F_TXSIZE_RND (1<<6) /* Transmit size is random */
+#define F_IPV6 (1<<7) /* Interface in IPV6 Mode */
+#define F_MPLS_RND (1<<8) /* Random MPLS labels */
/* Thread control flag bits */
-#define T_TERMINATE (1<<0)
-#define T_STOP (1<<1) /* Stop run */
-#define T_RUN (1<<2) /* Start run */
-#define T_REMDEV (1<<3) /* Remove all devs */
-
-/* Locks */
-#define thread_lock() down(&pktgen_sem)
-#define thread_unlock() up(&pktgen_sem)
+#define T_TERMINATE (1<<0)
+#define T_STOP (1<<1) /* Stop run */
+#define T_RUN (1<<2) /* Start run */
+#define T_REMDEVALL (1<<3) /* Remove all devs */
+#define T_REMDEV (1<<4) /* Remove one dev */
/* If lock -- can be removed after some work */
#define if_lock(t) spin_lock(&(t->if_lock));
@@ -194,10 +198,9 @@ static struct proc_dir_entry *pg_proc_dir = NULL;
#define MAX_CFLOWS 65536
-struct flow_state
-{
- __u32 cur_daddr;
- int count;
+struct flow_state {
+ __u32 cur_daddr;
+ int count;
};
struct pktgen_dev {
@@ -206,141 +209,148 @@ struct pktgen_dev {
* Try to keep frequent/infrequent used vars. separated.
*/
- char ifname[IFNAMSIZ];
- char result[512];
-
- struct pktgen_thread* pg_thread; /* the owner */
- struct pktgen_dev *next; /* Used for chaining in the thread's run-queue */
-
- int running; /* if this changes to false, the test will stop */
-
- /* If min != max, then we will either do a linear iteration, or
- * we will do a random selection from within the range.
- */
- __u32 flags;
-
- int min_pkt_size; /* = ETH_ZLEN; */
- int max_pkt_size; /* = ETH_ZLEN; */
- int nfrags;
- __u32 delay_us; /* Default delay */
- __u32 delay_ns;
- __u64 count; /* Default No packets to send */
- __u64 sofar; /* How many pkts we've sent so far */
- __u64 tx_bytes; /* How many bytes we've transmitted */
- __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
-
- /* runtime counters relating to clone_skb */
- __u64 next_tx_us; /* timestamp of when to tx next */
- __u32 next_tx_ns;
-
- __u64 allocated_skbs;
- __u32 clone_count;
- int last_ok; /* Was last skb sent?
- * Or a failed transmit of some sort? This will keep
- * sequence numbers in order, for example.
- */
- __u64 started_at; /* micro-seconds */
- __u64 stopped_at; /* micro-seconds */
- __u64 idle_acc; /* micro-seconds */
- __u32 seq_num;
-
- int clone_skb; /* Use multiple SKBs during packet gen. If this number
- * is greater than 1, then that many copies of the same
- * packet will be sent before a new packet is allocated.
- * For instance, if you want to send 1024 identical packets
- * before creating a new packet, set clone_skb to 1024.
- */
-
- char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
- char dst_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
- char src_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
- char src_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
-
- struct in6_addr in6_saddr;
- struct in6_addr in6_daddr;
- struct in6_addr cur_in6_daddr;
- struct in6_addr cur_in6_saddr;
+ char ifname[IFNAMSIZ];
+ char result[512];
+
+ struct pktgen_thread *pg_thread; /* the owner */
+ struct list_head list; /* Used for chaining in the thread's run-queue */
+
+ int running; /* if this changes to false, the test will stop */
+
+ /* If min != max, then we will either do a linear iteration, or
+ * we will do a random selection from within the range.
+ */
+ __u32 flags;
+ int removal_mark; /* non-zero => the device is marked for
+ * removal by worker thread */
+
+ int min_pkt_size; /* = ETH_ZLEN; */
+ int max_pkt_size; /* = ETH_ZLEN; */
+ int nfrags;
+ __u32 delay_us; /* Default delay */
+ __u32 delay_ns;
+ __u64 count; /* Default No packets to send */
+ __u64 sofar; /* How many pkts we've sent so far */
+ __u64 tx_bytes; /* How many bytes we've transmitted */
+ __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
+
+ /* runtime counters relating to clone_skb */
+ __u64 next_tx_us; /* timestamp of when to tx next */
+ __u32 next_tx_ns;
+
+ __u64 allocated_skbs;
+ __u32 clone_count;
+ int last_ok; /* Was last skb sent?
+ * Or a failed transmit of some sort? This will keep
+ * sequence numbers in order, for example.
+ */
+ __u64 started_at; /* micro-seconds */
+ __u64 stopped_at; /* micro-seconds */
+ __u64 idle_acc; /* micro-seconds */
+ __u32 seq_num;
+
+ int clone_skb; /* Use multiple SKBs during packet gen. If this number
+ * is greater than 1, then that many copies of the same
+ * packet will be sent before a new packet is allocated.
+ * For instance, if you want to send 1024 identical packets
+ * before creating a new packet, set clone_skb to 1024.
+ */
+
+ char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
+ char dst_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
+ char src_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
+ char src_max[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
+
+ struct in6_addr in6_saddr;
+ struct in6_addr in6_daddr;
+ struct in6_addr cur_in6_daddr;
+ struct in6_addr cur_in6_saddr;
/* For ranges */
- struct in6_addr min_in6_daddr;
- struct in6_addr max_in6_daddr;
- struct in6_addr min_in6_saddr;
- struct in6_addr max_in6_saddr;
-
- /* If we're doing ranges, random or incremental, then this
- * defines the min/max for those ranges.
- */
- __u32 saddr_min; /* inclusive, source IP address */
- __u32 saddr_max; /* exclusive, source IP address */
- __u32 daddr_min; /* inclusive, dest IP address */
- __u32 daddr_max; /* exclusive, dest IP address */
-
- __u16 udp_src_min; /* inclusive, source UDP port */
- __u16 udp_src_max; /* exclusive, source UDP port */
- __u16 udp_dst_min; /* inclusive, dest UDP port */
- __u16 udp_dst_max; /* exclusive, dest UDP port */
-
- __u32 src_mac_count; /* How many MACs to iterate through */
- __u32 dst_mac_count; /* How many MACs to iterate through */
-
- unsigned char dst_mac[ETH_ALEN];
- unsigned char src_mac[ETH_ALEN];
-
- __u32 cur_dst_mac_offset;
- __u32 cur_src_mac_offset;
- __u32 cur_saddr;
- __u32 cur_daddr;
- __u16 cur_udp_dst;
- __u16 cur_udp_src;
- __u32 cur_pkt_size;
-
- __u8 hh[14];
- /* = {
- 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
-
- We fill in SRC address later
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00
- };
- */
- __u16 pad; /* pad out the hh struct to an even 16 bytes */
-
- struct sk_buff* skb; /* skb we are to transmit next, mainly used for when we
- * are transmitting the same one multiple times
- */
- struct net_device* odev; /* The out-going device. Note that the device should
- * have it's pg_info pointer pointing back to this
- * device. This will be set when the user specifies
- * the out-going device name (not when the inject is
- * started as it used to do.)
- */
+ struct in6_addr min_in6_daddr;
+ struct in6_addr max_in6_daddr;
+ struct in6_addr min_in6_saddr;
+ struct in6_addr max_in6_saddr;
+
+ /* If we're doing ranges, random or incremental, then this
+ * defines the min/max for those ranges.
+ */
+ __u32 saddr_min; /* inclusive, source IP address */
+ __u32 saddr_max; /* exclusive, source IP address */
+ __u32 daddr_min; /* inclusive, dest IP address */
+ __u32 daddr_max; /* exclusive, dest IP address */
+
+ __u16 udp_src_min; /* inclusive, source UDP port */
+ __u16 udp_src_max; /* exclusive, source UDP port */
+ __u16 udp_dst_min; /* inclusive, dest UDP port */
+ __u16 udp_dst_max; /* exclusive, dest UDP port */
+
+ /* MPLS */
+ unsigned nr_labels; /* Depth of stack, 0 = no MPLS */
+ __be32 labels[MAX_MPLS_LABELS];
+
+ __u32 src_mac_count; /* How many MACs to iterate through */
+ __u32 dst_mac_count; /* How many MACs to iterate through */
+
+ unsigned char dst_mac[ETH_ALEN];
+ unsigned char src_mac[ETH_ALEN];
+
+ __u32 cur_dst_mac_offset;
+ __u32 cur_src_mac_offset;
+ __u32 cur_saddr;
+ __u32 cur_daddr;
+ __u16 cur_udp_dst;
+ __u16 cur_udp_src;
+ __u32 cur_pkt_size;
+
+ __u8 hh[14];
+ /* = {
+ 0x00, 0x80, 0xC8, 0x79, 0xB3, 0xCB,
+
+ We fill in SRC address later
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00
+ };
+ */
+ __u16 pad; /* pad out the hh struct to an even 16 bytes */
+
+ struct sk_buff *skb; /* skb we are to transmit next, mainly used for when we
+ * are transmitting the same one multiple times
+ */
+ struct net_device *odev; /* The out-going device. Note that the device should
+ * have it's pg_info pointer pointing back to this
+ * device. This will be set when the user specifies
+ * the out-going device name (not when the inject is
+ * started as it used to do.)
+ */
struct flow_state *flows;
- unsigned cflows; /* Concurrent flows (config) */
- unsigned lflow; /* Flow length (config) */
- unsigned nflows; /* accumulated flows (stats) */
+ unsigned cflows; /* Concurrent flows (config) */
+ unsigned lflow; /* Flow length (config) */
+ unsigned nflows; /* accumulated flows (stats) */
};
struct pktgen_hdr {
- __u32 pgh_magic;
- __u32 seq_num;
+ __u32 pgh_magic;
+ __u32 seq_num;
__u32 tv_sec;
__u32 tv_usec;
};
struct pktgen_thread {
- spinlock_t if_lock;
- struct pktgen_dev *if_list; /* All device here */
- struct pktgen_thread* next;
- char name[32];
- char result[512];
- u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
-
- /* Field for thread to receive "posted" events terminate, stop ifs etc.*/
-
- u32 control;
+ spinlock_t if_lock;
+ struct list_head if_list; /* All device here */
+ struct list_head th_list;
+ int removed;
+ char name[32];
+ char result[512];
+ u32 max_before_softirq; /* We'll call do_softirq to prevent starvation. */
+
+ /* Field for thread to receive "posted" events terminate, stop ifs etc. */
+
+ u32 control;
int pid;
int cpu;
- wait_queue_head_t queue;
+ wait_queue_head_t queue;
};
#define REMOVE 1
@@ -364,77 +374,76 @@ struct pktgen_thread {
*/
static inline s64 divremdi3(s64 x, s64 y, int type)
{
- u64 a = (x < 0) ? -x : x;
- u64 b = (y < 0) ? -y : y;
- u64 res = 0, d = 1;
-
- if (b > 0) {
- while (b < a) {
- b <<= 1;
- d <<= 1;
- }
- }
-
- do {
- if ( a >= b ) {
- a -= b;
- res += d;
- }
- b >>= 1;
- d >>= 1;
- }
- while (d);
-
- if (PG_DIV == type) {
- return (((x ^ y) & (1ll<<63)) == 0) ? res : -(s64)res;
- }
- else {
- return ((x & (1ll<<63)) == 0) ? a : -(s64)a;
- }
+ u64 a = (x < 0) ? -x : x;
+ u64 b = (y < 0) ? -y : y;
+ u64 res = 0, d = 1;
+
+ if (b > 0) {
+ while (b < a) {
+ b <<= 1;
+ d <<= 1;
+ }
+ }
+
+ do {
+ if (a >= b) {
+ a -= b;
+ res += d;
+ }
+ b >>= 1;
+ d >>= 1;
+ }
+ while (d);
+
+ if (PG_DIV == type) {
+ return (((x ^ y) & (1ll << 63)) == 0) ? res : -(s64) res;
+ } else {
+ return ((x & (1ll << 63)) == 0) ? a : -(s64) a;
+ }
}
/* End of hacks to deal with 64-bit math on x86 */
/** Convert to milliseconds */
-static inline __u64 tv_to_ms(const struct timeval* tv)
+static inline __u64 tv_to_ms(const struct timeval *tv)
{
- __u64 ms = tv->tv_usec / 1000;
- ms += (__u64)tv->tv_sec * (__u64)1000;
- return ms;
+ __u64 ms = tv->tv_usec / 1000;
+ ms += (__u64) tv->tv_sec * (__u64) 1000;
+ return ms;
}
-
/** Convert to micro-seconds */
-static inline __u64 tv_to_us(const struct timeval* tv)
+static inline __u64 tv_to_us(const struct timeval *tv)
{
- __u64 us = tv->tv_usec;
- us += (__u64)tv->tv_sec * (__u64)1000000;
- return us;
+ __u64 us = tv->tv_usec;
+ us += (__u64) tv->tv_sec * (__u64) 1000000;
+ return us;
}
-static inline __u64 pg_div(__u64 n, __u32 base) {
- __u64 tmp = n;
- do_div(tmp, base);
- /* printk("pktgen: pg_div, n: %llu base: %d rv: %llu\n",
- n, base, tmp); */
- return tmp;
+static inline __u64 pg_div(__u64 n, __u32 base)
+{
+ __u64 tmp = n;
+ do_div(tmp, base);
+ /* printk("pktgen: pg_div, n: %llu base: %d rv: %llu\n",
+ n, base, tmp); */
+ return tmp;
}
-static inline __u64 pg_div64(__u64 n, __u64 base)
+static inline __u64 pg_div64(__u64 n, __u64 base)
{
- __u64 tmp = n;
+ __u64 tmp = n;
/*
* How do we know if the architecture we are running on
* supports division with 64 bit base?
*
*/
-#if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
+#if defined(__sparc_v9__) || defined(__powerpc64__) || defined(__alpha__) || defined(__x86_64__) || defined(__ia64__)
- do_div(tmp, base);
+ do_div(tmp, base);
#else
- tmp = divremdi3(n, base, PG_DIV);
+ tmp = divremdi3(n, base, PG_DIV);
#endif
- return tmp;
+ return tmp;
}
static inline u32 pktgen_random(void)
@@ -448,51 +457,51 @@ static inline u32 pktgen_random(void)
#endif
}
-static inline __u64 getCurMs(void)
+static inline __u64 getCurMs(void)
{
- struct timeval tv;
- do_gettimeofday(&tv);
- return tv_to_ms(&tv);
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ return tv_to_ms(&tv);
}
-static inline __u64 getCurUs(void)
+static inline __u64 getCurUs(void)
{
- struct timeval tv;
- do_gettimeofday(&tv);
- return tv_to_us(&tv);
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ return tv_to_us(&tv);
}
-static inline __u64 tv_diff(const struct timeval* a, const struct timeval* b)
+static inline __u64 tv_diff(const struct timeval *a, const struct timeval *b)
{
- return tv_to_us(a) - tv_to_us(b);
+ return tv_to_us(a) - tv_to_us(b);
}
-
/* old include end */
static char version[] __initdata = VERSION;
-static int pktgen_remove_device(struct pktgen_thread* t, struct pktgen_dev *i);
-static int pktgen_add_device(struct pktgen_thread* t, const char* ifname);
-static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread* t, const char* ifname);
+static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
+static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
+static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
+ const char *ifname);
static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
static void pktgen_run_all_threads(void);
static void pktgen_stop_all_threads_ifs(void);
static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
-static void pktgen_stop(struct pktgen_thread* t);
+static void pktgen_stop(struct pktgen_thread *t);
static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
-static struct pktgen_dev *pktgen_NN_threads(const char* dev_name, int remove);
-static unsigned int scan_ip6(const char *s,char ip[16]);
-static unsigned int fmt_ip6(char *s,const char ip[16]);
+static int pktgen_mark_device(const char *ifname);
+static unsigned int scan_ip6(const char *s, char ip[16]);
+static unsigned int fmt_ip6(char *s, const char ip[16]);
/* Module parameters, defaults. */
-static int pg_count_d = 1000; /* 1000 pkts by default */
+static int pg_count_d = 1000; /* 1000 pkts by default */
static int pg_delay_d;
static int pg_clone_skb_d;
static int debug;
-static DECLARE_MUTEX(pktgen_sem);
-static struct pktgen_thread *pktgen_threads = NULL;
+static DEFINE_MUTEX(pktgen_thread_lock);
+static LIST_HEAD(pktgen_threads);
static struct notifier_block pktgen_notifier_block = {
.notifier_call = pktgen_device_event,
@@ -504,21 +513,21 @@ static struct notifier_block pktgen_notifier_block = {
*/
static int pgctrl_show(struct seq_file *seq, void *v)
-{
+{
seq_puts(seq, VERSION);
return 0;
}
-static ssize_t pgctrl_write(struct file* file,const char __user * buf,
- size_t count, loff_t *ppos)
+static ssize_t pgctrl_write(struct file *file, const char __user * buf,
+ size_t count, loff_t * ppos)
{
int err = 0;
char data[128];
- if (!capable(CAP_NET_ADMIN)){
- err = -EPERM;
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
goto out;
- }
+ }
if (count > sizeof(data))
count = sizeof(data);
@@ -526,22 +535,22 @@ static ssize_t pgctrl_write(struct file* file,const char __user * buf,
if (copy_from_user(data, buf, count)) {
err = -EFAULT;
goto out;
- }
- data[count-1] = 0; /* Make string */
+ }
+ data[count - 1] = 0; /* Make string */
- if (!strcmp(data, "stop"))
+ if (!strcmp(data, "stop"))
pktgen_stop_all_threads_ifs();
- else if (!strcmp(data, "start"))
+ else if (!strcmp(data, "start"))
pktgen_run_all_threads();
- else
+ else
printk("pktgen: Unknown command: %s\n", data);
err = count;
- out:
- return err;
+out:
+ return err;
}
static int pgctrl_open(struct inode *inode, struct file *file)
@@ -550,147 +559,194 @@ static int pgctrl_open(struct inode *inode, struct file *file)
}
static struct file_operations pktgen_fops = {
- .owner = THIS_MODULE,
- .open = pgctrl_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = pgctrl_write,
- .release = single_release,
+ .owner = THIS_MODULE,
+ .open = pgctrl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = pgctrl_write,
+ .release = single_release,
};
static int pktgen_if_show(struct seq_file *seq, void *v)
{
int i;
- struct pktgen_dev *pkt_dev = seq->private;
- __u64 sa;
- __u64 stopped;
- __u64 now = getCurUs();
-
- seq_printf(seq, "Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
- (unsigned long long) pkt_dev->count,
- pkt_dev->min_pkt_size, pkt_dev->max_pkt_size);
+ struct pktgen_dev *pkt_dev = seq->private;
+ __u64 sa;
+ __u64 stopped;
+ __u64 now = getCurUs();
+
+ seq_printf(seq,
+ "Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
+ (unsigned long long)pkt_dev->count, pkt_dev->min_pkt_size,
+ pkt_dev->max_pkt_size);
+
+ seq_printf(seq,
+ " frags: %d delay: %u clone_skb: %d ifname: %s\n",
+ pkt_dev->nfrags,
+ 1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
+ pkt_dev->clone_skb, pkt_dev->ifname);
+
+ seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows,
+ pkt_dev->lflow);
+
+ if (pkt_dev->flags & F_IPV6) {
+ char b1[128], b2[128], b3[128];
+ fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
+ fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
+ fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
+ seq_printf(seq,
+ " saddr: %s min_saddr: %s max_saddr: %s\n", b1,
+ b2, b3);
+
+ fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
+ fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
+ fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
+ seq_printf(seq,
+ " daddr: %s min_daddr: %s max_daddr: %s\n", b1,
+ b2, b3);
+
+ } else
+ seq_printf(seq,
+ " dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n",
+ pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min,
+ pkt_dev->src_max);
+
+ seq_puts(seq, " src_mac: ");
+
+ if (is_zero_ether_addr(pkt_dev->src_mac))
+ for (i = 0; i < 6; i++)
+ seq_printf(seq, "%02X%s", pkt_dev->odev->dev_addr[i],
+ i == 5 ? " " : ":");
+ else
+ for (i = 0; i < 6; i++)
+ seq_printf(seq, "%02X%s", pkt_dev->src_mac[i],
+ i == 5 ? " " : ":");
+
+ seq_printf(seq, "dst_mac: ");
+ for (i = 0; i < 6; i++)
+ seq_printf(seq, "%02X%s", pkt_dev->dst_mac[i],
+ i == 5 ? "\n" : ":");
+
+ seq_printf(seq,
+ " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
+ pkt_dev->udp_src_min, pkt_dev->udp_src_max,
+ pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
+
+ seq_printf(seq,
+ " src_mac_count: %d dst_mac_count: %d\n",
+ pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
- seq_printf(seq, " frags: %d delay: %u clone_skb: %d ifname: %s\n",
- pkt_dev->nfrags, 1000*pkt_dev->delay_us+pkt_dev->delay_ns, pkt_dev->clone_skb, pkt_dev->ifname);
+ if (pkt_dev->nr_labels) {
+ unsigned i;
+ seq_printf(seq, " mpls: ");
+ for(i = 0; i < pkt_dev->nr_labels; i++)
+ seq_printf(seq, "%08x%s", ntohl(pkt_dev->labels[i]),
+ i == pkt_dev->nr_labels-1 ? "\n" : ", ");
+ }
- seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows, pkt_dev->lflow);
+ seq_printf(seq, " Flags: ");
+ if (pkt_dev->flags & F_IPV6)
+ seq_printf(seq, "IPV6 ");
- if(pkt_dev->flags & F_IPV6) {
- char b1[128], b2[128], b3[128];
- fmt_ip6(b1, pkt_dev->in6_saddr.s6_addr);
- fmt_ip6(b2, pkt_dev->min_in6_saddr.s6_addr);
- fmt_ip6(b3, pkt_dev->max_in6_saddr.s6_addr);
- seq_printf(seq, " saddr: %s min_saddr: %s max_saddr: %s\n", b1, b2, b3);
+ if (pkt_dev->flags & F_IPSRC_RND)
+ seq_printf(seq, "IPSRC_RND ");
- fmt_ip6(b1, pkt_dev->in6_daddr.s6_addr);
- fmt_ip6(b2, pkt_dev->min_in6_daddr.s6_addr);
- fmt_ip6(b3, pkt_dev->max_in6_daddr.s6_addr);
- seq_printf(seq, " daddr: %s min_daddr: %s max_daddr: %s\n", b1, b2, b3);
+ if (pkt_dev->flags & F_IPDST_RND)
+ seq_printf(seq, "IPDST_RND ");
- }
- else
- seq_printf(seq," dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n",
- pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min, pkt_dev->src_max);
+ if (pkt_dev->flags & F_TXSIZE_RND)
+ seq_printf(seq, "TXSIZE_RND ");
- seq_puts(seq, " src_mac: ");
+ if (pkt_dev->flags & F_UDPSRC_RND)
+ seq_printf(seq, "UDPSRC_RND ");
- if (is_zero_ether_addr(pkt_dev->src_mac))
- for (i = 0; i < 6; i++)
- seq_printf(seq, "%02X%s", pkt_dev->odev->dev_addr[i], i == 5 ? " " : ":");
- else
- for (i = 0; i < 6; i++)
- seq_printf(seq, "%02X%s", pkt_dev->src_mac[i], i == 5 ? " " : ":");
+ if (pkt_dev->flags & F_UDPDST_RND)
+ seq_printf(seq, "UDPDST_RND ");
- seq_printf(seq, "dst_mac: ");
- for (i = 0; i < 6; i++)
- seq_printf(seq, "%02X%s", pkt_dev->dst_mac[i], i == 5 ? "\n" : ":");
+ if (pkt_dev->flags & F_MPLS_RND)
+ seq_printf(seq, "MPLS_RND ");
- seq_printf(seq, " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
- pkt_dev->udp_src_min, pkt_dev->udp_src_max, pkt_dev->udp_dst_min,
- pkt_dev->udp_dst_max);
+ if (pkt_dev->flags & F_MACSRC_RND)
+ seq_printf(seq, "MACSRC_RND ");
- seq_printf(seq, " src_mac_count: %d dst_mac_count: %d \n Flags: ",
- pkt_dev->src_mac_count, pkt_dev->dst_mac_count);
+ if (pkt_dev->flags & F_MACDST_RND)
+ seq_printf(seq, "MACDST_RND ");
+ seq_puts(seq, "\n");
- if (pkt_dev->flags & F_IPV6)
- seq_printf(seq, "IPV6 ");
-
- if (pkt_dev->flags & F_IPSRC_RND)
- seq_printf(seq, "IPSRC_RND ");
-
- if (pkt_dev->flags & F_IPDST_RND)
- seq_printf(seq, "IPDST_RND ");
-
- if (pkt_dev->flags & F_TXSIZE_RND)
- seq_printf(seq, "TXSIZE_RND ");
-
- if (pkt_dev->flags & F_UDPSRC_RND)
- seq_printf(seq, "UDPSRC_RND ");
-
- if (pkt_dev->flags & F_UDPDST_RND)
- seq_printf(seq, "UDPDST_RND ");
-
- if (pkt_dev->flags & F_MACSRC_RND)
- seq_printf(seq, "MACSRC_RND ");
-
- if (pkt_dev->flags & F_MACDST_RND)
- seq_printf(seq, "MACDST_RND ");
-
-
- seq_puts(seq, "\n");
-
- sa = pkt_dev->started_at;
- stopped = pkt_dev->stopped_at;
- if (pkt_dev->running)
- stopped = now; /* not really stopped, more like last-running-at */
-
- seq_printf(seq, "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n",
- (unsigned long long) pkt_dev->sofar,
- (unsigned long long) pkt_dev->errors,
- (unsigned long long) sa,
- (unsigned long long) stopped,
- (unsigned long long) pkt_dev->idle_acc);
-
- seq_printf(seq, " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
+ sa = pkt_dev->started_at;
+ stopped = pkt_dev->stopped_at;
+ if (pkt_dev->running)
+ stopped = now; /* not really stopped, more like last-running-at */
+
+ seq_printf(seq,
+ "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n",
+ (unsigned long long)pkt_dev->sofar,
+ (unsigned long long)pkt_dev->errors, (unsigned long long)sa,
+ (unsigned long long)stopped,
+ (unsigned long long)pkt_dev->idle_acc);
+
+ seq_printf(seq,
+ " seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
pkt_dev->seq_num, pkt_dev->cur_dst_mac_offset,
pkt_dev->cur_src_mac_offset);
- if(pkt_dev->flags & F_IPV6) {
+ if (pkt_dev->flags & F_IPV6) {
char b1[128], b2[128];
- fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
- fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
- seq_printf(seq, " cur_saddr: %s cur_daddr: %s\n", b2, b1);
- }
- else
- seq_printf(seq, " cur_saddr: 0x%x cur_daddr: 0x%x\n",
+ fmt_ip6(b1, pkt_dev->cur_in6_daddr.s6_addr);
+ fmt_ip6(b2, pkt_dev->cur_in6_saddr.s6_addr);
+ seq_printf(seq, " cur_saddr: %s cur_daddr: %s\n", b2, b1);
+ } else
+ seq_printf(seq, " cur_saddr: 0x%x cur_daddr: 0x%x\n",
pkt_dev->cur_saddr, pkt_dev->cur_daddr);
-
- seq_printf(seq, " cur_udp_dst: %d cur_udp_src: %d\n",
+ seq_printf(seq, " cur_udp_dst: %d cur_udp_src: %d\n",
pkt_dev->cur_udp_dst, pkt_dev->cur_udp_src);
- seq_printf(seq, " flows: %u\n", pkt_dev->nflows);
+ seq_printf(seq, " flows: %u\n", pkt_dev->nflows);
if (pkt_dev->result[0])
- seq_printf(seq, "Result: %s\n", pkt_dev->result);
+ seq_printf(seq, "Result: %s\n", pkt_dev->result);
else
- seq_printf(seq, "Result: Idle\n");
+ seq_printf(seq, "Result: Idle\n");
return 0;
}
-static int count_trail_chars(const char __user *user_buffer, unsigned int maxlen)
+static int hex32_arg(const char __user *user_buffer, __u32 *num)
+{
+ int i = 0;
+ *num = 0;
+
+ for(; i < 8; i++) {
+ char c;
+ *num <<= 4;
+ if (get_user(c, &user_buffer[i]))
+ return -EFAULT;
+ if ((c >= '0') && (c <= '9'))
+ *num |= c - '0';
+ else if ((c >= 'a') && (c <= 'f'))
+ *num |= c - 'a' + 10;
+ else if ((c >= 'A') && (c <= 'F'))
+ *num |= c - 'A' + 10;
+ else
+ break;
+ }
+ return i;
+}
+
+static int count_trail_chars(const char __user * user_buffer,
+ unsigned int maxlen)
{
int i;
for (i = 0; i < maxlen; i++) {
- char c;
- if (get_user(c, &user_buffer[i]))
- return -EFAULT;
- switch (c) {
+ char c;
+ if (get_user(c, &user_buffer[i]))
+ return -EFAULT;
+ switch (c) {
case '\"':
case '\n':
case '\r':
@@ -706,34 +762,34 @@ done:
return i;
}
-static unsigned long num_arg(const char __user *user_buffer, unsigned long maxlen,
- unsigned long *num)
+static unsigned long num_arg(const char __user * user_buffer,
+ unsigned long maxlen, unsigned long *num)
{
int i = 0;
*num = 0;
-
- for(; i < maxlen; i++) {
- char c;
- if (get_user(c, &user_buffer[i]))
- return -EFAULT;
- if ((c >= '0') && (c <= '9')) {
+
+ for (; i < maxlen; i++) {
+ char c;
+ if (get_user(c, &user_buffer[i]))
+ return -EFAULT;
+ if ((c >= '0') && (c <= '9')) {
*num *= 10;
- *num += c -'0';
+ *num += c - '0';
} else
break;
}
return i;
}
-static int strn_len(const char __user *user_buffer, unsigned int maxlen)
+static int strn_len(const char __user * user_buffer, unsigned int maxlen)
{
int i = 0;
- for(; i < maxlen; i++) {
- char c;
- if (get_user(c, &user_buffer[i]))
- return -EFAULT;
- switch (c) {
+ for (; i < maxlen; i++) {
+ char c;
+ if (get_user(c, &user_buffer[i]))
+ return -EFAULT;
+ switch (c) {
case '\"':
case '\n':
case '\r':
@@ -746,119 +802,162 @@ static int strn_len(const char __user *user_buffer, unsigned int maxlen)
};
}
done_str:
+ return i;
+}
+
+static ssize_t get_labels(const char __user *buffer, struct pktgen_dev *pkt_dev)
+{
+ unsigned n = 0;
+ char c;
+ ssize_t i = 0;
+ int len;
+
+ pkt_dev->nr_labels = 0;
+ do {
+ __u32 tmp;
+ len = hex32_arg(&buffer[i], &tmp);
+ if (len <= 0)
+ return len;
+ pkt_dev->labels[n] = htonl(tmp);
+ if (pkt_dev->labels[n] & MPLS_STACK_BOTTOM)
+ pkt_dev->flags |= F_MPLS_RND;
+ i += len;
+ if (get_user(c, &buffer[i]))
+ return -EFAULT;
+ i++;
+ n++;
+ if (n >= MAX_MPLS_LABELS)
+ return -E2BIG;
+ } while(c == ',');
+ pkt_dev->nr_labels = n;
return i;
}
-static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer,
- size_t count, loff_t *offset)
+static ssize_t pktgen_if_write(struct file *file,
+ const char __user * user_buffer, size_t count,
+ loff_t * offset)
{
- struct seq_file *seq = (struct seq_file *) file->private_data;
- struct pktgen_dev *pkt_dev = seq->private;
+ struct seq_file *seq = (struct seq_file *)file->private_data;
+ struct pktgen_dev *pkt_dev = seq->private;
int i = 0, max, len;
char name[16], valstr[32];
unsigned long value = 0;
- char* pg_result = NULL;
- int tmp = 0;
+ char *pg_result = NULL;
+ int tmp = 0;
char buf[128];
-
- pg_result = &(pkt_dev->result[0]);
-
+
+ pg_result = &(pkt_dev->result[0]);
+
if (count < 1) {
printk("pktgen: wrong command format\n");
return -EINVAL;
}
-
+
max = count - i;
tmp = count_trail_chars(&user_buffer[i], max);
- if (tmp < 0) {
+ if (tmp < 0) {
printk("pktgen: illegal format\n");
- return tmp;
+ return tmp;
}
- i += tmp;
-
+ i += tmp;
+
/* Read variable name */
len = strn_len(&user_buffer[i], sizeof(name) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
memset(name, 0, sizeof(name));
- if (copy_from_user(name, &user_buffer[i], len) )
+ if (copy_from_user(name, &user_buffer[i], len))
return -EFAULT;
i += len;
-
- max = count -i;
+
+ max = count - i;
len = count_trail_chars(&user_buffer[i], max);
- if (len < 0)
- return len;
-
+ if (len < 0)
+ return len;
+
i += len;
if (debug) {
- char tb[count + 1];
- if (copy_from_user(tb, user_buffer, count))
+ char tb[count + 1];
+ if (copy_from_user(tb, user_buffer, count))
return -EFAULT;
- tb[count] = 0;
+ tb[count] = 0;
printk("pktgen: %s,%lu buffer -:%s:-\n", name,
- (unsigned long) count, tb);
- }
+ (unsigned long)count, tb);
+ }
if (!strcmp(name, "min_pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value < 14+20+8)
- value = 14+20+8;
- if (value != pkt_dev->min_pkt_size) {
- pkt_dev->min_pkt_size = value;
- pkt_dev->cur_pkt_size = value;
- }
- sprintf(pg_result, "OK: min_pkt_size=%u", pkt_dev->min_pkt_size);
+ if (value < 14 + 20 + 8)
+ value = 14 + 20 + 8;
+ if (value != pkt_dev->min_pkt_size) {
+ pkt_dev->min_pkt_size = value;
+ pkt_dev->cur_pkt_size = value;
+ }
+ sprintf(pg_result, "OK: min_pkt_size=%u",
+ pkt_dev->min_pkt_size);
return count;
}
- if (!strcmp(name, "max_pkt_size")) {
+ if (!strcmp(name, "max_pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value < 14+20+8)
- value = 14+20+8;
- if (value != pkt_dev->max_pkt_size) {
- pkt_dev->max_pkt_size = value;
- pkt_dev->cur_pkt_size = value;
- }
- sprintf(pg_result, "OK: max_pkt_size=%u", pkt_dev->max_pkt_size);
+ if (value < 14 + 20 + 8)
+ value = 14 + 20 + 8;
+ if (value != pkt_dev->max_pkt_size) {
+ pkt_dev->max_pkt_size = value;
+ pkt_dev->cur_pkt_size = value;
+ }
+ sprintf(pg_result, "OK: max_pkt_size=%u",
+ pkt_dev->max_pkt_size);
return count;
}
- /* Shortcut for min = max */
+ /* Shortcut for min = max */
if (!strcmp(name, "pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value < 14+20+8)
- value = 14+20+8;
- if (value != pkt_dev->min_pkt_size) {
- pkt_dev->min_pkt_size = value;
- pkt_dev->max_pkt_size = value;
- pkt_dev->cur_pkt_size = value;
- }
+ if (value < 14 + 20 + 8)
+ value = 14 + 20 + 8;
+ if (value != pkt_dev->min_pkt_size) {
+ pkt_dev->min_pkt_size = value;
+ pkt_dev->max_pkt_size = value;
+ pkt_dev->cur_pkt_size = value;
+ }
sprintf(pg_result, "OK: pkt_size=%u", pkt_dev->min_pkt_size);
return count;
}
- if (!strcmp(name, "debug")) {
+ if (!strcmp(name, "debug")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- debug = value;
+ debug = value;
sprintf(pg_result, "OK: debug=%u", debug);
return count;
}
- if (!strcmp(name, "frags")) {
+ if (!strcmp(name, "frags")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
pkt_dev->nfrags = value;
sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
@@ -866,7 +965,9 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
}
if (!strcmp(name, "delay")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
if (value == 0x7FFFFFFF) {
pkt_dev->delay_us = 0x7FFFFFFF;
@@ -875,308 +976,353 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
pkt_dev->delay_us = value / 1000;
pkt_dev->delay_ns = value % 1000;
}
- sprintf(pg_result, "OK: delay=%u", 1000*pkt_dev->delay_us+pkt_dev->delay_ns);
+ sprintf(pg_result, "OK: delay=%u",
+ 1000 * pkt_dev->delay_us + pkt_dev->delay_ns);
return count;
}
- if (!strcmp(name, "udp_src_min")) {
+ if (!strcmp(name, "udp_src_min")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value != pkt_dev->udp_src_min) {
- pkt_dev->udp_src_min = value;
- pkt_dev->cur_udp_src = value;
- }
+ if (value != pkt_dev->udp_src_min) {
+ pkt_dev->udp_src_min = value;
+ pkt_dev->cur_udp_src = value;
+ }
sprintf(pg_result, "OK: udp_src_min=%u", pkt_dev->udp_src_min);
return count;
}
- if (!strcmp(name, "udp_dst_min")) {
+ if (!strcmp(name, "udp_dst_min")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value != pkt_dev->udp_dst_min) {
- pkt_dev->udp_dst_min = value;
- pkt_dev->cur_udp_dst = value;
- }
+ if (value != pkt_dev->udp_dst_min) {
+ pkt_dev->udp_dst_min = value;
+ pkt_dev->cur_udp_dst = value;
+ }
sprintf(pg_result, "OK: udp_dst_min=%u", pkt_dev->udp_dst_min);
return count;
}
- if (!strcmp(name, "udp_src_max")) {
+ if (!strcmp(name, "udp_src_max")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value != pkt_dev->udp_src_max) {
- pkt_dev->udp_src_max = value;
- pkt_dev->cur_udp_src = value;
- }
+ if (value != pkt_dev->udp_src_max) {
+ pkt_dev->udp_src_max = value;
+ pkt_dev->cur_udp_src = value;
+ }
sprintf(pg_result, "OK: udp_src_max=%u", pkt_dev->udp_src_max);
return count;
}
- if (!strcmp(name, "udp_dst_max")) {
+ if (!strcmp(name, "udp_dst_max")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- if (value != pkt_dev->udp_dst_max) {
- pkt_dev->udp_dst_max = value;
- pkt_dev->cur_udp_dst = value;
- }
+ if (value != pkt_dev->udp_dst_max) {
+ pkt_dev->udp_dst_max = value;
+ pkt_dev->cur_udp_dst = value;
+ }
sprintf(pg_result, "OK: udp_dst_max=%u", pkt_dev->udp_dst_max);
return count;
}
if (!strcmp(name, "clone_skb")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
- pkt_dev->clone_skb = value;
-
+ pkt_dev->clone_skb = value;
+
sprintf(pg_result, "OK: clone_skb=%d", pkt_dev->clone_skb);
return count;
}
if (!strcmp(name, "count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
pkt_dev->count = value;
sprintf(pg_result, "OK: count=%llu",
- (unsigned long long) pkt_dev->count);
+ (unsigned long long)pkt_dev->count);
return count;
}
if (!strcmp(name, "src_mac_count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
if (pkt_dev->src_mac_count != value) {
- pkt_dev->src_mac_count = value;
- pkt_dev->cur_src_mac_offset = 0;
- }
- sprintf(pg_result, "OK: src_mac_count=%d", pkt_dev->src_mac_count);
+ pkt_dev->src_mac_count = value;
+ pkt_dev->cur_src_mac_offset = 0;
+ }
+ sprintf(pg_result, "OK: src_mac_count=%d",
+ pkt_dev->src_mac_count);
return count;
}
if (!strcmp(name, "dst_mac_count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
if (pkt_dev->dst_mac_count != value) {
- pkt_dev->dst_mac_count = value;
- pkt_dev->cur_dst_mac_offset = 0;
- }
- sprintf(pg_result, "OK: dst_mac_count=%d", pkt_dev->dst_mac_count);
+ pkt_dev->dst_mac_count = value;
+ pkt_dev->cur_dst_mac_offset = 0;
+ }
+ sprintf(pg_result, "OK: dst_mac_count=%d",
+ pkt_dev->dst_mac_count);
return count;
}
if (!strcmp(name, "flag")) {
- char f[32];
- memset(f, 0, 32);
+ char f[32];
+ memset(f, 0, 32);
len = strn_len(&user_buffer[i], sizeof(f) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
if (copy_from_user(f, &user_buffer[i], len))
return -EFAULT;
i += len;
- if (strcmp(f, "IPSRC_RND") == 0)
- pkt_dev->flags |= F_IPSRC_RND;
-
- else if (strcmp(f, "!IPSRC_RND") == 0)
- pkt_dev->flags &= ~F_IPSRC_RND;
-
- else if (strcmp(f, "TXSIZE_RND") == 0)
- pkt_dev->flags |= F_TXSIZE_RND;
-
- else if (strcmp(f, "!TXSIZE_RND") == 0)
- pkt_dev->flags &= ~F_TXSIZE_RND;
-
- else if (strcmp(f, "IPDST_RND") == 0)
- pkt_dev->flags |= F_IPDST_RND;
-
- else if (strcmp(f, "!IPDST_RND") == 0)
- pkt_dev->flags &= ~F_IPDST_RND;
-
- else if (strcmp(f, "UDPSRC_RND") == 0)
- pkt_dev->flags |= F_UDPSRC_RND;
-
- else if (strcmp(f, "!UDPSRC_RND") == 0)
- pkt_dev->flags &= ~F_UDPSRC_RND;
-
- else if (strcmp(f, "UDPDST_RND") == 0)
- pkt_dev->flags |= F_UDPDST_RND;
-
- else if (strcmp(f, "!UDPDST_RND") == 0)
- pkt_dev->flags &= ~F_UDPDST_RND;
-
- else if (strcmp(f, "MACSRC_RND") == 0)
- pkt_dev->flags |= F_MACSRC_RND;
-
- else if (strcmp(f, "!MACSRC_RND") == 0)
- pkt_dev->flags &= ~F_MACSRC_RND;
-
- else if (strcmp(f, "MACDST_RND") == 0)
- pkt_dev->flags |= F_MACDST_RND;
-
- else if (strcmp(f, "!MACDST_RND") == 0)
- pkt_dev->flags &= ~F_MACDST_RND;
-
- else {
- sprintf(pg_result, "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
- f,
- "IPSRC_RND, IPDST_RND, TXSIZE_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
- return count;
- }
+ if (strcmp(f, "IPSRC_RND") == 0)
+ pkt_dev->flags |= F_IPSRC_RND;
+
+ else if (strcmp(f, "!IPSRC_RND") == 0)
+ pkt_dev->flags &= ~F_IPSRC_RND;
+
+ else if (strcmp(f, "TXSIZE_RND") == 0)
+ pkt_dev->flags |= F_TXSIZE_RND;
+
+ else if (strcmp(f, "!TXSIZE_RND") == 0)
+ pkt_dev->flags &= ~F_TXSIZE_RND;
+
+ else if (strcmp(f, "IPDST_RND") == 0)
+ pkt_dev->flags |= F_IPDST_RND;
+
+ else if (strcmp(f, "!IPDST_RND") == 0)
+ pkt_dev->flags &= ~F_IPDST_RND;
+
+ else if (strcmp(f, "UDPSRC_RND") == 0)
+ pkt_dev->flags |= F_UDPSRC_RND;
+
+ else if (strcmp(f, "!UDPSRC_RND") == 0)
+ pkt_dev->flags &= ~F_UDPSRC_RND;
+
+ else if (strcmp(f, "UDPDST_RND") == 0)
+ pkt_dev->flags |= F_UDPDST_RND;
+
+ else if (strcmp(f, "!UDPDST_RND") == 0)
+ pkt_dev->flags &= ~F_UDPDST_RND;
+
+ else if (strcmp(f, "MACSRC_RND") == 0)
+ pkt_dev->flags |= F_MACSRC_RND;
+
+ else if (strcmp(f, "!MACSRC_RND") == 0)
+ pkt_dev->flags &= ~F_MACSRC_RND;
+
+ else if (strcmp(f, "MACDST_RND") == 0)
+ pkt_dev->flags |= F_MACDST_RND;
+
+ else if (strcmp(f, "!MACDST_RND") == 0)
+ pkt_dev->flags &= ~F_MACDST_RND;
+
+ else if (strcmp(f, "MPLS_RND") == 0)
+ pkt_dev->flags |= F_MPLS_RND;
+
+ else if (strcmp(f, "!MPLS_RND") == 0)
+ pkt_dev->flags &= ~F_MPLS_RND;
+
+ else {
+ sprintf(pg_result,
+ "Flag -:%s:- unknown\nAvailable flags, (prepend ! to un-set flag):\n%s",
+ f,
+ "IPSRC_RND, IPDST_RND, TXSIZE_RND, UDPSRC_RND, UDPDST_RND, MACSRC_RND, MACDST_RND\n");
+ return count;
+ }
sprintf(pg_result, "OK: flags=0x%x", pkt_dev->flags);
return count;
}
if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
- if (strcmp(buf, pkt_dev->dst_min) != 0) {
- memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
- strncpy(pkt_dev->dst_min, buf, len);
- pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
- pkt_dev->cur_daddr = pkt_dev->daddr_min;
- }
- if(debug)
- printk("pktgen: dst_min set to: %s\n", pkt_dev->dst_min);
- i += len;
+ buf[len] = 0;
+ if (strcmp(buf, pkt_dev->dst_min) != 0) {
+ memset(pkt_dev->dst_min, 0, sizeof(pkt_dev->dst_min));
+ strncpy(pkt_dev->dst_min, buf, len);
+ pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
+ pkt_dev->cur_daddr = pkt_dev->daddr_min;
+ }
+ if (debug)
+ printk("pktgen: dst_min set to: %s\n",
+ pkt_dev->dst_min);
+ i += len;
sprintf(pg_result, "OK: dst_min=%s", pkt_dev->dst_min);
return count;
}
if (!strcmp(name, "dst_max")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
- if (strcmp(buf, pkt_dev->dst_max) != 0) {
- memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
- strncpy(pkt_dev->dst_max, buf, len);
- pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
- pkt_dev->cur_daddr = pkt_dev->daddr_max;
- }
- if(debug)
- printk("pktgen: dst_max set to: %s\n", pkt_dev->dst_max);
+ buf[len] = 0;
+ if (strcmp(buf, pkt_dev->dst_max) != 0) {
+ memset(pkt_dev->dst_max, 0, sizeof(pkt_dev->dst_max));
+ strncpy(pkt_dev->dst_max, buf, len);
+ pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
+ pkt_dev->cur_daddr = pkt_dev->daddr_max;
+ }
+ if (debug)
+ printk("pktgen: dst_max set to: %s\n",
+ pkt_dev->dst_max);
i += len;
sprintf(pg_result, "OK: dst_max=%s", pkt_dev->dst_max);
return count;
}
if (!strcmp(name, "dst6")) {
len = strn_len(&user_buffer[i], sizeof(buf) - 1);
- if (len < 0) return len;
+ if (len < 0)
+ return len;
pkt_dev->flags |= F_IPV6;
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
+ buf[len] = 0;
scan_ip6(buf, pkt_dev->in6_daddr.s6_addr);
- fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
+ fmt_ip6(buf, pkt_dev->in6_daddr.s6_addr);
ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->in6_daddr);
- if(debug)
+ if (debug)
printk("pktgen: dst6 set to: %s\n", buf);
- i += len;
+ i += len;
sprintf(pg_result, "OK: dst6=%s", buf);
return count;
}
if (!strcmp(name, "dst6_min")) {
len = strn_len(&user_buffer[i], sizeof(buf) - 1);
- if (len < 0) return len;
+ if (len < 0)
+ return len;
pkt_dev->flags |= F_IPV6;
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
+ buf[len] = 0;
scan_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
- fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
+ fmt_ip6(buf, pkt_dev->min_in6_daddr.s6_addr);
- ipv6_addr_copy(&pkt_dev->cur_in6_daddr, &pkt_dev->min_in6_daddr);
- if(debug)
+ ipv6_addr_copy(&pkt_dev->cur_in6_daddr,
+ &pkt_dev->min_in6_daddr);
+ if (debug)
printk("pktgen: dst6_min set to: %s\n", buf);
- i += len;
+ i += len;
sprintf(pg_result, "OK: dst6_min=%s", buf);
return count;
}
if (!strcmp(name, "dst6_max")) {
len = strn_len(&user_buffer[i], sizeof(buf) - 1);
- if (len < 0) return len;
+ if (len < 0)
+ return len;
pkt_dev->flags |= F_IPV6;
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
+ buf[len] = 0;
scan_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
- fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
+ fmt_ip6(buf, pkt_dev->max_in6_daddr.s6_addr);
- if(debug)
+ if (debug)
printk("pktgen: dst6_max set to: %s\n", buf);
- i += len;
+ i += len;
sprintf(pg_result, "OK: dst6_max=%s", buf);
return count;
}
if (!strcmp(name, "src6")) {
len = strn_len(&user_buffer[i], sizeof(buf) - 1);
- if (len < 0) return len;
+ if (len < 0)
+ return len;
pkt_dev->flags |= F_IPV6;
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
+ buf[len] = 0;
scan_ip6(buf, pkt_dev->in6_saddr.s6_addr);
- fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
+ fmt_ip6(buf, pkt_dev->in6_saddr.s6_addr);
ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &pkt_dev->in6_saddr);
- if(debug)
+ if (debug)
printk("pktgen: src6 set to: %s\n", buf);
-
- i += len;
+
+ i += len;
sprintf(pg_result, "OK: src6=%s", buf);
return count;
}
if (!strcmp(name, "src_min")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
- if (len < 0) { return len; }
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (len < 0) {
+ return len;
+ }
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
- if (strcmp(buf, pkt_dev->src_min) != 0) {
- memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
- strncpy(pkt_dev->src_min, buf, len);
- pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
- pkt_dev->cur_saddr = pkt_dev->saddr_min;
- }
- if(debug)
- printk("pktgen: src_min set to: %s\n", pkt_dev->src_min);
+ buf[len] = 0;
+ if (strcmp(buf, pkt_dev->src_min) != 0) {
+ memset(pkt_dev->src_min, 0, sizeof(pkt_dev->src_min));
+ strncpy(pkt_dev->src_min, buf, len);
+ pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
+ pkt_dev->cur_saddr = pkt_dev->saddr_min;
+ }
+ if (debug)
+ printk("pktgen: src_min set to: %s\n",
+ pkt_dev->src_min);
i += len;
sprintf(pg_result, "OK: src_min=%s", pkt_dev->src_min);
return count;
}
if (!strcmp(name, "src_max")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
- if (len < 0) { return len; }
- if (copy_from_user(buf, &user_buffer[i], len))
+ if (len < 0) {
+ return len;
+ }
+ if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
- buf[len] = 0;
- if (strcmp(buf, pkt_dev->src_max) != 0) {
- memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
- strncpy(pkt_dev->src_max, buf, len);
- pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
- pkt_dev->cur_saddr = pkt_dev->saddr_max;
- }
- if(debug)
- printk("pktgen: src_max set to: %s\n", pkt_dev->src_max);
+ buf[len] = 0;
+ if (strcmp(buf, pkt_dev->src_max) != 0) {
+ memset(pkt_dev->src_max, 0, sizeof(pkt_dev->src_max));
+ strncpy(pkt_dev->src_max, buf, len);
+ pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
+ pkt_dev->cur_saddr = pkt_dev->saddr_max;
+ }
+ if (debug)
+ printk("pktgen: src_max set to: %s\n",
+ pkt_dev->src_max);
i += len;
sprintf(pg_result, "OK: src_max=%s", pkt_dev->src_max);
return count;
@@ -1186,15 +1332,17 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
unsigned char old_dmac[ETH_ALEN];
unsigned char *m = pkt_dev->dst_mac;
memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
-
+
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
memset(valstr, 0, sizeof(valstr));
- if( copy_from_user(valstr, &user_buffer[i], len))
+ if (copy_from_user(valstr, &user_buffer[i], len))
return -EFAULT;
i += len;
- for(*m = 0;*v && m < pkt_dev->dst_mac + 6; v++) {
+ for (*m = 0; *v && m < pkt_dev->dst_mac + 6; v++) {
if (*v >= '0' && *v <= '9') {
*m *= 16;
*m += *v - '0';
@@ -1216,7 +1364,7 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
/* Set up Dest MAC */
if (compare_ether_addr(old_dmac, pkt_dev->dst_mac))
memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
-
+
sprintf(pg_result, "OK: dstmac");
return count;
}
@@ -1225,13 +1373,15 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
unsigned char *m = pkt_dev->src_mac;
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
memset(valstr, 0, sizeof(valstr));
- if( copy_from_user(valstr, &user_buffer[i], len))
+ if (copy_from_user(valstr, &user_buffer[i], len))
return -EFAULT;
i += len;
- for(*m = 0;*v && m < pkt_dev->src_mac + 6; v++) {
+ for (*m = 0; *v && m < pkt_dev->src_mac + 6; v++) {
if (*v >= '0' && *v <= '9') {
*m *= 16;
*m += *v - '0';
@@ -1248,21 +1398,23 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
m++;
*m = 0;
}
- }
+ }
- sprintf(pg_result, "OK: srcmac");
+ sprintf(pg_result, "OK: srcmac");
return count;
}
- if (!strcmp(name, "clear_counters")) {
- pktgen_clear_counters(pkt_dev);
- sprintf(pg_result, "OK: Clearing counters.\n");
- return count;
- }
+ if (!strcmp(name, "clear_counters")) {
+ pktgen_clear_counters(pkt_dev);
+ sprintf(pg_result, "OK: Clearing counters.\n");
+ return count;
+ }
if (!strcmp(name, "flows")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
if (value > MAX_CFLOWS)
value = MAX_CFLOWS;
@@ -1274,13 +1426,28 @@ static ssize_t pktgen_if_write(struct file *file, const char __user *user_buffer
if (!strcmp(name, "flowlen")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) { return len; }
+ if (len < 0) {
+ return len;
+ }
i += len;
pkt_dev->lflow = value;
sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
return count;
}
-
+
+ if (!strcmp(name, "mpls")) {
+ unsigned n, offset;
+ len = get_labels(&user_buffer[i], pkt_dev);
+ if (len < 0) { return len; }
+ i += len;
+ offset = sprintf(pg_result, "OK: mpls=");
+ for(n = 0; n < pkt_dev->nr_labels; n++)
+ offset += sprintf(pg_result + offset,
+ "%08x%s", ntohl(pkt_dev->labels[n]),
+ n == pkt_dev->nr_labels-1 ? "" : ",");
+ return count;
+ }
+
sprintf(pkt_dev->result, "No such parameter \"%s\"", name);
return -EINVAL;
}
@@ -1291,35 +1458,35 @@ static int pktgen_if_open(struct inode *inode, struct file *file)
}
static struct file_operations pktgen_if_fops = {
- .owner = THIS_MODULE,
- .open = pktgen_if_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = pktgen_if_write,
- .release = single_release,
+ .owner = THIS_MODULE,
+ .open = pktgen_if_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = pktgen_if_write,
+ .release = single_release,
};
static int pktgen_thread_show(struct seq_file *seq, void *v)
{
- struct pktgen_thread *t = seq->private;
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_thread *t = seq->private;
+ struct pktgen_dev *pkt_dev;
BUG_ON(!t);
seq_printf(seq, "Name: %s max_before_softirq: %d\n",
- t->name, t->max_before_softirq);
+ t->name, t->max_before_softirq);
+
+ seq_printf(seq, "Running: ");
- seq_printf(seq, "Running: ");
-
- if_lock(t);
- for(pkt_dev = t->if_list;pkt_dev; pkt_dev = pkt_dev->next)
- if(pkt_dev->running)
+ if_lock(t);
+ list_for_each_entry(pkt_dev, &t->if_list, list)
+ if (pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->ifname);
-
- seq_printf(seq, "\nStopped: ");
- for(pkt_dev = t->if_list;pkt_dev; pkt_dev = pkt_dev->next)
- if(!pkt_dev->running)
+ seq_printf(seq, "\nStopped: ");
+
+ list_for_each_entry(pkt_dev, &t->if_list, list)
+ if (!pkt_dev->running)
seq_printf(seq, "%s ", pkt_dev->ifname);
if (t->result[0])
@@ -1327,30 +1494,30 @@ static int pktgen_thread_show(struct seq_file *seq, void *v)
else
seq_printf(seq, "\nResult: NA\n");
- if_unlock(t);
+ if_unlock(t);
return 0;
}
static ssize_t pktgen_thread_write(struct file *file,
- const char __user *user_buffer,
- size_t count, loff_t *offset)
+ const char __user * user_buffer,
+ size_t count, loff_t * offset)
{
- struct seq_file *seq = (struct seq_file *) file->private_data;
- struct pktgen_thread *t = seq->private;
+ struct seq_file *seq = (struct seq_file *)file->private_data;
+ struct pktgen_thread *t = seq->private;
int i = 0, max, len, ret;
char name[40];
- char *pg_result;
- unsigned long value = 0;
+ char *pg_result;
+ unsigned long value = 0;
if (count < 1) {
- // sprintf(pg_result, "Wrong command format");
+ // sprintf(pg_result, "Wrong command format");
return -EINVAL;
}
max = count - i;
- len = count_trail_chars(&user_buffer[i], max);
- if (len < 0)
+ len = count_trail_chars(&user_buffer[i], max);
+ if (len < 0)
return len;
i += len;
@@ -1358,26 +1525,25 @@ static ssize_t pktgen_thread_write(struct file *file,
/* Read variable name */
len = strn_len(&user_buffer[i], sizeof(name) - 1);
- if (len < 0)
+ if (len < 0)
return len;
-
+
memset(name, 0, sizeof(name));
if (copy_from_user(name, &user_buffer[i], len))
return -EFAULT;
i += len;
- max = count -i;
+ max = count - i;
len = count_trail_chars(&user_buffer[i], max);
- if (len < 0)
+ if (len < 0)
return len;
i += len;
if (debug)
- printk("pktgen: t=%s, count=%lu\n", name,
- (unsigned long) count);
+ printk("pktgen: t=%s, count=%lu\n", name, (unsigned long)count);
- if(!t) {
+ if (!t) {
printk("pktgen: ERROR: No thread\n");
ret = -EINVAL;
goto out;
@@ -1385,48 +1551,47 @@ static ssize_t pktgen_thread_write(struct file *file,
pg_result = &(t->result[0]);
- if (!strcmp(name, "add_device")) {
- char f[32];
- memset(f, 0, 32);
+ if (!strcmp(name, "add_device")) {
+ char f[32];
+ memset(f, 0, 32);
len = strn_len(&user_buffer[i], sizeof(f) - 1);
- if (len < 0) {
- ret = len;
+ if (len < 0) {
+ ret = len;
goto out;
}
- if( copy_from_user(f, &user_buffer[i], len) )
+ if (copy_from_user(f, &user_buffer[i], len))
return -EFAULT;
i += len;
- thread_lock();
- pktgen_add_device(t, f);
- thread_unlock();
- ret = count;
- sprintf(pg_result, "OK: add_device=%s", f);
+ mutex_lock(&pktgen_thread_lock);
+ pktgen_add_device(t, f);
+ mutex_unlock(&pktgen_thread_lock);
+ ret = count;
+ sprintf(pg_result, "OK: add_device=%s", f);
goto out;
}
- if (!strcmp(name, "rem_device_all")) {
- thread_lock();
- t->control |= T_REMDEV;
- thread_unlock();
- schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
+ if (!strcmp(name, "rem_device_all")) {
+ mutex_lock(&pktgen_thread_lock);
+ t->control |= T_REMDEVALL;
+ mutex_unlock(&pktgen_thread_lock);
+ schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
ret = count;
- sprintf(pg_result, "OK: rem_device_all");
+ sprintf(pg_result, "OK: rem_device_all");
goto out;
}
- if (!strcmp(name, "max_before_softirq")) {
- len = num_arg(&user_buffer[i], 10, &value);
- thread_lock();
- t->max_before_softirq = value;
- thread_unlock();
- ret = count;
- sprintf(pg_result, "OK: max_before_softirq=%lu", value);
+ if (!strcmp(name, "max_before_softirq")) {
+ len = num_arg(&user_buffer[i], 10, &value);
+ mutex_lock(&pktgen_thread_lock);
+ t->max_before_softirq = value;
+ mutex_unlock(&pktgen_thread_lock);
+ ret = count;
+ sprintf(pg_result, "OK: max_before_softirq=%lu", value);
goto out;
}
ret = -EINVAL;
- out:
-
+out:
return ret;
}
@@ -1436,47 +1601,78 @@ static int pktgen_thread_open(struct inode *inode, struct file *file)
}
static struct file_operations pktgen_thread_fops = {
- .owner = THIS_MODULE,
- .open = pktgen_thread_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = pktgen_thread_write,
- .release = single_release,
+ .owner = THIS_MODULE,
+ .open = pktgen_thread_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = pktgen_thread_write,
+ .release = single_release,
};
/* Think find or remove for NN */
-static struct pktgen_dev *__pktgen_NN_threads(const char* ifname, int remove)
+static struct pktgen_dev *__pktgen_NN_threads(const char *ifname, int remove)
{
struct pktgen_thread *t;
struct pktgen_dev *pkt_dev = NULL;
- t = pktgen_threads;
-
- while (t) {
+ list_for_each_entry(t, &pktgen_threads, th_list) {
pkt_dev = pktgen_find_dev(t, ifname);
if (pkt_dev) {
- if(remove) {
- if_lock(t);
- pktgen_remove_device(t, pkt_dev);
- if_unlock(t);
- }
+ if (remove) {
+ if_lock(t);
+ pkt_dev->removal_mark = 1;
+ t->control |= T_REMDEV;
+ if_unlock(t);
+ }
break;
}
- t = t->next;
}
- return pkt_dev;
+ return pkt_dev;
}
-static struct pktgen_dev *pktgen_NN_threads(const char* ifname, int remove)
+/*
+ * mark a device for removal
+ */
+static int pktgen_mark_device(const char *ifname)
{
struct pktgen_dev *pkt_dev = NULL;
- thread_lock();
- pkt_dev = __pktgen_NN_threads(ifname, remove);
- thread_unlock();
- return pkt_dev;
+ const int max_tries = 10, msec_per_try = 125;
+ int i = 0;
+ int ret = 0;
+
+ mutex_lock(&pktgen_thread_lock);
+ PG_DEBUG(printk("pktgen: pktgen_mark_device marking %s for removal\n",
+ ifname));
+
+ while (1) {
+
+ pkt_dev = __pktgen_NN_threads(ifname, REMOVE);
+ if (pkt_dev == NULL)
+ break; /* success */
+
+ mutex_unlock(&pktgen_thread_lock);
+ PG_DEBUG(printk("pktgen: pktgen_mark_device waiting for %s "
+ "to disappear....\n", ifname));
+ schedule_timeout_interruptible(msecs_to_jiffies(msec_per_try));
+ mutex_lock(&pktgen_thread_lock);
+
+ if (++i >= max_tries) {
+ printk("pktgen_mark_device: timed out after waiting "
+ "%d msec for device %s to be removed\n",
+ msec_per_try * i, ifname);
+ ret = 1;
+ break;
+ }
+
+ }
+
+ mutex_unlock(&pktgen_thread_lock);
+
+ return ret;
}
-static int pktgen_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
+static int pktgen_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
{
struct net_device *dev = (struct net_device *)(ptr);
@@ -1491,9 +1687,9 @@ static int pktgen_device_event(struct notifier_block *unused, unsigned long even
case NETDEV_UP:
/* Ignore for now */
break;
-
+
case NETDEV_UNREGISTER:
- pktgen_NN_threads(dev->name, REMOVE);
+ pktgen_mark_device(dev->name);
break;
};
@@ -1502,15 +1698,16 @@ static int pktgen_device_event(struct notifier_block *unused, unsigned long even
/* Associate pktgen_dev with a device. */
-static struct net_device* pktgen_setup_dev(struct pktgen_dev *pkt_dev) {
+static struct net_device *pktgen_setup_dev(struct pktgen_dev *pkt_dev)
+{
struct net_device *odev;
/* Clean old setups */
if (pkt_dev->odev) {
dev_put(pkt_dev->odev);
- pkt_dev->odev = NULL;
- }
+ pkt_dev->odev = NULL;
+ }
odev = dev_get_by_name(pkt_dev->ifname);
@@ -1519,7 +1716,8 @@ static struct net_device* pktgen_setup_dev(struct pktgen_dev *pkt_dev) {
goto out;
}
if (odev->type != ARPHRD_ETHER) {
- printk("pktgen: not an ethernet device: \"%s\"\n", pkt_dev->ifname);
+ printk("pktgen: not an ethernet device: \"%s\"\n",
+ pkt_dev->ifname);
goto out_put;
}
if (!netif_running(odev)) {
@@ -1527,13 +1725,13 @@ static struct net_device* pktgen_setup_dev(struct pktgen_dev *pkt_dev) {
goto out_put;
}
pkt_dev->odev = odev;
-
- return pkt_dev->odev;
+
+ return pkt_dev->odev;
out_put:
dev_put(odev);
out:
- return NULL;
+ return NULL;
}
@@ -1543,59 +1741,64 @@ out:
static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
{
/* Try once more, just in case it works now. */
- if (!pkt_dev->odev)
- pktgen_setup_dev(pkt_dev);
-
- if (!pkt_dev->odev) {
- printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n");
- sprintf(pkt_dev->result, "ERROR: pkt_dev->odev == NULL in setup_inject.\n");
- return;
- }
-
- /* Default to the interface's mac if not explicitly set. */
+ if (!pkt_dev->odev)
+ pktgen_setup_dev(pkt_dev);
+
+ if (!pkt_dev->odev) {
+ printk("pktgen: ERROR: pkt_dev->odev == NULL in setup_inject.\n");
+ sprintf(pkt_dev->result,
+ "ERROR: pkt_dev->odev == NULL in setup_inject.\n");
+ return;
+ }
+
+ /* Default to the interface's mac if not explicitly set. */
if (is_zero_ether_addr(pkt_dev->src_mac))
- memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN);
+ memcpy(&(pkt_dev->hh[6]), pkt_dev->odev->dev_addr, ETH_ALEN);
- /* Set up Dest MAC */
+ /* Set up Dest MAC */
memcpy(&(pkt_dev->hh[0]), pkt_dev->dst_mac, ETH_ALEN);
- /* Set up pkt size */
- pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
-
- if(pkt_dev->flags & F_IPV6) {
+ /* Set up pkt size */
+ pkt_dev->cur_pkt_size = pkt_dev->min_pkt_size;
+
+ if (pkt_dev->flags & F_IPV6) {
/*
* Skip this automatic address setting until locks or functions
* gets exported
*/
#ifdef NOTNOW
- int i, set = 0, err=1;
+ int i, set = 0, err = 1;
struct inet6_dev *idev;
- for(i=0; i< IN6_ADDR_HSIZE; i++)
- if(pkt_dev->cur_in6_saddr.s6_addr[i]) {
+ for (i = 0; i < IN6_ADDR_HSIZE; i++)
+ if (pkt_dev->cur_in6_saddr.s6_addr[i]) {
set = 1;
break;
}
- if(!set) {
-
+ if (!set) {
+
/*
* Use linklevel address if unconfigured.
*
* use ipv6_get_lladdr if/when it's get exported
*/
-
read_lock(&addrconf_lock);
if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) {
struct inet6_ifaddr *ifp;
read_lock_bh(&idev->lock);
- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
- if (ifp->scope == IFA_LINK && !(ifp->flags&IFA_F_TENTATIVE)) {
- ipv6_addr_copy(&pkt_dev->cur_in6_saddr, &ifp->addr);
+ for (ifp = idev->addr_list; ifp;
+ ifp = ifp->if_next) {
+ if (ifp->scope == IFA_LINK
+ && !(ifp->
+ flags & IFA_F_TENTATIVE)) {
+ ipv6_addr_copy(&pkt_dev->
+ cur_in6_saddr,
+ &ifp->addr);
err = 0;
break;
}
@@ -1603,28 +1806,28 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
read_unlock_bh(&idev->lock);
}
read_unlock(&addrconf_lock);
- if(err) printk("pktgen: ERROR: IPv6 link address not availble.\n");
+ if (err)
+ printk("pktgen: ERROR: IPv6 link address not availble.\n");
}
#endif
- }
- else {
+ } else {
pkt_dev->saddr_min = 0;
pkt_dev->saddr_max = 0;
if (strlen(pkt_dev->src_min) == 0) {
-
- struct in_device *in_dev;
+
+ struct in_device *in_dev;
rcu_read_lock();
in_dev = __in_dev_get_rcu(pkt_dev->odev);
if (in_dev) {
if (in_dev->ifa_list) {
- pkt_dev->saddr_min = in_dev->ifa_list->ifa_address;
+ pkt_dev->saddr_min =
+ in_dev->ifa_list->ifa_address;
pkt_dev->saddr_max = pkt_dev->saddr_min;
}
}
rcu_read_unlock();
- }
- else {
+ } else {
pkt_dev->saddr_min = in_aton(pkt_dev->src_min);
pkt_dev->saddr_max = in_aton(pkt_dev->src_max);
}
@@ -1632,13 +1835,13 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
pkt_dev->daddr_min = in_aton(pkt_dev->dst_min);
pkt_dev->daddr_max = in_aton(pkt_dev->dst_max);
}
- /* Initialize current values. */
- pkt_dev->cur_dst_mac_offset = 0;
- pkt_dev->cur_src_mac_offset = 0;
- pkt_dev->cur_saddr = pkt_dev->saddr_min;
- pkt_dev->cur_daddr = pkt_dev->daddr_min;
- pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
- pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
+ /* Initialize current values. */
+ pkt_dev->cur_dst_mac_offset = 0;
+ pkt_dev->cur_src_mac_offset = 0;
+ pkt_dev->cur_saddr = pkt_dev->saddr_min;
+ pkt_dev->cur_daddr = pkt_dev->daddr_min;
+ pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
+ pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
pkt_dev->nflows = 0;
}
@@ -1651,7 +1854,7 @@ static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
printk(KERN_INFO "sleeping for %d\n", (int)(spin_until_us - now));
while (now < spin_until_us) {
/* TODO: optimize sleeping behavior */
- if (spin_until_us - now > jiffies_to_usecs(1)+1)
+ if (spin_until_us - now > jiffies_to_usecs(1) + 1)
schedule_timeout_interruptible(1);
else if (spin_until_us - now > 100) {
do_softirq();
@@ -1667,102 +1870,119 @@ static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
pkt_dev->idle_acc += now - start;
}
-
/* Increment/randomize headers according to flags and current values
* for IP src/dest, UDP src/dst port, MAC-Addr src/dst
*/
-static void mod_cur_headers(struct pktgen_dev *pkt_dev) {
- __u32 imn;
- __u32 imx;
- int flow = 0;
+static void mod_cur_headers(struct pktgen_dev *pkt_dev)
+{
+ __u32 imn;
+ __u32 imx;
+ int flow = 0;
- if(pkt_dev->cflows) {
+ if (pkt_dev->cflows) {
flow = pktgen_random() % pkt_dev->cflows;
-
+
if (pkt_dev->flows[flow].count > pkt_dev->lflow)
pkt_dev->flows[flow].count = 0;
- }
-
+ }
/* Deal with source MAC */
- if (pkt_dev->src_mac_count > 1) {
- __u32 mc;
- __u32 tmp;
-
- if (pkt_dev->flags & F_MACSRC_RND)
- mc = pktgen_random() % (pkt_dev->src_mac_count);
- else {
- mc = pkt_dev->cur_src_mac_offset++;
- if (pkt_dev->cur_src_mac_offset > pkt_dev->src_mac_count)
- pkt_dev->cur_src_mac_offset = 0;
- }
-
- tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
- pkt_dev->hh[11] = tmp;
- tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[10] = tmp;
- tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[9] = tmp;
- tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[8] = tmp;
- tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
- pkt_dev->hh[7] = tmp;
- }
-
- /* Deal with Destination MAC */
- if (pkt_dev->dst_mac_count > 1) {
- __u32 mc;
- __u32 tmp;
-
- if (pkt_dev->flags & F_MACDST_RND)
- mc = pktgen_random() % (pkt_dev->dst_mac_count);
-
- else {
- mc = pkt_dev->cur_dst_mac_offset++;
- if (pkt_dev->cur_dst_mac_offset > pkt_dev->dst_mac_count) {
- pkt_dev->cur_dst_mac_offset = 0;
- }
- }
-
- tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
- pkt_dev->hh[5] = tmp;
- tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[4] = tmp;
- tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[3] = tmp;
- tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
- pkt_dev->hh[2] = tmp;
- tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
- pkt_dev->hh[1] = tmp;
- }
-
- if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
- if (pkt_dev->flags & F_UDPSRC_RND)
- pkt_dev->cur_udp_src = ((pktgen_random() % (pkt_dev->udp_src_max - pkt_dev->udp_src_min)) + pkt_dev->udp_src_min);
-
- else {
+ if (pkt_dev->src_mac_count > 1) {
+ __u32 mc;
+ __u32 tmp;
+
+ if (pkt_dev->flags & F_MACSRC_RND)
+ mc = pktgen_random() % (pkt_dev->src_mac_count);
+ else {
+ mc = pkt_dev->cur_src_mac_offset++;
+ if (pkt_dev->cur_src_mac_offset >
+ pkt_dev->src_mac_count)
+ pkt_dev->cur_src_mac_offset = 0;
+ }
+
+ tmp = pkt_dev->src_mac[5] + (mc & 0xFF);
+ pkt_dev->hh[11] = tmp;
+ tmp = (pkt_dev->src_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[10] = tmp;
+ tmp = (pkt_dev->src_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[9] = tmp;
+ tmp = (pkt_dev->src_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[8] = tmp;
+ tmp = (pkt_dev->src_mac[1] + (tmp >> 8));
+ pkt_dev->hh[7] = tmp;
+ }
+
+ /* Deal with Destination MAC */
+ if (pkt_dev->dst_mac_count > 1) {
+ __u32 mc;
+ __u32 tmp;
+
+ if (pkt_dev->flags & F_MACDST_RND)
+ mc = pktgen_random() % (pkt_dev->dst_mac_count);
+
+ else {
+ mc = pkt_dev->cur_dst_mac_offset++;
+ if (pkt_dev->cur_dst_mac_offset >
+ pkt_dev->dst_mac_count) {
+ pkt_dev->cur_dst_mac_offset = 0;
+ }
+ }
+
+ tmp = pkt_dev->dst_mac[5] + (mc & 0xFF);
+ pkt_dev->hh[5] = tmp;
+ tmp = (pkt_dev->dst_mac[4] + ((mc >> 8) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[4] = tmp;
+ tmp = (pkt_dev->dst_mac[3] + ((mc >> 16) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[3] = tmp;
+ tmp = (pkt_dev->dst_mac[2] + ((mc >> 24) & 0xFF) + (tmp >> 8));
+ pkt_dev->hh[2] = tmp;
+ tmp = (pkt_dev->dst_mac[1] + (tmp >> 8));
+ pkt_dev->hh[1] = tmp;
+ }
+
+ if (pkt_dev->flags & F_MPLS_RND) {
+ unsigned i;
+ for(i = 0; i < pkt_dev->nr_labels; i++)
+ if (pkt_dev->labels[i] & MPLS_STACK_BOTTOM)
+ pkt_dev->labels[i] = MPLS_STACK_BOTTOM |
+ (pktgen_random() &
+ htonl(0x000fffff));
+ }
+
+ if (pkt_dev->udp_src_min < pkt_dev->udp_src_max) {
+ if (pkt_dev->flags & F_UDPSRC_RND)
+ pkt_dev->cur_udp_src =
+ ((pktgen_random() %
+ (pkt_dev->udp_src_max - pkt_dev->udp_src_min)) +
+ pkt_dev->udp_src_min);
+
+ else {
pkt_dev->cur_udp_src++;
if (pkt_dev->cur_udp_src >= pkt_dev->udp_src_max)
pkt_dev->cur_udp_src = pkt_dev->udp_src_min;
- }
- }
-
- if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
- if (pkt_dev->flags & F_UDPDST_RND) {
- pkt_dev->cur_udp_dst = ((pktgen_random() % (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)) + pkt_dev->udp_dst_min);
- }
- else {
+ }
+ }
+
+ if (pkt_dev->udp_dst_min < pkt_dev->udp_dst_max) {
+ if (pkt_dev->flags & F_UDPDST_RND) {
+ pkt_dev->cur_udp_dst =
+ ((pktgen_random() %
+ (pkt_dev->udp_dst_max - pkt_dev->udp_dst_min)) +
+ pkt_dev->udp_dst_min);
+ } else {
pkt_dev->cur_udp_dst++;
- if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
+ if (pkt_dev->cur_udp_dst >= pkt_dev->udp_dst_max)
pkt_dev->cur_udp_dst = pkt_dev->udp_dst_min;
- }
- }
+ }
+ }
if (!(pkt_dev->flags & F_IPV6)) {
- if ((imn = ntohl(pkt_dev->saddr_min)) < (imx = ntohl(pkt_dev->saddr_max))) {
+ if ((imn = ntohl(pkt_dev->saddr_min)) < (imx =
+ ntohl(pkt_dev->
+ saddr_max))) {
__u32 t;
- if (pkt_dev->flags & F_IPSRC_RND)
+ if (pkt_dev->flags & F_IPSRC_RND)
t = ((pktgen_random() % (imx - imn)) + imn);
else {
t = ntohl(pkt_dev->cur_saddr);
@@ -1773,25 +1993,32 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) {
}
pkt_dev->cur_saddr = htonl(t);
}
-
+
if (pkt_dev->cflows && pkt_dev->flows[flow].count != 0) {
pkt_dev->cur_daddr = pkt_dev->flows[flow].cur_daddr;
} else {
- if ((imn = ntohl(pkt_dev->daddr_min)) < (imx = ntohl(pkt_dev->daddr_max))) {
+ if ((imn = ntohl(pkt_dev->daddr_min)) < (imx =
+ ntohl(pkt_dev->
+ daddr_max)))
+ {
__u32 t;
if (pkt_dev->flags & F_IPDST_RND) {
- t = ((pktgen_random() % (imx - imn)) + imn);
+ t = ((pktgen_random() % (imx - imn)) +
+ imn);
t = htonl(t);
- while( LOOPBACK(t) || MULTICAST(t) || BADCLASS(t) || ZERONET(t) || LOCAL_MCAST(t) ) {
- t = ((pktgen_random() % (imx - imn)) + imn);
+ while (LOOPBACK(t) || MULTICAST(t)
+ || BADCLASS(t) || ZERONET(t)
+ || LOCAL_MCAST(t)) {
+ t = ((pktgen_random() %
+ (imx - imn)) + imn);
t = htonl(t);
}
pkt_dev->cur_daddr = t;
}
-
+
else {
t = ntohl(pkt_dev->cur_daddr);
t++;
@@ -1801,67 +2028,82 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev) {
pkt_dev->cur_daddr = htonl(t);
}
}
- if(pkt_dev->cflows) {
- pkt_dev->flows[flow].cur_daddr = pkt_dev->cur_daddr;
+ if (pkt_dev->cflows) {
+ pkt_dev->flows[flow].cur_daddr =
+ pkt_dev->cur_daddr;
pkt_dev->nflows++;
}
}
- }
- else /* IPV6 * */
- {
- if(pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[3] == 0);
+ } else { /* IPV6 * */
+
+ if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
+ pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
+ pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
+ pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ;
else {
int i;
/* Only random destinations yet */
- for(i=0; i < 4; i++) {
+ for (i = 0; i < 4; i++) {
pkt_dev->cur_in6_daddr.s6_addr32[i] =
- ((pktgen_random() |
- pkt_dev->min_in6_daddr.s6_addr32[i]) &
- pkt_dev->max_in6_daddr.s6_addr32[i]);
+ ((pktgen_random() |
+ pkt_dev->min_in6_daddr.s6_addr32[i]) &
+ pkt_dev->max_in6_daddr.s6_addr32[i]);
}
- }
+ }
}
- if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
- __u32 t;
- if (pkt_dev->flags & F_TXSIZE_RND) {
- t = ((pktgen_random() % (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size))
- + pkt_dev->min_pkt_size);
- }
- else {
+ if (pkt_dev->min_pkt_size < pkt_dev->max_pkt_size) {
+ __u32 t;
+ if (pkt_dev->flags & F_TXSIZE_RND) {
+ t = ((pktgen_random() %
+ (pkt_dev->max_pkt_size - pkt_dev->min_pkt_size))
+ + pkt_dev->min_pkt_size);
+ } else {
t = pkt_dev->cur_pkt_size + 1;
- if (t > pkt_dev->max_pkt_size)
+ if (t > pkt_dev->max_pkt_size)
t = pkt_dev->min_pkt_size;
- }
- pkt_dev->cur_pkt_size = t;
- }
+ }
+ pkt_dev->cur_pkt_size = t;
+ }
pkt_dev->flows[flow].count++;
}
+static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
+{
+ unsigned i;
+ for(i = 0; i < pkt_dev->nr_labels; i++) {
+ *mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
+ }
+ mpls--;
+ *mpls |= MPLS_STACK_BOTTOM;
+}
-static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
- struct pktgen_dev *pkt_dev)
+static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
+ struct pktgen_dev *pkt_dev)
{
struct sk_buff *skb = NULL;
__u8 *eth;
struct udphdr *udph;
int datalen, iplen;
struct iphdr *iph;
- struct pktgen_hdr *pgh = NULL;
-
+ struct pktgen_hdr *pgh = NULL;
+ __be16 protocol = __constant_htons(ETH_P_IP);
+ __be32 *mpls;
+
+ if (pkt_dev->nr_labels)
+ protocol = __constant_htons(ETH_P_MPLS_UC);
+
/* Update any of the values, used when we're incrementing various
* fields.
*/
mod_cur_headers(pkt_dev);
datalen = (odev->hard_header_len + 16) & ~0xf;
- skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen, GFP_ATOMIC);
+ skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen +
+ pkt_dev->nr_labels*sizeof(u32), GFP_ATOMIC);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
@@ -1871,51 +2113,56 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
/* Reserve for ethernet and IP header */
eth = (__u8 *) skb_push(skb, 14);
+ mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
+ if (pkt_dev->nr_labels)
+ mpls_push(mpls, pkt_dev);
iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr));
udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
memcpy(eth, pkt_dev->hh, 12);
- *(u16*)&eth[12] = __constant_htons(ETH_P_IP);
+ *(u16 *) & eth[12] = protocol;
- datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8; /* Eth + IPh + UDPh */
- if (datalen < sizeof(struct pktgen_hdr))
+ /* Eth + IPh + UDPh + mpls */
+ datalen = pkt_dev->cur_pkt_size - 14 - 20 - 8 -
+ pkt_dev->nr_labels*sizeof(u32);
+ if (datalen < sizeof(struct pktgen_hdr))
datalen = sizeof(struct pktgen_hdr);
-
+
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(datalen + 8); /* DATA + udphdr */
- udph->check = 0; /* No checksum */
+ udph->len = htons(datalen + 8); /* DATA + udphdr */
+ udph->check = 0; /* No checksum */
iph->ihl = 5;
iph->version = 4;
iph->ttl = 32;
iph->tos = 0;
- iph->protocol = IPPROTO_UDP; /* UDP */
+ iph->protocol = IPPROTO_UDP; /* UDP */
iph->saddr = pkt_dev->cur_saddr;
iph->daddr = pkt_dev->cur_daddr;
iph->frag_off = 0;
iplen = 20 + 8 + datalen;
iph->tot_len = htons(iplen);
iph->check = 0;
- iph->check = ip_fast_csum((void *) iph, iph->ihl);
- skb->protocol = __constant_htons(ETH_P_IP);
- skb->mac.raw = ((u8 *)iph) - 14;
+ iph->check = ip_fast_csum((void *)iph, iph->ihl);
+ skb->protocol = protocol;
+ skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32);
skb->dev = odev;
skb->pkt_type = PACKET_HOST;
- if (pkt_dev->nfrags <= 0)
- pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
+ if (pkt_dev->nfrags <= 0)
+ pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
else {
int frags = pkt_dev->nfrags;
int i;
- pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
-
+ pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
+
if (frags > MAX_SKB_FRAGS)
frags = MAX_SKB_FRAGS;
- if (datalen > frags*PAGE_SIZE) {
- skb_put(skb, datalen-frags*PAGE_SIZE);
- datalen = frags*PAGE_SIZE;
+ if (datalen > frags * PAGE_SIZE) {
+ skb_put(skb, datalen - frags * PAGE_SIZE);
+ datalen = frags * PAGE_SIZE;
}
i = 0;
@@ -1924,7 +2171,7 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
skb_shinfo(skb)->frags[i].page = page;
skb_shinfo(skb)->frags[i].page_offset = 0;
skb_shinfo(skb)->frags[i].size =
- (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
+ (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
datalen -= skb_shinfo(skb)->frags[i].size;
skb->len += skb_shinfo(skb)->frags[i].size;
skb->data_len += skb_shinfo(skb)->frags[i].size;
@@ -1944,30 +2191,33 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
skb_shinfo(skb)->frags[i - 1].size -= rem;
- skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
+ skb_shinfo(skb)->frags[i] =
+ skb_shinfo(skb)->frags[i - 1];
get_page(skb_shinfo(skb)->frags[i].page);
- skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i - 1].page;
- skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i - 1].size;
+ skb_shinfo(skb)->frags[i].page =
+ skb_shinfo(skb)->frags[i - 1].page;
+ skb_shinfo(skb)->frags[i].page_offset +=
+ skb_shinfo(skb)->frags[i - 1].size;
skb_shinfo(skb)->frags[i].size = rem;
i++;
skb_shinfo(skb)->nr_frags = i;
}
}
- /* Stamp the time, and sequence number, convert them to network byte order */
+ /* Stamp the time, and sequence number, convert them to network byte order */
+
+ if (pgh) {
+ struct timeval timestamp;
+
+ pgh->pgh_magic = htonl(PKTGEN_MAGIC);
+ pgh->seq_num = htonl(pkt_dev->seq_num);
+
+ do_gettimeofday(&timestamp);
+ pgh->tv_sec = htonl(timestamp.tv_sec);
+ pgh->tv_usec = htonl(timestamp.tv_usec);
+ }
+ pkt_dev->seq_num++;
- if (pgh) {
- struct timeval timestamp;
-
- pgh->pgh_magic = htonl(PKTGEN_MAGIC);
- pgh->seq_num = htonl(pkt_dev->seq_num);
-
- do_gettimeofday(&timestamp);
- pgh->tv_sec = htonl(timestamp.tv_sec);
- pgh->tv_usec = htonl(timestamp.tv_usec);
- }
- pkt_dev->seq_num++;
-
return skb;
}
@@ -1980,23 +2230,24 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
* --ro
*/
-static unsigned int scan_ip6(const char *s,char ip[16])
+static unsigned int scan_ip6(const char *s, char ip[16])
{
unsigned int i;
- unsigned int len=0;
+ unsigned int len = 0;
unsigned long u;
char suffix[16];
- unsigned int prefixlen=0;
- unsigned int suffixlen=0;
+ unsigned int prefixlen = 0;
+ unsigned int suffixlen = 0;
__u32 tmp;
- for (i=0; i<16; i++) ip[i]=0;
+ for (i = 0; i < 16; i++)
+ ip[i] = 0;
for (;;) {
if (*s == ':') {
len++;
- if (s[1] == ':') { /* Found "::", skip to part 2 */
- s+=2;
+ if (s[1] == ':') { /* Found "::", skip to part 2 */
+ s += 2;
len++;
break;
}
@@ -2004,136 +2255,162 @@ static unsigned int scan_ip6(const char *s,char ip[16])
}
{
char *tmp;
- u=simple_strtoul(s,&tmp,16);
- i=tmp-s;
+ u = simple_strtoul(s, &tmp, 16);
+ i = tmp - s;
}
- if (!i) return 0;
- if (prefixlen==12 && s[i]=='.') {
+ if (!i)
+ return 0;
+ if (prefixlen == 12 && s[i] == '.') {
/* the last 4 bytes may be written as IPv4 address */
tmp = in_aton(s);
- memcpy((struct in_addr*)(ip+12), &tmp, sizeof(tmp));
- return i+len;
+ memcpy((struct in_addr *)(ip + 12), &tmp, sizeof(tmp));
+ return i + len;
}
ip[prefixlen++] = (u >> 8);
ip[prefixlen++] = (u & 255);
- s += i; len += i;
- if (prefixlen==16)
+ s += i;
+ len += i;
+ if (prefixlen == 16)
return len;
}
/* part 2, after "::" */
for (;;) {
if (*s == ':') {
- if (suffixlen==0)
+ if (suffixlen == 0)
break;
s++;
len++;
- } else if (suffixlen!=0)
+ } else if (suffixlen != 0)
break;
{
char *tmp;
- u=simple_strtol(s,&tmp,16);
- i=tmp-s;
+ u = simple_strtol(s, &tmp, 16);
+ i = tmp - s;
}
if (!i) {
- if (*s) len--;
+ if (*s)
+ len--;
break;
}
- if (suffixlen+prefixlen<=12 && s[i]=='.') {
+ if (suffixlen + prefixlen <= 12 && s[i] == '.') {
tmp = in_aton(s);
- memcpy((struct in_addr*)(suffix+suffixlen), &tmp, sizeof(tmp));
- suffixlen+=4;
- len+=strlen(s);
+ memcpy((struct in_addr *)(suffix + suffixlen), &tmp,
+ sizeof(tmp));
+ suffixlen += 4;
+ len += strlen(s);
break;
}
suffix[suffixlen++] = (u >> 8);
suffix[suffixlen++] = (u & 255);
- s += i; len += i;
- if (prefixlen+suffixlen==16)
+ s += i;
+ len += i;
+ if (prefixlen + suffixlen == 16)
break;
}
- for (i=0; i<suffixlen; i++)
- ip[16-suffixlen+i] = suffix[i];
+ for (i = 0; i < suffixlen; i++)
+ ip[16 - suffixlen + i] = suffix[i];
return len;
}
-static char tohex(char hexdigit) {
- return hexdigit>9?hexdigit+'a'-10:hexdigit+'0';
+static char tohex(char hexdigit)
+{
+ return hexdigit > 9 ? hexdigit + 'a' - 10 : hexdigit + '0';
}
-static int fmt_xlong(char* s,unsigned int i) {
- char* bak=s;
- *s=tohex((i>>12)&0xf); if (s!=bak || *s!='0') ++s;
- *s=tohex((i>>8)&0xf); if (s!=bak || *s!='0') ++s;
- *s=tohex((i>>4)&0xf); if (s!=bak || *s!='0') ++s;
- *s=tohex(i&0xf);
- return s-bak+1;
+static int fmt_xlong(char *s, unsigned int i)
+{
+ char *bak = s;
+ *s = tohex((i >> 12) & 0xf);
+ if (s != bak || *s != '0')
+ ++s;
+ *s = tohex((i >> 8) & 0xf);
+ if (s != bak || *s != '0')
+ ++s;
+ *s = tohex((i >> 4) & 0xf);
+ if (s != bak || *s != '0')
+ ++s;
+ *s = tohex(i & 0xf);
+ return s - bak + 1;
}
-static unsigned int fmt_ip6(char *s,const char ip[16]) {
+static unsigned int fmt_ip6(char *s, const char ip[16])
+{
unsigned int len;
unsigned int i;
unsigned int temp;
unsigned int compressing;
int j;
- len = 0; compressing = 0;
- for (j=0; j<16; j+=2) {
+ len = 0;
+ compressing = 0;
+ for (j = 0; j < 16; j += 2) {
#ifdef V4MAPPEDPREFIX
- if (j==12 && !memcmp(ip,V4mappedprefix,12)) {
- inet_ntoa_r(*(struct in_addr*)(ip+12),s);
- temp=strlen(s);
- return len+temp;
+ if (j == 12 && !memcmp(ip, V4mappedprefix, 12)) {
+ inet_ntoa_r(*(struct in_addr *)(ip + 12), s);
+ temp = strlen(s);
+ return len + temp;
}
#endif
- temp = ((unsigned long) (unsigned char) ip[j] << 8) +
- (unsigned long) (unsigned char) ip[j+1];
+ temp = ((unsigned long)(unsigned char)ip[j] << 8) +
+ (unsigned long)(unsigned char)ip[j + 1];
if (temp == 0) {
if (!compressing) {
- compressing=1;
- if (j==0) {
- *s++=':'; ++len;
+ compressing = 1;
+ if (j == 0) {
+ *s++ = ':';
+ ++len;
}
}
} else {
if (compressing) {
- compressing=0;
- *s++=':'; ++len;
+ compressing = 0;
+ *s++ = ':';
+ ++len;
}
- i = fmt_xlong(s,temp); len += i; s += i;
- if (j<14) {
+ i = fmt_xlong(s, temp);
+ len += i;
+ s += i;
+ if (j < 14) {
*s++ = ':';
++len;
}
}
}
if (compressing) {
- *s++=':'; ++len;
+ *s++ = ':';
+ ++len;
}
- *s=0;
+ *s = 0;
return len;
}
-static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
- struct pktgen_dev *pkt_dev)
+static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
+ struct pktgen_dev *pkt_dev)
{
struct sk_buff *skb = NULL;
__u8 *eth;
struct udphdr *udph;
int datalen;
struct ipv6hdr *iph;
- struct pktgen_hdr *pgh = NULL;
+ struct pktgen_hdr *pgh = NULL;
+ __be16 protocol = __constant_htons(ETH_P_IPV6);
+ __be32 *mpls;
+
+ if (pkt_dev->nr_labels)
+ protocol = __constant_htons(ETH_P_MPLS_UC);
/* Update any of the values, used when we're incrementing various
* fields.
*/
mod_cur_headers(pkt_dev);
- skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16, GFP_ATOMIC);
+ skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 +
+ pkt_dev->nr_labels*sizeof(u32), GFP_ATOMIC);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
@@ -2143,27 +2420,33 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
/* Reserve for ethernet and IP header */
eth = (__u8 *) skb_push(skb, 14);
+ mpls = (__be32 *)skb_put(skb, pkt_dev->nr_labels*sizeof(__u32));
+ if (pkt_dev->nr_labels)
+ mpls_push(mpls, pkt_dev);
iph = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr));
udph = (struct udphdr *)skb_put(skb, sizeof(struct udphdr));
memcpy(eth, pkt_dev->hh, 12);
- *(u16*)&eth[12] = __constant_htons(ETH_P_IPV6);
+ *(u16 *) & eth[12] = __constant_htons(ETH_P_IPV6);
- datalen = pkt_dev->cur_pkt_size-14-
- sizeof(struct ipv6hdr)-sizeof(struct udphdr); /* Eth + IPh + UDPh */
+ /* Eth + IPh + UDPh + mpls */
+ datalen = pkt_dev->cur_pkt_size - 14 -
+ sizeof(struct ipv6hdr) - sizeof(struct udphdr) -
+ pkt_dev->nr_labels*sizeof(u32);
- if (datalen < sizeof(struct pktgen_hdr)) {
+ if (datalen < sizeof(struct pktgen_hdr)) {
datalen = sizeof(struct pktgen_hdr);
if (net_ratelimit())
- printk(KERN_INFO "pktgen: increased datalen to %d\n", datalen);
+ printk(KERN_INFO "pktgen: increased datalen to %d\n",
+ datalen);
}
udph->source = htons(pkt_dev->cur_udp_src);
udph->dest = htons(pkt_dev->cur_udp_dst);
- udph->len = htons(datalen + sizeof(struct udphdr));
- udph->check = 0; /* No checksum */
+ udph->len = htons(datalen + sizeof(struct udphdr));
+ udph->check = 0; /* No checksum */
- *(u32*)iph = __constant_htonl(0x60000000); /* Version + flow */
+ *(u32 *) iph = __constant_htonl(0x60000000); /* Version + flow */
iph->hop_limit = 32;
@@ -2173,24 +2456,24 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
ipv6_addr_copy(&iph->daddr, &pkt_dev->cur_in6_daddr);
ipv6_addr_copy(&iph->saddr, &pkt_dev->cur_in6_saddr);
- skb->mac.raw = ((u8 *)iph) - 14;
- skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->mac.raw = ((u8 *) iph) - 14 - pkt_dev->nr_labels*sizeof(u32);
+ skb->protocol = protocol;
skb->dev = odev;
skb->pkt_type = PACKET_HOST;
- if (pkt_dev->nfrags <= 0)
- pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
+ if (pkt_dev->nfrags <= 0)
+ pgh = (struct pktgen_hdr *)skb_put(skb, datalen);
else {
int frags = pkt_dev->nfrags;
int i;
- pgh = (struct pktgen_hdr*)(((char*)(udph)) + 8);
-
+ pgh = (struct pktgen_hdr *)(((char *)(udph)) + 8);
+
if (frags > MAX_SKB_FRAGS)
frags = MAX_SKB_FRAGS;
- if (datalen > frags*PAGE_SIZE) {
- skb_put(skb, datalen-frags*PAGE_SIZE);
- datalen = frags*PAGE_SIZE;
+ if (datalen > frags * PAGE_SIZE) {
+ skb_put(skb, datalen - frags * PAGE_SIZE);
+ datalen = frags * PAGE_SIZE;
}
i = 0;
@@ -2199,7 +2482,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
skb_shinfo(skb)->frags[i].page = page;
skb_shinfo(skb)->frags[i].page_offset = 0;
skb_shinfo(skb)->frags[i].size =
- (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
+ (datalen < PAGE_SIZE ? datalen : PAGE_SIZE);
datalen -= skb_shinfo(skb)->frags[i].size;
skb->len += skb_shinfo(skb)->frags[i].size;
skb->data_len += skb_shinfo(skb)->frags[i].size;
@@ -2219,305 +2502,333 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
skb_shinfo(skb)->frags[i - 1].size -= rem;
- skb_shinfo(skb)->frags[i] = skb_shinfo(skb)->frags[i - 1];
+ skb_shinfo(skb)->frags[i] =
+ skb_shinfo(skb)->frags[i - 1];
get_page(skb_shinfo(skb)->frags[i].page);
- skb_shinfo(skb)->frags[i].page = skb_shinfo(skb)->frags[i - 1].page;
- skb_shinfo(skb)->frags[i].page_offset += skb_shinfo(skb)->frags[i - 1].size;
+ skb_shinfo(skb)->frags[i].page =
+ skb_shinfo(skb)->frags[i - 1].page;
+ skb_shinfo(skb)->frags[i].page_offset +=
+ skb_shinfo(skb)->frags[i - 1].size;
skb_shinfo(skb)->frags[i].size = rem;
i++;
skb_shinfo(skb)->nr_frags = i;
}
}
- /* Stamp the time, and sequence number, convert them to network byte order */
+ /* Stamp the time, and sequence number, convert them to network byte order */
/* should we update cloned packets too ? */
- if (pgh) {
- struct timeval timestamp;
-
- pgh->pgh_magic = htonl(PKTGEN_MAGIC);
- pgh->seq_num = htonl(pkt_dev->seq_num);
-
- do_gettimeofday(&timestamp);
- pgh->tv_sec = htonl(timestamp.tv_sec);
- pgh->tv_usec = htonl(timestamp.tv_usec);
- }
- pkt_dev->seq_num++;
-
+ if (pgh) {
+ struct timeval timestamp;
+
+ pgh->pgh_magic = htonl(PKTGEN_MAGIC);
+ pgh->seq_num = htonl(pkt_dev->seq_num);
+
+ do_gettimeofday(&timestamp);
+ pgh->tv_sec = htonl(timestamp.tv_sec);
+ pgh->tv_usec = htonl(timestamp.tv_usec);
+ }
+ pkt_dev->seq_num++;
+
return skb;
}
-static inline struct sk_buff *fill_packet(struct net_device *odev,
- struct pktgen_dev *pkt_dev)
+static inline struct sk_buff *fill_packet(struct net_device *odev,
+ struct pktgen_dev *pkt_dev)
{
- if(pkt_dev->flags & F_IPV6)
+ if (pkt_dev->flags & F_IPV6)
return fill_packet_ipv6(odev, pkt_dev);
else
return fill_packet_ipv4(odev, pkt_dev);
}
-static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
+static void pktgen_clear_counters(struct pktgen_dev *pkt_dev)
{
- pkt_dev->seq_num = 1;
- pkt_dev->idle_acc = 0;
+ pkt_dev->seq_num = 1;
+ pkt_dev->idle_acc = 0;
pkt_dev->sofar = 0;
- pkt_dev->tx_bytes = 0;
- pkt_dev->errors = 0;
+ pkt_dev->tx_bytes = 0;
+ pkt_dev->errors = 0;
}
/* Set up structure for sending pkts, clear counters */
static void pktgen_run(struct pktgen_thread *t)
{
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_dev *pkt_dev;
int started = 0;
PG_DEBUG(printk("pktgen: entering pktgen_run. %p\n", t));
if_lock(t);
- for (pkt_dev = t->if_list; pkt_dev; pkt_dev = pkt_dev->next ) {
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
/*
* setup odev and create initial packet.
*/
pktgen_setup_inject(pkt_dev);
- if(pkt_dev->odev) {
+ if (pkt_dev->odev) {
pktgen_clear_counters(pkt_dev);
- pkt_dev->running = 1; /* Cranke yeself! */
+ pkt_dev->running = 1; /* Cranke yeself! */
pkt_dev->skb = NULL;
pkt_dev->started_at = getCurUs();
- pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */
+ pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */
pkt_dev->next_tx_ns = 0;
-
+
strcpy(pkt_dev->result, "Starting");
started++;
- }
- else
+ } else
strcpy(pkt_dev->result, "Error starting");
}
if_unlock(t);
- if(started) t->control &= ~(T_STOP);
+ if (started)
+ t->control &= ~(T_STOP);
}
static void pktgen_stop_all_threads_ifs(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
- PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads.\n"));
+ PG_DEBUG(printk("pktgen: entering pktgen_stop_all_threads_ifs.\n"));
- thread_lock();
- while(t) {
- pktgen_stop(t);
- t = t->next;
- }
- thread_unlock();
+ mutex_lock(&pktgen_thread_lock);
+
+ list_for_each_entry(t, &pktgen_threads, th_list)
+ t->control |= T_STOP;
+
+ mutex_unlock(&pktgen_thread_lock);
}
-static int thread_is_running(struct pktgen_thread *t )
+static int thread_is_running(struct pktgen_thread *t)
{
- struct pktgen_dev *next;
- int res = 0;
+ struct pktgen_dev *pkt_dev;
+ int res = 0;
- for(next=t->if_list; next; next=next->next) {
- if(next->running) {
+ list_for_each_entry(pkt_dev, &t->if_list, list)
+ if (pkt_dev->running) {
res = 1;
break;
}
- }
- return res;
+ return res;
}
-static int pktgen_wait_thread_run(struct pktgen_thread *t )
+static int pktgen_wait_thread_run(struct pktgen_thread *t)
{
- if_lock(t);
+ if_lock(t);
- while(thread_is_running(t)) {
+ while (thread_is_running(t)) {
- if_unlock(t);
+ if_unlock(t);
- msleep_interruptible(100);
+ msleep_interruptible(100);
- if (signal_pending(current))
- goto signal;
- if_lock(t);
- }
- if_unlock(t);
- return 1;
- signal:
- return 0;
+ if (signal_pending(current))
+ goto signal;
+ if_lock(t);
+ }
+ if_unlock(t);
+ return 1;
+signal:
+ return 0;
}
static int pktgen_wait_all_threads_run(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
int sig = 1;
-
- while (t) {
+
+ mutex_lock(&pktgen_thread_lock);
+
+ list_for_each_entry(t, &pktgen_threads, th_list) {
sig = pktgen_wait_thread_run(t);
- if( sig == 0 ) break;
- thread_lock();
- t=t->next;
- thread_unlock();
- }
- if(sig == 0) {
- thread_lock();
- while (t) {
- t->control |= (T_STOP);
- t=t->next;
- }
- thread_unlock();
+ if (sig == 0)
+ break;
}
+
+ if (sig == 0)
+ list_for_each_entry(t, &pktgen_threads, th_list)
+ t->control |= (T_STOP);
+
+ mutex_unlock(&pktgen_thread_lock);
return sig;
}
static void pktgen_run_all_threads(void)
{
- struct pktgen_thread *t = pktgen_threads;
+ struct pktgen_thread *t;
PG_DEBUG(printk("pktgen: entering pktgen_run_all_threads.\n"));
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
- while(t) {
+ list_for_each_entry(t, &pktgen_threads, th_list)
t->control |= (T_RUN);
- t = t->next;
- }
- thread_unlock();
- schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
-
+ mutex_unlock(&pktgen_thread_lock);
+
+ schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
+
pktgen_wait_all_threads_run();
}
-
static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
{
- __u64 total_us, bps, mbps, pps, idle;
- char *p = pkt_dev->result;
-
- total_us = pkt_dev->stopped_at - pkt_dev->started_at;
-
- idle = pkt_dev->idle_acc;
-
- p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
- (unsigned long long) total_us,
- (unsigned long long)(total_us - idle),
- (unsigned long long) idle,
- (unsigned long long) pkt_dev->sofar,
- pkt_dev->cur_pkt_size, nr_frags);
-
- pps = pkt_dev->sofar * USEC_PER_SEC;
-
- while ((total_us >> 32) != 0) {
- pps >>= 1;
- total_us >>= 1;
- }
-
- do_div(pps, total_us);
-
- bps = pps * 8 * pkt_dev->cur_pkt_size;
-
- mbps = bps;
- do_div(mbps, 1000000);
- p += sprintf(p, " %llupps %lluMb/sec (%llubps) errors: %llu",
- (unsigned long long) pps,
- (unsigned long long) mbps,
- (unsigned long long) bps,
- (unsigned long long) pkt_dev->errors);
+ __u64 total_us, bps, mbps, pps, idle;
+ char *p = pkt_dev->result;
+
+ total_us = pkt_dev->stopped_at - pkt_dev->started_at;
+
+ idle = pkt_dev->idle_acc;
+
+ p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
+ (unsigned long long)total_us,
+ (unsigned long long)(total_us - idle),
+ (unsigned long long)idle,
+ (unsigned long long)pkt_dev->sofar,
+ pkt_dev->cur_pkt_size, nr_frags);
+
+ pps = pkt_dev->sofar * USEC_PER_SEC;
+
+ while ((total_us >> 32) != 0) {
+ pps >>= 1;
+ total_us >>= 1;
+ }
+
+ do_div(pps, total_us);
+
+ bps = pps * 8 * pkt_dev->cur_pkt_size;
+
+ mbps = bps;
+ do_div(mbps, 1000000);
+ p += sprintf(p, " %llupps %lluMb/sec (%llubps) errors: %llu",
+ (unsigned long long)pps,
+ (unsigned long long)mbps,
+ (unsigned long long)bps,
+ (unsigned long long)pkt_dev->errors);
}
-
/* Set stopped-at timer, remove from running list, do counters & statistics */
-static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
+static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
{
-
- if (!pkt_dev->running) {
- printk("pktgen: interface: %s is already stopped\n", pkt_dev->ifname);
- return -EINVAL;
- }
+ int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
- pkt_dev->stopped_at = getCurUs();
- pkt_dev->running = 0;
+ if (!pkt_dev->running) {
+ printk("pktgen: interface: %s is already stopped\n",
+ pkt_dev->ifname);
+ return -EINVAL;
+ }
- show_results(pkt_dev, skb_shinfo(pkt_dev->skb)->nr_frags);
+ pkt_dev->stopped_at = getCurUs();
+ pkt_dev->running = 0;
- if (pkt_dev->skb)
- kfree_skb(pkt_dev->skb);
+ show_results(pkt_dev, nr_frags);
- pkt_dev->skb = NULL;
-
- return 0;
+ return 0;
}
-static struct pktgen_dev *next_to_run(struct pktgen_thread *t )
+static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
{
- struct pktgen_dev *next, *best = NULL;
-
+ struct pktgen_dev *pkt_dev, *best = NULL;
+
if_lock(t);
- for(next=t->if_list; next ; next=next->next) {
- if(!next->running) continue;
- if(best == NULL) best=next;
- else if ( next->next_tx_us < best->next_tx_us)
- best = next;
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
+ if (!pkt_dev->running)
+ continue;
+ if (best == NULL)
+ best = pkt_dev;
+ else if (pkt_dev->next_tx_us < best->next_tx_us)
+ best = pkt_dev;
}
if_unlock(t);
- return best;
+ return best;
}
-static void pktgen_stop(struct pktgen_thread *t) {
- struct pktgen_dev *next = NULL;
+static void pktgen_stop(struct pktgen_thread *t)
+{
+ struct pktgen_dev *pkt_dev;
- PG_DEBUG(printk("pktgen: entering pktgen_stop.\n"));
+ PG_DEBUG(printk("pktgen: entering pktgen_stop\n"));
- if_lock(t);
+ if_lock(t);
+
+ list_for_each_entry(pkt_dev, &t->if_list, list) {
+ pktgen_stop_device(pkt_dev);
+ if (pkt_dev->skb)
+ kfree_skb(pkt_dev->skb);
- for(next=t->if_list; next; next=next->next)
- pktgen_stop_device(next);
+ pkt_dev->skb = NULL;
+ }
- if_unlock(t);
+ if_unlock(t);
}
-static void pktgen_rem_all_ifs(struct pktgen_thread *t)
+/*
+ * one of our devices needs to be removed - find it
+ * and remove it
+ */
+static void pktgen_rem_one_if(struct pktgen_thread *t)
{
- struct pktgen_dev *cur, *next = NULL;
-
- /* Remove all devices, free mem */
-
- if_lock(t);
-
- for(cur=t->if_list; cur; cur=next) {
- next = cur->next;
+ struct list_head *q, *n;
+ struct pktgen_dev *cur;
+
+ PG_DEBUG(printk("pktgen: entering pktgen_rem_one_if\n"));
+
+ if_lock(t);
+
+ list_for_each_safe(q, n, &t->if_list) {
+ cur = list_entry(q, struct pktgen_dev, list);
+
+ if (!cur->removal_mark)
+ continue;
+
+ if (cur->skb)
+ kfree_skb(cur->skb);
+ cur->skb = NULL;
+
pktgen_remove_device(t, cur);
+
+ break;
}
- if_unlock(t);
+ if_unlock(t);
}
-static void pktgen_rem_thread(struct pktgen_thread *t)
+static void pktgen_rem_all_ifs(struct pktgen_thread *t)
{
- /* Remove from the thread list */
+ struct list_head *q, *n;
+ struct pktgen_dev *cur;
- struct pktgen_thread *tmp = pktgen_threads;
+ /* Remove all devices, free mem */
- remove_proc_entry(t->name, pg_proc_dir);
+ PG_DEBUG(printk("pktgen: entering pktgen_rem_all_ifs\n"));
+ if_lock(t);
- thread_lock();
+ list_for_each_safe(q, n, &t->if_list) {
+ cur = list_entry(q, struct pktgen_dev, list);
- if (tmp == t)
- pktgen_threads = tmp->next;
- else {
- while (tmp) {
- if (tmp->next == t) {
- tmp->next = t->next;
- t->next = NULL;
- break;
- }
- tmp = tmp->next;
- }
+ if (cur->skb)
+ kfree_skb(cur->skb);
+ cur->skb = NULL;
+
+ pktgen_remove_device(t, cur);
}
- thread_unlock();
+
+ if_unlock(t);
+}
+
+static void pktgen_rem_thread(struct pktgen_thread *t)
+{
+ /* Remove from the thread list */
+
+ remove_proc_entry(t->name, pg_proc_dir);
+
+ mutex_lock(&pktgen_thread_lock);
+
+ list_del(&t->th_list);
+
+ mutex_unlock(&pktgen_thread_lock);
}
static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
@@ -2527,7 +2838,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
int ret;
odev = pkt_dev->odev;
-
+
if (pkt_dev->delay_us || pkt_dev->delay_ns) {
u64 now;
@@ -2544,67 +2855,71 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
goto out;
}
}
-
+
if (netif_queue_stopped(odev) || need_resched()) {
idle_start = getCurUs();
-
+
if (!netif_running(odev)) {
pktgen_stop_device(pkt_dev);
+ if (pkt_dev->skb)
+ kfree_skb(pkt_dev->skb);
+ pkt_dev->skb = NULL;
goto out;
}
- if (need_resched())
+ if (need_resched())
schedule();
-
+
pkt_dev->idle_acc += getCurUs() - idle_start;
-
+
if (netif_queue_stopped(odev)) {
- pkt_dev->next_tx_us = getCurUs(); /* TODO */
+ pkt_dev->next_tx_us = getCurUs(); /* TODO */
pkt_dev->next_tx_ns = 0;
- goto out; /* Try the next interface */
+ goto out; /* Try the next interface */
}
}
-
+
if (pkt_dev->last_ok || !pkt_dev->skb) {
- if ((++pkt_dev->clone_count >= pkt_dev->clone_skb ) || (!pkt_dev->skb)) {
+ if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
+ || (!pkt_dev->skb)) {
/* build a new pkt */
- if (pkt_dev->skb)
+ if (pkt_dev->skb)
kfree_skb(pkt_dev->skb);
-
+
pkt_dev->skb = fill_packet(odev, pkt_dev);
if (pkt_dev->skb == NULL) {
printk("pktgen: ERROR: couldn't allocate skb in fill_packet.\n");
schedule();
- pkt_dev->clone_count--; /* back out increment, OOM */
+ pkt_dev->clone_count--; /* back out increment, OOM */
goto out;
}
pkt_dev->allocated_skbs++;
- pkt_dev->clone_count = 0; /* reset counter */
+ pkt_dev->clone_count = 0; /* reset counter */
}
}
-
+
spin_lock_bh(&odev->xmit_lock);
if (!netif_queue_stopped(odev)) {
atomic_inc(&(pkt_dev->skb->users));
-retry_now:
+ retry_now:
ret = odev->hard_start_xmit(pkt_dev->skb, odev);
if (likely(ret == NETDEV_TX_OK)) {
- pkt_dev->last_ok = 1;
+ pkt_dev->last_ok = 1;
pkt_dev->sofar++;
pkt_dev->seq_num++;
pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
-
- } else if (ret == NETDEV_TX_LOCKED
+
+ } else if (ret == NETDEV_TX_LOCKED
&& (odev->features & NETIF_F_LLTX)) {
cpu_relax();
goto retry_now;
- } else { /* Retry it next time */
-
+ } else { /* Retry it next time */
+
atomic_dec(&(pkt_dev->skb->users));
-
+
if (debug && net_ratelimit())
printk(KERN_INFO "pktgen: Hard xmit error\n");
-
+
pkt_dev->errors++;
pkt_dev->last_ok = 0;
}
@@ -2619,16 +2934,16 @@ retry_now:
pkt_dev->next_tx_us++;
pkt_dev->next_tx_ns -= 1000;
}
- }
+ }
- else { /* Retry it next time */
- pkt_dev->last_ok = 0;
- pkt_dev->next_tx_us = getCurUs(); /* TODO */
+ else { /* Retry it next time */
+ pkt_dev->last_ok = 0;
+ pkt_dev->next_tx_us = getCurUs(); /* TODO */
pkt_dev->next_tx_ns = 0;
- }
+ }
spin_unlock_bh(&odev->xmit_lock);
-
+
/* If pkt_dev->count is zero, then run forever */
if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
if (atomic_read(&(pkt_dev->skb->users)) != 1) {
@@ -2641,72 +2956,74 @@ retry_now:
}
pkt_dev->idle_acc += getCurUs() - idle_start;
}
-
+
/* Done with this */
pktgen_stop_device(pkt_dev);
- }
- out:;
- }
+ if (pkt_dev->skb)
+ kfree_skb(pkt_dev->skb);
+ pkt_dev->skb = NULL;
+ }
+out:;
+}
/*
* Main loop of the thread goes here
*/
-static void pktgen_thread_worker(struct pktgen_thread *t)
+static void pktgen_thread_worker(struct pktgen_thread *t)
{
DEFINE_WAIT(wait);
- struct pktgen_dev *pkt_dev = NULL;
+ struct pktgen_dev *pkt_dev = NULL;
int cpu = t->cpu;
sigset_t tmpsig;
u32 max_before_softirq;
- u32 tx_since_softirq = 0;
+ u32 tx_since_softirq = 0;
daemonize("pktgen/%d", cpu);
- /* Block all signals except SIGKILL, SIGSTOP and SIGTERM */
+ /* Block all signals except SIGKILL, SIGSTOP and SIGTERM */
- spin_lock_irq(&current->sighand->siglock);
- tmpsig = current->blocked;
- siginitsetinv(&current->blocked,
- sigmask(SIGKILL) |
- sigmask(SIGSTOP)|
- sigmask(SIGTERM));
+ spin_lock_irq(&current->sighand->siglock);
+ tmpsig = current->blocked;
+ siginitsetinv(&current->blocked,
+ sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGTERM));
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
+ recalc_sigpending();
+ spin_unlock_irq(&current->sighand->siglock);
/* Migrate to the right CPU */
set_cpus_allowed(current, cpumask_of_cpu(cpu));
- if (smp_processor_id() != cpu)
- BUG();
+ if (smp_processor_id() != cpu)
+ BUG();
init_waitqueue_head(&t->queue);
t->control &= ~(T_TERMINATE);
t->control &= ~(T_RUN);
t->control &= ~(T_STOP);
+ t->control &= ~(T_REMDEVALL);
t->control &= ~(T_REMDEV);
- t->pid = current->pid;
+ t->pid = current->pid;
- PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid));
+ PG_DEBUG(printk("pktgen: starting pktgen/%d: pid=%d\n", cpu, current->pid));
max_before_softirq = t->max_before_softirq;
-
- __set_current_state(TASK_INTERRUPTIBLE);
- mb();
- while (1) {
-
+ __set_current_state(TASK_INTERRUPTIBLE);
+ mb();
+
+ while (1) {
+
__set_current_state(TASK_RUNNING);
/*
* Get next dev to xmit -- if any.
*/
- pkt_dev = next_to_run(t);
-
- if (pkt_dev) {
+ pkt_dev = next_to_run(t);
+
+ if (pkt_dev) {
pktgen_xmit(pkt_dev);
@@ -2724,115 +3041,125 @@ static void pktgen_thread_worker(struct pktgen_thread *t)
}
} else {
prepare_to_wait(&(t->queue), &wait, TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/10);
+ schedule_timeout(HZ / 10);
finish_wait(&(t->queue), &wait);
}
- /*
+ /*
* Back from sleep, either due to the timeout or signal.
* We check if we have any "posted" work for us.
*/
- if (t->control & T_TERMINATE || signal_pending(current))
- /* we received a request to terminate ourself */
- break;
-
+ if (t->control & T_TERMINATE || signal_pending(current))
+ /* we received a request to terminate ourself */
+ break;
- if(t->control & T_STOP) {
+ if (t->control & T_STOP) {
pktgen_stop(t);
t->control &= ~(T_STOP);
}
- if(t->control & T_RUN) {
+ if (t->control & T_RUN) {
pktgen_run(t);
t->control &= ~(T_RUN);
}
- if(t->control & T_REMDEV) {
+ if (t->control & T_REMDEVALL) {
pktgen_rem_all_ifs(t);
+ t->control &= ~(T_REMDEVALL);
+ }
+
+ if (t->control & T_REMDEV) {
+ pktgen_rem_one_if(t);
t->control &= ~(T_REMDEV);
}
- if (need_resched())
+ if (need_resched())
schedule();
- }
+ }
+
+ PG_DEBUG(printk("pktgen: %s stopping all device\n", t->name));
+ pktgen_stop(t);
- PG_DEBUG(printk("pktgen: %s stopping all device\n", t->name));
- pktgen_stop(t);
+ PG_DEBUG(printk("pktgen: %s removing all device\n", t->name));
+ pktgen_rem_all_ifs(t);
- PG_DEBUG(printk("pktgen: %s removing all device\n", t->name));
- pktgen_rem_all_ifs(t);
+ PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
+ pktgen_rem_thread(t);
- PG_DEBUG(printk("pktgen: %s removing thread.\n", t->name));
- pktgen_rem_thread(t);
+ t->removed = 1;
}
-static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t, const char* ifname)
+static struct pktgen_dev *pktgen_find_dev(struct pktgen_thread *t,
+ const char *ifname)
{
- struct pktgen_dev *pkt_dev = NULL;
- if_lock(t);
-
- for(pkt_dev=t->if_list; pkt_dev; pkt_dev = pkt_dev->next ) {
- if (strncmp(pkt_dev->ifname, ifname, IFNAMSIZ) == 0) {
- break;
- }
- }
-
- if_unlock(t);
- PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname,pkt_dev));
- return pkt_dev;
+ struct pktgen_dev *p, *pkt_dev = NULL;
+ if_lock(t);
+
+ list_for_each_entry(p, &t->if_list, list)
+ if (strncmp(p->ifname, ifname, IFNAMSIZ) == 0) {
+ pkt_dev = p;
+ break;
+ }
+
+ if_unlock(t);
+ PG_DEBUG(printk("pktgen: find_dev(%s) returning %p\n", ifname, pkt_dev));
+ return pkt_dev;
}
/*
* Adds a dev at front of if_list.
*/
-static int add_dev_to_thread(struct pktgen_thread *t, struct pktgen_dev *pkt_dev)
+static int add_dev_to_thread(struct pktgen_thread *t,
+ struct pktgen_dev *pkt_dev)
{
int rv = 0;
-
- if_lock(t);
-
- if (pkt_dev->pg_thread) {
- printk("pktgen: ERROR: already assigned to a thread.\n");
- rv = -EBUSY;
- goto out;
- }
- pkt_dev->next =t->if_list; t->if_list=pkt_dev;
- pkt_dev->pg_thread = t;
+
+ if_lock(t);
+
+ if (pkt_dev->pg_thread) {
+ printk("pktgen: ERROR: already assigned to a thread.\n");
+ rv = -EBUSY;
+ goto out;
+ }
+
+ list_add(&pkt_dev->list, &t->if_list);
+ pkt_dev->pg_thread = t;
pkt_dev->running = 0;
- out:
- if_unlock(t);
- return rv;
+out:
+ if_unlock(t);
+ return rv;
}
/* Called under thread lock */
-static int pktgen_add_device(struct pktgen_thread *t, const char* ifname)
+static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
{
- struct pktgen_dev *pkt_dev;
+ struct pktgen_dev *pkt_dev;
struct proc_dir_entry *pe;
-
+
/* We don't allow a device to be on several threads */
pkt_dev = __pktgen_NN_threads(ifname, FIND);
if (pkt_dev) {
- printk("pktgen: ERROR: interface already used.\n");
- return -EBUSY;
- }
+ printk("pktgen: ERROR: interface already used.\n");
+ return -EBUSY;
+ }
pkt_dev = kzalloc(sizeof(struct pktgen_dev), GFP_KERNEL);
if (!pkt_dev)
return -ENOMEM;
- pkt_dev->flows = vmalloc(MAX_CFLOWS*sizeof(struct flow_state));
+ pkt_dev->flows = vmalloc(MAX_CFLOWS * sizeof(struct flow_state));
if (pkt_dev->flows == NULL) {
kfree(pkt_dev);
return -ENOMEM;
}
- memset(pkt_dev->flows, 0, MAX_CFLOWS*sizeof(struct flow_state));
+ memset(pkt_dev->flows, 0, MAX_CFLOWS * sizeof(struct flow_state));
+ pkt_dev->removal_mark = 0;
pkt_dev->min_pkt_size = ETH_ZLEN;
pkt_dev->max_pkt_size = ETH_ZLEN;
pkt_dev->nfrags = 0;
@@ -2841,14 +3168,14 @@ static int pktgen_add_device(struct pktgen_thread *t, const char* ifname)
pkt_dev->delay_ns = pg_delay_d % 1000;
pkt_dev->count = pg_count_d;
pkt_dev->sofar = 0;
- pkt_dev->udp_src_min = 9; /* sink port */
+ pkt_dev->udp_src_min = 9; /* sink port */
pkt_dev->udp_src_max = 9;
pkt_dev->udp_dst_min = 9;
pkt_dev->udp_dst_max = 9;
strncpy(pkt_dev->ifname, ifname, IFNAMSIZ);
- if (! pktgen_setup_dev(pkt_dev)) {
+ if (!pktgen_setup_dev(pkt_dev)) {
printk("pktgen: ERROR: pktgen_setup_dev failed.\n");
if (pkt_dev->flows)
vfree(pkt_dev->flows);
@@ -2871,65 +3198,74 @@ static int pktgen_add_device(struct pktgen_thread *t, const char* ifname)
return add_dev_to_thread(t, pkt_dev);
}
-static struct pktgen_thread * __init pktgen_find_thread(const char* name)
+static struct pktgen_thread *__init pktgen_find_thread(const char *name)
{
- struct pktgen_thread *t = NULL;
+ struct pktgen_thread *t;
- thread_lock();
+ mutex_lock(&pktgen_thread_lock);
- t = pktgen_threads;
- while (t) {
- if (strcmp(t->name, name) == 0)
- break;
+ list_for_each_entry(t, &pktgen_threads, th_list)
+ if (strcmp(t->name, name) == 0) {
+ mutex_unlock(&pktgen_thread_lock);
+ return t;
+ }
- t = t->next;
- }
- thread_unlock();
- return t;
+ mutex_unlock(&pktgen_thread_lock);
+ return NULL;
}
-static int __init pktgen_create_thread(const char* name, int cpu)
+static int __init pktgen_create_thread(const char *name, int cpu)
{
- struct pktgen_thread *t = NULL;
+ int err;
+ struct pktgen_thread *t = NULL;
struct proc_dir_entry *pe;
- if (strlen(name) > 31) {
- printk("pktgen: ERROR: Thread name cannot be more than 31 characters.\n");
- return -EINVAL;
- }
-
- if (pktgen_find_thread(name)) {
- printk("pktgen: ERROR: thread: %s already exists\n", name);
- return -EINVAL;
- }
-
- t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
- if (!t) {
- printk("pktgen: ERROR: out of memory, can't create new thread.\n");
- return -ENOMEM;
- }
-
- strcpy(t->name, name);
- spin_lock_init(&t->if_lock);
+ if (strlen(name) > 31) {
+ printk("pktgen: ERROR: Thread name cannot be more than 31 characters.\n");
+ return -EINVAL;
+ }
+
+ if (pktgen_find_thread(name)) {
+ printk("pktgen: ERROR: thread: %s already exists\n", name);
+ return -EINVAL;
+ }
+
+ t = kzalloc(sizeof(struct pktgen_thread), GFP_KERNEL);
+ if (!t) {
+ printk("pktgen: ERROR: out of memory, can't create new thread.\n");
+ return -ENOMEM;
+ }
+
+ strcpy(t->name, name);
+ spin_lock_init(&t->if_lock);
t->cpu = cpu;
-
- pe = create_proc_entry(t->name, 0600, pg_proc_dir);
- if (!pe) {
- printk("pktgen: cannot create %s/%s procfs entry.\n",
+
+ pe = create_proc_entry(t->name, 0600, pg_proc_dir);
+ if (!pe) {
+ printk("pktgen: cannot create %s/%s procfs entry.\n",
PG_PROC_DIR, t->name);
- kfree(t);
- return -EINVAL;
- }
+ kfree(t);
+ return -EINVAL;
+ }
pe->proc_fops = &pktgen_thread_fops;
pe->data = t;
- t->next = pktgen_threads;
- pktgen_threads = t;
+ INIT_LIST_HEAD(&t->if_list);
+
+ list_add_tail(&t->th_list, &pktgen_threads);
+
+ t->removed = 0;
- if (kernel_thread((void *) pktgen_thread_worker, (void *) t,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND) < 0)
+ err = kernel_thread((void *)pktgen_thread_worker, (void *)t,
+ CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ if (err < 0) {
printk("pktgen: kernel_thread() failed for cpu %d\n", t->cpu);
+ remove_proc_entry(t->name, pg_proc_dir);
+ list_del(&t->th_list);
+ kfree(t);
+ return err;
+ }
return 0;
}
@@ -2937,55 +3273,52 @@ static int __init pktgen_create_thread(const char* name, int cpu)
/*
* Removes a device from the thread if_list.
*/
-static void _rem_dev_from_if_list(struct pktgen_thread *t, struct pktgen_dev *pkt_dev)
+static void _rem_dev_from_if_list(struct pktgen_thread *t,
+ struct pktgen_dev *pkt_dev)
{
- struct pktgen_dev *i, *prev = NULL;
-
- i = t->if_list;
+ struct list_head *q, *n;
+ struct pktgen_dev *p;
- while(i) {
- if(i == pkt_dev) {
- if(prev) prev->next = i->next;
- else t->if_list = NULL;
- break;
- }
- prev = i;
- i=i->next;
+ list_for_each_safe(q, n, &t->if_list) {
+ p = list_entry(q, struct pktgen_dev, list);
+ if (p == pkt_dev)
+ list_del(&p->list);
}
}
-static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *pkt_dev)
+static int pktgen_remove_device(struct pktgen_thread *t,
+ struct pktgen_dev *pkt_dev)
{
PG_DEBUG(printk("pktgen: remove_device pkt_dev=%p\n", pkt_dev));
- if (pkt_dev->running) {
- printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n");
- pktgen_stop_device(pkt_dev);
- }
-
- /* Dis-associate from the interface */
+ if (pkt_dev->running) {
+ printk("pktgen:WARNING: trying to remove a running interface, stopping it now.\n");
+ pktgen_stop_device(pkt_dev);
+ }
+
+ /* Dis-associate from the interface */
if (pkt_dev->odev) {
dev_put(pkt_dev->odev);
- pkt_dev->odev = NULL;
- }
-
+ pkt_dev->odev = NULL;
+ }
+
/* And update the thread if_list */
_rem_dev_from_if_list(t, pkt_dev);
- /* Clean up proc file system */
+ /* Clean up proc file system */
remove_proc_entry(pkt_dev->ifname, pg_proc_dir);
if (pkt_dev->flows)
vfree(pkt_dev->flows);
kfree(pkt_dev);
- return 0;
+ return 0;
}
-static int __init pg_init(void)
+static int __init pg_init(void)
{
int cpu;
struct proc_dir_entry *pe;
@@ -2998,50 +3331,65 @@ static int __init pg_init(void)
pg_proc_dir->owner = THIS_MODULE;
pe = create_proc_entry(PGCTRL, 0600, pg_proc_dir);
- if (pe == NULL) {
- printk("pktgen: ERROR: cannot create %s procfs entry.\n", PGCTRL);
+ if (pe == NULL) {
+ printk("pktgen: ERROR: cannot create %s procfs entry.\n",
+ PGCTRL);
proc_net_remove(PG_PROC_DIR);
- return -EINVAL;
- }
+ return -EINVAL;
+ }
- pe->proc_fops = &pktgen_fops;
- pe->data = NULL;
+ pe->proc_fops = &pktgen_fops;
+ pe->data = NULL;
/* Register us to receive netdevice events */
register_netdevice_notifier(&pktgen_notifier_block);
-
+
for_each_online_cpu(cpu) {
+ int err;
char buf[30];
- sprintf(buf, "kpktgend_%i", cpu);
- pktgen_create_thread(buf, cpu);
- }
- return 0;
+ sprintf(buf, "kpktgend_%i", cpu);
+ err = pktgen_create_thread(buf, cpu);
+ if (err)
+ printk("pktgen: WARNING: Cannot create thread for cpu %d (%d)\n",
+ cpu, err);
+ }
+
+ if (list_empty(&pktgen_threads)) {
+ printk("pktgen: ERROR: Initialization failed for all threads\n");
+ unregister_netdevice_notifier(&pktgen_notifier_block);
+ remove_proc_entry(PGCTRL, pg_proc_dir);
+ proc_net_remove(PG_PROC_DIR);
+ return -ENODEV;
+ }
+
+ return 0;
}
static void __exit pg_cleanup(void)
{
+ struct pktgen_thread *t;
+ struct list_head *q, *n;
wait_queue_head_t queue;
init_waitqueue_head(&queue);
- /* Stop all interfaces & threads */
+ /* Stop all interfaces & threads */
- while (pktgen_threads) {
- struct pktgen_thread *t = pktgen_threads;
- pktgen_threads->control |= (T_TERMINATE);
+ list_for_each_safe(q, n, &pktgen_threads) {
+ t = list_entry(q, struct pktgen_thread, th_list);
+ t->control |= (T_TERMINATE);
- wait_event_interruptible_timeout(queue, (t != pktgen_threads), HZ);
- }
+ wait_event_interruptible_timeout(queue, (t->removed == 1), HZ);
+ }
- /* Un-register us from receiving netdevice events */
+ /* Un-register us from receiving netdevice events */
unregister_netdevice_notifier(&pktgen_notifier_block);
- /* Clean up proc file system */
+ /* Clean up proc file system */
remove_proc_entry(PGCTRL, pg_proc_dir);
proc_net_remove(PG_PROC_DIR);
}
-
module_init(pg_init);
module_exit(pg_cleanup);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index eca2976abb25..3fcfa9c59e1f 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -35,6 +35,7 @@
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/security.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -50,26 +51,36 @@
#include <net/sock.h>
#include <net/pkt_sched.h>
#include <net/netlink.h>
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+#include <linux/wireless.h>
+#include <net/iw_handler.h>
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
-DECLARE_MUTEX(rtnl_sem);
+static DEFINE_MUTEX(rtnl_mutex);
void rtnl_lock(void)
{
- rtnl_shlock();
+ mutex_lock(&rtnl_mutex);
}
-int rtnl_lock_interruptible(void)
+void __rtnl_unlock(void)
{
- return down_interruptible(&rtnl_sem);
+ mutex_unlock(&rtnl_mutex);
}
-
+
void rtnl_unlock(void)
{
- rtnl_shunlock();
-
+ mutex_unlock(&rtnl_mutex);
+ if (rtnl && rtnl->sk_receive_queue.qlen)
+ rtnl->sk_data_ready(rtnl, 0);
netdev_run_todo();
}
+int rtnl_trylock(void)
+{
+ return mutex_trylock(&rtnl_mutex);
+}
+
int rtattr_parse(struct rtattr *tb[], int maxattr, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr*)*maxattr);
@@ -179,6 +190,33 @@ rtattr_failure:
}
+static void set_operstate(struct net_device *dev, unsigned char transition)
+{
+ unsigned char operstate = dev->operstate;
+
+ switch(transition) {
+ case IF_OPER_UP:
+ if ((operstate == IF_OPER_DORMANT ||
+ operstate == IF_OPER_UNKNOWN) &&
+ !netif_dormant(dev))
+ operstate = IF_OPER_UP;
+ break;
+
+ case IF_OPER_DORMANT:
+ if (operstate == IF_OPER_UP ||
+ operstate == IF_OPER_UNKNOWN)
+ operstate = IF_OPER_DORMANT;
+ break;
+ };
+
+ if (dev->operstate != operstate) {
+ write_lock_bh(&dev_base_lock);
+ dev->operstate = operstate;
+ write_unlock_bh(&dev_base_lock);
+ netdev_state_change(dev);
+ }
+}
+
static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
int type, u32 pid, u32 seq, u32 change,
unsigned int flags)
@@ -209,6 +247,13 @@ static int rtnetlink_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
}
if (1) {
+ u8 operstate = netif_running(dev)?dev->operstate:IF_OPER_DOWN;
+ u8 link_mode = dev->link_mode;
+ RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);
+ RTA_PUT(skb, IFLA_LINKMODE, sizeof(link_mode), &link_mode);
+ }
+
+ if (1) {
struct rtnl_link_ifmap map = {
.mem_start = dev->mem_start,
.mem_end = dev->mem_end,
@@ -399,6 +444,22 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
dev->weight = *((u32 *) RTA_DATA(ida[IFLA_WEIGHT - 1]));
}
+ if (ida[IFLA_OPERSTATE - 1]) {
+ if (ida[IFLA_OPERSTATE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))
+ goto out;
+
+ set_operstate(dev, *((u8 *) RTA_DATA(ida[IFLA_OPERSTATE - 1])));
+ }
+
+ if (ida[IFLA_LINKMODE - 1]) {
+ if (ida[IFLA_LINKMODE - 1]->rta_len != RTA_LENGTH(sizeof(u8)))
+ goto out;
+
+ write_lock_bh(&dev_base_lock);
+ dev->link_mode = *((u8 *) RTA_DATA(ida[IFLA_LINKMODE - 1]));
+ write_unlock_bh(&dev_base_lock);
+ }
+
if (ifm->ifi_index >= 0 && ida[IFLA_IFNAME - 1]) {
char ifname[IFNAMSIZ];
@@ -410,6 +471,17 @@ static int do_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
goto out;
}
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ if (ida[IFLA_WIRELESS - 1]) {
+
+ /* Call Wireless Extensions.
+ * Various stuff checked in there... */
+ err = wireless_rtnetlink_set(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len);
+ if (err)
+ goto out;
+ }
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
err = 0;
out:
@@ -420,6 +492,83 @@ out:
return err;
}
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+static int do_getlink(struct sk_buff *in_skb, struct nlmsghdr* in_nlh, void *arg)
+{
+ struct ifinfomsg *ifm = NLMSG_DATA(in_nlh);
+ struct rtattr **ida = arg;
+ struct net_device *dev;
+ struct ifinfomsg *r;
+ struct nlmsghdr *nlh;
+ int err = -ENOBUFS;
+ struct sk_buff *skb;
+ unsigned char *b;
+ char *iw_buf = NULL;
+ int iw_buf_len = 0;
+
+ if (ifm->ifi_index >= 0)
+ dev = dev_get_by_index(ifm->ifi_index);
+ else
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ if (ida[IFLA_WIRELESS - 1]) {
+
+ /* Call Wireless Extensions. We need to know the size before
+ * we can alloc. Various stuff checked in there... */
+ err = wireless_rtnetlink_get(dev, RTA_DATA(ida[IFLA_WIRELESS - 1]), ida[IFLA_WIRELESS - 1]->rta_len, &iw_buf, &iw_buf_len);
+ if (err)
+ goto out;
+ }
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
+ /* Create a skb big enough to include all the data.
+ * Some requests are way bigger than 4k... Jean II */
+ skb = alloc_skb((NLMSG_LENGTH(sizeof(*r))) + (RTA_SPACE(iw_buf_len)),
+ GFP_KERNEL);
+ if (!skb)
+ goto out;
+ b = skb->tail;
+
+ /* Put in the message the usual good stuff */
+ nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, in_nlh->nlmsg_seq,
+ RTM_NEWLINK, sizeof(*r));
+ r = NLMSG_DATA(nlh);
+ r->ifi_family = AF_UNSPEC;
+ r->__ifi_pad = 0;
+ r->ifi_type = dev->type;
+ r->ifi_index = dev->ifindex;
+ r->ifi_flags = dev->flags;
+ r->ifi_change = 0;
+
+ /* Put the wireless payload if it exist */
+ if(iw_buf != NULL)
+ RTA_PUT(skb, IFLA_WIRELESS, iw_buf_len,
+ iw_buf + IW_EV_POINT_OFF);
+
+ nlh->nlmsg_len = skb->tail - b;
+
+ /* Needed ? */
+ NETLINK_CB(skb).dst_pid = NETLINK_CB(in_skb).pid;
+
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
+ if (err > 0)
+ err = 0;
+out:
+ if(iw_buf != NULL)
+ kfree(iw_buf);
+ dev_put(dev);
+ return err;
+
+rtattr_failure:
+nlmsg_failure:
+ kfree_skb(skb);
+ goto out;
+}
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
static int rtnetlink_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
{
int idx;
@@ -575,9 +724,9 @@ static void rtnetlink_rcv(struct sock *sk, int len)
unsigned int qlen = 0;
do {
- rtnl_lock();
+ mutex_lock(&rtnl_mutex);
netlink_run_queue(sk, &qlen, &rtnetlink_rcv_msg);
- up(&rtnl_sem);
+ mutex_unlock(&rtnl_mutex);
netdev_run_todo();
} while (qlen);
@@ -585,7 +734,11 @@ static void rtnetlink_rcv(struct sock *sk, int len)
static struct rtnetlink_link link_rtnetlink_table[RTM_NR_MSGTYPES] =
{
- [RTM_GETLINK - RTM_BASE] = { .dumpit = rtnetlink_dump_ifinfo },
+ [RTM_GETLINK - RTM_BASE] = {
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+ .doit = do_getlink,
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+ .dumpit = rtnetlink_dump_ifinfo },
[RTM_SETLINK - RTM_BASE] = { .doit = do_setlink },
[RTM_GETADDR - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
[RTM_GETROUTE - RTM_BASE] = { .dumpit = rtnetlink_dump_all },
@@ -654,6 +807,5 @@ EXPORT_SYMBOL(rtnetlink_links);
EXPORT_SYMBOL(rtnetlink_put_metrics);
EXPORT_SYMBOL(rtnl);
EXPORT_SYMBOL(rtnl_lock);
-EXPORT_SYMBOL(rtnl_lock_interruptible);
-EXPORT_SYMBOL(rtnl_sem);
+EXPORT_SYMBOL(rtnl_trylock);
EXPORT_SYMBOL(rtnl_unlock);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 2144952d1c6c..c9f878454531 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -356,6 +356,24 @@ void __kfree_skb(struct sk_buff *skb)
}
/**
+ * kfree_skb - free an sk_buff
+ * @skb: buffer to free
+ *
+ * Drop a reference to the buffer and free it if the usage count has
+ * hit zero.
+ */
+void kfree_skb(struct sk_buff *skb)
+{
+ if (unlikely(!skb))
+ return;
+ if (likely(atomic_read(&skb->users) == 1))
+ smp_rmb();
+ else if (likely(!atomic_dec_and_test(&skb->users)))
+ return;
+ __kfree_skb(skb);
+}
+
+/**
* skb_clone - duplicate an sk_buff
* @skb: buffer to clone
* @gfp_mask: allocation priority
@@ -1777,6 +1795,29 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb,
return 0;
}
+/**
+ * skb_pull_rcsum - pull skb and update receive checksum
+ * @skb: buffer to update
+ * @start: start of data before pull
+ * @len: length of data pulled
+ *
+ * This function performs an skb_pull on the packet and updates
+ * update the CHECKSUM_HW checksum. It should be used on receive
+ * path processing instead of skb_pull unless you know that the
+ * checksum difference is zero (e.g., a valid IP header) or you
+ * are setting ip_summed to CHECKSUM_NONE.
+ */
+unsigned char *skb_pull_rcsum(struct sk_buff *skb, unsigned int len)
+{
+ BUG_ON(len > skb->len);
+ skb->len -= len;
+ BUG_ON(skb->len < skb->data_len);
+ skb_postpull_rcsum(skb, skb->data, len);
+ return skb->data += len;
+}
+
+EXPORT_SYMBOL_GPL(skb_pull_rcsum);
+
void __init skb_init(void)
{
skbuff_head_cache = kmem_cache_create("skbuff_head_cache",
@@ -1799,6 +1840,7 @@ void __init skb_init(void)
EXPORT_SYMBOL(___pskb_trim);
EXPORT_SYMBOL(__kfree_skb);
+EXPORT_SYMBOL(kfree_skb);
EXPORT_SYMBOL(__pskb_pull_tail);
EXPORT_SYMBOL(__alloc_skb);
EXPORT_SYMBOL(pskb_copy);
diff --git a/net/core/sock.c b/net/core/sock.c
index 6e00811d44bc..1a7e6eac90b0 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -616,7 +616,7 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
break;
case SO_PEERSEC:
- return security_socket_getpeersec(sock, optval, optlen, len);
+ return security_socket_getpeersec_stream(sock, optval, optlen, len);
default:
return(-ENOPROTOOPT);
@@ -1385,6 +1385,20 @@ int sock_common_getsockopt(struct socket *sock, int level, int optname,
EXPORT_SYMBOL(sock_common_getsockopt);
+#ifdef CONFIG_COMPAT
+int compat_sock_common_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->sk_prot->compat_setsockopt != NULL)
+ return sk->sk_prot->compat_getsockopt(sk, level, optname,
+ optval, optlen);
+ return sk->sk_prot->getsockopt(sk, level, optname, optval, optlen);
+}
+EXPORT_SYMBOL(compat_sock_common_getsockopt);
+#endif
+
int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags)
{
@@ -1414,6 +1428,20 @@ int sock_common_setsockopt(struct socket *sock, int level, int optname,
EXPORT_SYMBOL(sock_common_setsockopt);
+#ifdef CONFIG_COMPAT
+int compat_sock_common_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int optlen)
+{
+ struct sock *sk = sock->sk;
+
+ if (sk->sk_prot->compat_setsockopt != NULL)
+ return sk->sk_prot->compat_setsockopt(sk, level, optname,
+ optval, optlen);
+ return sk->sk_prot->setsockopt(sk, level, optname, optval, optlen);
+}
+EXPORT_SYMBOL(compat_sock_common_setsockopt);
+#endif
+
void sk_common_release(struct sock *sk)
{
if (sk->sk_prot->destroy)
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index 2f278c8e4743..710453656721 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -26,6 +26,11 @@ extern int sysctl_core_destroy_delay;
extern char sysctl_divert_version[];
#endif /* CONFIG_NET_DIVERT */
+#ifdef CONFIG_XFRM
+extern u32 sysctl_xfrm_aevent_etime;
+extern u32 sysctl_xfrm_aevent_rseqth;
+#endif
+
ctl_table core_table[] = {
#ifdef CONFIG_NET
{
@@ -111,6 +116,24 @@ ctl_table core_table[] = {
.proc_handler = &proc_dostring
},
#endif /* CONFIG_NET_DIVERT */
+#ifdef CONFIG_XFRM
+ {
+ .ctl_name = NET_CORE_AEVENT_ETIME,
+ .procname = "xfrm_aevent_etime",
+ .data = &sysctl_xfrm_aevent_etime,
+ .maxlen = sizeof(u32),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+ {
+ .ctl_name = NET_CORE_AEVENT_RSEQTH,
+ .procname = "xfrm_aevent_rseqth",
+ .data = &sysctl_xfrm_aevent_rseqth,
+ .maxlen = sizeof(u32),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec
+ },
+#endif /* CONFIG_XFRM */
#endif /* CONFIG_NET */
{
.ctl_name = NET_CORE_SOMAXCONN,
diff --git a/net/core/wireless.c b/net/core/wireless.c
index 2add7ed609e9..81d6995fcfdb 100644
--- a/net/core/wireless.c
+++ b/net/core/wireless.c
@@ -2,7 +2,7 @@
* This file implement the Wireless Extensions APIs.
*
* Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com>
- * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
+ * Copyright (c) 1997-2006 Jean Tourrilhes, All Rights Reserved.
*
* (As all part of the Linux kernel, this file is GPL)
*/
@@ -65,6 +65,9 @@
* o Start deprecating dev->get_wireless_stats, output a warning
* o If IW_QUAL_DBM is set, show dBm values in /proc/net/wireless
* o Don't loose INVALID/DBM flags when clearing UPDATED flags (iwstats)
+ *
+ * v8 - 17.02.06 - Jean II
+ * o RtNetlink requests support (SET/GET)
*/
/***************************** INCLUDES *****************************/
@@ -89,11 +92,13 @@
/* Debugging stuff */
#undef WE_IOCTL_DEBUG /* Debug IOCTL API */
+#undef WE_RTNETLINK_DEBUG /* Debug RtNetlink API */
#undef WE_EVENT_DEBUG /* Debug Event dispatcher */
#undef WE_SPY_DEBUG /* Debug enhanced spy support */
/* Options */
-#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */
+//CONFIG_NET_WIRELESS_RTNETLINK /* Wireless requests over RtNetlink */
+#define WE_EVENT_RTNETLINK /* Propagate events using RtNetlink */
#define WE_SET_EVENT /* Generate an event on some set commands */
/************************* GLOBAL VARIABLES *************************/
@@ -156,13 +161,18 @@ static const struct iw_ioctl_description standard_ioctl[] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
- .header_type = IW_HEADER_TYPE_NULL,
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = sizeof(struct iw_priv_args),
+ .max_tokens = 16,
+ .flags = IW_DESCR_FLAG_NOMAX,
},
[SIOCSIWSTATS - SIOCIWFIRST] = {
.header_type = IW_HEADER_TYPE_NULL,
},
[SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
- .header_type = IW_HEADER_TYPE_NULL,
+ .header_type = IW_HEADER_TYPE_POINT,
+ .token_size = 1,
+ .max_tokens = sizeof(struct iw_statistics),
.flags = IW_DESCR_FLAG_DUMP,
},
[SIOCSIWSPY - SIOCIWFIRST] = {
@@ -529,6 +539,70 @@ static inline int adjust_priv_size(__u16 args,
return num * iw_priv_type_size[type];
}
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get wireless stats
+ * Allow programatic access to /proc/net/wireless even if /proc
+ * doesn't exist... Also more efficient...
+ */
+static int iw_handler_get_iwstats(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Get stats from the driver */
+ struct iw_statistics *stats;
+
+ stats = get_wireless_stats(dev);
+ if (stats != (struct iw_statistics *) NULL) {
+
+ /* Copy statistics to extra */
+ memcpy(extra, stats, sizeof(struct iw_statistics));
+ wrqu->data.length = sizeof(struct iw_statistics);
+
+ /* Check if we need to clear the updated flag */
+ if(wrqu->data.flags != 0)
+ stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
+ return 0;
+ } else
+ return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Standard Wireless Handler : get iwpriv definitions
+ * Export the driver private handler definition
+ * They will be picked up by tools like iwpriv...
+ */
+static int iw_handler_get_private(struct net_device * dev,
+ struct iw_request_info * info,
+ union iwreq_data * wrqu,
+ char * extra)
+{
+ /* Check if the driver has something to export */
+ if((dev->wireless_handlers->num_private_args == 0) ||
+ (dev->wireless_handlers->private_args == NULL))
+ return -EOPNOTSUPP;
+
+ /* Check if there is enough buffer up there */
+ if(wrqu->data.length < dev->wireless_handlers->num_private_args) {
+ /* User space can't know in advance how large the buffer
+ * needs to be. Give it a hint, so that we can support
+ * any size buffer we want somewhat efficiently... */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+ return -E2BIG;
+ }
+
+ /* Set the number of available ioctls. */
+ wrqu->data.length = dev->wireless_handlers->num_private_args;
+
+ /* Copy structure to the user buffer. */
+ memcpy(extra, dev->wireless_handlers->private_args,
+ sizeof(struct iw_priv_args) * wrqu->data.length);
+
+ return 0;
+}
+
/******************** /proc/net/wireless SUPPORT ********************/
/*
@@ -630,81 +704,14 @@ int __init wireless_proc_init(void)
/* ---------------------------------------------------------------- */
/*
- * Allow programatic access to /proc/net/wireless even if /proc
- * doesn't exist... Also more efficient...
- */
-static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr)
-{
- /* Get stats from the driver */
- struct iw_statistics *stats;
-
- stats = get_wireless_stats(dev);
- if (stats != (struct iw_statistics *) NULL) {
- struct iwreq * wrq = (struct iwreq *)ifr;
-
- /* Copy statistics to the user buffer */
- if(copy_to_user(wrq->u.data.pointer, stats,
- sizeof(struct iw_statistics)))
- return -EFAULT;
-
- /* Check if we need to clear the updated flag */
- if(wrq->u.data.flags != 0)
- stats->qual.updated &= ~IW_QUAL_ALL_UPDATED;
- return 0;
- } else
- return -EOPNOTSUPP;
-}
-
-/* ---------------------------------------------------------------- */
-/*
- * Export the driver private handler definition
- * They will be picked up by tools like iwpriv...
- */
-static inline int ioctl_export_private(struct net_device * dev,
- struct ifreq * ifr)
-{
- struct iwreq * iwr = (struct iwreq *) ifr;
-
- /* Check if the driver has something to export */
- if((dev->wireless_handlers->num_private_args == 0) ||
- (dev->wireless_handlers->private_args == NULL))
- return -EOPNOTSUPP;
-
- /* Check NULL pointer */
- if(iwr->u.data.pointer == NULL)
- return -EFAULT;
-
- /* Check if there is enough buffer up there */
- if(iwr->u.data.length < dev->wireless_handlers->num_private_args) {
- /* User space can't know in advance how large the buffer
- * needs to be. Give it a hint, so that we can support
- * any size buffer we want somewhat efficiently... */
- iwr->u.data.length = dev->wireless_handlers->num_private_args;
- return -E2BIG;
- }
-
- /* Set the number of available ioctls. */
- iwr->u.data.length = dev->wireless_handlers->num_private_args;
-
- /* Copy structure to the user buffer. */
- if (copy_to_user(iwr->u.data.pointer,
- dev->wireless_handlers->private_args,
- sizeof(struct iw_priv_args) * iwr->u.data.length))
- return -EFAULT;
-
- return 0;
-}
-
-/* ---------------------------------------------------------------- */
-/*
* Wrapper to call a standard Wireless Extension handler.
* We do various checks and also take care of moving data between
* user space and kernel space.
*/
-static inline int ioctl_standard_call(struct net_device * dev,
- struct ifreq * ifr,
- unsigned int cmd,
- iw_handler handler)
+static int ioctl_standard_call(struct net_device * dev,
+ struct ifreq * ifr,
+ unsigned int cmd,
+ iw_handler handler)
{
struct iwreq * iwr = (struct iwreq *) ifr;
const struct iw_ioctl_description * descr;
@@ -1048,14 +1055,20 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
{
case SIOCGIWSTATS:
/* Get Wireless Stats */
- return dev_iwstats(dev, ifr);
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ &iw_handler_get_iwstats);
case SIOCGIWPRIV:
/* Check if we have some wireless handlers defined */
if(dev->wireless_handlers != NULL) {
/* We export to user space the definition of
* the private handler ourselves */
- return ioctl_export_private(dev, ifr);
+ return ioctl_standard_call(dev,
+ ifr,
+ cmd,
+ &iw_handler_get_private);
}
// ## Fall-through for old API ##
default:
@@ -1088,16 +1101,739 @@ int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd)
return -EINVAL;
}
+/********************** RTNETLINK REQUEST API **********************/
+/*
+ * The alternate user space API to configure all those Wireless Extensions
+ * is through RtNetlink.
+ * This API support only the new driver API (iw_handler).
+ *
+ * This RtNetlink API use the same query/reply model as the ioctl API.
+ * Maximum effort has been done to fit in the RtNetlink model, and
+ * we support both RtNetlink Set and RtNelink Get operations.
+ * On the other hand, we don't offer Dump operations because of the
+ * following reasons :
+ * o Large number of parameters, most optional
+ * o Large size of some parameters (> 100 bytes)
+ * o Each parameters need to be extracted from hardware
+ * o Scan requests can take seconds and disable network activity.
+ * Because of this high cost/overhead, we want to return only the
+ * parameters the user application is really interested in.
+ * We could offer partial Dump using the IW_DESCR_FLAG_DUMP flag.
+ *
+ * The API uses the standard RtNetlink socket. When the RtNetlink code
+ * find a IFLA_WIRELESS field in a RtNetlink SET_LINK request,
+ * it calls here.
+ */
+
+#ifdef CONFIG_NET_WIRELESS_RTNETLINK
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension GET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static int rtnetlink_standard_get(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler,
+ char ** p_buf,
+ int * p_len)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ int hdr_len;
+ struct iw_request_info info;
+ char * buffer = NULL;
+ int buffer_size = 0;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ return -EOPNOTSUPP;
+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Check if wrqu is complete */
+ hdr_len = event_type_size[descr->header_type];
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have extra data in the reply or not */
+ if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+ /* Create the kernel buffer that we will return.
+ * It's at an offset to match the TYPE_POINT case... */
+ buffer_size = request_len + IW_EV_POINT_OFF;
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+ /* Copy event data */
+ memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+ /* Use our own copy of wrqu */
+ wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ + IW_EV_LCP_LEN);
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, NULL);
+
+ } else {
+ union iwreq_data wrqu_point;
+ char * extra = NULL;
+ int extra_size = 0;
+
+ /* Get a temp copy of wrqu (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ ((char *) request) + IW_EV_LCP_LEN,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+ /* Calculate space needed by arguments. Always allocate
+ * for max space. Easier, and won't last long... */
+ extra_size = descr->max_tokens * descr->token_size;
+ /* Support for very large requests */
+ if((descr->flags & IW_DESCR_FLAG_NOMAX) &&
+ (wrqu_point.data.length > descr->max_tokens))
+ extra_size = (wrqu_point.data.length
+ * descr->token_size);
+ buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+ dev->name, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Create the kernel buffer that we will return */
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Put wrqu in the right place (just before extra).
+ * Leave space for IWE header and dummy pointer...
+ * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ */
+ memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ ((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+ /* Extra comes logically after that. Offset +12 bytes. */
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+ /* Call the handler */
+ ret = handler(dev, &info, wrqu, extra);
+
+ /* Calculate real returned length */
+ extra_size = (wrqu->data.length * descr->token_size);
+ /* Re-adjust reply size */
+ request->len = extra_size + IW_EV_POINT_LEN;
+
+ /* Put the iwe header where it should, i.e. scrap the
+ * dummy pointer. */
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Check if there is enough buffer up there */
+ if(wrqu_point.data.length < wrqu->data.length)
+ ret = -E2BIG;
+ }
+
+ /* Return the buffer to the caller */
+ if (!ret) {
+ *p_buf = buffer;
+ *p_len = request->len;
+ } else {
+ /* Cleanup */
+ if(buffer)
+ kfree(buffer);
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a standard Wireless Extension SET handler.
+ * We do various checks and call the handler with the proper args.
+ */
+static inline int rtnetlink_standard_set(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler)
+{
+ const struct iw_ioctl_description * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ union iwreq_data wrqu_point;
+ int hdr_len;
+ char * extra = NULL;
+ int extra_size = 0;
+ struct iw_request_info info;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ if((cmd - SIOCIWFIRST) >= standard_ioctl_num)
+ return -EOPNOTSUPP;
+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found standard SET handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Extract fixed header from request. This is properly aligned. */
+ wrqu = &request->u;
+
+ /* Check if wrqu is complete */
+ hdr_len = event_type_size[descr->header_type];
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have extra data in the request or not */
+ if(descr->header_type != IW_HEADER_TYPE_POINT) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, NULL);
+
+ } else {
+ int extra_len;
+
+ /* Put wrqu in the right place (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ /* Don't forget about the event code... */
+ wrqu = &wrqu_point;
+
+ /* Check if number of token fits within bounds */
+ if(wrqu_point.data.length > descr->max_tokens)
+ return -E2BIG;
+ if(wrqu_point.data.length < descr->min_tokens)
+ return -EINVAL;
+
+ /* Real length of payload */
+ extra_len = wrqu_point.data.length * descr->token_size;
+
+ /* Check if request is self consistent */
+ if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra_size = descr->max_tokens * descr->token_size;
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (extra == NULL)
+ return -ENOMEM;
+
+ /* Copy extra in aligned buffer */
+ memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+ /* Call the handler */
+ ret = handler(dev, &info, &wrqu_point, extra);
+ }
+
+#ifdef WE_SET_EVENT
+ /* Generate an event to notify listeners of the change */
+ if((descr->flags & IW_DESCR_FLAG_EVENT) &&
+ ((ret == 0) || (ret == -EIWCOMMIT))) {
+ if(descr->flags & IW_DESCR_FLAG_RESTRICT)
+ /* If the event is restricted, don't
+ * export the payload */
+ wireless_send_event(dev, cmd, wrqu, NULL);
+ else
+ wireless_send_event(dev, cmd, wrqu, extra);
+ }
+#endif /* WE_SET_EVENT */
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ if(extra)
+ kfree(extra);
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension GET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_get(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler,
+ char ** p_buf,
+ int * p_len)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ int hdr_len;
+ struct iw_request_info info;
+ int extra_size = 0;
+ int i;
+ char * buffer = NULL;
+ int buffer_size = 0;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ dev->name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the max size of the get arguments */
+ extra_size = get_priv_size(descr->get_args);
+
+ /* Does it fits in wrqu ? */
+ if((descr->get_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ)) {
+ hdr_len = extra_size;
+ extra_size = 0;
+ } else {
+ hdr_len = IW_EV_POINT_LEN;
+ }
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* Create the kernel buffer that we will return.
+ * It's at an offset to match the TYPE_POINT case... */
+ buffer_size = request_len + IW_EV_POINT_OFF;
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+ /* Copy event data */
+ memcpy(buffer + IW_EV_POINT_OFF, request, request_len);
+ /* Use our own copy of wrqu */
+ wrqu = (union iwreq_data *) (buffer + IW_EV_POINT_OFF
+ + IW_EV_LCP_LEN);
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+ } else {
+ char * extra;
+
+ /* Buffer for full reply */
+ buffer_size = extra_size + IW_EV_POINT_LEN + IW_EV_POINT_OFF;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes (%d bytes)\n",
+ dev->name, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Create the kernel buffer that we will return */
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer == NULL) {
+ return -ENOMEM;
+ }
+
+ /* Put wrqu in the right place (just before extra).
+ * Leave space for IWE header and dummy pointer...
+ * Note that IW_EV_LCP_LEN==4 bytes, so it's still aligned...
+ */
+ memcpy(buffer + IW_EV_LCP_LEN + IW_EV_POINT_OFF,
+ ((char *) request) + IW_EV_LCP_LEN,
+ IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+ wrqu = (union iwreq_data *) (buffer + IW_EV_LCP_LEN);
+
+ /* Extra comes logically after that. Offset +12 bytes. */
+ extra = buffer + IW_EV_POINT_OFF + IW_EV_POINT_LEN;
+
+ /* Call the handler */
+ ret = handler(dev, &info, wrqu, extra);
+
+ /* Adjust for the actual length if it's variable,
+ * avoid leaking kernel bits outside. */
+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED))
+ extra_size = adjust_priv_size(descr->get_args, wrqu);
+ /* Re-adjust reply size */
+ request->len = extra_size + IW_EV_POINT_LEN;
+
+ /* Put the iwe header where it should, i.e. scrap the
+ * dummy pointer. */
+ memcpy(buffer + IW_EV_POINT_OFF, request, IW_EV_LCP_LEN);
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Reply 0x%04X, hdr_len %d, tokens %d, extra_size %d, buffer_size %d\n", dev->name, cmd, hdr_len, wrqu->data.length, extra_size, buffer_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ }
+
+ /* Return the buffer to the caller */
+ if (!ret) {
+ *p_buf = buffer;
+ *p_len = request->len;
+ } else {
+ /* Cleanup */
+ if(buffer)
+ kfree(buffer);
+ }
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Wrapper to call a private Wireless Extension SET handler.
+ * Same as above...
+ * It's not as nice and slimline as the standard wrapper. The cause
+ * is struct iw_priv_args, which was not really designed for the
+ * job we are going here.
+ *
+ * IMPORTANT : This function prevent to set and get data on the same
+ * IOCTL and enforce the SET/GET convention. Not doing it would be
+ * far too hairy...
+ * If you need to set and get data at the same time, please don't use
+ * a iw_handler but process it in your ioctl handler (i.e. use the
+ * old driver API).
+ */
+static inline int rtnetlink_private_set(struct net_device * dev,
+ struct iw_event * request,
+ int request_len,
+ iw_handler handler)
+{
+ const struct iw_priv_args * descr = NULL;
+ unsigned int cmd;
+ union iwreq_data * wrqu;
+ union iwreq_data wrqu_point;
+ int hdr_len;
+ char * extra = NULL;
+ int extra_size = 0;
+ int offset = 0; /* For sub-ioctls */
+ struct iw_request_info info;
+ int i;
+ int ret = -EINVAL;
+
+ /* Get the description of the Request */
+ cmd = request->cmd;
+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++)
+ if(cmd == dev->wireless_handlers->private_args[i].cmd) {
+ descr = &(dev->wireless_handlers->private_args[i]);
+ break;
+ }
+ if(descr == NULL)
+ return -EOPNOTSUPP;
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Found private handler for 0x%04X\n",
+ ifr->ifr_name, cmd);
+ printk(KERN_DEBUG "%s (WE.r) : Name %s, set %X, get %X\n",
+ dev->name, descr->name, descr->set_args, descr->get_args);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Compute the size of the set arguments */
+ /* Check for sub-ioctl handler */
+ if(descr->name[0] == '\0')
+ /* Reserve one int for sub-ioctl index */
+ offset = sizeof(__u32);
+
+ /* Size of set arguments */
+ extra_size = get_priv_size(descr->set_args);
+
+ /* Does it fits in wrqu ? */
+ if((descr->set_args & IW_PRIV_SIZE_FIXED) &&
+ (extra_size <= IFNAMSIZ)) {
+ hdr_len = IW_EV_LCP_LEN + extra_size;
+ extra_size = 0;
+ } else {
+ hdr_len = IW_EV_POINT_LEN;
+ }
+
+ /* Extract fixed header from request. This is properly aligned. */
+ wrqu = &request->u;
+
+ /* Check if wrqu is complete */
+ if(request_len < hdr_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG
+ "%s (WE.r) : Wireless request too short (%d)\n",
+ dev->name, request_len);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+ /* Prepare the call */
+ info.cmd = cmd;
+ info.flags = 0;
+
+ /* Check if we have a pointer to user space data or not. */
+ if(extra_size == 0) {
+
+ /* No extra arguments. Trivial to handle */
+ ret = handler(dev, &info, wrqu, (char *) wrqu);
+
+ } else {
+ int extra_len;
+
+ /* Put wrqu in the right place (skip pointer) */
+ memcpy(((char *) &wrqu_point) + IW_EV_POINT_OFF,
+ wrqu, IW_EV_POINT_LEN - IW_EV_LCP_LEN);
+
+ /* Does it fits within bounds ? */
+ if(wrqu_point.data.length > (descr->set_args &
+ IW_PRIV_SIZE_MASK))
+ return -E2BIG;
+
+ /* Real length of payload */
+ extra_len = adjust_priv_size(descr->set_args, &wrqu_point);
+
+ /* Check if request is self consistent */
+ if((request_len - hdr_len) < extra_len) {
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Wireless request data too short (%d)\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+ return -EINVAL;
+ }
+
+#ifdef WE_RTNETLINK_DEBUG
+ printk(KERN_DEBUG "%s (WE.r) : Malloc %d bytes\n",
+ dev->name, extra_size);
+#endif /* WE_RTNETLINK_DEBUG */
+
+ /* Always allocate for max space. Easier, and won't last
+ * long... */
+ extra = kmalloc(extra_size, GFP_KERNEL);
+ if (extra == NULL)
+ return -ENOMEM;
+
+ /* Copy extra in aligned buffer */
+ memcpy(extra, ((char *) request) + hdr_len, extra_len);
+
+ /* Call the handler */
+ ret = handler(dev, &info, &wrqu_point, extra);
+
+ /* Cleanup - I told you it wasn't that long ;-) */
+ kfree(extra);
+ }
+
+ /* Call commit handler if needed and defined */
+ if(ret == -EIWCOMMIT)
+ ret = call_commit_handler(dev);
+
+ return ret;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_getlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_get(struct net_device * dev,
+ char * data,
+ int len,
+ char ** p_buf,
+ int * p_len)
+{
+ struct iw_event * request = (struct iw_event *) data;
+ iw_handler handler;
+
+ /* Check length */
+ if(len < IW_EV_LCP_LEN) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+ dev->name, len);
+ return -EINVAL;
+ }
+
+ /* ReCheck length (len may have padding) */
+ if(request->len > len) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+ dev->name, request->len, len);
+ return -EINVAL;
+ }
+
+ /* Only accept GET requests in here */
+ if(!IW_IS_GET(request->cmd))
+ return -EOPNOTSUPP;
+
+ /* Special cases */
+ if(request->cmd == SIOCGIWSTATS)
+ /* Get Wireless Stats */
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ &iw_handler_get_iwstats,
+ p_buf, p_len);
+ if(request->cmd == SIOCGIWPRIV) {
+ /* Check if we have some wireless handlers defined */
+ if(dev->wireless_handlers == NULL)
+ return -EOPNOTSUPP;
+ /* Get Wireless Stats */
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ &iw_handler_get_private,
+ p_buf, p_len);
+ }
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* Try to find the handler */
+ handler = get_handler(dev, request->cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_get(dev,
+ request,
+ request->len,
+ handler,
+ p_buf, p_len);
+ else
+ return rtnetlink_private_get(dev,
+ request,
+ request->len,
+ handler,
+ p_buf, p_len);
+ }
+
+ return -EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+/*
+ * Main RtNetlink dispatcher. Called from the main networking code
+ * (do_setlink() in net/core/rtnetlink.c).
+ * Check the type of Request and call the appropriate wrapper...
+ */
+int wireless_rtnetlink_set(struct net_device * dev,
+ char * data,
+ int len)
+{
+ struct iw_event * request = (struct iw_event *) data;
+ iw_handler handler;
+
+ /* Check length */
+ if(len < IW_EV_LCP_LEN) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request too short (%d)\n",
+ dev->name, len);
+ return -EINVAL;
+ }
+
+ /* ReCheck length (len may have padding) */
+ if(request->len > len) {
+ printk(KERN_DEBUG "%s (WE.r) : RtNetlink request len invalid (%d-%d)\n",
+ dev->name, request->len, len);
+ return -EINVAL;
+ }
+
+ /* Only accept SET requests in here */
+ if(!IW_IS_SET(request->cmd))
+ return -EOPNOTSUPP;
+
+ /* Basic check */
+ if (!netif_device_present(dev))
+ return -ENODEV;
+
+ /* New driver API : try to find the handler */
+ handler = get_handler(dev, request->cmd);
+ if(handler != NULL) {
+ /* Standard and private are not the same */
+ if(request->cmd < SIOCIWFIRSTPRIV)
+ return rtnetlink_standard_set(dev,
+ request,
+ request->len,
+ handler);
+ else
+ return rtnetlink_private_set(dev,
+ request,
+ request->len,
+ handler);
+ }
+
+ return -EOPNOTSUPP;
+}
+#endif /* CONFIG_NET_WIRELESS_RTNETLINK */
+
+
/************************* EVENT PROCESSING *************************/
/*
* Process events generated by the wireless layer or the driver.
* Most often, the event will be propagated through rtnetlink
*/
-#ifdef WE_EVENT_NETLINK
-/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here.
- * It is declared in <linux/rtnetlink.h> */
-
+#ifdef WE_EVENT_RTNETLINK
/* ---------------------------------------------------------------- */
/*
* Fill a rtnetlink message with our event data.
@@ -1121,12 +1857,11 @@ static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb,
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
- r->ifi_flags = dev->flags;
+ r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0; /* Wireless changes don't affect those flags */
/* Add the wireless events in the netlink packet */
- RTA_PUT(skb, IFLA_WIRELESS,
- event_len, event);
+ RTA_PUT(skb, IFLA_WIRELESS, event_len, event);
nlh->nlmsg_len = skb->tail - b;
return skb->len;
@@ -1163,7 +1898,7 @@ static inline void rtmsg_iwinfo(struct net_device * dev,
NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
}
-#endif /* WE_EVENT_NETLINK */
+#endif /* WE_EVENT_RTNETLINK */
/* ---------------------------------------------------------------- */
/*
@@ -1255,10 +1990,10 @@ void wireless_send_event(struct net_device * dev,
if(extra != NULL)
memcpy(((char *) event) + hdr_len, extra, extra_len);
-#ifdef WE_EVENT_NETLINK
- /* rtnetlink event channel */
+#ifdef WE_EVENT_RTNETLINK
+ /* Send via the RtNetlink event channel */
rtmsg_iwinfo(dev, (char *) event, event_len);
-#endif /* WE_EVENT_NETLINK */
+#endif /* WE_EVENT_RTNETLINK */
/* Cleanup */
kfree(event);
diff --git a/net/dccp/Kconfig b/net/dccp/Kconfig
index 187ac182e24b..7e096ba8454f 100644
--- a/net/dccp/Kconfig
+++ b/net/dccp/Kconfig
@@ -24,6 +24,10 @@ config INET_DCCP_DIAG
def_tristate y if (IP_DCCP = y && INET_DIAG = y)
def_tristate m
+config IP_DCCP_ACKVEC
+ depends on IP_DCCP
+ def_bool N
+
source "net/dccp/ccids/Kconfig"
menu "DCCP Kernel Hacking"
@@ -36,15 +40,6 @@ config IP_DCCP_DEBUG
Just say N.
-config IP_DCCP_UNLOAD_HACK
- depends on IP_DCCP=m && IP_DCCP_CCID3=m
- bool "DCCP control sock unload hack"
- ---help---
- Enable this to be able to unload the dccp module when the it
- has only one refcount held, the control sock one. Just execute
- "rmmod dccp_ccid3 dccp"
-
- Just say N.
endmenu
endmenu
diff --git a/net/dccp/Makefile b/net/dccp/Makefile
index 87b27fff6e3b..7696e219b05d 100644
--- a/net/dccp/Makefile
+++ b/net/dccp/Makefile
@@ -2,15 +2,18 @@ obj-$(CONFIG_IPV6) += dccp_ipv6.o
dccp_ipv6-y := ipv6.o
-obj-$(CONFIG_IP_DCCP) += dccp.o
+obj-$(CONFIG_IP_DCCP) += dccp.o dccp_ipv4.o
-dccp-y := ccid.o input.o ipv4.o minisocks.o options.o output.o proto.o \
- timer.o
+dccp-y := ccid.o feat.o input.o minisocks.o options.o output.o proto.o timer.o
+
+dccp_ipv4-y := ipv4.o
dccp-$(CONFIG_IP_DCCP_ACKVEC) += ackvec.o
obj-$(CONFIG_INET_DCCP_DIAG) += dccp_diag.o
+dccp-$(CONFIG_SYSCTL) += sysctl.o
+
dccp_diag-y := diag.o
obj-y += ccids/
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index 2c77dafbd091..b5981e5f6b00 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -13,36 +13,83 @@
#include "dccp.h"
#include <linux/dccp.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
#include <linux/skbuff.h>
+#include <linux/slab.h>
#include <net/sock.h>
+static kmem_cache_t *dccp_ackvec_slab;
+static kmem_cache_t *dccp_ackvec_record_slab;
+
+static struct dccp_ackvec_record *dccp_ackvec_record_new(void)
+{
+ struct dccp_ackvec_record *avr =
+ kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC);
+
+ if (avr != NULL)
+ INIT_LIST_HEAD(&avr->dccpavr_node);
+
+ return avr;
+}
+
+static void dccp_ackvec_record_delete(struct dccp_ackvec_record *avr)
+{
+ if (unlikely(avr == NULL))
+ return;
+ /* Check if deleting a linked record */
+ WARN_ON(!list_empty(&avr->dccpavr_node));
+ kmem_cache_free(dccp_ackvec_record_slab, avr);
+}
+
+static void dccp_ackvec_insert_avr(struct dccp_ackvec *av,
+ struct dccp_ackvec_record *avr)
+{
+ /*
+ * AVRs are sorted by seqno. Since we are sending them in order, we
+ * just add the AVR at the head of the list.
+ * -sorbo.
+ */
+ if (!list_empty(&av->dccpav_records)) {
+ const struct dccp_ackvec_record *head =
+ list_entry(av->dccpav_records.next,
+ struct dccp_ackvec_record,
+ dccpavr_node);
+ BUG_ON(before48(avr->dccpavr_ack_seqno,
+ head->dccpavr_ack_seqno));
+ }
+
+ list_add(&avr->dccpavr_node, &av->dccpav_records);
+}
+
int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
+#ifdef CONFIG_IP_DCCP_DEBUG
+ const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ?
+ "CLIENT tx: " : "server tx: ";
+#endif
struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec;
int len = av->dccpav_vec_len + 2;
struct timeval now;
u32 elapsed_time;
unsigned char *to, *from;
+ struct dccp_ackvec_record *avr;
+
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
+ return -1;
dccp_timestamp(sk, &now);
elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10;
- if (elapsed_time != 0)
- dccp_insert_option_elapsed_time(sk, skb, elapsed_time);
-
- if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
+ if (elapsed_time != 0 &&
+ dccp_insert_option_elapsed_time(sk, skb, elapsed_time))
return -1;
- /*
- * XXX: now we have just one ack vector sent record, so
- * we have to wait for it to be cleared.
- *
- * Of course this is not acceptable, but this is just for
- * basic testing now.
- */
- if (av->dccpav_ack_seqno != DCCP_MAX_SEQNO + 1)
+ avr = dccp_ackvec_record_new();
+ if (avr == NULL)
return -1;
DCCP_SKB_CB(skb)->dccpd_opt_len += len;
@@ -55,8 +102,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
from = av->dccpav_buf + av->dccpav_buf_head;
/* Check if buf_head wraps */
- if ((int)av->dccpav_buf_head + len > av->dccpav_vec_len) {
- const u32 tailsize = av->dccpav_vec_len - av->dccpav_buf_head;
+ if ((int)av->dccpav_buf_head + len > DCCP_MAX_ACKVEC_LEN) {
+ const u32 tailsize = DCCP_MAX_ACKVEC_LEN - av->dccpav_buf_head;
memcpy(to, from, tailsize);
to += tailsize;
@@ -73,45 +120,37 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb)
* sequence number it used for the ack packet; ack_ptr will equal
* buf_head; ack_ackno will equal buf_ackno; and ack_nonce will
* equal buf_nonce.
- *
- * This implemention uses just one ack record for now.
*/
- av->dccpav_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
- av->dccpav_ack_ptr = av->dccpav_buf_head;
- av->dccpav_ack_ackno = av->dccpav_buf_ackno;
- av->dccpav_ack_nonce = av->dccpav_buf_nonce;
- av->dccpav_sent_len = av->dccpav_vec_len;
+ avr->dccpavr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq;
+ avr->dccpavr_ack_ptr = av->dccpav_buf_head;
+ avr->dccpavr_ack_ackno = av->dccpav_buf_ackno;
+ avr->dccpavr_ack_nonce = av->dccpav_buf_nonce;
+ avr->dccpavr_sent_len = av->dccpav_vec_len;
+
+ dccp_ackvec_insert_avr(av, avr);
dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, "
"ack_ackno=%llu\n",
- debug_prefix, av->dccpav_sent_len,
- (unsigned long long)av->dccpav_ack_seqno,
- (unsigned long long)av->dccpav_ack_ackno);
- return -1;
+ debug_prefix, avr->dccpavr_sent_len,
+ (unsigned long long)avr->dccpavr_ack_seqno,
+ (unsigned long long)avr->dccpavr_ack_ackno);
+ return 0;
}
-struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len,
- const gfp_t priority)
+struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority)
{
- struct dccp_ackvec *av;
-
- BUG_ON(len == 0);
+ struct dccp_ackvec *av = kmem_cache_alloc(dccp_ackvec_slab, priority);
- if (len > DCCP_MAX_ACKVEC_LEN)
- return NULL;
-
- av = kmalloc(sizeof(*av) + len, priority);
if (av != NULL) {
- av->dccpav_buf_len = len;
av->dccpav_buf_head =
- av->dccpav_buf_tail = av->dccpav_buf_len - 1;
- av->dccpav_buf_ackno =
- av->dccpav_ack_ackno = av->dccpav_ack_seqno = ~0LLU;
+ av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1;
+ av->dccpav_buf_ackno = DCCP_MAX_SEQNO + 1;
av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0;
av->dccpav_ack_ptr = 0;
av->dccpav_time.tv_sec = 0;
av->dccpav_time.tv_usec = 0;
av->dccpav_sent_len = av->dccpav_vec_len = 0;
+ INIT_LIST_HEAD(&av->dccpav_records);
}
return av;
@@ -119,7 +158,20 @@ struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len,
void dccp_ackvec_free(struct dccp_ackvec *av)
{
- kfree(av);
+ if (unlikely(av == NULL))
+ return;
+
+ if (!list_empty(&av->dccpav_records)) {
+ struct dccp_ackvec_record *avr, *next;
+
+ list_for_each_entry_safe(avr, next, &av->dccpav_records,
+ dccpavr_node) {
+ list_del_init(&avr->dccpavr_node);
+ dccp_ackvec_record_delete(avr);
+ }
+ }
+
+ kmem_cache_free(dccp_ackvec_slab, av);
}
static inline u8 dccp_ackvec_state(const struct dccp_ackvec *av,
@@ -146,7 +198,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av,
unsigned int gap;
long new_head;
- if (av->dccpav_vec_len + packets > av->dccpav_buf_len)
+ if (av->dccpav_vec_len + packets > DCCP_MAX_ACKVEC_LEN)
return -ENOBUFS;
gap = packets - 1;
@@ -158,7 +210,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av,
gap + new_head + 1);
gap = -new_head;
}
- new_head += av->dccpav_buf_len;
+ new_head += DCCP_MAX_ACKVEC_LEN;
}
av->dccpav_buf_head = new_head;
@@ -251,7 +303,7 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,
goto out_duplicate;
delta -= len + 1;
- if (++index == av->dccpav_buf_len)
+ if (++index == DCCP_MAX_ACKVEC_LEN)
index = 0;
}
}
@@ -259,7 +311,6 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,
av->dccpav_buf_ackno = ackno;
dccp_timestamp(sk, &av->dccpav_time);
out:
- dccp_pr_debug("");
return 0;
out_duplicate:
@@ -297,44 +348,50 @@ void dccp_ackvec_print(const struct dccp_ackvec *av)
}
#endif
-static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av)
+static void dccp_ackvec_throw_record(struct dccp_ackvec *av,
+ struct dccp_ackvec_record *avr)
{
- /*
- * As we're keeping track of the ack vector size (dccpav_vec_len) and
- * the sent ack vector size (dccpav_sent_len) we don't need
- * dccpav_buf_tail at all, but keep this code here as in the future
- * we'll implement a vector of ack records, as suggested in
- * draft-ietf-dccp-spec-11.txt Appendix A. -acme
- */
-#if 0
- u32 new_buf_tail = av->dccpav_ack_ptr + 1;
- if (new_buf_tail >= av->dccpav_vec_len)
- new_buf_tail -= av->dccpav_vec_len;
- av->dccpav_buf_tail = new_buf_tail;
-#endif
- av->dccpav_vec_len -= av->dccpav_sent_len;
+ struct dccp_ackvec_record *next;
+
+ av->dccpav_buf_tail = avr->dccpavr_ack_ptr - 1;
+ if (av->dccpav_buf_tail == 0)
+ av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1;
+
+ av->dccpav_vec_len -= avr->dccpavr_sent_len;
+
+ /* free records */
+ list_for_each_entry_safe_from(avr, next, &av->dccpav_records,
+ dccpavr_node) {
+ list_del_init(&avr->dccpavr_node);
+ dccp_ackvec_record_delete(avr);
+ }
}
void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk,
const u64 ackno)
{
- /* Check if we actually sent an ACK vector */
- if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1)
- return;
+ struct dccp_ackvec_record *avr;
- if (ackno == av->dccpav_ack_seqno) {
+ /*
+ * If we traverse backwards, it should be faster when we have large
+ * windows. We will be receiving ACKs for stuff we sent a while back
+ * -sorbo.
+ */
+ list_for_each_entry_reverse(avr, &av->dccpav_records, dccpavr_node) {
+ if (ackno == avr->dccpavr_ack_seqno) {
#ifdef CONFIG_IP_DCCP_DEBUG
- struct dccp_sock *dp = dccp_sk(sk);
- const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ?
- "CLIENT rx ack: " : "server rx ack: ";
+ struct dccp_sock *dp = dccp_sk(sk);
+ const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ?
+ "CLIENT rx ack: " : "server rx ack: ";
#endif
- dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
- "ack_ackno=%llu, ACKED!\n",
- debug_prefix, 1,
- (unsigned long long)av->dccpav_ack_seqno,
- (unsigned long long)av->dccpav_ack_ackno);
- dccp_ackvec_throw_away_ack_record(av);
- av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
+ dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, "
+ "ack_ackno=%llu, ACKED!\n",
+ debug_prefix, 1,
+ (unsigned long long)avr->dccpavr_ack_seqno,
+ (unsigned long long)avr->dccpavr_ack_ackno);
+ dccp_ackvec_throw_record(av, avr);
+ break;
+ }
}
}
@@ -344,28 +401,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av,
const unsigned char *vector)
{
unsigned char i;
+ struct dccp_ackvec_record *avr;
/* Check if we actually sent an ACK vector */
- if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1)
- return;
- /*
- * We're in the receiver half connection, so if the received an ACK
- * vector ackno (e.g. 50) before dccpav_ack_seqno (e.g. 52), we're
- * not interested.
- *
- * Extra explanation with example:
- *
- * if we received an ACK vector with ackno 50, it can only be acking
- * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent).
- */
- /* dccp_pr_debug("is %llu < %llu? ", ackno, av->dccpav_ack_seqno); */
- if (before48(ackno, av->dccpav_ack_seqno)) {
- /* dccp_pr_debug_cat("yes\n"); */
+ if (list_empty(&av->dccpav_records))
return;
- }
- /* dccp_pr_debug_cat("no\n"); */
i = len;
+ /*
+ * XXX
+ * I think it might be more efficient to work backwards. See comment on
+ * rcv_ackno. -sorbo.
+ */
+ avr = list_entry(av->dccpav_records.next, struct dccp_ackvec_record,
+ dccpavr_node);
while (i--) {
const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK;
u64 ackno_end_rl;
@@ -373,14 +422,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av,
dccp_set_seqno(&ackno_end_rl, ackno - rl);
/*
- * dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl,
- * av->dccpav_ack_seqno, ackno);
+ * If our AVR sequence number is greater than the ack, go
+ * forward in the AVR list until it is not so.
*/
- if (between48(av->dccpav_ack_seqno, ackno_end_rl, ackno)) {
+ list_for_each_entry_from(avr, &av->dccpav_records,
+ dccpavr_node) {
+ if (!after48(avr->dccpavr_ack_seqno, ackno))
+ goto found;
+ }
+ /* End of the dccpav_records list, not found, exit */
+ break;
+found:
+ if (between48(avr->dccpavr_ack_seqno, ackno_end_rl, ackno)) {
const u8 state = (*vector &
DCCP_ACKVEC_STATE_MASK) >> 6;
- /* dccp_pr_debug_cat("yes\n"); */
-
if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED) {
#ifdef CONFIG_IP_DCCP_DEBUG
struct dccp_sock *dp = dccp_sk(sk);
@@ -393,19 +448,16 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av,
"ACKED!\n",
debug_prefix, len,
(unsigned long long)
- av->dccpav_ack_seqno,
+ avr->dccpavr_ack_seqno,
(unsigned long long)
- av->dccpav_ack_ackno);
- dccp_ackvec_throw_away_ack_record(av);
+ avr->dccpavr_ack_ackno);
+ dccp_ackvec_throw_record(av, avr);
}
/*
- * If dccpav_ack_seqno was not received, no problem
- * we'll send another ACK vector.
+ * If it wasn't received, continue scanning... we might
+ * find another one.
*/
- av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1;
- break;
}
- /* dccp_pr_debug_cat("no\n"); */
dccp_set_seqno(&ackno, ackno_end_rl - 1);
++vector;
@@ -424,3 +476,43 @@ int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb,
len, value);
return 0;
}
+
+static char dccp_ackvec_slab_msg[] __initdata =
+ KERN_CRIT "DCCP: Unable to create ack vectors slab caches\n";
+
+int __init dccp_ackvec_init(void)
+{
+ dccp_ackvec_slab = kmem_cache_create("dccp_ackvec",
+ sizeof(struct dccp_ackvec), 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (dccp_ackvec_slab == NULL)
+ goto out_err;
+
+ dccp_ackvec_record_slab =
+ kmem_cache_create("dccp_ackvec_record",
+ sizeof(struct dccp_ackvec_record),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (dccp_ackvec_record_slab == NULL)
+ goto out_destroy_slab;
+
+ return 0;
+
+out_destroy_slab:
+ kmem_cache_destroy(dccp_ackvec_slab);
+ dccp_ackvec_slab = NULL;
+out_err:
+ printk(dccp_ackvec_slab_msg);
+ return -ENOBUFS;
+}
+
+void dccp_ackvec_exit(void)
+{
+ if (dccp_ackvec_slab != NULL) {
+ kmem_cache_destroy(dccp_ackvec_slab);
+ dccp_ackvec_slab = NULL;
+ }
+ if (dccp_ackvec_record_slab != NULL) {
+ kmem_cache_destroy(dccp_ackvec_record_slab);
+ dccp_ackvec_record_slab = NULL;
+ }
+}
diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h
index f7dfb5f67b87..ec7a89bb7b39 100644
--- a/net/dccp/ackvec.h
+++ b/net/dccp/ackvec.h
@@ -13,6 +13,7 @@
#include <linux/config.h>
#include <linux/compiler.h>
+#include <linux/list.h>
#include <linux/time.h>
#include <linux/types.h>
@@ -42,39 +43,57 @@
* Ack Vectors it has recently sent. For each packet sent carrying an
* Ack Vector, it remembers four variables:
*
- * @dccpav_ack_seqno - the Sequence Number used for the packet
- * (HC-Receiver seqno)
* @dccpav_ack_ptr - the value of buf_head at the time of acknowledgement.
- * @dccpav_ack_ackno - the Acknowledgement Number used for the packet
- * (HC-Sender seqno)
+ * @dccpav_records - list of dccp_ackvec_record
* @dccpav_ack_nonce - the one-bit sum of the ECN Nonces for all State 0.
*
- * @dccpav_buf_len - circular buffer length
* @dccpav_time - the time in usecs
* @dccpav_buf - circular buffer of acknowledgeable packets
*/
struct dccp_ackvec {
u64 dccpav_buf_ackno;
- u64 dccpav_ack_seqno;
- u64 dccpav_ack_ackno;
+ struct list_head dccpav_records;
struct timeval dccpav_time;
u8 dccpav_buf_head;
u8 dccpav_buf_tail;
u8 dccpav_ack_ptr;
u8 dccpav_sent_len;
u8 dccpav_vec_len;
- u8 dccpav_buf_len;
u8 dccpav_buf_nonce;
u8 dccpav_ack_nonce;
- u8 dccpav_buf[0];
+ u8 dccpav_buf[DCCP_MAX_ACKVEC_LEN];
+};
+
+/** struct dccp_ackvec_record - ack vector record
+ *
+ * ACK vector record as defined in Appendix A of spec.
+ *
+ * The list is sorted by dccpavr_ack_seqno
+ *
+ * @dccpavr_node - node in dccpav_records
+ * @dccpavr_ack_seqno - sequence number of the packet this record was sent on
+ * @dccpavr_ack_ackno - sequence number being acknowledged
+ * @dccpavr_ack_ptr - pointer into dccpav_buf where this record starts
+ * @dccpavr_ack_nonce - dccpav_ack_nonce at the time this record was sent
+ * @dccpavr_sent_len - lenght of the record in dccpav_buf
+ */
+struct dccp_ackvec_record {
+ struct list_head dccpavr_node;
+ u64 dccpavr_ack_seqno;
+ u64 dccpavr_ack_ackno;
+ u8 dccpavr_ack_ptr;
+ u8 dccpavr_ack_nonce;
+ u8 dccpavr_sent_len;
};
struct sock;
struct sk_buff;
#ifdef CONFIG_IP_DCCP_ACKVEC
-extern struct dccp_ackvec *dccp_ackvec_alloc(unsigned int len,
- const gfp_t priority);
+extern int dccp_ackvec_init(void);
+extern void dccp_ackvec_exit(void);
+
+extern struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority);
extern void dccp_ackvec_free(struct dccp_ackvec *av);
extern int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,
@@ -92,8 +111,16 @@ static inline int dccp_ackvec_pending(const struct dccp_ackvec *av)
return av->dccpav_sent_len != av->dccpav_vec_len;
}
#else /* CONFIG_IP_DCCP_ACKVEC */
-static inline struct dccp_ackvec *dccp_ackvec_alloc(unsigned int len,
- const gfp_t priority)
+static inline int dccp_ackvec_init(void)
+{
+ return 0;
+}
+
+static inline void dccp_ackvec_exit(void)
+{
+}
+
+static inline struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority)
{
return NULL;
}
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c
index 9d8fc0e289ea..ff05e59043cd 100644
--- a/net/dccp/ccid.c
+++ b/net/dccp/ccid.c
@@ -13,7 +13,7 @@
#include "ccid.h"
-static struct ccid *ccids[CCID_MAX];
+static struct ccid_operations *ccids[CCID_MAX];
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT)
static atomic_t ccids_lockct = ATOMIC_INIT(0);
static DEFINE_SPINLOCK(ccids_lock);
@@ -55,85 +55,202 @@ static inline void ccids_read_unlock(void)
#define ccids_read_unlock() do { } while(0)
#endif
-int ccid_register(struct ccid *ccid)
+static kmem_cache_t *ccid_kmem_cache_create(int obj_size, const char *fmt,...)
{
- int err;
+ kmem_cache_t *slab;
+ char slab_name_fmt[32], *slab_name;
+ va_list args;
- if (ccid->ccid_init == NULL)
- return -1;
+ va_start(args, fmt);
+ vsnprintf(slab_name_fmt, sizeof(slab_name_fmt), fmt, args);
+ va_end(args);
+
+ slab_name = kstrdup(slab_name_fmt, GFP_KERNEL);
+ if (slab_name == NULL)
+ return NULL;
+ slab = kmem_cache_create(slab_name, sizeof(struct ccid) + obj_size, 0,
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
+ if (slab == NULL)
+ kfree(slab_name);
+ return slab;
+}
+
+static void ccid_kmem_cache_destroy(kmem_cache_t *slab)
+{
+ if (slab != NULL) {
+ const char *name = kmem_cache_name(slab);
+
+ kmem_cache_destroy(slab);
+ kfree(name);
+ }
+}
+
+int ccid_register(struct ccid_operations *ccid_ops)
+{
+ int err = -ENOBUFS;
+
+ ccid_ops->ccid_hc_rx_slab =
+ ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
+ "%s_hc_rx_sock",
+ ccid_ops->ccid_name);
+ if (ccid_ops->ccid_hc_rx_slab == NULL)
+ goto out;
+
+ ccid_ops->ccid_hc_tx_slab =
+ ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
+ "%s_hc_tx_sock",
+ ccid_ops->ccid_name);
+ if (ccid_ops->ccid_hc_tx_slab == NULL)
+ goto out_free_rx_slab;
ccids_write_lock();
err = -EEXIST;
- if (ccids[ccid->ccid_id] == NULL) {
- ccids[ccid->ccid_id] = ccid;
+ if (ccids[ccid_ops->ccid_id] == NULL) {
+ ccids[ccid_ops->ccid_id] = ccid_ops;
err = 0;
}
ccids_write_unlock();
- if (err == 0)
- pr_info("CCID: Registered CCID %d (%s)\n",
- ccid->ccid_id, ccid->ccid_name);
+ if (err != 0)
+ goto out_free_tx_slab;
+
+ pr_info("CCID: Registered CCID %d (%s)\n",
+ ccid_ops->ccid_id, ccid_ops->ccid_name);
+out:
return err;
+out_free_tx_slab:
+ ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
+ ccid_ops->ccid_hc_tx_slab = NULL;
+ goto out;
+out_free_rx_slab:
+ ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
+ ccid_ops->ccid_hc_rx_slab = NULL;
+ goto out;
}
EXPORT_SYMBOL_GPL(ccid_register);
-int ccid_unregister(struct ccid *ccid)
+int ccid_unregister(struct ccid_operations *ccid_ops)
{
ccids_write_lock();
- ccids[ccid->ccid_id] = NULL;
+ ccids[ccid_ops->ccid_id] = NULL;
ccids_write_unlock();
+
+ ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
+ ccid_ops->ccid_hc_tx_slab = NULL;
+ ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
+ ccid_ops->ccid_hc_rx_slab = NULL;
+
pr_info("CCID: Unregistered CCID %d (%s)\n",
- ccid->ccid_id, ccid->ccid_name);
+ ccid_ops->ccid_id, ccid_ops->ccid_name);
return 0;
}
EXPORT_SYMBOL_GPL(ccid_unregister);
-struct ccid *ccid_init(unsigned char id, struct sock *sk)
+struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp)
{
- struct ccid *ccid;
+ struct ccid_operations *ccid_ops;
+ struct ccid *ccid = NULL;
+ ccids_read_lock();
#ifdef CONFIG_KMOD
- if (ccids[id] == NULL)
+ if (ccids[id] == NULL) {
+ /* We only try to load if in process context */
+ ccids_read_unlock();
+ if (gfp & GFP_ATOMIC)
+ goto out;
request_module("net-dccp-ccid-%d", id);
+ ccids_read_lock();
+ }
#endif
- ccids_read_lock();
+ ccid_ops = ccids[id];
+ if (ccid_ops == NULL)
+ goto out_unlock;
- ccid = ccids[id];
- if (ccid == NULL)
- goto out;
+ if (!try_module_get(ccid_ops->ccid_owner))
+ goto out_unlock;
- if (!try_module_get(ccid->ccid_owner))
- goto out_err;
+ ccids_read_unlock();
- if (ccid->ccid_init(sk) != 0)
+ ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
+ ccid_ops->ccid_hc_tx_slab, gfp);
+ if (ccid == NULL)
goto out_module_put;
+ ccid->ccid_ops = ccid_ops;
+ if (rx) {
+ memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
+ if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
+ ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
+ goto out_free_ccid;
+ } else {
+ memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
+ if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
+ ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
+ goto out_free_ccid;
+ }
out:
- ccids_read_unlock();
return ccid;
-out_module_put:
- module_put(ccid->ccid_owner);
-out_err:
+out_unlock:
+ ccids_read_unlock();
+ goto out;
+out_free_ccid:
+ kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
+ ccid_ops->ccid_hc_tx_slab, ccid);
ccid = NULL;
+out_module_put:
+ module_put(ccid_ops->ccid_owner);
goto out;
}
-EXPORT_SYMBOL_GPL(ccid_init);
+EXPORT_SYMBOL_GPL(ccid_new);
+
+struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp)
+{
+ return ccid_new(id, sk, 1, gfp);
+}
+
+EXPORT_SYMBOL_GPL(ccid_hc_rx_new);
+
+struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp)
+{
+ return ccid_new(id, sk, 0, gfp);
+}
+
+EXPORT_SYMBOL_GPL(ccid_hc_tx_new);
-void ccid_exit(struct ccid *ccid, struct sock *sk)
+static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx)
{
+ struct ccid_operations *ccid_ops;
+
if (ccid == NULL)
return;
+ ccid_ops = ccid->ccid_ops;
+ if (rx) {
+ if (ccid_ops->ccid_hc_rx_exit != NULL)
+ ccid_ops->ccid_hc_rx_exit(sk);
+ kmem_cache_free(ccid_ops->ccid_hc_rx_slab, ccid);
+ } else {
+ if (ccid_ops->ccid_hc_tx_exit != NULL)
+ ccid_ops->ccid_hc_tx_exit(sk);
+ kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid);
+ }
ccids_read_lock();
+ if (ccids[ccid_ops->ccid_id] != NULL)
+ module_put(ccid_ops->ccid_owner);
+ ccids_read_unlock();
+}
- if (ccids[ccid->ccid_id] != NULL) {
- if (ccid->ccid_exit != NULL)
- ccid->ccid_exit(sk);
- module_put(ccid->ccid_owner);
- }
+void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
+{
+ ccid_delete(ccid, sk, 1);
+}
- ccids_read_unlock();
+EXPORT_SYMBOL_GPL(ccid_hc_rx_delete);
+
+void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
+{
+ ccid_delete(ccid, sk, 0);
}
-EXPORT_SYMBOL_GPL(ccid_exit);
+EXPORT_SYMBOL_GPL(ccid_hc_tx_delete);
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index de681c6ad081..f7eb6c613414 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -23,14 +23,16 @@
struct tcp_info;
-struct ccid {
+struct ccid_operations {
unsigned char ccid_id;
const char *ccid_name;
struct module *ccid_owner;
- int (*ccid_init)(struct sock *sk);
- void (*ccid_exit)(struct sock *sk);
- int (*ccid_hc_rx_init)(struct sock *sk);
- int (*ccid_hc_tx_init)(struct sock *sk);
+ kmem_cache_t *ccid_hc_rx_slab;
+ __u32 ccid_hc_rx_obj_size;
+ kmem_cache_t *ccid_hc_tx_slab;
+ __u32 ccid_hc_tx_obj_size;
+ int (*ccid_hc_rx_init)(struct ccid *ccid, struct sock *sk);
+ int (*ccid_hc_tx_init)(struct ccid *ccid, struct sock *sk);
void (*ccid_hc_rx_exit)(struct sock *sk);
void (*ccid_hc_tx_exit)(struct sock *sk);
void (*ccid_hc_rx_packet_recv)(struct sock *sk,
@@ -39,9 +41,9 @@ struct ccid {
unsigned char option,
unsigned char len, u16 idx,
unsigned char* value);
- void (*ccid_hc_rx_insert_options)(struct sock *sk,
+ int (*ccid_hc_rx_insert_options)(struct sock *sk,
struct sk_buff *skb);
- void (*ccid_hc_tx_insert_options)(struct sock *sk,
+ int (*ccid_hc_tx_insert_options)(struct sock *sk,
struct sk_buff *skb);
void (*ccid_hc_tx_packet_recv)(struct sock *sk,
struct sk_buff *skb);
@@ -67,75 +69,58 @@ struct ccid {
int __user *optlen);
};
-extern int ccid_register(struct ccid *ccid);
-extern int ccid_unregister(struct ccid *ccid);
+extern int ccid_register(struct ccid_operations *ccid_ops);
+extern int ccid_unregister(struct ccid_operations *ccid_ops);
-extern struct ccid *ccid_init(unsigned char id, struct sock *sk);
-extern void ccid_exit(struct ccid *ccid, struct sock *sk);
+struct ccid {
+ struct ccid_operations *ccid_ops;
+ char ccid_priv[0];
+};
-static inline void __ccid_get(struct ccid *ccid)
+static inline void *ccid_priv(const struct ccid *ccid)
{
- __module_get(ccid->ccid_owner);
+ return (void *)ccid->ccid_priv;
}
+extern struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx,
+ gfp_t gfp);
+
+extern struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk,
+ gfp_t gfp);
+extern struct ccid *ccid_hc_tx_new(unsigned char id, struct sock *sk,
+ gfp_t gfp);
+
+extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk);
+extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk);
+
static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk,
struct sk_buff *skb, int len)
{
int rc = 0;
- if (ccid->ccid_hc_tx_send_packet != NULL)
- rc = ccid->ccid_hc_tx_send_packet(sk, skb, len);
+ if (ccid->ccid_ops->ccid_hc_tx_send_packet != NULL)
+ rc = ccid->ccid_ops->ccid_hc_tx_send_packet(sk, skb, len);
return rc;
}
static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk,
int more, int len)
{
- if (ccid->ccid_hc_tx_packet_sent != NULL)
- ccid->ccid_hc_tx_packet_sent(sk, more, len);
-}
-
-static inline int ccid_hc_rx_init(struct ccid *ccid, struct sock *sk)
-{
- int rc = 0;
- if (ccid->ccid_hc_rx_init != NULL)
- rc = ccid->ccid_hc_rx_init(sk);
- return rc;
-}
-
-static inline int ccid_hc_tx_init(struct ccid *ccid, struct sock *sk)
-{
- int rc = 0;
- if (ccid->ccid_hc_tx_init != NULL)
- rc = ccid->ccid_hc_tx_init(sk);
- return rc;
-}
-
-static inline void ccid_hc_rx_exit(struct ccid *ccid, struct sock *sk)
-{
- if (ccid != NULL && ccid->ccid_hc_rx_exit != NULL &&
- dccp_sk(sk)->dccps_hc_rx_ccid_private != NULL)
- ccid->ccid_hc_rx_exit(sk);
-}
-
-static inline void ccid_hc_tx_exit(struct ccid *ccid, struct sock *sk)
-{
- if (ccid != NULL && ccid->ccid_hc_tx_exit != NULL &&
- dccp_sk(sk)->dccps_hc_tx_ccid_private != NULL)
- ccid->ccid_hc_tx_exit(sk);
+ if (ccid->ccid_ops->ccid_hc_tx_packet_sent != NULL)
+ ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, more, len);
}
static inline void ccid_hc_rx_packet_recv(struct ccid *ccid, struct sock *sk,
struct sk_buff *skb)
{
- if (ccid->ccid_hc_rx_packet_recv != NULL)
- ccid->ccid_hc_rx_packet_recv(sk, skb);
+ if (ccid->ccid_ops->ccid_hc_rx_packet_recv != NULL)
+ ccid->ccid_ops->ccid_hc_rx_packet_recv(sk, skb);
}
static inline void ccid_hc_tx_packet_recv(struct ccid *ccid, struct sock *sk,
struct sk_buff *skb)
{
- if (ccid->ccid_hc_tx_packet_recv != NULL)
- ccid->ccid_hc_tx_packet_recv(sk, skb);
+ if (ccid->ccid_ops->ccid_hc_tx_packet_recv != NULL)
+ ccid->ccid_ops->ccid_hc_tx_packet_recv(sk, skb);
}
static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk,
@@ -144,8 +129,8 @@ static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk,
unsigned char* value)
{
int rc = 0;
- if (ccid->ccid_hc_tx_parse_options != NULL)
- rc = ccid->ccid_hc_tx_parse_options(sk, option, len, idx,
+ if (ccid->ccid_ops->ccid_hc_tx_parse_options != NULL)
+ rc = ccid->ccid_ops->ccid_hc_tx_parse_options(sk, option, len, idx,
value);
return rc;
}
@@ -156,37 +141,39 @@ static inline int ccid_hc_rx_parse_options(struct ccid *ccid, struct sock *sk,
unsigned char* value)
{
int rc = 0;
- if (ccid->ccid_hc_rx_parse_options != NULL)
- rc = ccid->ccid_hc_rx_parse_options(sk, option, len, idx, value);
+ if (ccid->ccid_ops->ccid_hc_rx_parse_options != NULL)
+ rc = ccid->ccid_ops->ccid_hc_rx_parse_options(sk, option, len, idx, value);
return rc;
}
-static inline void ccid_hc_tx_insert_options(struct ccid *ccid, struct sock *sk,
- struct sk_buff *skb)
+static inline int ccid_hc_tx_insert_options(struct ccid *ccid, struct sock *sk,
+ struct sk_buff *skb)
{
- if (ccid->ccid_hc_tx_insert_options != NULL)
- ccid->ccid_hc_tx_insert_options(sk, skb);
+ if (ccid->ccid_ops->ccid_hc_tx_insert_options != NULL)
+ return ccid->ccid_ops->ccid_hc_tx_insert_options(sk, skb);
+ return 0;
}
-static inline void ccid_hc_rx_insert_options(struct ccid *ccid, struct sock *sk,
- struct sk_buff *skb)
+static inline int ccid_hc_rx_insert_options(struct ccid *ccid, struct sock *sk,
+ struct sk_buff *skb)
{
- if (ccid->ccid_hc_rx_insert_options != NULL)
- ccid->ccid_hc_rx_insert_options(sk, skb);
+ if (ccid->ccid_ops->ccid_hc_rx_insert_options != NULL)
+ return ccid->ccid_ops->ccid_hc_rx_insert_options(sk, skb);
+ return 0;
}
static inline void ccid_hc_rx_get_info(struct ccid *ccid, struct sock *sk,
struct tcp_info *info)
{
- if (ccid->ccid_hc_rx_get_info != NULL)
- ccid->ccid_hc_rx_get_info(sk, info);
+ if (ccid->ccid_ops->ccid_hc_rx_get_info != NULL)
+ ccid->ccid_ops->ccid_hc_rx_get_info(sk, info);
}
static inline void ccid_hc_tx_get_info(struct ccid *ccid, struct sock *sk,
struct tcp_info *info)
{
- if (ccid->ccid_hc_tx_get_info != NULL)
- ccid->ccid_hc_tx_get_info(sk, info);
+ if (ccid->ccid_ops->ccid_hc_tx_get_info != NULL)
+ ccid->ccid_ops->ccid_hc_tx_get_info(sk, info);
}
static inline int ccid_hc_rx_getsockopt(struct ccid *ccid, struct sock *sk,
@@ -194,8 +181,8 @@ static inline int ccid_hc_rx_getsockopt(struct ccid *ccid, struct sock *sk,
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_hc_rx_getsockopt != NULL)
- rc = ccid->ccid_hc_rx_getsockopt(sk, optname, len,
+ if (ccid->ccid_ops->ccid_hc_rx_getsockopt != NULL)
+ rc = ccid->ccid_ops->ccid_hc_rx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
}
@@ -205,8 +192,8 @@ static inline int ccid_hc_tx_getsockopt(struct ccid *ccid, struct sock *sk,
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_hc_tx_getsockopt != NULL)
- rc = ccid->ccid_hc_tx_getsockopt(sk, optname, len,
+ if (ccid->ccid_ops->ccid_hc_tx_getsockopt != NULL)
+ rc = ccid->ccid_ops->ccid_hc_tx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
}
diff --git a/net/dccp/ccids/Kconfig b/net/dccp/ccids/Kconfig
index 7684d83946a4..ca00191628f7 100644
--- a/net/dccp/ccids/Kconfig
+++ b/net/dccp/ccids/Kconfig
@@ -1,9 +1,39 @@
menu "DCCP CCIDs Configuration (EXPERIMENTAL)"
depends on IP_DCCP && EXPERIMENTAL
+config IP_DCCP_CCID2
+ tristate "CCID2 (TCP-Like) (EXPERIMENTAL)"
+ depends on IP_DCCP
+ def_tristate IP_DCCP
+ select IP_DCCP_ACKVEC
+ ---help---
+ CCID 2, TCP-like Congestion Control, denotes Additive Increase,
+ Multiplicative Decrease (AIMD) congestion control with behavior
+ modelled directly on TCP, including congestion window, slow start,
+ timeouts, and so forth [RFC 2581]. CCID 2 achieves maximum
+ bandwidth over the long term, consistent with the use of end-to-end
+ congestion control, but halves its congestion window in response to
+ each congestion event. This leads to the abrupt rate changes
+ typical of TCP. Applications should use CCID 2 if they prefer
+ maximum bandwidth utilization to steadiness of rate. This is often
+ the case for applications that are not playing their data directly
+ to the user. For example, a hypothetical application that
+ transferred files over DCCP, using application-level retransmissions
+ for lost packets, would prefer CCID 2 to CCID 3. On-line games may
+ also prefer CCID 2.
+
+ CCID 2 is further described in:
+ http://www.icir.org/kohler/dccp/draft-ietf-dccp-ccid2-10.txt
+
+ This text was extracted from:
+ http://www.icir.org/kohler/dccp/draft-ietf-dccp-spec-13.txt
+
+ If in doubt, say M.
+
config IP_DCCP_CCID3
- tristate "CCID3 (TFRC) (EXPERIMENTAL)"
+ tristate "CCID3 (TCP-Friendly) (EXPERIMENTAL)"
depends on IP_DCCP
+ def_tristate IP_DCCP
---help---
CCID 3 denotes TCP-Friendly Rate Control (TFRC), an equation-based
rate-controlled congestion control mechanism. TFRC is designed to
@@ -15,10 +45,15 @@ config IP_DCCP_CCID3
suitable than CCID 2 for applications such streaming media where a
relatively smooth sending rate is of importance.
- CCID 3 is further described in [CCID 3 PROFILE]. The TFRC
- congestion control algorithms were initially described in RFC 3448.
+ CCID 3 is further described in:
+
+ http://www.icir.org/kohler/dccp/draft-ietf-dccp-ccid3-11.txt.
+
+ The TFRC congestion control algorithms were initially described in
+ RFC 3448.
- This text was extracted from draft-ietf-dccp-spec-11.txt.
+ This text was extracted from:
+ http://www.icir.org/kohler/dccp/draft-ietf-dccp-spec-13.txt
If in doubt, say M.
diff --git a/net/dccp/ccids/Makefile b/net/dccp/ccids/Makefile
index 956f79f50743..438f20bccff7 100644
--- a/net/dccp/ccids/Makefile
+++ b/net/dccp/ccids/Makefile
@@ -2,4 +2,8 @@ obj-$(CONFIG_IP_DCCP_CCID3) += dccp_ccid3.o
dccp_ccid3-y := ccid3.o
+obj-$(CONFIG_IP_DCCP_CCID2) += dccp_ccid2.o
+
+dccp_ccid2-y := ccid2.o
+
obj-y += lib/
diff --git a/net/dccp/ccids/ccid2.c b/net/dccp/ccids/ccid2.c
new file mode 100644
index 000000000000..d4f9e2d33453
--- /dev/null
+++ b/net/dccp/ccids/ccid2.c
@@ -0,0 +1,779 @@
+/*
+ * net/dccp/ccids/ccid2.c
+ *
+ * Copyright (c) 2005, 2006 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * Changes to meet Linux coding standards, and DCCP infrastructure fixes.
+ *
+ * Copyright (c) 2006 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * This implementation should follow: draft-ietf-dccp-ccid2-10.txt
+ *
+ * BUGS:
+ * - sequence number wrapping
+ * - jiffies wrapping
+ */
+
+#include <linux/config.h>
+#include "../ccid.h"
+#include "../dccp.h"
+#include "ccid2.h"
+
+static int ccid2_debug;
+
+#undef CCID2_DEBUG
+#ifdef CCID2_DEBUG
+#define ccid2_pr_debug(format, a...) \
+ do { if (ccid2_debug) \
+ printk(KERN_DEBUG "%s: " format, __FUNCTION__, ##a); \
+ } while (0)
+#else
+#define ccid2_pr_debug(format, a...)
+#endif
+
+static const int ccid2_seq_len = 128;
+
+#ifdef CCID2_DEBUG
+static void ccid2_hc_tx_check_sanity(const struct ccid2_hc_tx_sock *hctx)
+{
+ int len = 0;
+ int pipe = 0;
+ struct ccid2_seq *seqp = hctx->ccid2hctx_seqh;
+
+ /* there is data in the chain */
+ if (seqp != hctx->ccid2hctx_seqt) {
+ seqp = seqp->ccid2s_prev;
+ len++;
+ if (!seqp->ccid2s_acked)
+ pipe++;
+
+ while (seqp != hctx->ccid2hctx_seqt) {
+ struct ccid2_seq *prev = seqp->ccid2s_prev;
+
+ len++;
+ if (!prev->ccid2s_acked)
+ pipe++;
+
+ /* packets are sent sequentially */
+ BUG_ON(seqp->ccid2s_seq <= prev->ccid2s_seq);
+ BUG_ON(seqp->ccid2s_sent < prev->ccid2s_sent);
+ BUG_ON(len > ccid2_seq_len);
+
+ seqp = prev;
+ }
+ }
+
+ BUG_ON(pipe != hctx->ccid2hctx_pipe);
+ ccid2_pr_debug("len of chain=%d\n", len);
+
+ do {
+ seqp = seqp->ccid2s_prev;
+ len++;
+ BUG_ON(len > ccid2_seq_len);
+ } while (seqp != hctx->ccid2hctx_seqh);
+
+ BUG_ON(len != ccid2_seq_len);
+ ccid2_pr_debug("total len=%d\n", len);
+}
+#else
+#define ccid2_hc_tx_check_sanity(hctx) do {} while (0)
+#endif
+
+static int ccid2_hc_tx_send_packet(struct sock *sk,
+ struct sk_buff *skb, int len)
+{
+ struct ccid2_hc_tx_sock *hctx;
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case 0: /* XXX data packets from userland come through like this */
+ case DCCP_PKT_DATA:
+ case DCCP_PKT_DATAACK:
+ break;
+ /* No congestion control on other packets */
+ default:
+ return 0;
+ }
+
+ hctx = ccid2_hc_tx_sk(sk);
+
+ ccid2_pr_debug("pipe=%d cwnd=%d\n", hctx->ccid2hctx_pipe,
+ hctx->ccid2hctx_cwnd);
+
+ if (hctx->ccid2hctx_pipe < hctx->ccid2hctx_cwnd) {
+ /* OK we can send... make sure previous packet was sent off */
+ if (!hctx->ccid2hctx_sendwait) {
+ hctx->ccid2hctx_sendwait = 1;
+ return 0;
+ }
+ }
+
+ return 100; /* XXX */
+}
+
+static void ccid2_change_l_ack_ratio(struct sock *sk, int val)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ /*
+ * XXX I don't really agree with val != 2. If cwnd is 1, ack ratio
+ * should be 1... it shouldn't be allowed to become 2.
+ * -sorbo.
+ */
+ if (val != 2) {
+ const struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ int max = hctx->ccid2hctx_cwnd / 2;
+
+ /* round up */
+ if (hctx->ccid2hctx_cwnd & 1)
+ max++;
+
+ if (val > max)
+ val = max;
+ }
+
+ ccid2_pr_debug("changing local ack ratio to %d\n", val);
+ WARN_ON(val <= 0);
+ dp->dccps_l_ack_ratio = val;
+}
+
+static void ccid2_change_cwnd(struct sock *sk, int val)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ if (val == 0)
+ val = 1;
+
+ /* XXX do we need to change ack ratio? */
+ ccid2_pr_debug("change cwnd to %d\n", val);
+
+ BUG_ON(val < 1);
+ hctx->ccid2hctx_cwnd = val;
+}
+
+static void ccid2_start_rto_timer(struct sock *sk);
+
+static void ccid2_hc_tx_rto_expire(unsigned long data)
+{
+ struct sock *sk = (struct sock *)data;
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ long s;
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer,
+ jiffies + HZ / 5);
+ goto out;
+ }
+
+ ccid2_pr_debug("RTO_EXPIRE\n");
+
+ ccid2_hc_tx_check_sanity(hctx);
+
+ /* back-off timer */
+ hctx->ccid2hctx_rto <<= 1;
+
+ s = hctx->ccid2hctx_rto / HZ;
+ if (s > 60)
+ hctx->ccid2hctx_rto = 60 * HZ;
+
+ ccid2_start_rto_timer(sk);
+
+ /* adjust pipe, cwnd etc */
+ hctx->ccid2hctx_pipe = 0;
+ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd >> 1;
+ if (hctx->ccid2hctx_ssthresh < 2)
+ hctx->ccid2hctx_ssthresh = 2;
+ ccid2_change_cwnd(sk, 1);
+
+ /* clear state about stuff we sent */
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
+ hctx->ccid2hctx_ssacks = 0;
+ hctx->ccid2hctx_acks = 0;
+ hctx->ccid2hctx_sent = 0;
+
+ /* clear ack ratio state. */
+ hctx->ccid2hctx_arsent = 0;
+ hctx->ccid2hctx_ackloss = 0;
+ hctx->ccid2hctx_rpseq = 0;
+ hctx->ccid2hctx_rpdupack = -1;
+ ccid2_change_l_ack_ratio(sk, 1);
+ ccid2_hc_tx_check_sanity(hctx);
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+}
+
+static void ccid2_start_rto_timer(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ ccid2_pr_debug("setting RTO timeout=%ld\n", hctx->ccid2hctx_rto);
+
+ BUG_ON(timer_pending(&hctx->ccid2hctx_rtotimer));
+ sk_reset_timer(sk, &hctx->ccid2hctx_rtotimer,
+ jiffies + hctx->ccid2hctx_rto);
+}
+
+static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, int len)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ u64 seq;
+
+ ccid2_hc_tx_check_sanity(hctx);
+
+ BUG_ON(!hctx->ccid2hctx_sendwait);
+ hctx->ccid2hctx_sendwait = 0;
+ hctx->ccid2hctx_pipe++;
+ BUG_ON(hctx->ccid2hctx_pipe < 0);
+
+ /* There is an issue. What if another packet is sent between
+ * packet_send() and packet_sent(). Then the sequence number would be
+ * wrong.
+ * -sorbo.
+ */
+ seq = dp->dccps_gss;
+
+ hctx->ccid2hctx_seqh->ccid2s_seq = seq;
+ hctx->ccid2hctx_seqh->ccid2s_acked = 0;
+ hctx->ccid2hctx_seqh->ccid2s_sent = jiffies;
+ hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqh->ccid2s_next;
+
+ ccid2_pr_debug("cwnd=%d pipe=%d\n", hctx->ccid2hctx_cwnd,
+ hctx->ccid2hctx_pipe);
+
+ if (hctx->ccid2hctx_seqh == hctx->ccid2hctx_seqt) {
+ /* XXX allocate more space */
+ WARN_ON(1);
+ }
+
+ hctx->ccid2hctx_sent++;
+
+ /* Ack Ratio. Need to maintain a concept of how many windows we sent */
+ hctx->ccid2hctx_arsent++;
+ /* We had an ack loss in this window... */
+ if (hctx->ccid2hctx_ackloss) {
+ if (hctx->ccid2hctx_arsent >= hctx->ccid2hctx_cwnd) {
+ hctx->ccid2hctx_arsent = 0;
+ hctx->ccid2hctx_ackloss = 0;
+ }
+ } else {
+ /* No acks lost up to now... */
+ /* decrease ack ratio if enough packets were sent */
+ if (dp->dccps_l_ack_ratio > 1) {
+ /* XXX don't calculate denominator each time */
+ int denom = dp->dccps_l_ack_ratio * dp->dccps_l_ack_ratio -
+ dp->dccps_l_ack_ratio;
+
+ denom = hctx->ccid2hctx_cwnd * hctx->ccid2hctx_cwnd / denom;
+
+ if (hctx->ccid2hctx_arsent >= denom) {
+ ccid2_change_l_ack_ratio(sk, dp->dccps_l_ack_ratio - 1);
+ hctx->ccid2hctx_arsent = 0;
+ }
+ } else {
+ /* we can't increase ack ratio further [1] */
+ hctx->ccid2hctx_arsent = 0; /* or maybe set it to cwnd*/
+ }
+ }
+
+ /* setup RTO timer */
+ if (!timer_pending(&hctx->ccid2hctx_rtotimer))
+ ccid2_start_rto_timer(sk);
+
+#ifdef CCID2_DEBUG
+ ccid2_pr_debug("pipe=%d\n", hctx->ccid2hctx_pipe);
+ ccid2_pr_debug("Sent: seq=%llu\n", seq);
+ do {
+ struct ccid2_seq *seqp = hctx->ccid2hctx_seqt;
+
+ while (seqp != hctx->ccid2hctx_seqh) {
+ ccid2_pr_debug("out seq=%llu acked=%d time=%lu\n",
+ seqp->ccid2s_seq, seqp->ccid2s_acked,
+ seqp->ccid2s_sent);
+ seqp = seqp->ccid2s_next;
+ }
+ } while (0);
+ ccid2_pr_debug("=========\n");
+ ccid2_hc_tx_check_sanity(hctx);
+#endif
+}
+
+/* XXX Lame code duplication!
+ * returns -1 if none was found.
+ * else returns the next offset to use in the function call.
+ */
+static int ccid2_ackvector(struct sock *sk, struct sk_buff *skb, int offset,
+ unsigned char **vec, unsigned char *veclen)
+{
+ const struct dccp_hdr *dh = dccp_hdr(skb);
+ unsigned char *options = (unsigned char *)dh + dccp_hdr_len(skb);
+ unsigned char *opt_ptr;
+ const unsigned char *opt_end = (unsigned char *)dh +
+ (dh->dccph_doff * 4);
+ unsigned char opt, len;
+ unsigned char *value;
+
+ BUG_ON(offset < 0);
+ options += offset;
+ opt_ptr = options;
+ if (opt_ptr >= opt_end)
+ return -1;
+
+ while (opt_ptr != opt_end) {
+ opt = *opt_ptr++;
+ len = 0;
+ value = NULL;
+
+ /* Check if this isn't a single byte option */
+ if (opt > DCCPO_MAX_RESERVED) {
+ if (opt_ptr == opt_end)
+ goto out_invalid_option;
+
+ len = *opt_ptr++;
+ if (len < 3)
+ goto out_invalid_option;
+ /*
+ * Remove the type and len fields, leaving
+ * just the value size
+ */
+ len -= 2;
+ value = opt_ptr;
+ opt_ptr += len;
+
+ if (opt_ptr > opt_end)
+ goto out_invalid_option;
+ }
+
+ switch (opt) {
+ case DCCPO_ACK_VECTOR_0:
+ case DCCPO_ACK_VECTOR_1:
+ *vec = value;
+ *veclen = len;
+ return offset + (opt_ptr - options);
+ }
+ }
+
+ return -1;
+
+out_invalid_option:
+ BUG_ON(1); /* should never happen... options were previously parsed ! */
+ return -1;
+}
+
+static void ccid2_hc_tx_kill_rto_timer(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ sk_stop_timer(sk, &hctx->ccid2hctx_rtotimer);
+ ccid2_pr_debug("deleted RTO timer\n");
+}
+
+static inline void ccid2_new_ack(struct sock *sk,
+ struct ccid2_seq *seqp,
+ unsigned int *maxincr)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ /* slow start */
+ if (hctx->ccid2hctx_cwnd < hctx->ccid2hctx_ssthresh) {
+ hctx->ccid2hctx_acks = 0;
+
+ /* We can increase cwnd at most maxincr [ack_ratio/2] */
+ if (*maxincr) {
+ /* increase every 2 acks */
+ hctx->ccid2hctx_ssacks++;
+ if (hctx->ccid2hctx_ssacks == 2) {
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd + 1);
+ hctx->ccid2hctx_ssacks = 0;
+ *maxincr = *maxincr - 1;
+ }
+ } else {
+ /* increased cwnd enough for this single ack */
+ hctx->ccid2hctx_ssacks = 0;
+ }
+ } else {
+ hctx->ccid2hctx_ssacks = 0;
+ hctx->ccid2hctx_acks++;
+
+ if (hctx->ccid2hctx_acks >= hctx->ccid2hctx_cwnd) {
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd + 1);
+ hctx->ccid2hctx_acks = 0;
+ }
+ }
+
+ /* update RTO */
+ if (hctx->ccid2hctx_srtt == -1 ||
+ (jiffies - hctx->ccid2hctx_lastrtt) >= hctx->ccid2hctx_srtt) {
+ unsigned long r = jiffies - seqp->ccid2s_sent;
+ int s;
+
+ /* first measurement */
+ if (hctx->ccid2hctx_srtt == -1) {
+ ccid2_pr_debug("R: %lu Time=%lu seq=%llu\n",
+ r, jiffies, seqp->ccid2s_seq);
+ hctx->ccid2hctx_srtt = r;
+ hctx->ccid2hctx_rttvar = r >> 1;
+ } else {
+ /* RTTVAR */
+ long tmp = hctx->ccid2hctx_srtt - r;
+ if (tmp < 0)
+ tmp *= -1;
+
+ tmp >>= 2;
+ hctx->ccid2hctx_rttvar *= 3;
+ hctx->ccid2hctx_rttvar >>= 2;
+ hctx->ccid2hctx_rttvar += tmp;
+
+ /* SRTT */
+ hctx->ccid2hctx_srtt *= 7;
+ hctx->ccid2hctx_srtt >>= 3;
+ tmp = r >> 3;
+ hctx->ccid2hctx_srtt += tmp;
+ }
+ s = hctx->ccid2hctx_rttvar << 2;
+ /* clock granularity is 1 when based on jiffies */
+ if (!s)
+ s = 1;
+ hctx->ccid2hctx_rto = hctx->ccid2hctx_srtt + s;
+
+ /* must be at least a second */
+ s = hctx->ccid2hctx_rto / HZ;
+ /* DCCP doesn't require this [but I like it cuz my code sux] */
+#if 1
+ if (s < 1)
+ hctx->ccid2hctx_rto = HZ;
+#endif
+ /* max 60 seconds */
+ if (s > 60)
+ hctx->ccid2hctx_rto = HZ * 60;
+
+ hctx->ccid2hctx_lastrtt = jiffies;
+
+ ccid2_pr_debug("srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu\n",
+ hctx->ccid2hctx_srtt, hctx->ccid2hctx_rttvar,
+ hctx->ccid2hctx_rto, HZ, r);
+ hctx->ccid2hctx_sent = 0;
+ }
+
+ /* we got a new ack, so re-start RTO timer */
+ ccid2_hc_tx_kill_rto_timer(sk);
+ ccid2_start_rto_timer(sk);
+}
+
+static void ccid2_hc_tx_dec_pipe(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ hctx->ccid2hctx_pipe--;
+ BUG_ON(hctx->ccid2hctx_pipe < 0);
+
+ if (hctx->ccid2hctx_pipe == 0)
+ ccid2_hc_tx_kill_rto_timer(sk);
+}
+
+static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+ u64 ackno, seqno;
+ struct ccid2_seq *seqp;
+ unsigned char *vector;
+ unsigned char veclen;
+ int offset = 0;
+ int done = 0;
+ int loss = 0;
+ unsigned int maxincr = 0;
+
+ ccid2_hc_tx_check_sanity(hctx);
+ /* check reverse path congestion */
+ seqno = DCCP_SKB_CB(skb)->dccpd_seq;
+
+ /* XXX this whole "algorithm" is broken. Need to fix it to keep track
+ * of the seqnos of the dupacks so that rpseq and rpdupack are correct
+ * -sorbo.
+ */
+ /* need to bootstrap */
+ if (hctx->ccid2hctx_rpdupack == -1) {
+ hctx->ccid2hctx_rpdupack = 0;
+ hctx->ccid2hctx_rpseq = seqno;
+ } else {
+ /* check if packet is consecutive */
+ if ((hctx->ccid2hctx_rpseq + 1) == seqno)
+ hctx->ccid2hctx_rpseq++;
+ /* it's a later packet */
+ else if (after48(seqno, hctx->ccid2hctx_rpseq)) {
+ hctx->ccid2hctx_rpdupack++;
+
+ /* check if we got enough dupacks */
+ if (hctx->ccid2hctx_rpdupack >=
+ hctx->ccid2hctx_numdupack) {
+ hctx->ccid2hctx_rpdupack = -1; /* XXX lame */
+ hctx->ccid2hctx_rpseq = 0;
+
+ ccid2_change_l_ack_ratio(sk, dp->dccps_l_ack_ratio << 1);
+ }
+ }
+ }
+
+ /* check forward path congestion */
+ /* still didn't send out new data packets */
+ if (hctx->ccid2hctx_seqh == hctx->ccid2hctx_seqt)
+ return;
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case DCCP_PKT_ACK:
+ case DCCP_PKT_DATAACK:
+ break;
+ default:
+ return;
+ }
+
+ ackno = DCCP_SKB_CB(skb)->dccpd_ack_seq;
+ seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+
+ /* If in slow-start, cwnd can increase at most Ack Ratio / 2 packets for
+ * this single ack. I round up.
+ * -sorbo.
+ */
+ maxincr = dp->dccps_l_ack_ratio >> 1;
+ maxincr++;
+
+ /* go through all ack vectors */
+ while ((offset = ccid2_ackvector(sk, skb, offset,
+ &vector, &veclen)) != -1) {
+ /* go through this ack vector */
+ while (veclen--) {
+ const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK;
+ u64 ackno_end_rl;
+
+ dccp_set_seqno(&ackno_end_rl, ackno - rl);
+ ccid2_pr_debug("ackvec start:%llu end:%llu\n", ackno,
+ ackno_end_rl);
+ /* if the seqno we are analyzing is larger than the
+ * current ackno, then move towards the tail of our
+ * seqnos.
+ */
+ while (after48(seqp->ccid2s_seq, ackno)) {
+ if (seqp == hctx->ccid2hctx_seqt) {
+ done = 1;
+ break;
+ }
+ seqp = seqp->ccid2s_prev;
+ }
+ if (done)
+ break;
+
+ /* check all seqnos in the range of the vector
+ * run length
+ */
+ while (between48(seqp->ccid2s_seq,ackno_end_rl,ackno)) {
+ const u8 state = (*vector &
+ DCCP_ACKVEC_STATE_MASK) >> 6;
+
+ /* new packet received or marked */
+ if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED &&
+ !seqp->ccid2s_acked) {
+ if (state ==
+ DCCP_ACKVEC_STATE_ECN_MARKED) {
+ loss = 1;
+ } else
+ ccid2_new_ack(sk, seqp,
+ &maxincr);
+
+ seqp->ccid2s_acked = 1;
+ ccid2_pr_debug("Got ack for %llu\n",
+ seqp->ccid2s_seq);
+ ccid2_hc_tx_dec_pipe(sk);
+ }
+ if (seqp == hctx->ccid2hctx_seqt) {
+ done = 1;
+ break;
+ }
+ seqp = seqp->ccid2s_next;
+ }
+ if (done)
+ break;
+
+
+ dccp_set_seqno(&ackno, ackno_end_rl - 1);
+ vector++;
+ }
+ if (done)
+ break;
+ }
+
+ /* The state about what is acked should be correct now
+ * Check for NUMDUPACK
+ */
+ seqp = hctx->ccid2hctx_seqh->ccid2s_prev;
+ done = 0;
+ while (1) {
+ if (seqp->ccid2s_acked) {
+ done++;
+ if (done == hctx->ccid2hctx_numdupack)
+ break;
+ }
+ if (seqp == hctx->ccid2hctx_seqt)
+ break;
+ seqp = seqp->ccid2s_prev;
+ }
+
+ /* If there are at least 3 acknowledgements, anything unacknowledged
+ * below the last sequence number is considered lost
+ */
+ if (done == hctx->ccid2hctx_numdupack) {
+ struct ccid2_seq *last_acked = seqp;
+
+ /* check for lost packets */
+ while (1) {
+ if (!seqp->ccid2s_acked) {
+ loss = 1;
+ ccid2_hc_tx_dec_pipe(sk);
+ }
+ if (seqp == hctx->ccid2hctx_seqt)
+ break;
+ seqp = seqp->ccid2s_prev;
+ }
+
+ hctx->ccid2hctx_seqt = last_acked;
+ }
+
+ /* trim acked packets in tail */
+ while (hctx->ccid2hctx_seqt != hctx->ccid2hctx_seqh) {
+ if (!hctx->ccid2hctx_seqt->ccid2s_acked)
+ break;
+
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqt->ccid2s_next;
+ }
+
+ if (loss) {
+ /* XXX do bit shifts guarantee a 0 as the new bit? */
+ ccid2_change_cwnd(sk, hctx->ccid2hctx_cwnd >> 1);
+ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd;
+ if (hctx->ccid2hctx_ssthresh < 2)
+ hctx->ccid2hctx_ssthresh = 2;
+ }
+
+ ccid2_hc_tx_check_sanity(hctx);
+}
+
+static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid_priv(ccid);
+ int seqcount = ccid2_seq_len;
+ int i;
+
+ /* XXX init variables with proper values */
+ hctx->ccid2hctx_cwnd = 1;
+ hctx->ccid2hctx_ssthresh = 10;
+ hctx->ccid2hctx_numdupack = 3;
+
+ /* XXX init ~ to window size... */
+ hctx->ccid2hctx_seqbuf = kmalloc(sizeof(*hctx->ccid2hctx_seqbuf) *
+ seqcount, gfp_any());
+ if (hctx->ccid2hctx_seqbuf == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < (seqcount - 1); i++) {
+ hctx->ccid2hctx_seqbuf[i].ccid2s_next =
+ &hctx->ccid2hctx_seqbuf[i + 1];
+ hctx->ccid2hctx_seqbuf[i + 1].ccid2s_prev =
+ &hctx->ccid2hctx_seqbuf[i];
+ }
+ hctx->ccid2hctx_seqbuf[seqcount - 1].ccid2s_next =
+ hctx->ccid2hctx_seqbuf;
+ hctx->ccid2hctx_seqbuf->ccid2s_prev =
+ &hctx->ccid2hctx_seqbuf[seqcount - 1];
+
+ hctx->ccid2hctx_seqh = hctx->ccid2hctx_seqbuf;
+ hctx->ccid2hctx_seqt = hctx->ccid2hctx_seqh;
+ hctx->ccid2hctx_sent = 0;
+ hctx->ccid2hctx_rto = 3 * HZ;
+ hctx->ccid2hctx_srtt = -1;
+ hctx->ccid2hctx_rttvar = -1;
+ hctx->ccid2hctx_lastrtt = 0;
+ hctx->ccid2hctx_rpdupack = -1;
+
+ hctx->ccid2hctx_rtotimer.function = &ccid2_hc_tx_rto_expire;
+ hctx->ccid2hctx_rtotimer.data = (unsigned long)sk;
+ init_timer(&hctx->ccid2hctx_rtotimer);
+
+ ccid2_hc_tx_check_sanity(hctx);
+ return 0;
+}
+
+static void ccid2_hc_tx_exit(struct sock *sk)
+{
+ struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk);
+
+ ccid2_hc_tx_kill_rto_timer(sk);
+ kfree(hctx->ccid2hctx_seqbuf);
+ hctx->ccid2hctx_seqbuf = NULL;
+}
+
+static void ccid2_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
+{
+ const struct dccp_sock *dp = dccp_sk(sk);
+ struct ccid2_hc_rx_sock *hcrx = ccid2_hc_rx_sk(sk);
+
+ switch (DCCP_SKB_CB(skb)->dccpd_type) {
+ case DCCP_PKT_DATA:
+ case DCCP_PKT_DATAACK:
+ hcrx->ccid2hcrx_data++;
+ if (hcrx->ccid2hcrx_data >= dp->dccps_r_ack_ratio) {
+ dccp_send_ack(sk);
+ hcrx->ccid2hcrx_data = 0;
+ }
+ break;
+ }
+}
+
+static struct ccid_operations ccid2 = {
+ .ccid_id = 2,
+ .ccid_name = "ccid2",
+ .ccid_owner = THIS_MODULE,
+ .ccid_hc_tx_obj_size = sizeof(struct ccid2_hc_tx_sock),
+ .ccid_hc_tx_init = ccid2_hc_tx_init,
+ .ccid_hc_tx_exit = ccid2_hc_tx_exit,
+ .ccid_hc_tx_send_packet = ccid2_hc_tx_send_packet,
+ .ccid_hc_tx_packet_sent = ccid2_hc_tx_packet_sent,
+ .ccid_hc_tx_packet_recv = ccid2_hc_tx_packet_recv,
+ .ccid_hc_rx_obj_size = sizeof(struct ccid2_hc_rx_sock),
+ .ccid_hc_rx_packet_recv = ccid2_hc_rx_packet_recv,
+};
+
+module_param(ccid2_debug, int, 0444);
+MODULE_PARM_DESC(ccid2_debug, "Enable debug messages");
+
+static __init int ccid2_module_init(void)
+{
+ return ccid_register(&ccid2);
+}
+module_init(ccid2_module_init);
+
+static __exit void ccid2_module_exit(void)
+{
+ ccid_unregister(&ccid2);
+}
+module_exit(ccid2_module_exit);
+
+MODULE_AUTHOR("Andrea Bittau <a.bittau@cs.ucl.ac.uk>");
+MODULE_DESCRIPTION("DCCP TCP-Like (CCID2) CCID");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("net-dccp-ccid-2");
diff --git a/net/dccp/ccids/ccid2.h b/net/dccp/ccids/ccid2.h
new file mode 100644
index 000000000000..451a87464fa5
--- /dev/null
+++ b/net/dccp/ccids/ccid2.h
@@ -0,0 +1,85 @@
+/*
+ * net/dccp/ccids/ccid2.h
+ *
+ * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _DCCP_CCID2_H_
+#define _DCCP_CCID2_H_
+
+#include <linux/dccp.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include "../ccid.h"
+
+struct sock;
+
+struct ccid2_seq {
+ u64 ccid2s_seq;
+ unsigned long ccid2s_sent;
+ int ccid2s_acked;
+ struct ccid2_seq *ccid2s_prev;
+ struct ccid2_seq *ccid2s_next;
+};
+
+/** struct ccid2_hc_tx_sock - CCID2 TX half connection
+ *
+ * @ccid2hctx_ssacks - ACKs recv in slow start
+ * @ccid2hctx_acks - ACKS recv in AI phase
+ * @ccid2hctx_sent - packets sent in this window
+ * @ccid2hctx_lastrtt -time RTT was last measured
+ * @ccid2hctx_arsent - packets sent [ack ratio]
+ * @ccid2hctx_ackloss - ack was lost in this win
+ * @ccid2hctx_rpseq - last consecutive seqno
+ * @ccid2hctx_rpdupack - dupacks since rpseq
+*/
+struct ccid2_hc_tx_sock {
+ int ccid2hctx_cwnd;
+ int ccid2hctx_ssacks;
+ int ccid2hctx_acks;
+ int ccid2hctx_ssthresh;
+ int ccid2hctx_pipe;
+ int ccid2hctx_numdupack;
+ struct ccid2_seq *ccid2hctx_seqbuf;
+ struct ccid2_seq *ccid2hctx_seqh;
+ struct ccid2_seq *ccid2hctx_seqt;
+ long ccid2hctx_rto;
+ long ccid2hctx_srtt;
+ long ccid2hctx_rttvar;
+ int ccid2hctx_sent;
+ unsigned long ccid2hctx_lastrtt;
+ struct timer_list ccid2hctx_rtotimer;
+ unsigned long ccid2hctx_arsent;
+ int ccid2hctx_ackloss;
+ u64 ccid2hctx_rpseq;
+ int ccid2hctx_rpdupack;
+ int ccid2hctx_sendwait;
+};
+
+struct ccid2_hc_rx_sock {
+ int ccid2hcrx_data;
+};
+
+static inline struct ccid2_hc_tx_sock *ccid2_hc_tx_sk(const struct sock *sk)
+{
+ return ccid_priv(dccp_sk(sk)->dccps_hc_tx_ccid);
+}
+
+static inline struct ccid2_hc_rx_sock *ccid2_hc_rx_sk(const struct sock *sk)
+{
+ return ccid_priv(dccp_sk(sk)->dccps_hc_rx_ccid);
+}
+#endif /* _DCCP_CCID2_H_ */
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index 35d1d347541c..b4a51d0355a5 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -46,7 +46,7 @@
* Reason for maths here is to avoid 32 bit overflow when a is big.
* With this we get close to the limit.
*/
-static inline u32 usecs_div(const u32 a, const u32 b)
+static u32 usecs_div(const u32 a, const u32 b)
{
const u32 div = a < (UINT_MAX / (USEC_PER_SEC / 10)) ? 10 :
a < (UINT_MAX / (USEC_PER_SEC / 50)) ? 50 :
@@ -76,15 +76,6 @@ static struct dccp_tx_hist *ccid3_tx_hist;
static struct dccp_rx_hist *ccid3_rx_hist;
static struct dccp_li_hist *ccid3_li_hist;
-static int ccid3_init(struct sock *sk)
-{
- return 0;
-}
-
-static void ccid3_exit(struct sock *sk)
-{
-}
-
/* TFRC sender states */
enum ccid3_hc_tx_states {
TFRC_SSTATE_NO_SENT = 1,
@@ -107,8 +98,8 @@ static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state)
}
#endif
-static inline void ccid3_hc_tx_set_state(struct sock *sk,
- enum ccid3_hc_tx_states state)
+static void ccid3_hc_tx_set_state(struct sock *sk,
+ enum ccid3_hc_tx_states state)
{
struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
enum ccid3_hc_tx_states oldstate = hctx->ccid3hctx_state;
@@ -316,8 +307,6 @@ static int ccid3_hc_tx_send_packet(struct sock *sk,
switch (hctx->ccid3hctx_state) {
case TFRC_SSTATE_NO_SENT:
- hctx->ccid3hctx_no_feedback_timer.function = ccid3_hc_tx_no_feedback_timer;
- hctx->ccid3hctx_no_feedback_timer.data = (unsigned long)sk;
sk_reset_timer(sk, &hctx->ccid3hctx_no_feedback_timer,
jiffies + usecs_to_jiffies(TFRC_INITIAL_TIMEOUT));
hctx->ccid3hctx_last_win_count = 0;
@@ -585,16 +574,15 @@ static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
}
}
-static void ccid3_hc_tx_insert_options(struct sock *sk, struct sk_buff *skb)
+static int ccid3_hc_tx_insert_options(struct sock *sk, struct sk_buff *skb)
{
const struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
BUG_ON(hctx == NULL);
- if (!(sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN))
- return;
-
- DCCP_SKB_CB(skb)->dccpd_ccval = hctx->ccid3hctx_last_win_count;
+ if (sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN)
+ DCCP_SKB_CB(skb)->dccpd_ccval = hctx->ccid3hctx_last_win_count;
+ return 0;
}
static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option,
@@ -626,7 +614,7 @@ static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option,
__FUNCTION__, dccp_role(sk), sk);
rc = -EINVAL;
} else {
- opt_recv->ccid3or_loss_event_rate = ntohl(*(u32 *)value);
+ opt_recv->ccid3or_loss_event_rate = ntohl(*(__be32 *)value);
ccid3_pr_debug("%s, sk=%p, LOSS_EVENT_RATE=%u\n",
dccp_role(sk), sk,
opt_recv->ccid3or_loss_event_rate);
@@ -647,7 +635,7 @@ static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option,
__FUNCTION__, dccp_role(sk), sk);
rc = -EINVAL;
} else {
- opt_recv->ccid3or_receive_rate = ntohl(*(u32 *)value);
+ opt_recv->ccid3or_receive_rate = ntohl(*(__be32 *)value);
ccid3_pr_debug("%s, sk=%p, RECEIVE_RATE=%u\n",
dccp_role(sk), sk,
opt_recv->ccid3or_receive_rate);
@@ -658,17 +646,10 @@ static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option,
return rc;
}
-static int ccid3_hc_tx_init(struct sock *sk)
+static int ccid3_hc_tx_init(struct ccid *ccid, struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct ccid3_hc_tx_sock *hctx;
-
- dp->dccps_hc_tx_ccid_private = kmalloc(sizeof(*hctx), gfp_any());
- if (dp->dccps_hc_tx_ccid_private == NULL)
- return -ENOMEM;
-
- hctx = ccid3_hc_tx_sk(sk);
- memset(hctx, 0, sizeof(*hctx));
+ struct ccid3_hc_tx_sock *hctx = ccid_priv(ccid);
if (dp->dccps_packet_size >= TFRC_MIN_PACKET_SIZE &&
dp->dccps_packet_size <= TFRC_MAX_PACKET_SIZE)
@@ -681,6 +662,9 @@ static int ccid3_hc_tx_init(struct sock *sk)
hctx->ccid3hctx_t_rto = USEC_PER_SEC;
hctx->ccid3hctx_state = TFRC_SSTATE_NO_SENT;
INIT_LIST_HEAD(&hctx->ccid3hctx_hist);
+
+ hctx->ccid3hctx_no_feedback_timer.function = ccid3_hc_tx_no_feedback_timer;
+ hctx->ccid3hctx_no_feedback_timer.data = (unsigned long)sk;
init_timer(&hctx->ccid3hctx_no_feedback_timer);
return 0;
@@ -688,7 +672,6 @@ static int ccid3_hc_tx_init(struct sock *sk)
static void ccid3_hc_tx_exit(struct sock *sk)
{
- struct dccp_sock *dp = dccp_sk(sk);
struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk);
BUG_ON(hctx == NULL);
@@ -698,9 +681,6 @@ static void ccid3_hc_tx_exit(struct sock *sk)
/* Empty packet history */
dccp_tx_hist_purge(ccid3_tx_hist, &hctx->ccid3hctx_hist);
-
- kfree(dp->dccps_hc_tx_ccid_private);
- dp->dccps_hc_tx_ccid_private = NULL;
}
/*
@@ -727,8 +707,8 @@ static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state)
}
#endif
-static inline void ccid3_hc_rx_set_state(struct sock *sk,
- enum ccid3_hc_rx_states state)
+static void ccid3_hc_rx_set_state(struct sock *sk,
+ enum ccid3_hc_rx_states state)
{
struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
enum ccid3_hc_rx_states oldstate = hcrx->ccid3hcrx_state;
@@ -793,31 +773,35 @@ static void ccid3_hc_rx_send_feedback(struct sock *sk)
dccp_send_ack(sk);
}
-static void ccid3_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb)
+static int ccid3_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb)
{
const struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
- u32 x_recv, pinv;
+ __be32 x_recv, pinv;
BUG_ON(hcrx == NULL);
if (!(sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN))
- return;
+ return 0;
DCCP_SKB_CB(skb)->dccpd_ccval = hcrx->ccid3hcrx_last_counter;
if (dccp_packet_without_ack(skb))
- return;
-
- if (hcrx->ccid3hcrx_elapsed_time != 0)
- dccp_insert_option_elapsed_time(sk, skb,
- hcrx->ccid3hcrx_elapsed_time);
- dccp_insert_option_timestamp(sk, skb);
+ return 0;
+
x_recv = htonl(hcrx->ccid3hcrx_x_recv);
pinv = htonl(hcrx->ccid3hcrx_pinv);
- dccp_insert_option(sk, skb, TFRC_OPT_LOSS_EVENT_RATE,
- &pinv, sizeof(pinv));
- dccp_insert_option(sk, skb, TFRC_OPT_RECEIVE_RATE,
- &x_recv, sizeof(x_recv));
+
+ if ((hcrx->ccid3hcrx_elapsed_time != 0 &&
+ dccp_insert_option_elapsed_time(sk, skb,
+ hcrx->ccid3hcrx_elapsed_time)) ||
+ dccp_insert_option_timestamp(sk, skb) ||
+ dccp_insert_option(sk, skb, TFRC_OPT_LOSS_EVENT_RATE,
+ &pinv, sizeof(pinv)) ||
+ dccp_insert_option(sk, skb, TFRC_OPT_RECEIVE_RATE,
+ &x_recv, sizeof(x_recv)))
+ return -1;
+
+ return 0;
}
/* calculate first loss interval
@@ -1047,20 +1031,13 @@ static void ccid3_hc_rx_packet_recv(struct sock *sk, struct sk_buff *skb)
}
}
-static int ccid3_hc_rx_init(struct sock *sk)
+static int ccid3_hc_rx_init(struct ccid *ccid, struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
- struct ccid3_hc_rx_sock *hcrx;
+ struct ccid3_hc_rx_sock *hcrx = ccid_priv(ccid);
ccid3_pr_debug("%s, sk=%p\n", dccp_role(sk), sk);
- dp->dccps_hc_rx_ccid_private = kmalloc(sizeof(*hcrx), gfp_any());
- if (dp->dccps_hc_rx_ccid_private == NULL)
- return -ENOMEM;
-
- hcrx = ccid3_hc_rx_sk(sk);
- memset(hcrx, 0, sizeof(*hcrx));
-
if (dp->dccps_packet_size >= TFRC_MIN_PACKET_SIZE &&
dp->dccps_packet_size <= TFRC_MAX_PACKET_SIZE)
hcrx->ccid3hcrx_s = dp->dccps_packet_size;
@@ -1079,7 +1056,6 @@ static int ccid3_hc_rx_init(struct sock *sk)
static void ccid3_hc_rx_exit(struct sock *sk)
{
struct ccid3_hc_rx_sock *hcrx = ccid3_hc_rx_sk(sk);
- struct dccp_sock *dp = dccp_sk(sk);
BUG_ON(hcrx == NULL);
@@ -1090,9 +1066,6 @@ static void ccid3_hc_rx_exit(struct sock *sk)
/* Empty loss interval history */
dccp_li_hist_purge(ccid3_li_hist, &hcrx->ccid3hcrx_li_hist);
-
- kfree(dp->dccps_hc_rx_ccid_private);
- dp->dccps_hc_rx_ccid_private = NULL;
}
static void ccid3_hc_rx_get_info(struct sock *sk, struct tcp_info *info)
@@ -1178,12 +1151,11 @@ static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
return 0;
}
-static struct ccid ccid3 = {
+static struct ccid_operations ccid3 = {
.ccid_id = 3,
.ccid_name = "ccid3",
.ccid_owner = THIS_MODULE,
- .ccid_init = ccid3_init,
- .ccid_exit = ccid3_exit,
+ .ccid_hc_tx_obj_size = sizeof(struct ccid3_hc_tx_sock),
.ccid_hc_tx_init = ccid3_hc_tx_init,
.ccid_hc_tx_exit = ccid3_hc_tx_exit,
.ccid_hc_tx_send_packet = ccid3_hc_tx_send_packet,
@@ -1191,6 +1163,7 @@ static struct ccid ccid3 = {
.ccid_hc_tx_packet_recv = ccid3_hc_tx_packet_recv,
.ccid_hc_tx_insert_options = ccid3_hc_tx_insert_options,
.ccid_hc_tx_parse_options = ccid3_hc_tx_parse_options,
+ .ccid_hc_rx_obj_size = sizeof(struct ccid3_hc_rx_sock),
.ccid_hc_rx_init = ccid3_hc_rx_init,
.ccid_hc_rx_exit = ccid3_hc_rx_exit,
.ccid_hc_rx_insert_options = ccid3_hc_rx_insert_options,
@@ -1241,15 +1214,6 @@ module_init(ccid3_module_init);
static __exit void ccid3_module_exit(void)
{
-#ifdef CONFIG_IP_DCCP_UNLOAD_HACK
- /*
- * Hack to use while developing, so that we get rid of the control
- * sock, that is what keeps a refcount on dccp.ko -acme
- */
- extern void dccp_ctl_sock_exit(void);
-
- dccp_ctl_sock_exit();
-#endif
ccid_unregister(&ccid3);
if (ccid3_tx_hist != NULL) {
diff --git a/net/dccp/ccids/ccid3.h b/net/dccp/ccids/ccid3.h
index 0bde4583d091..f18b96d4e5a2 100644
--- a/net/dccp/ccids/ccid3.h
+++ b/net/dccp/ccids/ccid3.h
@@ -41,6 +41,7 @@
#include <linux/time.h>
#include <linux/types.h>
#include <linux/tfrc.h>
+#include "../ccid.h"
#define TFRC_MIN_PACKET_SIZE 16
#define TFRC_STD_PACKET_SIZE 256
@@ -135,12 +136,12 @@ struct ccid3_hc_rx_sock {
static inline struct ccid3_hc_tx_sock *ccid3_hc_tx_sk(const struct sock *sk)
{
- return dccp_sk(sk)->dccps_hc_tx_ccid_private;
+ return ccid_priv(dccp_sk(sk)->dccps_hc_tx_ccid);
}
static inline struct ccid3_hc_rx_sock *ccid3_hc_rx_sk(const struct sock *sk)
{
- return dccp_sk(sk)->dccps_hc_rx_ccid_private;
+ return ccid_priv(dccp_sk(sk)->dccps_hc_rx_ccid);
}
#endif /* _DCCP_CCID3_H_ */
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 93f26dd6e6cb..1fe509148689 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -59,8 +59,6 @@ extern void dccp_time_wait(struct sock *sk, int state, int timeo);
#define DCCP_RTO_MAX ((unsigned)(120 * HZ)) /* FIXME: using TCP value */
-extern struct proto dccp_prot;
-
/* is seq1 < seq2 ? */
static inline int before48(const u64 seq1, const u64 seq2)
{
@@ -120,7 +118,6 @@ DECLARE_SNMP_STAT(struct dccp_mib, dccp_statistics);
extern int dccp_retransmit_skb(struct sock *sk, struct sk_buff *skb);
-extern int dccp_send_response(struct sock *sk);
extern void dccp_send_ack(struct sock *sk);
extern void dccp_send_delayed_ack(struct sock *sk);
extern void dccp_send_sync(struct sock *sk, const u64 seq,
@@ -140,53 +137,8 @@ extern unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu);
extern const char *dccp_packet_name(const int type);
extern const char *dccp_state_name(const int state);
-static inline void dccp_set_state(struct sock *sk, const int state)
-{
- const int oldstate = sk->sk_state;
-
- dccp_pr_debug("%s(%p) %-10.10s -> %s\n",
- dccp_role(sk), sk,
- dccp_state_name(oldstate), dccp_state_name(state));
- WARN_ON(state == oldstate);
-
- switch (state) {
- case DCCP_OPEN:
- if (oldstate != DCCP_OPEN)
- DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
- break;
-
- case DCCP_CLOSED:
- if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN)
- DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
-
- sk->sk_prot->unhash(sk);
- if (inet_csk(sk)->icsk_bind_hash != NULL &&
- !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
- inet_put_port(&dccp_hashinfo, sk);
- /* fall through */
- default:
- if (oldstate == DCCP_OPEN)
- DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
- }
-
- /* Change state AFTER socket is unhashed to avoid closed
- * socket sitting in hash tables.
- */
- sk->sk_state = state;
-}
-
-static inline void dccp_done(struct sock *sk)
-{
- dccp_set_state(sk, DCCP_CLOSED);
- dccp_clear_xmit_timers(sk);
-
- sk->sk_shutdown = SHUTDOWN_MASK;
-
- if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_state_change(sk);
- else
- inet_csk_destroy_sock(sk);
-}
+extern void dccp_set_state(struct sock *sk, const int state);
+extern void dccp_done(struct sock *sk);
static inline void dccp_openreq_init(struct request_sock *req,
struct dccp_sock *dp,
@@ -209,10 +161,6 @@ extern struct sock *dccp_create_openreq_child(struct sock *sk,
extern int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
-extern void dccp_v4_err(struct sk_buff *skb, u32);
-
-extern int dccp_v4_rcv(struct sk_buff *skb);
-
extern struct sock *dccp_v4_request_recv_sock(struct sock *sk,
struct sk_buff *skb,
struct request_sock *req,
@@ -228,24 +176,30 @@ extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
const struct dccp_hdr *dh, const unsigned len);
-extern int dccp_v4_init_sock(struct sock *sk);
-extern int dccp_v4_destroy_sock(struct sock *sk);
+extern int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized);
+extern int dccp_destroy_sock(struct sock *sk);
extern void dccp_close(struct sock *sk, long timeout);
extern struct sk_buff *dccp_make_response(struct sock *sk,
struct dst_entry *dst,
struct request_sock *req);
-extern struct sk_buff *dccp_make_reset(struct sock *sk,
- struct dst_entry *dst,
- enum dccp_reset_codes code);
extern int dccp_connect(struct sock *sk);
extern int dccp_disconnect(struct sock *sk, int flags);
+extern void dccp_hash(struct sock *sk);
extern void dccp_unhash(struct sock *sk);
extern int dccp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen);
extern int dccp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen);
+#ifdef CONFIG_COMPAT
+extern int compat_dccp_getsockopt(struct sock *sk,
+ int level, int optname,
+ char __user *optval, int __user *optlen);
+extern int compat_dccp_setsockopt(struct sock *sk,
+ int level, int optname,
+ char __user *optval, int optlen);
+#endif
extern int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg);
extern int dccp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t size);
@@ -262,15 +216,14 @@ extern int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len);
extern int dccp_v4_checksum(const struct sk_buff *skb,
- const u32 saddr, const u32 daddr);
+ const __be32 saddr, const __be32 daddr);
-extern int dccp_v4_send_reset(struct sock *sk,
- enum dccp_reset_codes code);
+extern int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code);
extern void dccp_send_close(struct sock *sk, const int active);
extern int dccp_invalid_packet(struct sk_buff *skb);
static inline int dccp_bad_service_code(const struct sock *sk,
- const __u32 service)
+ const __be32 service)
{
const struct dccp_sock *dp = dccp_sk(sk);
@@ -334,41 +287,29 @@ static inline void dccp_hdr_set_seq(struct dccp_hdr *dh, const u64 gss)
{
struct dccp_hdr_ext *dhx = (struct dccp_hdr_ext *)((void *)dh +
sizeof(*dh));
-
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- dh->dccph_seq = htonl((gss >> 32)) >> 8;
-#elif defined(__BIG_ENDIAN_BITFIELD)
- dh->dccph_seq = htonl((gss >> 32));
-#else
-#error "Adjust your <asm/byteorder.h> defines"
-#endif
+ dh->dccph_seq2 = 0;
+ dh->dccph_seq = htons((gss >> 32) & 0xfffff);
dhx->dccph_seq_low = htonl(gss & 0xffffffff);
}
static inline void dccp_hdr_set_ack(struct dccp_hdr_ack_bits *dhack,
const u64 gsr)
{
-#if defined(__LITTLE_ENDIAN_BITFIELD)
- dhack->dccph_ack_nr_high = htonl((gsr >> 32)) >> 8;
-#elif defined(__BIG_ENDIAN_BITFIELD)
- dhack->dccph_ack_nr_high = htonl((gsr >> 32));
-#else
-#error "Adjust your <asm/byteorder.h> defines"
-#endif
+ dhack->dccph_reserved1 = 0;
+ dhack->dccph_ack_nr_high = htons(gsr >> 32);
dhack->dccph_ack_nr_low = htonl(gsr & 0xffffffff);
}
static inline void dccp_update_gsr(struct sock *sk, u64 seq)
{
struct dccp_sock *dp = dccp_sk(sk);
+ const struct dccp_minisock *dmsk = dccp_msk(sk);
dp->dccps_gsr = seq;
dccp_set_seqno(&dp->dccps_swl,
- (dp->dccps_gsr + 1 -
- (dp->dccps_options.dccpo_sequence_window / 4)));
+ dp->dccps_gsr + 1 - (dmsk->dccpms_sequence_window / 4));
dccp_set_seqno(&dp->dccps_swh,
- (dp->dccps_gsr +
- (3 * dp->dccps_options.dccpo_sequence_window) / 4));
+ dp->dccps_gsr + (3 * dmsk->dccpms_sequence_window) / 4);
}
static inline void dccp_update_gss(struct sock *sk, u64 seq)
@@ -378,7 +319,7 @@ static inline void dccp_update_gss(struct sock *sk, u64 seq)
dp->dccps_awh = dp->dccps_gss = seq;
dccp_set_seqno(&dp->dccps_awl,
(dp->dccps_gss -
- dp->dccps_options.dccpo_sequence_window + 1));
+ dccp_msk(sk)->dccpms_sequence_window + 1));
}
static inline int dccp_ack_pending(const struct sock *sk)
@@ -386,24 +327,22 @@ static inline int dccp_ack_pending(const struct sock *sk)
const struct dccp_sock *dp = dccp_sk(sk);
return dp->dccps_timestamp_echo != 0 ||
#ifdef CONFIG_IP_DCCP_ACKVEC
- (dp->dccps_options.dccpo_send_ack_vector &&
+ (dccp_msk(sk)->dccpms_send_ack_vector &&
dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) ||
#endif
inet_csk_ack_scheduled(sk);
}
-extern void dccp_insert_options(struct sock *sk, struct sk_buff *skb);
-extern void dccp_insert_option_elapsed_time(struct sock *sk,
+extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb);
+extern int dccp_insert_option_elapsed_time(struct sock *sk,
struct sk_buff *skb,
u32 elapsed_time);
-extern void dccp_insert_option_timestamp(struct sock *sk,
+extern int dccp_insert_option_timestamp(struct sock *sk,
struct sk_buff *skb);
-extern void dccp_insert_option(struct sock *sk, struct sk_buff *skb,
+extern int dccp_insert_option(struct sock *sk, struct sk_buff *skb,
unsigned char option,
const void *value, unsigned char len);
-extern struct socket *dccp_ctl_socket;
-
extern void dccp_timestamp(const struct sock *sk, struct timeval *tv);
static inline suseconds_t timeval_usecs(const struct timeval *tv)
@@ -444,4 +383,18 @@ static inline void timeval_sub_usecs(struct timeval *tv,
}
}
+#ifdef CONFIG_SYSCTL
+extern int dccp_sysctl_init(void);
+extern void dccp_sysctl_exit(void);
+#else
+static inline int dccp_sysctl_init(void)
+{
+ return 0;
+}
+
+static inline void dccp_sysctl_exit(void)
+{
+}
+#endif
+
#endif /* _DCCP_H */
diff --git a/net/dccp/diag.c b/net/dccp/diag.c
index 3f78c00e3822..0f25dc395967 100644
--- a/net/dccp/diag.c
+++ b/net/dccp/diag.c
@@ -30,7 +30,7 @@ static void dccp_get_info(struct sock *sk, struct tcp_info *info)
info->tcpi_backoff = icsk->icsk_backoff;
info->tcpi_pmtu = icsk->icsk_pmtu_cookie;
- if (dp->dccps_options.dccpo_send_ack_vector)
+ if (dccp_msk(sk)->dccpms_send_ack_vector)
info->tcpi_options |= TCPI_OPT_SACK;
ccid_hc_rx_get_info(dp->dccps_hc_rx_ccid, sk, info);
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
new file mode 100644
index 000000000000..e3dd30d36c8a
--- /dev/null
+++ b/net/dccp/feat.c
@@ -0,0 +1,586 @@
+/*
+ * net/dccp/feat.c
+ *
+ * An implementation of the DCCP protocol
+ * Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * 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 "dccp.h"
+#include "ccid.h"
+#include "feat.h"
+
+#define DCCP_FEAT_SP_NOAGREE (-123)
+
+int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
+ u8 *val, u8 len, gfp_t gfp)
+{
+ struct dccp_opt_pend *opt;
+
+ dccp_pr_debug("feat change type=%d feat=%d\n", type, feature);
+
+ /* XXX sanity check feat change request */
+
+ /* check if that feature is already being negotiated */
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+ /* ok we found a negotiation for this option already */
+ if (opt->dccpop_feat == feature && opt->dccpop_type == type) {
+ dccp_pr_debug("Replacing old\n");
+ /* replace */
+ BUG_ON(opt->dccpop_val == NULL);
+ kfree(opt->dccpop_val);
+ opt->dccpop_val = val;
+ opt->dccpop_len = len;
+ opt->dccpop_conf = 0;
+ return 0;
+ }
+ }
+
+ /* negotiation for a new feature */
+ opt = kmalloc(sizeof(*opt), gfp);
+ if (opt == NULL)
+ return -ENOMEM;
+
+ opt->dccpop_type = type;
+ opt->dccpop_feat = feature;
+ opt->dccpop_len = len;
+ opt->dccpop_val = val;
+ opt->dccpop_conf = 0;
+ opt->dccpop_sc = NULL;
+
+ BUG_ON(opt->dccpop_val == NULL);
+
+ list_add_tail(&opt->dccpop_node, &dmsk->dccpms_pending);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_change);
+
+static int dccp_feat_update_ccid(struct sock *sk, u8 type, u8 new_ccid_nr)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ /* figure out if we are changing our CCID or the peer's */
+ const int rx = type == DCCPO_CHANGE_R;
+ const u8 ccid_nr = rx ? dmsk->dccpms_rx_ccid : dmsk->dccpms_tx_ccid;
+ struct ccid *new_ccid;
+
+ /* Check if nothing is being changed. */
+ if (ccid_nr == new_ccid_nr)
+ return 0;
+
+ new_ccid = ccid_new(new_ccid_nr, sk, rx, GFP_ATOMIC);
+ if (new_ccid == NULL)
+ return -ENOMEM;
+
+ if (rx) {
+ ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+ dp->dccps_hc_rx_ccid = new_ccid;
+ dmsk->dccpms_rx_ccid = new_ccid_nr;
+ } else {
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_tx_ccid = new_ccid;
+ dmsk->dccpms_tx_ccid = new_ccid_nr;
+ }
+
+ return 0;
+}
+
+/* XXX taking only u8 vals */
+static int dccp_feat_update(struct sock *sk, u8 type, u8 feat, u8 val)
+{
+ dccp_pr_debug("changing [%d] feat %d to %d\n", type, feat, val);
+
+ switch (feat) {
+ case DCCPF_CCID:
+ return dccp_feat_update_ccid(sk, type, val);
+ default:
+ dccp_pr_debug("IMPLEMENT changing [%d] feat %d to %d\n",
+ type, feat, val);
+ break;
+ }
+ return 0;
+}
+
+static int dccp_feat_reconcile(struct sock *sk, struct dccp_opt_pend *opt,
+ u8 *rpref, u8 rlen)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ u8 *spref, slen, *res = NULL;
+ int i, j, rc, agree = 1;
+
+ BUG_ON(rpref == NULL);
+
+ /* check if we are the black sheep */
+ if (dp->dccps_role == DCCP_ROLE_CLIENT) {
+ spref = rpref;
+ slen = rlen;
+ rpref = opt->dccpop_val;
+ rlen = opt->dccpop_len;
+ } else {
+ spref = opt->dccpop_val;
+ slen = opt->dccpop_len;
+ }
+ /*
+ * Now we have server preference list in spref and client preference in
+ * rpref
+ */
+ BUG_ON(spref == NULL);
+ BUG_ON(rpref == NULL);
+
+ /* FIXME sanity check vals */
+
+ /* Are values in any order? XXX Lame "algorithm" here */
+ /* XXX assume values are 1 byte */
+ for (i = 0; i < slen; i++) {
+ for (j = 0; j < rlen; j++) {
+ if (spref[i] == rpref[j]) {
+ res = &spref[i];
+ break;
+ }
+ }
+ if (res)
+ break;
+ }
+
+ /* we didn't agree on anything */
+ if (res == NULL) {
+ /* confirm previous value */
+ switch (opt->dccpop_feat) {
+ case DCCPF_CCID:
+ /* XXX did i get this right? =P */
+ if (opt->dccpop_type == DCCPO_CHANGE_L)
+ res = &dccp_msk(sk)->dccpms_tx_ccid;
+ else
+ res = &dccp_msk(sk)->dccpms_rx_ccid;
+ break;
+
+ default:
+ WARN_ON(1); /* XXX implement res */
+ return -EFAULT;
+ }
+
+ dccp_pr_debug("Don't agree... reconfirming %d\n", *res);
+ agree = 0; /* this is used for mandatory options... */
+ }
+
+ /* need to put result and our preference list */
+ /* XXX assume 1 byte vals */
+ rlen = 1 + opt->dccpop_len;
+ rpref = kmalloc(rlen, GFP_ATOMIC);
+ if (rpref == NULL)
+ return -ENOMEM;
+
+ *rpref = *res;
+ memcpy(&rpref[1], opt->dccpop_val, opt->dccpop_len);
+
+ /* put it in the "confirm queue" */
+ if (opt->dccpop_sc == NULL) {
+ opt->dccpop_sc = kmalloc(sizeof(*opt->dccpop_sc), GFP_ATOMIC);
+ if (opt->dccpop_sc == NULL) {
+ kfree(rpref);
+ return -ENOMEM;
+ }
+ } else {
+ /* recycle the confirm slot */
+ BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
+ kfree(opt->dccpop_sc->dccpoc_val);
+ dccp_pr_debug("recycling confirm slot\n");
+ }
+ memset(opt->dccpop_sc, 0, sizeof(*opt->dccpop_sc));
+
+ opt->dccpop_sc->dccpoc_val = rpref;
+ opt->dccpop_sc->dccpoc_len = rlen;
+
+ /* update the option on our side [we are about to send the confirm] */
+ rc = dccp_feat_update(sk, opt->dccpop_type, opt->dccpop_feat, *res);
+ if (rc) {
+ kfree(opt->dccpop_sc->dccpoc_val);
+ kfree(opt->dccpop_sc);
+ opt->dccpop_sc = 0;
+ return rc;
+ }
+
+ dccp_pr_debug("Will confirm %d\n", *rpref);
+
+ /* say we want to change to X but we just got a confirm X, suppress our
+ * change
+ */
+ if (!opt->dccpop_conf) {
+ if (*opt->dccpop_val == *res)
+ opt->dccpop_conf = 1;
+ dccp_pr_debug("won't ask for change of same feature\n");
+ }
+
+ return agree ? 0 : DCCP_FEAT_SP_NOAGREE; /* used for mandatory opts */
+}
+
+static int dccp_feat_sp(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+{
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ struct dccp_opt_pend *opt;
+ int rc = 1;
+ u8 t;
+
+ /*
+ * We received a CHANGE. We gotta match it against our own preference
+ * list. If we got a CHANGE_R it means it's a change for us, so we need
+ * to compare our CHANGE_L list.
+ */
+ if (type == DCCPO_CHANGE_L)
+ t = DCCPO_CHANGE_R;
+ else
+ t = DCCPO_CHANGE_L;
+
+ /* find our preference list for this feature */
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+ if (opt->dccpop_type != t || opt->dccpop_feat != feature)
+ continue;
+
+ /* find the winner from the two preference lists */
+ rc = dccp_feat_reconcile(sk, opt, val, len);
+ break;
+ }
+
+ /* We didn't deal with the change. This can happen if we have no
+ * preference list for the feature. In fact, it just shouldn't
+ * happen---if we understand a feature, we should have a preference list
+ * with at least the default value.
+ */
+ BUG_ON(rc == 1);
+
+ return rc;
+}
+
+static int dccp_feat_nn(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+{
+ struct dccp_opt_pend *opt;
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ u8 *copy;
+ int rc;
+
+ /* NN features must be change L */
+ if (type == DCCPO_CHANGE_R) {
+ dccp_pr_debug("received CHANGE_R %d for NN feat %d\n",
+ type, feature);
+ return -EFAULT;
+ }
+
+ /* XXX sanity check opt val */
+
+ /* copy option so we can confirm it */
+ opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
+ if (opt == NULL)
+ return -ENOMEM;
+
+ copy = kmalloc(len, GFP_ATOMIC);
+ if (copy == NULL) {
+ kfree(opt);
+ return -ENOMEM;
+ }
+ memcpy(copy, val, len);
+
+ opt->dccpop_type = DCCPO_CONFIRM_R; /* NN can only confirm R */
+ opt->dccpop_feat = feature;
+ opt->dccpop_val = copy;
+ opt->dccpop_len = len;
+
+ /* change feature */
+ rc = dccp_feat_update(sk, type, feature, *val);
+ if (rc) {
+ kfree(opt->dccpop_val);
+ kfree(opt);
+ return rc;
+ }
+
+ dccp_pr_debug("Confirming NN feature %d (val=%d)\n", feature, *copy);
+ list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
+
+ return 0;
+}
+
+static void dccp_feat_empty_confirm(struct dccp_minisock *dmsk,
+ u8 type, u8 feature)
+{
+ /* XXX check if other confirms for that are queued and recycle slot */
+ struct dccp_opt_pend *opt = kzalloc(sizeof(*opt), GFP_ATOMIC);
+
+ if (opt == NULL) {
+ /* XXX what do we do? Ignoring should be fine. It's a change
+ * after all =P
+ */
+ return;
+ }
+
+ opt->dccpop_type = type == DCCPO_CHANGE_L ? DCCPO_CONFIRM_R :
+ DCCPO_CONFIRM_L;
+ opt->dccpop_feat = feature;
+ opt->dccpop_val = 0;
+ opt->dccpop_len = 0;
+
+ /* change feature */
+ dccp_pr_debug("Empty confirm feature %d type %d\n", feature, type);
+ list_add_tail(&opt->dccpop_node, &dmsk->dccpms_conf);
+}
+
+static void dccp_feat_flush_confirm(struct sock *sk)
+{
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ /* Check if there is anything to confirm in the first place */
+ int yes = !list_empty(&dmsk->dccpms_conf);
+
+ if (!yes) {
+ struct dccp_opt_pend *opt;
+
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+ if (opt->dccpop_conf) {
+ yes = 1;
+ break;
+ }
+ }
+ }
+
+ if (!yes)
+ return;
+
+ /* OK there is something to confirm... */
+ /* XXX check if packet is in flight? Send delayed ack?? */
+ if (sk->sk_state == DCCP_OPEN)
+ dccp_send_ack(sk);
+}
+
+int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature, u8 *val, u8 len)
+{
+ int rc;
+
+ dccp_pr_debug("got feat change type=%d feat=%d\n", type, feature);
+
+ /* figure out if it's SP or NN feature */
+ switch (feature) {
+ /* deal with SP features */
+ case DCCPF_CCID:
+ rc = dccp_feat_sp(sk, type, feature, val, len);
+ break;
+
+ /* deal with NN features */
+ case DCCPF_ACK_RATIO:
+ rc = dccp_feat_nn(sk, type, feature, val, len);
+ break;
+
+ /* XXX implement other features */
+ default:
+ rc = -EFAULT;
+ break;
+ }
+
+ /* check if there were problems changing features */
+ if (rc) {
+ /* If we don't agree on SP, we sent a confirm for old value.
+ * However we propagate rc to caller in case option was
+ * mandatory
+ */
+ if (rc != DCCP_FEAT_SP_NOAGREE)
+ dccp_feat_empty_confirm(dccp_msk(sk), type, feature);
+ }
+
+ /* generate the confirm [if required] */
+ dccp_feat_flush_confirm(sk);
+
+ return rc;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_change_recv);
+
+int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
+ u8 *val, u8 len)
+{
+ u8 t;
+ struct dccp_opt_pend *opt;
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ int rc = 1;
+ int all_confirmed = 1;
+
+ dccp_pr_debug("got feat confirm type=%d feat=%d\n", type, feature);
+
+ /* XXX sanity check type & feat */
+
+ /* locate our change request */
+ t = type == DCCPO_CONFIRM_L ? DCCPO_CHANGE_R : DCCPO_CHANGE_L;
+
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+ if (!opt->dccpop_conf && opt->dccpop_type == t &&
+ opt->dccpop_feat == feature) {
+ /* we found it */
+ /* XXX do sanity check */
+
+ opt->dccpop_conf = 1;
+
+ /* We got a confirmation---change the option */
+ dccp_feat_update(sk, opt->dccpop_type,
+ opt->dccpop_feat, *val);
+
+ dccp_pr_debug("feat %d type %d confirmed %d\n",
+ feature, type, *val);
+ rc = 0;
+ break;
+ }
+
+ if (!opt->dccpop_conf)
+ all_confirmed = 0;
+ }
+
+ /* fix re-transmit timer */
+ /* XXX gotta make sure that no option negotiation occurs during
+ * connection shutdown. Consider that the CLOSEREQ is sent and timer is
+ * on. if all options are confirmed it might kill timer which should
+ * remain alive until close is received.
+ */
+ if (all_confirmed) {
+ dccp_pr_debug("clear feat negotiation timer %p\n", sk);
+ inet_csk_clear_xmit_timer(sk, ICSK_TIME_RETRANS);
+ }
+
+ if (rc)
+ dccp_pr_debug("feat %d type %d never requested\n",
+ feature, type);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_confirm_recv);
+
+void dccp_feat_clean(struct dccp_minisock *dmsk)
+{
+ struct dccp_opt_pend *opt, *next;
+
+ list_for_each_entry_safe(opt, next, &dmsk->dccpms_pending,
+ dccpop_node) {
+ BUG_ON(opt->dccpop_val == NULL);
+ kfree(opt->dccpop_val);
+
+ if (opt->dccpop_sc != NULL) {
+ BUG_ON(opt->dccpop_sc->dccpoc_val == NULL);
+ kfree(opt->dccpop_sc->dccpoc_val);
+ kfree(opt->dccpop_sc);
+ }
+
+ kfree(opt);
+ }
+ INIT_LIST_HEAD(&dmsk->dccpms_pending);
+
+ list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
+ BUG_ON(opt == NULL);
+ if (opt->dccpop_val != NULL)
+ kfree(opt->dccpop_val);
+ kfree(opt);
+ }
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_clean);
+
+/* this is to be called only when a listening sock creates its child. It is
+ * assumed by the function---the confirm is not duplicated, but rather it is
+ * "passed on".
+ */
+int dccp_feat_clone(struct sock *oldsk, struct sock *newsk)
+{
+ struct dccp_minisock *olddmsk = dccp_msk(oldsk);
+ struct dccp_minisock *newdmsk = dccp_msk(newsk);
+ struct dccp_opt_pend *opt;
+ int rc = 0;
+
+ INIT_LIST_HEAD(&newdmsk->dccpms_pending);
+ INIT_LIST_HEAD(&newdmsk->dccpms_conf);
+
+ list_for_each_entry(opt, &olddmsk->dccpms_pending, dccpop_node) {
+ struct dccp_opt_pend *newopt;
+ /* copy the value of the option */
+ u8 *val = kmalloc(opt->dccpop_len, GFP_ATOMIC);
+
+ if (val == NULL)
+ goto out_clean;
+ memcpy(val, opt->dccpop_val, opt->dccpop_len);
+
+ newopt = kmalloc(sizeof(*newopt), GFP_ATOMIC);
+ if (newopt == NULL) {
+ kfree(val);
+ goto out_clean;
+ }
+
+ /* insert the option */
+ memcpy(newopt, opt, sizeof(*newopt));
+ newopt->dccpop_val = val;
+ list_add_tail(&newopt->dccpop_node, &newdmsk->dccpms_pending);
+
+ /* XXX what happens with backlogs and multiple connections at
+ * once...
+ */
+ /* the master socket no longer needs to worry about confirms */
+ opt->dccpop_sc = 0; /* it's not a memleak---new socket has it */
+
+ /* reset state for a new socket */
+ opt->dccpop_conf = 0;
+ }
+
+ /* XXX not doing anything about the conf queue */
+
+out:
+ return rc;
+
+out_clean:
+ dccp_feat_clean(newdmsk);
+ rc = -ENOMEM;
+ goto out;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_clone);
+
+static int __dccp_feat_init(struct dccp_minisock *dmsk, u8 type, u8 feat,
+ u8 *val, u8 len)
+{
+ int rc = -ENOMEM;
+ u8 *copy = kmalloc(len, GFP_KERNEL);
+
+ if (copy != NULL) {
+ memcpy(copy, val, len);
+ rc = dccp_feat_change(dmsk, type, feat, copy, len, GFP_KERNEL);
+ if (rc)
+ kfree(copy);
+ }
+ return rc;
+}
+
+int dccp_feat_init(struct dccp_minisock *dmsk)
+{
+ int rc;
+
+ INIT_LIST_HEAD(&dmsk->dccpms_pending);
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
+
+ /* CCID L */
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_CCID,
+ &dmsk->dccpms_tx_ccid, 1);
+ if (rc)
+ goto out;
+
+ /* CCID R */
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_R, DCCPF_CCID,
+ &dmsk->dccpms_rx_ccid, 1);
+ if (rc)
+ goto out;
+
+ /* Ack ratio */
+ rc = __dccp_feat_init(dmsk, DCCPO_CHANGE_L, DCCPF_ACK_RATIO,
+ &dmsk->dccpms_ack_ratio, 1);
+out:
+ return rc;
+}
+
+EXPORT_SYMBOL_GPL(dccp_feat_init);
diff --git a/net/dccp/feat.h b/net/dccp/feat.h
new file mode 100644
index 000000000000..6048373c7186
--- /dev/null
+++ b/net/dccp/feat.h
@@ -0,0 +1,29 @@
+#ifndef _DCCP_FEAT_H
+#define _DCCP_FEAT_H
+/*
+ * net/dccp/feat.h
+ *
+ * An implementation of the DCCP protocol
+ * Copyright (c) 2005 Andrea Bittau <a.bittau@cs.ucl.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+struct sock;
+struct dccp_minisock;
+
+extern int dccp_feat_change(struct dccp_minisock *dmsk, u8 type, u8 feature,
+ u8 *val, u8 len, gfp_t gfp);
+extern int dccp_feat_change_recv(struct sock *sk, u8 type, u8 feature,
+ u8 *val, u8 len);
+extern int dccp_feat_confirm_recv(struct sock *sk, u8 type, u8 feature,
+ u8 *val, u8 len);
+extern void dccp_feat_clean(struct dccp_minisock *dmsk);
+extern int dccp_feat_clone(struct sock *oldsk, struct sock *newsk);
+extern int dccp_feat_init(struct dccp_minisock *dmsk);
+
+#endif /* _DCCP_FEAT_H */
diff --git a/net/dccp/input.c b/net/dccp/input.c
index b6cba72b44e8..bfc53665516b 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -32,7 +32,7 @@ static void dccp_fin(struct sock *sk, struct sk_buff *skb)
static void dccp_rcv_close(struct sock *sk, struct sk_buff *skb)
{
- dccp_v4_send_reset(sk, DCCP_RESET_CODE_CLOSED);
+ dccp_send_reset(sk, DCCP_RESET_CODE_CLOSED);
dccp_fin(sk, skb);
dccp_set_state(sk, DCCP_CLOSED);
sk_wake_async(sk, 1, POLL_HUP);
@@ -56,11 +56,11 @@ static void dccp_rcv_closereq(struct sock *sk, struct sk_buff *skb)
dccp_send_close(sk, 0);
}
-static inline void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb)
+static void dccp_event_ack_recv(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
- if (dp->dccps_options.dccpo_send_ack_vector)
+ if (dccp_msk(sk)->dccpms_send_ack_vector)
dccp_ackvec_check_rcv_ackno(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_ack_seq);
}
@@ -151,9 +151,8 @@ static int dccp_check_seqno(struct sock *sk, struct sk_buff *skb)
return 0;
}
-static inline int __dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
- const struct dccp_hdr *dh,
- const unsigned len)
+static int __dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
+ const struct dccp_hdr *dh, const unsigned len)
{
struct dccp_sock *dp = dccp_sk(sk);
@@ -247,7 +246,7 @@ int dccp_rcv_established(struct sock *sk, struct sk_buff *skb,
if (DCCP_SKB_CB(skb)->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- if (dp->dccps_options.dccpo_send_ack_vector &&
+ if (dccp_msk(sk)->dccpms_send_ack_vector &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
@@ -300,7 +299,10 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
goto out_invalid_packet;
}
- if (dp->dccps_options.dccpo_send_ack_vector &&
+ if (dccp_parse_options(sk, skb))
+ goto out_invalid_packet;
+
+ if (dccp_msk(sk)->dccpms_send_ack_vector &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
@@ -321,14 +323,6 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
dccp_set_seqno(&dp->dccps_swl,
max48(dp->dccps_swl, dp->dccps_isr));
- if (ccid_hc_rx_init(dp->dccps_hc_rx_ccid, sk) != 0 ||
- ccid_hc_tx_init(dp->dccps_hc_tx_ccid, sk) != 0) {
- ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk);
- ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk);
- /* FIXME: send appropriate RESET code */
- goto out_invalid_packet;
- }
-
dccp_sync_mss(sk, icsk->icsk_pmtu_cookie);
/*
@@ -492,7 +486,7 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (dcb->dccpd_ack_seq != DCCP_PKT_WITHOUT_ACK_SEQ)
dccp_event_ack_recv(sk, skb);
- if (dp->dccps_options.dccpo_send_ack_vector &&
+ if (dccp_msk(sk)->dccpms_send_ack_vector &&
dccp_ackvec_add(dp->dccps_hc_rx_ackvec, sk,
DCCP_SKB_CB(skb)->dccpd_seq,
DCCP_ACKVEC_STATE_RECEIVED))
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index dc0487b5bace..29047995c695 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -18,8 +18,10 @@
#include <linux/random.h>
#include <net/icmp.h>
+#include <net/inet_common.h>
#include <net/inet_hashtables.h>
#include <net/inet_sock.h>
+#include <net/protocol.h>
#include <net/sock.h>
#include <net/timewait_sock.h>
#include <net/tcp_states.h>
@@ -28,14 +30,14 @@
#include "ackvec.h"
#include "ccid.h"
#include "dccp.h"
+#include "feat.h"
-struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
- .lhash_lock = RW_LOCK_UNLOCKED,
- .lhash_users = ATOMIC_INIT(0),
- .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
-};
-
-EXPORT_SYMBOL_GPL(dccp_hashinfo);
+/*
+ * This is the global socket data structure used for responding to
+ * the Out-of-the-blue (OOTB) packets. A control sock will be created
+ * for this socket at the initialization time.
+ */
+static struct socket *dccp_v4_ctl_socket;
static int dccp_v4_get_port(struct sock *sk, const unsigned short snum)
{
@@ -43,18 +45,6 @@ static int dccp_v4_get_port(struct sock *sk, const unsigned short snum)
inet_csk_bind_conflict);
}
-static void dccp_v4_hash(struct sock *sk)
-{
- inet_hash(&dccp_hashinfo, sk);
-}
-
-void dccp_unhash(struct sock *sk)
-{
- inet_unhash(&dccp_hashinfo, sk);
-}
-
-EXPORT_SYMBOL_GPL(dccp_unhash);
-
int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
@@ -207,11 +197,12 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk,
} /* else let the usual retransmit timer handle it */
}
-static void dccp_v4_ctl_send_ack(struct sk_buff *rxskb)
+static void dccp_v4_reqsk_send_ack(struct sk_buff *rxskb,
+ struct request_sock *req)
{
int err;
struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
- const int dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
+ const u32 dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
sizeof(struct dccp_hdr_ext) +
sizeof(struct dccp_hdr_ack_bits);
struct sk_buff *skb;
@@ -219,12 +210,12 @@ static void dccp_v4_ctl_send_ack(struct sk_buff *rxskb)
if (((struct rtable *)rxskb->dst)->rt_type != RTN_LOCAL)
return;
- skb = alloc_skb(MAX_DCCP_HEADER + 15, GFP_ATOMIC);
+ skb = alloc_skb(dccp_v4_ctl_socket->sk->sk_prot->max_header, GFP_ATOMIC);
if (skb == NULL)
return;
/* Reserve space for headers. */
- skb_reserve(skb, MAX_DCCP_HEADER);
+ skb_reserve(skb, dccp_v4_ctl_socket->sk->sk_prot->max_header);
skb->dst = dst_clone(rxskb->dst);
@@ -243,11 +234,11 @@ static void dccp_v4_ctl_send_ack(struct sk_buff *rxskb)
dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
DCCP_SKB_CB(rxskb)->dccpd_seq);
- bh_lock_sock(dccp_ctl_socket->sk);
- err = ip_build_and_send_pkt(skb, dccp_ctl_socket->sk,
+ bh_lock_sock(dccp_v4_ctl_socket->sk);
+ err = ip_build_and_send_pkt(skb, dccp_v4_ctl_socket->sk,
rxskb->nh.iph->daddr,
rxskb->nh.iph->saddr, NULL);
- bh_unlock_sock(dccp_ctl_socket->sk);
+ bh_unlock_sock(dccp_v4_ctl_socket->sk);
if (err == NET_XMIT_CN || err == 0) {
DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
@@ -255,12 +246,6 @@ static void dccp_v4_ctl_send_ack(struct sk_buff *rxskb)
}
}
-static void dccp_v4_reqsk_send_ack(struct sk_buff *skb,
- struct request_sock *req)
-{
- dccp_v4_ctl_send_ack(skb);
-}
-
static int dccp_v4_send_response(struct sock *sk, struct request_sock *req,
struct dst_entry *dst)
{
@@ -275,7 +260,10 @@ static int dccp_v4_send_response(struct sock *sk, struct request_sock *req,
skb = dccp_make_response(sk, dst, req);
if (skb != NULL) {
const struct inet_request_sock *ireq = inet_rsk(req);
+ struct dccp_hdr *dh = dccp_hdr(skb);
+ dh->dccph_checksum = dccp_v4_checksum(skb, ireq->loc_addr,
+ ireq->rmt_addr);
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr,
ireq->rmt_addr,
@@ -301,7 +289,7 @@ out:
* check at all. A more general error queue to queue errors for later handling
* is probably better.
*/
-void dccp_v4_err(struct sk_buff *skb, u32 info)
+static void dccp_v4_err(struct sk_buff *skb, u32 info)
{
const struct iphdr *iph = (struct iphdr *)skb->data;
const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data +
@@ -456,32 +444,6 @@ void dccp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb)
EXPORT_SYMBOL_GPL(dccp_v4_send_check);
-int dccp_v4_send_reset(struct sock *sk, enum dccp_reset_codes code)
-{
- struct sk_buff *skb;
- /*
- * FIXME: what if rebuild_header fails?
- * Should we be doing a rebuild_header here?
- */
- int err = inet_sk_rebuild_header(sk);
-
- if (err != 0)
- return err;
-
- skb = dccp_make_reset(sk, sk->sk_dst_cache, code);
- if (skb != NULL) {
- const struct inet_sock *inet = inet_sk(sk);
-
- memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
- err = ip_build_and_send_pkt(skb, sk,
- inet->saddr, inet->daddr, NULL);
- if (err == NET_XMIT_CN)
- err = 0;
- }
-
- return err;
-}
-
static inline u64 dccp_v4_init_sequence(const struct sock *sk,
const struct sk_buff *skb)
{
@@ -497,9 +459,9 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
struct dccp_sock dp;
struct request_sock *req;
struct dccp_request_sock *dreq;
- const __u32 saddr = skb->nh.iph->saddr;
- const __u32 daddr = skb->nh.iph->daddr;
- const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
+ const __be32 saddr = skb->nh.iph->saddr;
+ const __be32 daddr = skb->nh.iph->daddr;
+ const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
__u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
@@ -535,7 +497,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (req == NULL)
goto drop;
- /* FIXME: process options */
+ if (dccp_parse_options(sk, skb))
+ goto drop;
dccp_openreq_init(req, &dp, skb);
@@ -660,8 +623,8 @@ static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
return sk;
}
-int dccp_v4_checksum(const struct sk_buff *skb, const u32 saddr,
- const u32 daddr)
+int dccp_v4_checksum(const struct sk_buff *skb, const __be32 saddr,
+ const __be32 daddr)
{
const struct dccp_hdr* dh = dccp_hdr(skb);
int checksum_len;
@@ -680,8 +643,10 @@ int dccp_v4_checksum(const struct sk_buff *skb, const u32 saddr,
IPPROTO_DCCP, tmp);
}
+EXPORT_SYMBOL_GPL(dccp_v4_checksum);
+
static int dccp_v4_verify_checksum(struct sk_buff *skb,
- const u32 saddr, const u32 daddr)
+ const __be32 saddr, const __be32 daddr)
{
struct dccp_hdr *dh = dccp_hdr(skb);
int checksum_len;
@@ -741,16 +706,17 @@ static void dccp_v4_ctl_send_reset(struct sk_buff *rxskb)
if (((struct rtable *)rxskb->dst)->rt_type != RTN_LOCAL)
return;
- dst = dccp_v4_route_skb(dccp_ctl_socket->sk, rxskb);
+ dst = dccp_v4_route_skb(dccp_v4_ctl_socket->sk, rxskb);
if (dst == NULL)
return;
- skb = alloc_skb(MAX_DCCP_HEADER + 15, GFP_ATOMIC);
+ skb = alloc_skb(dccp_v4_ctl_socket->sk->sk_prot->max_header,
+ GFP_ATOMIC);
if (skb == NULL)
goto out;
/* Reserve space for headers. */
- skb_reserve(skb, MAX_DCCP_HEADER);
+ skb_reserve(skb, dccp_v4_ctl_socket->sk->sk_prot->max_header);
skb->dst = dst_clone(dst);
skb->h.raw = skb_push(skb, dccp_hdr_reset_len);
@@ -778,11 +744,11 @@ static void dccp_v4_ctl_send_reset(struct sk_buff *rxskb)
dh->dccph_checksum = dccp_v4_checksum(skb, rxskb->nh.iph->saddr,
rxskb->nh.iph->daddr);
- bh_lock_sock(dccp_ctl_socket->sk);
- err = ip_build_and_send_pkt(skb, dccp_ctl_socket->sk,
+ bh_lock_sock(dccp_v4_ctl_socket->sk);
+ err = ip_build_and_send_pkt(skb, dccp_v4_ctl_socket->sk,
rxskb->nh.iph->daddr,
rxskb->nh.iph->saddr, NULL);
- bh_unlock_sock(dccp_ctl_socket->sk);
+ bh_unlock_sock(dccp_v4_ctl_socket->sk);
if (err == NET_XMIT_CN || err == 0) {
DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
@@ -912,7 +878,7 @@ int dccp_invalid_packet(struct sk_buff *skb)
EXPORT_SYMBOL_GPL(dccp_invalid_packet);
/* this is called when real data arrives */
-int dccp_v4_rcv(struct sk_buff *skb)
+static int dccp_v4_rcv(struct sk_buff *skb)
{
const struct dccp_hdr *dh;
struct sock *sk;
@@ -1019,111 +985,37 @@ do_time_wait:
goto no_dccp_socket;
}
-struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
- .queue_xmit = ip_queue_xmit,
- .send_check = dccp_v4_send_check,
- .rebuild_header = inet_sk_rebuild_header,
- .conn_request = dccp_v4_conn_request,
- .syn_recv_sock = dccp_v4_request_recv_sock,
- .net_header_len = sizeof(struct iphdr),
- .setsockopt = ip_setsockopt,
- .getsockopt = ip_getsockopt,
- .addr2sockaddr = inet_csk_addr2sockaddr,
- .sockaddr_len = sizeof(struct sockaddr_in),
+static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
+ .queue_xmit = ip_queue_xmit,
+ .send_check = dccp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+ .conn_request = dccp_v4_conn_request,
+ .syn_recv_sock = dccp_v4_request_recv_sock,
+ .net_header_len = sizeof(struct iphdr),
+ .setsockopt = ip_setsockopt,
+ .getsockopt = ip_getsockopt,
+ .addr2sockaddr = inet_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in),
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_ip_setsockopt,
+ .compat_getsockopt = compat_ip_getsockopt,
+#endif
};
-int dccp_v4_init_sock(struct sock *sk)
-{
- struct dccp_sock *dp = dccp_sk(sk);
- struct inet_connection_sock *icsk = inet_csk(sk);
- static int dccp_ctl_socket_init = 1;
-
- dccp_options_init(&dp->dccps_options);
- do_gettimeofday(&dp->dccps_epoch);
-
- if (dp->dccps_options.dccpo_send_ack_vector) {
- dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(DCCP_MAX_ACKVEC_LEN,
- GFP_KERNEL);
- if (dp->dccps_hc_rx_ackvec == NULL)
- return -ENOMEM;
- }
-
- /*
- * FIXME: We're hardcoding the CCID, and doing this at this point makes
- * the listening (master) sock get CCID control blocks, which is not
- * necessary, but for now, to not mess with the test userspace apps,
- * lets leave it here, later the real solution is to do this in a
- * setsockopt(CCIDs-I-want/accept). -acme
- */
- if (likely(!dccp_ctl_socket_init)) {
- dp->dccps_hc_rx_ccid = ccid_init(dp->dccps_options.dccpo_rx_ccid,
- sk);
- dp->dccps_hc_tx_ccid = ccid_init(dp->dccps_options.dccpo_tx_ccid,
- sk);
- if (dp->dccps_hc_rx_ccid == NULL ||
- dp->dccps_hc_tx_ccid == NULL) {
- ccid_exit(dp->dccps_hc_rx_ccid, sk);
- ccid_exit(dp->dccps_hc_tx_ccid, sk);
- if (dp->dccps_options.dccpo_send_ack_vector) {
- dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
- dp->dccps_hc_rx_ackvec = NULL;
- }
- dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
- return -ENOMEM;
- }
- } else
- dccp_ctl_socket_init = 0;
-
- dccp_init_xmit_timers(sk);
- icsk->icsk_rto = DCCP_TIMEOUT_INIT;
- sk->sk_state = DCCP_CLOSED;
- sk->sk_write_space = dccp_write_space;
- icsk->icsk_af_ops = &dccp_ipv4_af_ops;
- icsk->icsk_sync_mss = dccp_sync_mss;
- dp->dccps_mss_cache = 536;
- dp->dccps_role = DCCP_ROLE_UNDEFINED;
- dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
-
- return 0;
-}
-
-EXPORT_SYMBOL_GPL(dccp_v4_init_sock);
-
-int dccp_v4_destroy_sock(struct sock *sk)
+static int dccp_v4_init_sock(struct sock *sk)
{
- struct dccp_sock *dp = dccp_sk(sk);
+ static __u8 dccp_v4_ctl_sock_initialized;
+ int err = dccp_init_sock(sk, dccp_v4_ctl_sock_initialized);
- /*
- * DCCP doesn't use sk_write_queue, just sk_send_head
- * for retransmissions
- */
- if (sk->sk_send_head != NULL) {
- kfree_skb(sk->sk_send_head);
- sk->sk_send_head = NULL;
+ if (err == 0) {
+ if (unlikely(!dccp_v4_ctl_sock_initialized))
+ dccp_v4_ctl_sock_initialized = 1;
+ inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops;
}
- /* Clean up a referenced DCCP bind bucket. */
- if (inet_csk(sk)->icsk_bind_hash != NULL)
- inet_put_port(&dccp_hashinfo, sk);
-
- kfree(dp->dccps_service_list);
- dp->dccps_service_list = NULL;
-
- ccid_hc_rx_exit(dp->dccps_hc_rx_ccid, sk);
- ccid_hc_tx_exit(dp->dccps_hc_tx_ccid, sk);
- if (dp->dccps_options.dccpo_send_ack_vector) {
- dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
- dp->dccps_hc_rx_ackvec = NULL;
- }
- ccid_exit(dp->dccps_hc_rx_ccid, sk);
- ccid_exit(dp->dccps_hc_tx_ccid, sk);
- dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
-
- return 0;
+ return err;
}
-EXPORT_SYMBOL_GPL(dccp_v4_destroy_sock);
-
static void dccp_v4_reqsk_destructor(struct request_sock *req)
{
kfree(inet_rsk(req)->opt);
@@ -1142,7 +1034,7 @@ static struct timewait_sock_ops dccp_timewait_sock_ops = {
.twsk_obj_size = sizeof(struct inet_timewait_sock),
};
-struct proto dccp_prot = {
+static struct proto dccp_v4_prot = {
.name = "DCCP",
.owner = THIS_MODULE,
.close = dccp_close,
@@ -1155,17 +1047,110 @@ struct proto dccp_prot = {
.sendmsg = dccp_sendmsg,
.recvmsg = dccp_recvmsg,
.backlog_rcv = dccp_v4_do_rcv,
- .hash = dccp_v4_hash,
+ .hash = dccp_hash,
.unhash = dccp_unhash,
.accept = inet_csk_accept,
.get_port = dccp_v4_get_port,
.shutdown = dccp_shutdown,
- .destroy = dccp_v4_destroy_sock,
+ .destroy = dccp_destroy_sock,
.orphan_count = &dccp_orphan_count,
.max_header = MAX_DCCP_HEADER,
.obj_size = sizeof(struct dccp_sock),
.rsk_prot = &dccp_request_sock_ops,
.twsk_prot = &dccp_timewait_sock_ops,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_dccp_setsockopt,
+ .compat_getsockopt = compat_dccp_getsockopt,
+#endif
+};
+
+static struct net_protocol dccp_v4_protocol = {
+ .handler = dccp_v4_rcv,
+ .err_handler = dccp_v4_err,
+ .no_policy = 1,
+};
+
+static const struct proto_ops inet_dccp_ops = {
+ .family = PF_INET,
+ .owner = THIS_MODULE,
+ .release = inet_release,
+ .bind = inet_bind,
+ .connect = inet_stream_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = inet_getname,
+ /* FIXME: work on tcp_poll to rename it to inet_csk_poll */
+ .poll = dccp_poll,
+ .ioctl = inet_ioctl,
+ /* FIXME: work on inet_listen to rename it to sock_common_listen */
+ .listen = inet_dccp_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
+#endif
};
-EXPORT_SYMBOL_GPL(dccp_prot);
+static struct inet_protosw dccp_v4_protosw = {
+ .type = SOCK_DCCP,
+ .protocol = IPPROTO_DCCP,
+ .prot = &dccp_v4_prot,
+ .ops = &inet_dccp_ops,
+ .capability = -1,
+ .no_check = 0,
+ .flags = INET_PROTOSW_ICSK,
+};
+
+static int __init dccp_v4_init(void)
+{
+ int err = proto_register(&dccp_v4_prot, 1);
+
+ if (err != 0)
+ goto out;
+
+ err = inet_add_protocol(&dccp_v4_protocol, IPPROTO_DCCP);
+ if (err != 0)
+ goto out_proto_unregister;
+
+ inet_register_protosw(&dccp_v4_protosw);
+
+ err = inet_csk_ctl_sock_create(&dccp_v4_ctl_socket, PF_INET,
+ SOCK_DCCP, IPPROTO_DCCP);
+ if (err)
+ goto out_unregister_protosw;
+out:
+ return err;
+out_unregister_protosw:
+ inet_unregister_protosw(&dccp_v4_protosw);
+ inet_del_protocol(&dccp_v4_protocol, IPPROTO_DCCP);
+out_proto_unregister:
+ proto_unregister(&dccp_v4_prot);
+ goto out;
+}
+
+static void __exit dccp_v4_exit(void)
+{
+ inet_unregister_protosw(&dccp_v4_protosw);
+ inet_del_protocol(&dccp_v4_protocol, IPPROTO_DCCP);
+ proto_unregister(&dccp_v4_prot);
+}
+
+module_init(dccp_v4_init);
+module_exit(dccp_v4_exit);
+
+/*
+ * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
+ * values directly, Also cover the case where the protocol is not specified,
+ * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP
+ */
+MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6");
+MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@mandriva.com>");
+MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 80c4d048869e..65e2ab0886e6 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -1,6 +1,6 @@
/*
* DCCP over IPv6
- * Linux INET6 implementation
+ * Linux INET6 implementation
*
* Based on net/dccp6/ipv6.c
*
@@ -33,6 +33,9 @@
#include "dccp.h"
#include "ipv6.h"
+/* Socket used for sending RSTs and ACKs */
+static struct socket *dccp_v6_ctl_socket;
+
static void dccp_v6_ctl_send_reset(struct sk_buff *skb);
static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
struct request_sock *req);
@@ -53,7 +56,7 @@ static void dccp_v6_hash(struct sock *sk)
{
if (sk->sk_state != DCCP_CLOSED) {
if (inet_csk(sk)->icsk_af_ops == &dccp_ipv6_mapped) {
- dccp_prot.hash(sk);
+ dccp_hash(sk);
return;
}
local_bh_disable();
@@ -63,8 +66,8 @@ static void dccp_v6_hash(struct sock *sk)
}
static inline u16 dccp_v6_check(struct dccp_hdr *dh, int len,
- struct in6_addr *saddr,
- struct in6_addr *daddr,
+ struct in6_addr *saddr,
+ struct in6_addr *daddr,
unsigned long base)
{
return csum_ipv6_magic(saddr, daddr, len, IPPROTO_DCCP, base);
@@ -79,17 +82,17 @@ static __u32 dccp_v6_init_sequence(struct sock *sk, struct sk_buff *skb)
skb->nh.ipv6h->saddr.s6_addr32,
dh->dccph_dport,
dh->dccph_sport);
- else
- return secure_dccp_sequence_number(skb->nh.iph->daddr,
- skb->nh.iph->saddr,
- dh->dccph_dport,
- dh->dccph_sport);
+
+ return secure_dccp_sequence_number(skb->nh.iph->daddr,
+ skb->nh.iph->saddr,
+ dh->dccph_dport,
+ dh->dccph_sport);
}
-static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len)
{
- struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *)uaddr;
struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk);
@@ -102,10 +105,10 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
dp->dccps_role = DCCP_ROLE_CLIENT;
- if (addr_len < SIN6_LEN_RFC2133)
+ if (addr_len < SIN6_LEN_RFC2133)
return -EINVAL;
- if (usin->sin6_family != AF_INET6)
+ if (usin->sin6_family != AF_INET6)
return -EAFNOSUPPORT;
memset(&fl, 0, sizeof(fl));
@@ -122,17 +125,15 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl6_sock_release(flowlabel);
}
}
-
/*
- * connect() to INADDR_ANY means loopback (BSD'ism).
- */
-
- if (ipv6_addr_any(&usin->sin6_addr))
- usin->sin6_addr.s6_addr[15] = 0x1;
+ * connect() to INADDR_ANY means loopback (BSD'ism).
+ */
+ if (ipv6_addr_any(&usin->sin6_addr))
+ usin->sin6_addr.s6_addr[15] = 1;
addr_type = ipv6_addr_type(&usin->sin6_addr);
- if(addr_type & IPV6_ADDR_MULTICAST)
+ if (addr_type & IPV6_ADDR_MULTICAST)
return -ENETUNREACH;
if (addr_type & IPV6_ADDR_LINKLOCAL) {
@@ -157,9 +158,8 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
np->flow_label = fl.fl6_flowlabel;
/*
- * DCCP over IPv4
+ * DCCP over IPv4
*/
-
if (addr_type == IPV6_ADDR_MAPPED) {
u32 exthdrlen = icsk->icsk_ext_hdr_len;
struct sockaddr_in sin;
@@ -177,7 +177,6 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
sk->sk_backlog_rcv = dccp_v4_do_rcv;
err = dccp_v4_connect(sk, (struct sockaddr *)&sin, sizeof(sin));
-
if (err) {
icsk->icsk_ext_hdr_len = exthdrlen;
icsk->icsk_af_ops = &dccp_ipv6_af_ops;
@@ -203,8 +202,9 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
fl.fl_ip_dport = usin->sin6_port;
fl.fl_ip_sport = inet->sport;
- if (np->opt && np->opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+ if (np->opt != NULL && np->opt->srcrt != NULL) {
+ const struct rt0_hdr *rt0 = (struct rt0_hdr *)np->opt->srcrt;
+
ipv6_addr_copy(&final, &fl.fl6_dst);
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
final_p = &final;
@@ -213,10 +213,12 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto failure;
+
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+ err = xfrm_lookup(&dst, &fl, sk, 0);
+ if (err < 0)
goto failure;
if (saddr == NULL) {
@@ -231,7 +233,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
ip6_dst_store(sk, dst, NULL);
icsk->icsk_ext_hdr_len = 0;
- if (np->opt)
+ if (np->opt != NULL)
icsk->icsk_ext_hdr_len = (np->opt->opt_flen +
np->opt->opt_nflen);
@@ -264,7 +266,7 @@ failure:
}
static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
- int type, int code, int offset, __u32 info)
+ int type, int code, int offset, __be32 info)
{
struct ipv6hdr *hdr = (struct ipv6hdr *)skb->data;
const struct dccp_hdr *dh = (struct dccp_hdr *)(skb->data + offset);
@@ -305,7 +307,6 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
/* icmp should have updated the destination cache entry */
dst = __sk_dst_check(sk, np->dst_cookie);
-
if (dst == NULL) {
struct inet_sock *inet = inet_sk(sk);
struct flowi fl;
@@ -322,16 +323,17 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
fl.fl_ip_dport = inet->dport;
fl.fl_ip_sport = inet->sport;
- if ((err = ip6_dst_lookup(sk, &dst, &fl))) {
+ err = ip6_dst_lookup(sk, &dst, &fl);
+ if (err) {
sk->sk_err_soft = -err;
goto out;
}
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) {
+ err = xfrm_lookup(&dst, &fl, sk, 0);
+ if (err < 0) {
sk->sk_err_soft = -err;
goto out;
}
-
} else
dst_hold(dst);
@@ -355,11 +357,12 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
req = inet6_csk_search_req(sk, &prev, dh->dccph_dport,
&hdr->daddr, &hdr->saddr,
inet6_iif(skb));
- if (!req)
+ if (req == NULL)
goto out;
- /* ICMPs are not backlogged, hence we cannot get
- * an established socket here.
+ /*
+ * ICMPs are not backlogged, hence we cannot get an established
+ * socket here.
*/
BUG_TRAP(req->sk == NULL);
@@ -373,7 +376,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
case DCCP_REQUESTING:
case DCCP_RESPOND: /* Cannot happen.
- It can, it SYNs are crossed. --ANK */
+ It can, it SYNs are crossed. --ANK */
if (!sock_owned_by_user(sk)) {
DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
sk->sk_err = err;
@@ -382,7 +385,6 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
* (see connect in sock.c)
*/
sk->sk_error_report(sk);
-
dccp_done(sk);
} else
sk->sk_err_soft = err;
@@ -428,14 +430,16 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
ireq6->pktopts) {
struct sk_buff *pktopts = ireq6->pktopts;
struct inet6_skb_parm *rxopt = IP6CB(pktopts);
+
if (rxopt->srcrt)
opt = ipv6_invert_rthdr(sk,
(struct ipv6_rt_hdr *)(pktopts->nh.raw +
rxopt->srcrt));
}
- if (opt && opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+ if (opt != NULL && opt->srcrt != NULL) {
+ const struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+
ipv6_addr_copy(&final, &fl.fl6_dst);
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
final_p = &final;
@@ -444,15 +448,19 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto done;
+
if (final_p)
ipv6_addr_copy(&fl.fl6_dst, final_p);
- if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)
+
+ err = xfrm_lookup(&dst, &fl, sk, 0);
+ if (err < 0)
goto done;
}
skb = dccp_make_response(sk, dst, req);
if (skb != NULL) {
struct dccp_hdr *dh = dccp_hdr(skb);
+
dh->dccph_checksum = dccp_v6_check(dh, skb->len,
&ireq6->loc_addr,
&ireq6->rmt_addr,
@@ -466,7 +474,7 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req,
}
done:
- if (opt && opt != np->opt)
+ if (opt != NULL && opt != np->opt)
sock_kfree_s(sk, opt, opt->tot_len);
dst_release(dst);
return err;
@@ -497,7 +505,7 @@ static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
struct dccp_hdr *dh = dccp_hdr(skb);
dh->dccph_checksum = csum_ipv6_magic(&np->saddr, &np->daddr,
- len, IPPROTO_DCCP,
+ len, IPPROTO_DCCP,
csum_partial((char *)dh,
dh->dccph_doff << 2,
skb->csum));
@@ -505,8 +513,8 @@ static void dccp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb)
static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
{
- struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
- const int dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
+ struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
+ const u32 dccp_hdr_reset_len = sizeof(struct dccp_hdr) +
sizeof(struct dccp_hdr_ext) +
sizeof(struct dccp_hdr_reset);
struct sk_buff *skb;
@@ -517,20 +525,14 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
return;
if (!ipv6_unicast_destination(rxskb))
- return;
-
- /*
- * We need to grab some memory, and put together an RST,
- * and then put it into the queue to be sent.
- */
+ return;
- skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
- dccp_hdr_reset_len, GFP_ATOMIC);
- if (skb == NULL)
+ skb = alloc_skb(dccp_v6_ctl_socket->sk->sk_prot->max_header,
+ GFP_ATOMIC);
+ if (skb == NULL)
return;
- skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
- dccp_hdr_reset_len);
+ skb_reserve(skb, dccp_v6_ctl_socket->sk->sk_prot->max_header);
skb->h.raw = skb_push(skb, dccp_hdr_reset_len);
dh = dccp_hdr(skb);
@@ -568,7 +570,7 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
/* sk = NULL, but it is safe for now. RST socket required. */
if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
- ip6_xmit(NULL, skb, &fl, NULL, 0);
+ ip6_xmit(dccp_v6_ctl_socket->sk, skb, &fl, NULL, 0);
DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS);
return;
@@ -578,22 +580,22 @@ static void dccp_v6_ctl_send_reset(struct sk_buff *rxskb)
kfree_skb(skb);
}
-static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
+static void dccp_v6_reqsk_send_ack(struct sk_buff *rxskb,
+ struct request_sock *req)
{
struct flowi fl;
struct dccp_hdr *rxdh = dccp_hdr(rxskb), *dh;
- const int dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
+ const u32 dccp_hdr_ack_len = sizeof(struct dccp_hdr) +
sizeof(struct dccp_hdr_ext) +
sizeof(struct dccp_hdr_ack_bits);
struct sk_buff *skb;
- skb = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) +
- dccp_hdr_ack_len, GFP_ATOMIC);
+ skb = alloc_skb(dccp_v6_ctl_socket->sk->sk_prot->max_header,
+ GFP_ATOMIC);
if (skb == NULL)
return;
- skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr) +
- dccp_hdr_ack_len);
+ skb_reserve(skb, dccp_v6_ctl_socket->sk->sk_prot->max_header);
skb->h.raw = skb_push(skb, dccp_hdr_ack_len);
dh = dccp_hdr(skb);
@@ -605,7 +607,7 @@ static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
dh->dccph_dport = rxdh->dccph_sport;
dh->dccph_doff = dccp_hdr_ack_len / 4;
dh->dccph_x = 1;
-
+
dccp_hdr_set_seq(dh, DCCP_SKB_CB(rxskb)->dccpd_ack_seq);
dccp_hdr_set_ack(dccp_hdr_ack_bits(skb),
DCCP_SKB_CB(rxskb)->dccpd_seq);
@@ -623,7 +625,7 @@ static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
if (!ip6_dst_lookup(NULL, &skb->dst, &fl)) {
if (xfrm_lookup(&skb->dst, &fl, NULL, 0) >= 0) {
- ip6_xmit(NULL, skb, &fl, NULL, 0);
+ ip6_xmit(dccp_v6_ctl_socket->sk, skb, &fl, NULL, 0);
DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS);
return;
}
@@ -632,12 +634,6 @@ static void dccp_v6_ctl_send_ack(struct sk_buff *rxskb)
kfree_skb(skb);
}
-static void dccp_v6_reqsk_send_ack(struct sk_buff *skb,
- struct request_sock *req)
-{
- dccp_v6_ctl_send_ack(skb);
-}
-
static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
{
const struct dccp_hdr *dh = dccp_hdr(skb);
@@ -657,7 +653,6 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
&iph->saddr, dh->dccph_sport,
&iph->daddr, ntohs(dh->dccph_dport),
inet6_iif(skb));
-
if (nsk != NULL) {
if (nsk->sk_state != DCCP_TIME_WAIT) {
bh_lock_sock(nsk);
@@ -678,7 +673,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
struct dccp_request_sock *dreq;
struct inet6_request_sock *ireq6;
struct ipv6_pinfo *np = inet6_sk(sk);
- const __u32 service = dccp_hdr_request(skb)->dccph_req_service;
+ const __be32 service = dccp_hdr_request(skb)->dccph_req_service;
struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
__u8 reset_code = DCCP_RESET_CODE_TOO_BUSY;
@@ -686,17 +681,17 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
return dccp_v4_conn_request(sk, skb);
if (!ipv6_unicast_destination(skb))
- goto drop;
+ goto drop;
if (dccp_bad_service_code(sk, service)) {
reset_code = DCCP_RESET_CODE_BAD_SERVICE_CODE;
goto drop;
}
/*
- * There are no SYN attacks on IPv6, yet...
+ * There are no SYN attacks on IPv6, yet...
*/
if (inet_csk_reqsk_queue_is_full(sk))
- goto drop;
+ goto drop;
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
@@ -730,7 +725,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL)
ireq6->iif = inet6_iif(skb);
- /*
+ /*
* Step 3: Process LISTEN state
*
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookie
@@ -774,9 +769,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
/*
* v6 mapped
*/
-
newsk = dccp_v4_request_recv_sock(sk, skb, req, dst);
- if (newsk == NULL)
+ if (newsk == NULL)
return NULL;
newdp6 = (struct dccp6_sock *)newsk;
@@ -822,9 +816,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
if (sk_acceptq_is_full(sk))
goto out_overflow;
- if (np->rxopt.bits.osrcrt == 2 &&
- opt == NULL && ireq6->pktopts) {
- struct inet6_skb_parm *rxopt = IP6CB(ireq6->pktopts);
+ if (np->rxopt.bits.osrcrt == 2 && opt == NULL && ireq6->pktopts) {
+ const struct inet6_skb_parm *rxopt = IP6CB(ireq6->pktopts);
+
if (rxopt->srcrt)
opt = ipv6_invert_rthdr(sk,
(struct ipv6_rt_hdr *)(ireq6->pktopts->nh.raw +
@@ -838,8 +832,9 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
memset(&fl, 0, sizeof(fl));
fl.proto = IPPROTO_DCCP;
ipv6_addr_copy(&fl.fl6_dst, &ireq6->rmt_addr);
- if (opt && opt->srcrt) {
- struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt;
+ if (opt != NULL && opt->srcrt != NULL) {
+ const struct rt0_hdr *rt0 = (struct rt0_hdr *)opt->srcrt;
+
ipv6_addr_copy(&final, &fl.fl6_dst);
ipv6_addr_copy(&fl.fl6_dst, rt0->addr);
final_p = &final;
@@ -857,7 +852,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
if ((xfrm_lookup(&dst, &fl, sk, 0)) < 0)
goto out;
- }
+ }
newsk = dccp_create_openreq_child(sk, req, skb);
if (newsk == NULL)
@@ -870,9 +865,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
*/
ip6_dst_store(newsk, dst, NULL);
- newsk->sk_route_caps = dst->dev->features &
- ~(NETIF_F_IP_CSUM | NETIF_F_TSO);
-
+ newsk->sk_route_caps = dst->dev->features & ~(NETIF_F_IP_CSUM |
+ NETIF_F_TSO);
newdp6 = (struct dccp6_sock *)newsk;
newinet = inet_sk(newsk);
newinet->pinet6 = &newdp6->inet6;
@@ -886,7 +880,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
ipv6_addr_copy(&newnp->rcv_saddr, &ireq6->loc_addr);
newsk->sk_bound_dev_if = ireq6->iif;
- /* Now IPv6 options...
+ /* Now IPv6 options...
First: no IPv4 options.
*/
@@ -908,20 +902,20 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
newnp->mcast_oif = inet6_iif(skb);
newnp->mcast_hops = skb->nh.ipv6h->hop_limit;
- /* Clone native IPv6 options from listening socket (if any)
-
- Yes, keeping reference count would be much more clever,
- but we make one more one thing there: reattach optmem
- to newsk.
+ /*
+ * Clone native IPv6 options from listening socket (if any)
+ *
+ * Yes, keeping reference count would be much more clever, but we make
+ * one more one thing there: reattach optmem to newsk.
*/
- if (opt) {
+ if (opt != NULL) {
newnp->opt = ipv6_dup_options(newsk, opt);
if (opt != np->opt)
sock_kfree_s(sk, opt, opt->tot_len);
}
inet_csk(newsk)->icsk_ext_hdr_len = 0;
- if (newnp->opt)
+ if (newnp->opt != NULL)
inet_csk(newsk)->icsk_ext_hdr_len = (newnp->opt->opt_nflen +
newnp->opt->opt_flen);
@@ -938,7 +932,7 @@ out_overflow:
NET_INC_STATS_BH(LINUX_MIB_LISTENOVERFLOWS);
out:
NET_INC_STATS_BH(LINUX_MIB_LISTENDROPS);
- if (opt && opt != np->opt)
+ if (opt != NULL && opt != np->opt)
sock_kfree_s(sk, opt, opt->tot_len);
dst_release(dst);
return NULL;
@@ -972,8 +966,8 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
goto discard;
/*
- * socket locking is here for SMP purposes as backlog rcv
- * is currently called with bh processing disabled.
+ * socket locking is here for SMP purposes as backlog rcv is currently
+ * called with bh processing disabled.
*/
/* Do Stevens' IPV6_PKTOPTIONS.
@@ -998,20 +992,20 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
return 0;
}
- if (sk->sk_state == DCCP_LISTEN) {
+ if (sk->sk_state == DCCP_LISTEN) {
struct sock *nsk = dccp_v6_hnd_req(sk, skb);
- if (!nsk)
- goto discard;
+ if (nsk == NULL)
+ goto discard;
/*
* Queue it on the new socket if the new socket is active,
* otherwise we just shortcircuit this and continue with
* the new socket..
*/
- if(nsk != sk) {
+ if (nsk != sk) {
if (dccp_child_process(sk, nsk, skb))
goto reset;
- if (opt_skb)
+ if (opt_skb != NULL)
__kfree_skb(opt_skb);
return 0;
}
@@ -1024,7 +1018,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
reset:
dccp_v6_ctl_send_reset(skb);
discard:
- if (opt_skb)
+ if (opt_skb != NULL)
__kfree_skb(opt_skb);
kfree_skb(skb);
return 0;
@@ -1057,7 +1051,7 @@ static int dccp_v6_rcv(struct sk_buff **pskb)
dh->dccph_sport,
&skb->nh.ipv6h->daddr, ntohs(dh->dccph_dport),
inet6_iif(skb));
- /*
+ /*
* Step 2:
* If no socket ...
* Generate Reset(No Connection) unless P.type == Reset
@@ -1066,15 +1060,14 @@ static int dccp_v6_rcv(struct sk_buff **pskb)
if (sk == NULL)
goto no_dccp_socket;
- /*
+ /*
* Step 2:
* ... or S.state == TIMEWAIT,
* Generate Reset(No Connection) unless P.type == Reset
* Drop packet and return
*/
-
if (sk->sk_state == DCCP_TIME_WAIT)
- goto do_time_wait;
+ goto do_time_wait;
if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_and_relse;
@@ -1113,32 +1106,40 @@ do_time_wait:
}
static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
- .queue_xmit = inet6_csk_xmit,
- .send_check = dccp_v6_send_check,
- .rebuild_header = inet6_sk_rebuild_header,
- .conn_request = dccp_v6_conn_request,
- .syn_recv_sock = dccp_v6_request_recv_sock,
- .net_header_len = sizeof(struct ipv6hdr),
- .setsockopt = ipv6_setsockopt,
- .getsockopt = ipv6_getsockopt,
- .addr2sockaddr = inet6_csk_addr2sockaddr,
- .sockaddr_len = sizeof(struct sockaddr_in6)
+ .queue_xmit = inet6_csk_xmit,
+ .send_check = dccp_v6_send_check,
+ .rebuild_header = inet6_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct ipv6hdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6),
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_ipv6_setsockopt,
+ .compat_getsockopt = compat_ipv6_getsockopt,
+#endif
};
/*
* DCCP over IPv4 via INET6 API
*/
static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
- .queue_xmit = ip_queue_xmit,
- .send_check = dccp_v4_send_check,
- .rebuild_header = inet_sk_rebuild_header,
- .conn_request = dccp_v6_conn_request,
- .syn_recv_sock = dccp_v6_request_recv_sock,
- .net_header_len = sizeof(struct iphdr),
- .setsockopt = ipv6_setsockopt,
- .getsockopt = ipv6_getsockopt,
- .addr2sockaddr = inet6_csk_addr2sockaddr,
- .sockaddr_len = sizeof(struct sockaddr_in6)
+ .queue_xmit = ip_queue_xmit,
+ .send_check = dccp_v4_send_check,
+ .rebuild_header = inet_sk_rebuild_header,
+ .conn_request = dccp_v6_conn_request,
+ .syn_recv_sock = dccp_v6_request_recv_sock,
+ .net_header_len = sizeof(struct iphdr),
+ .setsockopt = ipv6_setsockopt,
+ .getsockopt = ipv6_getsockopt,
+ .addr2sockaddr = inet6_csk_addr2sockaddr,
+ .sockaddr_len = sizeof(struct sockaddr_in6),
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_ipv6_setsockopt,
+ .compat_getsockopt = compat_ipv6_getsockopt,
+#endif
};
/* NOTE: A lot of things set to zero explicitly by call to
@@ -1146,71 +1147,83 @@ static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
*/
static int dccp_v6_init_sock(struct sock *sk)
{
- int err = dccp_v4_init_sock(sk);
+ static __u8 dccp_v6_ctl_sock_initialized;
+ int err = dccp_init_sock(sk, dccp_v6_ctl_sock_initialized);
- if (err == 0)
+ if (err == 0) {
+ if (unlikely(!dccp_v6_ctl_sock_initialized))
+ dccp_v6_ctl_sock_initialized = 1;
inet_csk(sk)->icsk_af_ops = &dccp_ipv6_af_ops;
+ }
return err;
}
static int dccp_v6_destroy_sock(struct sock *sk)
{
- dccp_v4_destroy_sock(sk);
+ dccp_destroy_sock(sk);
return inet6_destroy_sock(sk);
}
static struct proto dccp_v6_prot = {
- .name = "DCCPv6",
- .owner = THIS_MODULE,
- .close = dccp_close,
- .connect = dccp_v6_connect,
- .disconnect = dccp_disconnect,
- .ioctl = dccp_ioctl,
- .init = dccp_v6_init_sock,
- .setsockopt = dccp_setsockopt,
- .getsockopt = dccp_getsockopt,
- .sendmsg = dccp_sendmsg,
- .recvmsg = dccp_recvmsg,
- .backlog_rcv = dccp_v6_do_rcv,
- .hash = dccp_v6_hash,
- .unhash = dccp_unhash,
- .accept = inet_csk_accept,
- .get_port = dccp_v6_get_port,
- .shutdown = dccp_shutdown,
- .destroy = dccp_v6_destroy_sock,
- .orphan_count = &dccp_orphan_count,
- .max_header = MAX_DCCP_HEADER,
- .obj_size = sizeof(struct dccp6_sock),
- .rsk_prot = &dccp6_request_sock_ops,
- .twsk_prot = &dccp6_timewait_sock_ops,
+ .name = "DCCPv6",
+ .owner = THIS_MODULE,
+ .close = dccp_close,
+ .connect = dccp_v6_connect,
+ .disconnect = dccp_disconnect,
+ .ioctl = dccp_ioctl,
+ .init = dccp_v6_init_sock,
+ .setsockopt = dccp_setsockopt,
+ .getsockopt = dccp_getsockopt,
+ .sendmsg = dccp_sendmsg,
+ .recvmsg = dccp_recvmsg,
+ .backlog_rcv = dccp_v6_do_rcv,
+ .hash = dccp_v6_hash,
+ .unhash = dccp_unhash,
+ .accept = inet_csk_accept,
+ .get_port = dccp_v6_get_port,
+ .shutdown = dccp_shutdown,
+ .destroy = dccp_v6_destroy_sock,
+ .orphan_count = &dccp_orphan_count,
+ .max_header = MAX_DCCP_HEADER,
+ .obj_size = sizeof(struct dccp6_sock),
+ .rsk_prot = &dccp6_request_sock_ops,
+ .twsk_prot = &dccp6_timewait_sock_ops,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_dccp_setsockopt,
+ .compat_getsockopt = compat_dccp_getsockopt,
+#endif
};
static struct inet6_protocol dccp_v6_protocol = {
- .handler = dccp_v6_rcv,
- .err_handler = dccp_v6_err,
- .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
+ .handler = dccp_v6_rcv,
+ .err_handler = dccp_v6_err,
+ .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_FINAL,
};
static struct proto_ops inet6_dccp_ops = {
- .family = PF_INET6,
- .owner = THIS_MODULE,
- .release = inet6_release,
- .bind = inet6_bind,
- .connect = inet_stream_connect,
- .socketpair = sock_no_socketpair,
- .accept = inet_accept,
- .getname = inet6_getname,
- .poll = dccp_poll,
- .ioctl = inet6_ioctl,
- .listen = inet_dccp_listen,
- .shutdown = inet_shutdown,
- .setsockopt = sock_common_setsockopt,
- .getsockopt = sock_common_getsockopt,
- .sendmsg = inet_sendmsg,
- .recvmsg = sock_common_recvmsg,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = inet_stream_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = inet6_getname,
+ .poll = dccp_poll,
+ .ioctl = inet6_ioctl,
+ .listen = inet_dccp_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
+#endif
};
static struct inet_protosw dccp_v6_protosw = {
@@ -1234,8 +1247,16 @@ static int __init dccp_v6_init(void)
goto out_unregister_proto;
inet6_register_protosw(&dccp_v6_protosw);
+
+ err = inet_csk_ctl_sock_create(&dccp_v6_ctl_socket, PF_INET6,
+ SOCK_DCCP, IPPROTO_DCCP);
+ if (err != 0)
+ goto out_unregister_protosw;
out:
return err;
+out_unregister_protosw:
+ inet6_del_protocol(&dccp_v6_protocol, IPPROTO_DCCP);
+ inet6_unregister_protosw(&dccp_v6_protosw);
out_unregister_proto:
proto_unregister(&dccp_v6_prot);
goto out;
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 29261fc198e7..c0349e5b0551 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -22,6 +22,7 @@
#include "ackvec.h"
#include "ccid.h"
#include "dccp.h"
+#include "feat.h"
struct inet_timewait_death_row dccp_death_row = {
.sysctl_max_tw_buckets = NR_FILE * 2,
@@ -106,6 +107,7 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
const struct dccp_request_sock *dreq = dccp_rsk(req);
struct inet_connection_sock *newicsk = inet_csk(sk);
struct dccp_sock *newdp = dccp_sk(newsk);
+ struct dccp_minisock *newdmsk = dccp_msk(newsk);
newdp->dccps_role = DCCP_ROLE_SERVER;
newdp->dccps_hc_rx_ackvec = NULL;
@@ -114,27 +116,27 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
newicsk->icsk_rto = DCCP_TIMEOUT_INIT;
do_gettimeofday(&newdp->dccps_epoch);
- if (newdp->dccps_options.dccpo_send_ack_vector) {
+ if (dccp_feat_clone(sk, newsk))
+ goto out_free;
+
+ if (newdmsk->dccpms_send_ack_vector) {
newdp->dccps_hc_rx_ackvec =
- dccp_ackvec_alloc(DCCP_MAX_ACKVEC_LEN,
- GFP_ATOMIC);
- /*
- * XXX: We're using the same CCIDs set on the parent,
- * i.e. sk_clone copied the master sock and left the
- * CCID pointers for this child, that is why we do the
- * __ccid_get calls.
- */
+ dccp_ackvec_alloc(GFP_ATOMIC);
if (unlikely(newdp->dccps_hc_rx_ackvec == NULL))
goto out_free;
}
- if (unlikely(ccid_hc_rx_init(newdp->dccps_hc_rx_ccid,
- newsk) != 0 ||
- ccid_hc_tx_init(newdp->dccps_hc_tx_ccid,
- newsk) != 0)) {
+ newdp->dccps_hc_rx_ccid =
+ ccid_hc_rx_new(newdmsk->dccpms_rx_ccid,
+ newsk, GFP_ATOMIC);
+ newdp->dccps_hc_tx_ccid =
+ ccid_hc_tx_new(newdmsk->dccpms_tx_ccid,
+ newsk, GFP_ATOMIC);
+ if (unlikely(newdp->dccps_hc_rx_ccid == NULL ||
+ newdp->dccps_hc_tx_ccid == NULL)) {
dccp_ackvec_free(newdp->dccps_hc_rx_ackvec);
- ccid_hc_rx_exit(newdp->dccps_hc_rx_ccid, newsk);
- ccid_hc_tx_exit(newdp->dccps_hc_tx_ccid, newsk);
+ ccid_hc_rx_delete(newdp->dccps_hc_rx_ccid, newsk);
+ ccid_hc_tx_delete(newdp->dccps_hc_tx_ccid, newsk);
out_free:
/* It is still raw copy of parent, so invalidate
* destructor and make plain sk_free() */
@@ -143,9 +145,6 @@ out_free:
return NULL;
}
- __ccid_get(newdp->dccps_hc_rx_ccid);
- __ccid_get(newdp->dccps_hc_tx_ccid);
-
/*
* Step 3: Process LISTEN state
*
@@ -155,7 +154,7 @@ out_free:
*/
/* See dccp_v4_conn_request */
- newdp->dccps_options.dccpo_sequence_window = req->rcv_wnd;
+ newdmsk->dccpms_sequence_window = req->rcv_wnd;
newdp->dccps_gar = newdp->dccps_isr = dreq->dreq_isr;
dccp_update_gsr(newsk, dreq->dreq_isr);
diff --git a/net/dccp/options.c b/net/dccp/options.c
index 0a76426c9aea..e9feb2a0c770 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -21,19 +21,23 @@
#include "ackvec.h"
#include "ccid.h"
#include "dccp.h"
+#include "feat.h"
-/* stores the default values for new connection. may be changed with sysctl */
-static const struct dccp_options dccpo_default_values = {
- .dccpo_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW,
- .dccpo_rx_ccid = DCCPF_INITIAL_CCID,
- .dccpo_tx_ccid = DCCPF_INITIAL_CCID,
- .dccpo_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR,
- .dccpo_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT,
-};
+int dccp_feat_default_sequence_window = DCCPF_INITIAL_SEQUENCE_WINDOW;
+int dccp_feat_default_rx_ccid = DCCPF_INITIAL_CCID;
+int dccp_feat_default_tx_ccid = DCCPF_INITIAL_CCID;
+int dccp_feat_default_ack_ratio = DCCPF_INITIAL_ACK_RATIO;
+int dccp_feat_default_send_ack_vector = DCCPF_INITIAL_SEND_ACK_VECTOR;
+int dccp_feat_default_send_ndp_count = DCCPF_INITIAL_SEND_NDP_COUNT;
-void dccp_options_init(struct dccp_options *dccpo)
+void dccp_minisock_init(struct dccp_minisock *dmsk)
{
- memcpy(dccpo, &dccpo_default_values, sizeof(*dccpo));
+ dmsk->dccpms_sequence_window = dccp_feat_default_sequence_window;
+ dmsk->dccpms_rx_ccid = dccp_feat_default_rx_ccid;
+ dmsk->dccpms_tx_ccid = dccp_feat_default_tx_ccid;
+ dmsk->dccpms_ack_ratio = dccp_feat_default_ack_ratio;
+ dmsk->dccpms_send_ack_vector = dccp_feat_default_send_ack_vector;
+ dmsk->dccpms_send_ndp_count = dccp_feat_default_send_ndp_count;
}
static u32 dccp_decode_value_var(const unsigned char *bf, const u8 len)
@@ -69,9 +73,12 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
unsigned char opt, len;
unsigned char *value;
u32 elapsed_time;
+ int rc;
+ int mandatory = 0;
memset(opt_recv, 0, sizeof(*opt_recv));
+ opt = len = 0;
while (opt_ptr != opt_end) {
opt = *opt_ptr++;
len = 0;
@@ -100,6 +107,12 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
switch (opt) {
case DCCPO_PADDING:
break;
+ case DCCPO_MANDATORY:
+ if (mandatory)
+ goto out_invalid_option;
+ if (pkt_type != DCCP_PKT_DATA)
+ mandatory = 1;
+ break;
case DCCPO_NDP_COUNT:
if (len > 3)
goto out_invalid_option;
@@ -108,12 +121,37 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
dccp_pr_debug("%sNDP count=%d\n", debug_prefix,
opt_recv->dccpor_ndp);
break;
+ case DCCPO_CHANGE_L:
+ /* fall through */
+ case DCCPO_CHANGE_R:
+ if (len < 2)
+ goto out_invalid_option;
+ rc = dccp_feat_change_recv(sk, opt, *value, value + 1,
+ len - 1);
+ /*
+ * When there is a change error, change_recv is
+ * responsible for dealing with it. i.e. reply with an
+ * empty confirm.
+ * If the change was mandatory, then we need to die.
+ */
+ if (rc && mandatory)
+ goto out_invalid_option;
+ break;
+ case DCCPO_CONFIRM_L:
+ /* fall through */
+ case DCCPO_CONFIRM_R:
+ if (len < 2)
+ goto out_invalid_option;
+ if (dccp_feat_confirm_recv(sk, opt, *value,
+ value + 1, len - 1))
+ goto out_invalid_option;
+ break;
case DCCPO_ACK_VECTOR_0:
case DCCPO_ACK_VECTOR_1:
if (pkt_type == DCCP_PKT_DATA)
- continue;
+ break;
- if (dp->dccps_options.dccpo_send_ack_vector &&
+ if (dccp_msk(sk)->dccpms_send_ack_vector &&
dccp_ackvec_parse(sk, skb, opt, value, len))
goto out_invalid_option;
break;
@@ -121,7 +159,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
if (len != 4)
goto out_invalid_option;
- opt_recv->dccpor_timestamp = ntohl(*(u32 *)value);
+ opt_recv->dccpor_timestamp = ntohl(*(__be32 *)value);
dp->dccps_timestamp_echo = opt_recv->dccpor_timestamp;
dccp_timestamp(sk, &dp->dccps_timestamp_time);
@@ -135,7 +173,7 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
if (len != 4 && len != 6 && len != 8)
goto out_invalid_option;
- opt_recv->dccpor_timestamp_echo = ntohl(*(u32 *)value);
+ opt_recv->dccpor_timestamp_echo = ntohl(*(__be32 *)value);
dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, ackno=%llu, ",
debug_prefix,
@@ -149,9 +187,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
break;
if (len == 6)
- elapsed_time = ntohs(*(u16 *)(value + 4));
+ elapsed_time = ntohs(*(__be16 *)(value + 4));
else
- elapsed_time = ntohl(*(u32 *)(value + 4));
+ elapsed_time = ntohl(*(__be32 *)(value + 4));
/* Give precedence to the biggest ELAPSED_TIME */
if (elapsed_time > opt_recv->dccpor_elapsed_time)
@@ -165,9 +203,9 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
continue;
if (len == 2)
- elapsed_time = ntohs(*(u16 *)value);
+ elapsed_time = ntohs(*(__be16 *)value);
else
- elapsed_time = ntohl(*(u32 *)value);
+ elapsed_time = ntohl(*(__be32 *)value);
if (elapsed_time > opt_recv->dccpor_elapsed_time)
opt_recv->dccpor_elapsed_time = elapsed_time;
@@ -208,8 +246,15 @@ int dccp_parse_options(struct sock *sk, struct sk_buff *skb)
sk, opt, len);
break;
}
+
+ if (opt != DCCPO_MANDATORY)
+ mandatory = 0;
}
+ /* mandatory was the last byte in option list -> reset connection */
+ if (mandatory)
+ goto out_invalid_option;
+
return 0;
out_invalid_option:
@@ -219,6 +264,8 @@ out_invalid_option:
return -1;
}
+EXPORT_SYMBOL_GPL(dccp_parse_options);
+
static void dccp_encode_value_var(const u32 value, unsigned char *to,
const unsigned int len)
{
@@ -237,17 +284,14 @@ static inline int dccp_ndp_len(const int ndp)
return likely(ndp <= 0xFF) ? 1 : ndp <= 0xFFFF ? 2 : 3;
}
-void dccp_insert_option(struct sock *sk, struct sk_buff *skb,
+int dccp_insert_option(struct sock *sk, struct sk_buff *skb,
const unsigned char option,
const void *value, const unsigned char len)
{
unsigned char *to;
- if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN) {
- LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small to insert "
- "%d option!\n", option);
- return;
- }
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 2 > DCCP_MAX_OPT_LEN)
+ return -1;
DCCP_SKB_CB(skb)->dccpd_opt_len += len + 2;
@@ -256,11 +300,12 @@ void dccp_insert_option(struct sock *sk, struct sk_buff *skb,
*to++ = len + 2;
memcpy(to, value, len);
+ return 0;
}
EXPORT_SYMBOL_GPL(dccp_insert_option);
-static void dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
+static int dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
int ndp = dp->dccps_ndp_count;
@@ -276,7 +321,7 @@ static void dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
const int len = ndp_len + 2;
if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
- return;
+ return -1;
DCCP_SKB_CB(skb)->dccpd_opt_len += len;
@@ -285,6 +330,8 @@ static void dccp_insert_option_ndp(struct sock *sk, struct sk_buff *skb)
*ptr++ = len;
dccp_encode_value_var(ndp, ptr, ndp_len);
}
+
+ return 0;
}
static inline int dccp_elapsed_time_len(const u32 elapsed_time)
@@ -292,27 +339,18 @@ static inline int dccp_elapsed_time_len(const u32 elapsed_time)
return elapsed_time == 0 ? 0 : elapsed_time <= 0xFFFF ? 2 : 4;
}
-void dccp_insert_option_elapsed_time(struct sock *sk,
- struct sk_buff *skb,
- u32 elapsed_time)
+int dccp_insert_option_elapsed_time(struct sock *sk, struct sk_buff *skb,
+ u32 elapsed_time)
{
-#ifdef CONFIG_IP_DCCP_DEBUG
- struct dccp_sock *dp = dccp_sk(sk);
- const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ?
- "CLIENT TX opt: " : "server TX opt: ";
-#endif
const int elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
const int len = 2 + elapsed_time_len;
unsigned char *to;
if (elapsed_time_len == 0)
- return;
+ return 0;
- if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) {
- LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small to "
- "insert elapsed time!\n");
- return;
- }
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
+ return -1;
DCCP_SKB_CB(skb)->dccpd_opt_len += len;
@@ -321,17 +359,14 @@ void dccp_insert_option_elapsed_time(struct sock *sk,
*to++ = len;
if (elapsed_time_len == 2) {
- const u16 var16 = htons((u16)elapsed_time);
+ const __be16 var16 = htons((u16)elapsed_time);
memcpy(to, &var16, 2);
} else {
- const u32 var32 = htonl(elapsed_time);
+ const __be32 var32 = htonl(elapsed_time);
memcpy(to, &var32, 4);
}
- dccp_pr_debug("%sELAPSED_TIME=%u, len=%d, seqno=%llu\n",
- debug_prefix, elapsed_time,
- len,
- (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
+ return 0;
}
EXPORT_SYMBOL_GPL(dccp_insert_option_elapsed_time);
@@ -352,32 +387,27 @@ void dccp_timestamp(const struct sock *sk, struct timeval *tv)
EXPORT_SYMBOL_GPL(dccp_timestamp);
-void dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
+int dccp_insert_option_timestamp(struct sock *sk, struct sk_buff *skb)
{
struct timeval tv;
- u32 now;
-
+ __be32 now;
+
dccp_timestamp(sk, &tv);
- now = timeval_usecs(&tv) / 10;
+ now = htonl(timeval_usecs(&tv) / 10);
/* yes this will overflow but that is the point as we want a
* 10 usec 32 bit timer which mean it wraps every 11.9 hours */
- now = htonl(now);
- dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now));
+ return dccp_insert_option(sk, skb, DCCPO_TIMESTAMP, &now, sizeof(now));
}
EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp);
-static void dccp_insert_option_timestamp_echo(struct sock *sk,
- struct sk_buff *skb)
+static int dccp_insert_option_timestamp_echo(struct sock *sk,
+ struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
-#ifdef CONFIG_IP_DCCP_DEBUG
- const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ?
- "CLIENT TX opt: " : "server TX opt: ";
-#endif
struct timeval now;
- u32 tstamp_echo;
+ __be32 tstamp_echo;
u32 elapsed_time;
int len, elapsed_time_len;
unsigned char *to;
@@ -387,11 +417,8 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk,
elapsed_time_len = dccp_elapsed_time_len(elapsed_time);
len = 6 + elapsed_time_len;
- if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) {
- LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small to insert "
- "timestamp echo!\n");
- return;
- }
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN)
+ return -1;
DCCP_SKB_CB(skb)->dccpd_opt_len += len;
@@ -402,51 +429,149 @@ static void dccp_insert_option_timestamp_echo(struct sock *sk,
tstamp_echo = htonl(dp->dccps_timestamp_echo);
memcpy(to, &tstamp_echo, 4);
to += 4;
-
+
if (elapsed_time_len == 2) {
- const u16 var16 = htons((u16)elapsed_time);
+ const __be16 var16 = htons((u16)elapsed_time);
memcpy(to, &var16, 2);
} else if (elapsed_time_len == 4) {
- const u32 var32 = htonl(elapsed_time);
+ const __be32 var32 = htonl(elapsed_time);
memcpy(to, &var32, 4);
}
- dccp_pr_debug("%sTIMESTAMP_ECHO=%u, len=%d, seqno=%llu\n",
- debug_prefix, dp->dccps_timestamp_echo,
- len,
- (unsigned long long) DCCP_SKB_CB(skb)->dccpd_seq);
-
dp->dccps_timestamp_echo = 0;
dp->dccps_timestamp_time.tv_sec = 0;
dp->dccps_timestamp_time.tv_usec = 0;
+ return 0;
}
-void dccp_insert_options(struct sock *sk, struct sk_buff *skb)
+static int dccp_insert_feat_opt(struct sk_buff *skb, u8 type, u8 feat,
+ u8 *val, u8 len)
+{
+ u8 *to;
+
+ if (DCCP_SKB_CB(skb)->dccpd_opt_len + len + 3 > DCCP_MAX_OPT_LEN) {
+ LIMIT_NETDEBUG(KERN_INFO "DCCP: packet too small"
+ " to insert feature %d option!\n", feat);
+ return -1;
+ }
+
+ DCCP_SKB_CB(skb)->dccpd_opt_len += len + 3;
+
+ to = skb_push(skb, len + 3);
+ *to++ = type;
+ *to++ = len + 3;
+ *to++ = feat;
+
+ if (len)
+ memcpy(to, val, len);
+ dccp_pr_debug("option %d feat %d len %d\n", type, feat, len);
+
+ return 0;
+}
+
+static int dccp_insert_options_feat(struct sock *sk, struct sk_buff *skb)
{
struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ struct dccp_opt_pend *opt, *next;
+ int change = 0;
+
+ /* confirm any options [NN opts] */
+ list_for_each_entry_safe(opt, next, &dmsk->dccpms_conf, dccpop_node) {
+ dccp_insert_feat_opt(skb, opt->dccpop_type,
+ opt->dccpop_feat, opt->dccpop_val,
+ opt->dccpop_len);
+ /* fear empty confirms */
+ if (opt->dccpop_val)
+ kfree(opt->dccpop_val);
+ kfree(opt);
+ }
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
+
+ /* see which features we need to send */
+ list_for_each_entry(opt, &dmsk->dccpms_pending, dccpop_node) {
+ /* see if we need to send any confirm */
+ if (opt->dccpop_sc) {
+ dccp_insert_feat_opt(skb, opt->dccpop_type + 1,
+ opt->dccpop_feat,
+ opt->dccpop_sc->dccpoc_val,
+ opt->dccpop_sc->dccpoc_len);
+
+ BUG_ON(!opt->dccpop_sc->dccpoc_val);
+ kfree(opt->dccpop_sc->dccpoc_val);
+ kfree(opt->dccpop_sc);
+ opt->dccpop_sc = NULL;
+ }
+
+ /* any option not confirmed, re-send it */
+ if (!opt->dccpop_conf) {
+ dccp_insert_feat_opt(skb, opt->dccpop_type,
+ opt->dccpop_feat, opt->dccpop_val,
+ opt->dccpop_len);
+ change++;
+ }
+ }
+
+ /* Retransmit timer.
+ * If this is the master listening sock, we don't set a timer on it. It
+ * should be fine because if the dude doesn't receive our RESPONSE
+ * [which will contain the CHANGE] he will send another REQUEST which
+ * will "retrnasmit" the change.
+ */
+ if (change && dp->dccps_role != DCCP_ROLE_LISTEN) {
+ dccp_pr_debug("reset feat negotiation timer %p\n", sk);
+
+ /* XXX don't reset the timer on re-transmissions. I.e. reset it
+ * only when sending new stuff i guess. Currently the timer
+ * never backs off because on re-transmission it just resets it!
+ */
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+ inet_csk(sk)->icsk_rto, DCCP_RTO_MAX);
+ }
+
+ return 0;
+}
+
+int dccp_insert_options(struct sock *sk, struct sk_buff *skb)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
DCCP_SKB_CB(skb)->dccpd_opt_len = 0;
- if (dp->dccps_options.dccpo_send_ndp_count)
- dccp_insert_option_ndp(sk, skb);
+ if (dmsk->dccpms_send_ndp_count &&
+ dccp_insert_option_ndp(sk, skb))
+ return -1;
if (!dccp_packet_without_ack(skb)) {
- if (dp->dccps_options.dccpo_send_ack_vector &&
- dccp_ackvec_pending(dp->dccps_hc_rx_ackvec))
- dccp_insert_option_ackvec(sk, skb);
- if (dp->dccps_timestamp_echo != 0)
- dccp_insert_option_timestamp_echo(sk, skb);
+ if (dmsk->dccpms_send_ack_vector &&
+ dccp_ackvec_pending(dp->dccps_hc_rx_ackvec) &&
+ dccp_insert_option_ackvec(sk, skb))
+ return -1;
+
+ if (dp->dccps_timestamp_echo != 0 &&
+ dccp_insert_option_timestamp_echo(sk, skb))
+ return -1;
}
if (dp->dccps_hc_rx_insert_options) {
- ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb);
+ if (ccid_hc_rx_insert_options(dp->dccps_hc_rx_ccid, sk, skb))
+ return -1;
dp->dccps_hc_rx_insert_options = 0;
}
if (dp->dccps_hc_tx_insert_options) {
- ccid_hc_tx_insert_options(dp->dccps_hc_tx_ccid, sk, skb);
+ if (ccid_hc_tx_insert_options(dp->dccps_hc_tx_ccid, sk, skb))
+ return -1;
dp->dccps_hc_tx_insert_options = 0;
}
+ /* Feature negotiation */
+ /* Data packets can't do feat negotiation */
+ if (DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATA &&
+ DCCP_SKB_CB(skb)->dccpd_type != DCCP_PKT_DATAACK &&
+ dccp_insert_options_feat(sk, skb))
+ return -1;
+
/* XXX: insert other options when appropriate */
if (DCCP_SKB_CB(skb)->dccpd_opt_len != 0) {
@@ -459,4 +584,6 @@ void dccp_insert_options(struct sock *sk, struct sk_buff *skb)
DCCP_SKB_CB(skb)->dccpd_opt_len += padding;
}
}
+
+ return 0;
}
diff --git a/net/dccp/output.c b/net/dccp/output.c
index efd7ffb903a1..7409e4a3abdf 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -27,7 +27,7 @@ static inline void dccp_event_ack_sent(struct sock *sk)
inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);
}
-static inline void dccp_skb_entail(struct sock *sk, struct sk_buff *skb)
+static void dccp_skb_entail(struct sock *sk, struct sk_buff *skb)
{
skb_set_owner_w(skb, sk);
WARN_ON(sk->sk_send_head);
@@ -49,7 +49,7 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb);
struct dccp_hdr *dh;
/* XXX For now we're using only 48 bits sequence numbers */
- const int dccp_header_size = sizeof(*dh) +
+ const u32 dccp_header_size = sizeof(*dh) +
sizeof(struct dccp_hdr_ext) +
dccp_packet_hdr_len(dcb->dccpd_type);
int err, set_ack = 1;
@@ -64,6 +64,10 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
case DCCP_PKT_DATAACK:
break;
+ case DCCP_PKT_REQUEST:
+ set_ack = 0;
+ /* fall through */
+
case DCCP_PKT_SYNC:
case DCCP_PKT_SYNCACK:
ackno = dcb->dccpd_seq;
@@ -79,7 +83,11 @@ static int dccp_transmit_skb(struct sock *sk, struct sk_buff *skb)
}
dcb->dccpd_seq = dp->dccps_gss;
- dccp_insert_options(sk, skb);
+
+ if (dccp_insert_options(sk, skb)) {
+ kfree_skb(skb);
+ return -EPROTO;
+ }
skb->h.raw = skb_push(skb, dccp_header_size);
dh = dccp_hdr(skb);
@@ -275,17 +283,16 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
{
struct dccp_hdr *dh;
struct dccp_request_sock *dreq;
- const int dccp_header_size = sizeof(struct dccp_hdr) +
+ const u32 dccp_header_size = sizeof(struct dccp_hdr) +
sizeof(struct dccp_hdr_ext) +
sizeof(struct dccp_hdr_response);
- struct sk_buff *skb = sock_wmalloc(sk, MAX_HEADER + DCCP_MAX_OPT_LEN +
- dccp_header_size, 1,
+ struct sk_buff *skb = sock_wmalloc(sk, sk->sk_prot->max_header, 1,
GFP_ATOMIC);
if (skb == NULL)
return NULL;
/* Reserve space for headers. */
- skb_reserve(skb, MAX_HEADER + DCCP_MAX_OPT_LEN + dccp_header_size);
+ skb_reserve(skb, sk->sk_prot->max_header);
skb->dst = dst_clone(dst);
skb->csum = 0;
@@ -293,7 +300,11 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
dreq = dccp_rsk(req);
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESPONSE;
DCCP_SKB_CB(skb)->dccpd_seq = dreq->dreq_iss;
- dccp_insert_options(sk, skb);
+
+ if (dccp_insert_options(sk, skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
skb->h.raw = skb_push(skb, dccp_header_size);
@@ -310,32 +321,28 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst,
dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dreq->dreq_isr);
dccp_hdr_response(skb)->dccph_resp_service = dreq->dreq_service;
- dh->dccph_checksum = dccp_v4_checksum(skb, inet_rsk(req)->loc_addr,
- inet_rsk(req)->rmt_addr);
-
DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
return skb;
}
EXPORT_SYMBOL_GPL(dccp_make_response);
-struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
- const enum dccp_reset_codes code)
+static struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
+ const enum dccp_reset_codes code)
{
struct dccp_hdr *dh;
struct dccp_sock *dp = dccp_sk(sk);
- const int dccp_header_size = sizeof(struct dccp_hdr) +
+ const u32 dccp_header_size = sizeof(struct dccp_hdr) +
sizeof(struct dccp_hdr_ext) +
sizeof(struct dccp_hdr_reset);
- struct sk_buff *skb = sock_wmalloc(sk, MAX_HEADER + DCCP_MAX_OPT_LEN +
- dccp_header_size, 1,
+ struct sk_buff *skb = sock_wmalloc(sk, sk->sk_prot->max_header, 1,
GFP_ATOMIC);
if (skb == NULL)
return NULL;
/* Reserve space for headers. */
- skb_reserve(skb, MAX_HEADER + DCCP_MAX_OPT_LEN + dccp_header_size);
+ skb_reserve(skb, sk->sk_prot->max_header);
skb->dst = dst_clone(dst);
skb->csum = 0;
@@ -345,7 +352,11 @@ struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
DCCP_SKB_CB(skb)->dccpd_reset_code = code;
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_RESET;
DCCP_SKB_CB(skb)->dccpd_seq = dp->dccps_gss;
- dccp_insert_options(sk, skb);
+
+ if (dccp_insert_options(sk, skb)) {
+ kfree_skb(skb);
+ return NULL;
+ }
skb->h.raw = skb_push(skb, dccp_header_size);
@@ -362,14 +373,34 @@ struct sk_buff *dccp_make_reset(struct sock *sk, struct dst_entry *dst,
dccp_hdr_set_ack(dccp_hdr_ack_bits(skb), dp->dccps_gsr);
dccp_hdr_reset(skb)->dccph_reset_code = code;
-
- dh->dccph_checksum = dccp_v4_checksum(skb, inet_sk(sk)->saddr,
- inet_sk(sk)->daddr);
+ inet_csk(sk)->icsk_af_ops->send_check(sk, skb->len, skb);
DCCP_INC_STATS(DCCP_MIB_OUTSEGS);
return skb;
}
+int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code)
+{
+ /*
+ * FIXME: what if rebuild_header fails?
+ * Should we be doing a rebuild_header here?
+ */
+ int err = inet_sk_rebuild_header(sk);
+
+ if (err == 0) {
+ struct sk_buff *skb = dccp_make_reset(sk, sk->sk_dst_cache,
+ code);
+ if (skb != NULL) {
+ memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+ err = inet_csk(sk)->icsk_af_ops->queue_xmit(skb, 0);
+ if (err == NET_XMIT_CN)
+ err = 0;
+ }
+ }
+
+ return err;
+}
+
/*
* Do all connect socket setups that can be done AF independent.
*/
@@ -405,12 +436,12 @@ int dccp_connect(struct sock *sk)
dccp_connect_init(sk);
- skb = alloc_skb(MAX_DCCP_HEADER + 15, sk->sk_allocation);
+ skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation);
if (unlikely(skb == NULL))
return -ENOBUFS;
/* Reserve space for headers. */
- skb_reserve(skb, MAX_DCCP_HEADER);
+ skb_reserve(skb, sk->sk_prot->max_header);
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_REQUEST;
skb->csum = 0;
@@ -431,7 +462,8 @@ void dccp_send_ack(struct sock *sk)
{
/* If we have been reset, we may not send again. */
if (sk->sk_state != DCCP_CLOSED) {
- struct sk_buff *skb = alloc_skb(MAX_DCCP_HEADER, GFP_ATOMIC);
+ struct sk_buff *skb = alloc_skb(sk->sk_prot->max_header,
+ GFP_ATOMIC);
if (skb == NULL) {
inet_csk_schedule_ack(sk);
@@ -443,7 +475,7 @@ void dccp_send_ack(struct sock *sk)
}
/* Reserve space for headers */
- skb_reserve(skb, MAX_DCCP_HEADER);
+ skb_reserve(skb, sk->sk_prot->max_header);
skb->csum = 0;
DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_ACK;
dccp_transmit_skb(sk, skb);
@@ -490,14 +522,14 @@ void dccp_send_sync(struct sock *sk, const u64 seq,
* dccp_transmit_skb() will set the ownership to this
* sock.
*/
- struct sk_buff *skb = alloc_skb(MAX_DCCP_HEADER, GFP_ATOMIC);
+ struct sk_buff *skb = alloc_skb(sk->sk_prot->max_header, GFP_ATOMIC);
if (skb == NULL)
/* FIXME: how to make sure the sync is sent? */
return;
/* Reserve space for headers and prepare control bits. */
- skb_reserve(skb, MAX_DCCP_HEADER);
+ skb_reserve(skb, sk->sk_prot->max_header);
skb->csum = 0;
DCCP_SKB_CB(skb)->dccpd_type = pkt_type;
DCCP_SKB_CB(skb)->dccpd_seq = seq;
@@ -505,6 +537,8 @@ void dccp_send_sync(struct sock *sk, const u64 seq,
dccp_transmit_skb(sk, skb);
}
+EXPORT_SYMBOL_GPL(dccp_send_sync);
+
/*
* Send a DCCP_PKT_CLOSE/CLOSEREQ. The caller locks the socket for us. This
* cannot be allowed to fail queueing a DCCP_PKT_CLOSE/CLOSEREQ frame under
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 65b11ea90d85..d4b293e16283 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -23,9 +23,7 @@
#include <linux/random.h>
#include <net/checksum.h>
-#include <net/inet_common.h>
#include <net/inet_sock.h>
-#include <net/protocol.h>
#include <net/sock.h>
#include <net/xfrm.h>
@@ -37,6 +35,7 @@
#include "ccid.h"
#include "dccp.h"
+#include "feat.h"
DEFINE_SNMP_STAT(struct dccp_mib, dccp_statistics) __read_mostly;
@@ -46,12 +45,66 @@ atomic_t dccp_orphan_count = ATOMIC_INIT(0);
EXPORT_SYMBOL_GPL(dccp_orphan_count);
-static struct net_protocol dccp_protocol = {
- .handler = dccp_v4_rcv,
- .err_handler = dccp_v4_err,
- .no_policy = 1,
+struct inet_hashinfo __cacheline_aligned dccp_hashinfo = {
+ .lhash_lock = RW_LOCK_UNLOCKED,
+ .lhash_users = ATOMIC_INIT(0),
+ .lhash_wait = __WAIT_QUEUE_HEAD_INITIALIZER(dccp_hashinfo.lhash_wait),
};
+EXPORT_SYMBOL_GPL(dccp_hashinfo);
+
+void dccp_set_state(struct sock *sk, const int state)
+{
+ const int oldstate = sk->sk_state;
+
+ dccp_pr_debug("%s(%p) %-10.10s -> %s\n",
+ dccp_role(sk), sk,
+ dccp_state_name(oldstate), dccp_state_name(state));
+ WARN_ON(state == oldstate);
+
+ switch (state) {
+ case DCCP_OPEN:
+ if (oldstate != DCCP_OPEN)
+ DCCP_INC_STATS(DCCP_MIB_CURRESTAB);
+ break;
+
+ case DCCP_CLOSED:
+ if (oldstate == DCCP_CLOSING || oldstate == DCCP_OPEN)
+ DCCP_INC_STATS(DCCP_MIB_ESTABRESETS);
+
+ sk->sk_prot->unhash(sk);
+ if (inet_csk(sk)->icsk_bind_hash != NULL &&
+ !(sk->sk_userlocks & SOCK_BINDPORT_LOCK))
+ inet_put_port(&dccp_hashinfo, sk);
+ /* fall through */
+ default:
+ if (oldstate == DCCP_OPEN)
+ DCCP_DEC_STATS(DCCP_MIB_CURRESTAB);
+ }
+
+ /* Change state AFTER socket is unhashed to avoid closed
+ * socket sitting in hash tables.
+ */
+ sk->sk_state = state;
+}
+
+EXPORT_SYMBOL_GPL(dccp_set_state);
+
+void dccp_done(struct sock *sk)
+{
+ dccp_set_state(sk, DCCP_CLOSED);
+ dccp_clear_xmit_timers(sk);
+
+ sk->sk_shutdown = SHUTDOWN_MASK;
+
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_state_change(sk);
+ else
+ inet_csk_destroy_sock(sk);
+}
+
+EXPORT_SYMBOL_GPL(dccp_done);
+
const char *dccp_packet_name(const int type)
{
static const char *dccp_packet_names[] = {
@@ -96,6 +149,120 @@ const char *dccp_state_name(const int state)
EXPORT_SYMBOL_GPL(dccp_state_name);
+void dccp_hash(struct sock *sk)
+{
+ inet_hash(&dccp_hashinfo, sk);
+}
+
+EXPORT_SYMBOL_GPL(dccp_hash);
+
+void dccp_unhash(struct sock *sk)
+{
+ inet_unhash(&dccp_hashinfo, sk);
+}
+
+EXPORT_SYMBOL_GPL(dccp_unhash);
+
+int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+ struct inet_connection_sock *icsk = inet_csk(sk);
+
+ dccp_minisock_init(&dp->dccps_minisock);
+ do_gettimeofday(&dp->dccps_epoch);
+
+ /*
+ * FIXME: We're hardcoding the CCID, and doing this at this point makes
+ * the listening (master) sock get CCID control blocks, which is not
+ * necessary, but for now, to not mess with the test userspace apps,
+ * lets leave it here, later the real solution is to do this in a
+ * setsockopt(CCIDs-I-want/accept). -acme
+ */
+ if (likely(ctl_sock_initialized)) {
+ int rc = dccp_feat_init(dmsk);
+
+ if (rc)
+ return rc;
+
+ if (dmsk->dccpms_send_ack_vector) {
+ dp->dccps_hc_rx_ackvec = dccp_ackvec_alloc(GFP_KERNEL);
+ if (dp->dccps_hc_rx_ackvec == NULL)
+ return -ENOMEM;
+ }
+ dp->dccps_hc_rx_ccid = ccid_hc_rx_new(dmsk->dccpms_rx_ccid,
+ sk, GFP_KERNEL);
+ dp->dccps_hc_tx_ccid = ccid_hc_tx_new(dmsk->dccpms_tx_ccid,
+ sk, GFP_KERNEL);
+ if (unlikely(dp->dccps_hc_rx_ccid == NULL ||
+ dp->dccps_hc_tx_ccid == NULL)) {
+ ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ if (dmsk->dccpms_send_ack_vector) {
+ dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
+ dp->dccps_hc_rx_ackvec = NULL;
+ }
+ dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+ return -ENOMEM;
+ }
+ } else {
+ /* control socket doesn't need feat nego */
+ INIT_LIST_HEAD(&dmsk->dccpms_pending);
+ INIT_LIST_HEAD(&dmsk->dccpms_conf);
+ }
+
+ dccp_init_xmit_timers(sk);
+ icsk->icsk_rto = DCCP_TIMEOUT_INIT;
+ sk->sk_state = DCCP_CLOSED;
+ sk->sk_write_space = dccp_write_space;
+ icsk->icsk_sync_mss = dccp_sync_mss;
+ dp->dccps_mss_cache = 536;
+ dp->dccps_role = DCCP_ROLE_UNDEFINED;
+ dp->dccps_service = DCCP_SERVICE_INVALID_VALUE;
+ dp->dccps_l_ack_ratio = dp->dccps_r_ack_ratio = 1;
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dccp_init_sock);
+
+int dccp_destroy_sock(struct sock *sk)
+{
+ struct dccp_sock *dp = dccp_sk(sk);
+ struct dccp_minisock *dmsk = dccp_msk(sk);
+
+ /*
+ * DCCP doesn't use sk_write_queue, just sk_send_head
+ * for retransmissions
+ */
+ if (sk->sk_send_head != NULL) {
+ kfree_skb(sk->sk_send_head);
+ sk->sk_send_head = NULL;
+ }
+
+ /* Clean up a referenced DCCP bind bucket. */
+ if (inet_csk(sk)->icsk_bind_hash != NULL)
+ inet_put_port(&dccp_hashinfo, sk);
+
+ kfree(dp->dccps_service_list);
+ dp->dccps_service_list = NULL;
+
+ if (dmsk->dccpms_send_ack_vector) {
+ dccp_ackvec_free(dp->dccps_hc_rx_ackvec);
+ dp->dccps_hc_rx_ackvec = NULL;
+ }
+ ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
+ ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+ dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+
+ /* clean up feature negotiation state */
+ dccp_feat_clean(dmsk);
+
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(dccp_destroy_sock);
+
static inline int dccp_listen_start(struct sock *sk)
{
struct dccp_sock *dp = dccp_sk(sk);
@@ -220,7 +387,7 @@ int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg)
EXPORT_SYMBOL_GPL(dccp_ioctl);
-static int dccp_setsockopt_service(struct sock *sk, const u32 service,
+static int dccp_setsockopt_service(struct sock *sk, const __be32 service,
char __user *optval, int optlen)
{
struct dccp_sock *dp = dccp_sk(sk);
@@ -255,18 +422,46 @@ static int dccp_setsockopt_service(struct sock *sk, const u32 service,
return 0;
}
-int dccp_setsockopt(struct sock *sk, int level, int optname,
- char __user *optval, int optlen)
+/* byte 1 is feature. the rest is the preference list */
+static int dccp_setsockopt_change(struct sock *sk, int type,
+ struct dccp_so_feat __user *optval)
+{
+ struct dccp_so_feat opt;
+ u8 *val;
+ int rc;
+
+ if (copy_from_user(&opt, optval, sizeof(opt)))
+ return -EFAULT;
+
+ val = kmalloc(opt.dccpsf_len, GFP_KERNEL);
+ if (!val)
+ return -ENOMEM;
+
+ if (copy_from_user(val, opt.dccpsf_val, opt.dccpsf_len)) {
+ rc = -EFAULT;
+ goto out_free_val;
+ }
+
+ rc = dccp_feat_change(dccp_msk(sk), type, opt.dccpsf_feat,
+ val, opt.dccpsf_len, GFP_KERNEL);
+ if (rc)
+ goto out_free_val;
+
+out:
+ return rc;
+
+out_free_val:
+ kfree(val);
+ goto out;
+}
+
+static int do_dccp_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
{
struct dccp_sock *dp;
int err;
int val;
- if (level != SOL_DCCP)
- return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
- optname, optval,
- optlen);
-
if (optlen < sizeof(int))
return -EINVAL;
@@ -284,6 +479,25 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
case DCCP_SOCKOPT_PACKET_SIZE:
dp->dccps_packet_size = val;
break;
+
+ case DCCP_SOCKOPT_CHANGE_L:
+ if (optlen != sizeof(struct dccp_so_feat))
+ err = -EINVAL;
+ else
+ err = dccp_setsockopt_change(sk, DCCPO_CHANGE_L,
+ (struct dccp_so_feat *)
+ optval);
+ break;
+
+ case DCCP_SOCKOPT_CHANGE_R:
+ if (optlen != sizeof(struct dccp_so_feat))
+ err = -EINVAL;
+ else
+ err = dccp_setsockopt_change(sk, DCCPO_CHANGE_R,
+ (struct dccp_so_feat *)
+ optval);
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -293,10 +507,33 @@ int dccp_setsockopt(struct sock *sk, int level, int optname,
return err;
}
+int dccp_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
+{
+ if (level != SOL_DCCP)
+ return inet_csk(sk)->icsk_af_ops->setsockopt(sk, level,
+ optname, optval,
+ optlen);
+ return do_dccp_setsockopt(sk, level, optname, optval, optlen);
+}
+
EXPORT_SYMBOL_GPL(dccp_setsockopt);
+#ifdef CONFIG_COMPAT
+int compat_dccp_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
+{
+ if (level != SOL_DCCP)
+ return inet_csk_compat_setsockopt(sk, level, optname,
+ optval, optlen);
+ return do_dccp_setsockopt(sk, level, optname, optval, optlen);
+}
+
+EXPORT_SYMBOL_GPL(compat_dccp_setsockopt);
+#endif
+
static int dccp_getsockopt_service(struct sock *sk, int len,
- u32 __user *optval,
+ __be32 __user *optval,
int __user *optlen)
{
const struct dccp_sock *dp = dccp_sk(sk);
@@ -326,16 +563,12 @@ out:
return err;
}
-int dccp_getsockopt(struct sock *sk, int level, int optname,
+static int do_dccp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
struct dccp_sock *dp;
int val, len;
- if (level != SOL_DCCP)
- return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
- optname, optval,
- optlen);
if (get_user(len, optlen))
return -EFAULT;
@@ -351,7 +584,7 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
break;
case DCCP_SOCKOPT_SERVICE:
return dccp_getsockopt_service(sk, len,
- (u32 __user *)optval, optlen);
+ (__be32 __user *)optval, optlen);
case 128 ... 191:
return ccid_hc_rx_getsockopt(dp->dccps_hc_rx_ccid, sk, optname,
len, (u32 __user *)optval, optlen);
@@ -368,8 +601,31 @@ int dccp_getsockopt(struct sock *sk, int level, int optname,
return 0;
}
+int dccp_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ if (level != SOL_DCCP)
+ return inet_csk(sk)->icsk_af_ops->getsockopt(sk, level,
+ optname, optval,
+ optlen);
+ return do_dccp_getsockopt(sk, level, optname, optval, optlen);
+}
+
EXPORT_SYMBOL_GPL(dccp_getsockopt);
+#ifdef CONFIG_COMPAT
+int compat_dccp_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ if (level != SOL_DCCP)
+ return inet_csk_compat_getsockopt(sk, level, optname,
+ optval, optlen);
+ return do_dccp_getsockopt(sk, level, optname, optval, optlen);
+}
+
+EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
+#endif
+
int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
@@ -679,84 +935,7 @@ void dccp_shutdown(struct sock *sk, int how)
EXPORT_SYMBOL_GPL(dccp_shutdown);
-static const struct proto_ops inet_dccp_ops = {
- .family = PF_INET,
- .owner = THIS_MODULE,
- .release = inet_release,
- .bind = inet_bind,
- .connect = inet_stream_connect,
- .socketpair = sock_no_socketpair,
- .accept = inet_accept,
- .getname = inet_getname,
- /* FIXME: work on tcp_poll to rename it to inet_csk_poll */
- .poll = dccp_poll,
- .ioctl = inet_ioctl,
- /* FIXME: work on inet_listen to rename it to sock_common_listen */
- .listen = inet_dccp_listen,
- .shutdown = inet_shutdown,
- .setsockopt = sock_common_setsockopt,
- .getsockopt = sock_common_getsockopt,
- .sendmsg = inet_sendmsg,
- .recvmsg = sock_common_recvmsg,
- .mmap = sock_no_mmap,
- .sendpage = sock_no_sendpage,
-};
-
-extern struct net_proto_family inet_family_ops;
-
-static struct inet_protosw dccp_v4_protosw = {
- .type = SOCK_DCCP,
- .protocol = IPPROTO_DCCP,
- .prot = &dccp_prot,
- .ops = &inet_dccp_ops,
- .capability = -1,
- .no_check = 0,
- .flags = INET_PROTOSW_ICSK,
-};
-
-/*
- * This is the global socket data structure used for responding to
- * the Out-of-the-blue (OOTB) packets. A control sock will be created
- * for this socket at the initialization time.
- */
-struct socket *dccp_ctl_socket;
-
-static char dccp_ctl_socket_err_msg[] __initdata =
- KERN_ERR "DCCP: Failed to create the control socket.\n";
-
-static int __init dccp_ctl_sock_init(void)
-{
- int rc = sock_create_kern(PF_INET, SOCK_DCCP, IPPROTO_DCCP,
- &dccp_ctl_socket);
- if (rc < 0)
- printk(dccp_ctl_socket_err_msg);
- else {
- dccp_ctl_socket->sk->sk_allocation = GFP_ATOMIC;
- inet_sk(dccp_ctl_socket->sk)->uc_ttl = -1;
-
- /* Unhash it so that IP input processing does not even
- * see it, we do not wish this socket to see incoming
- * packets.
- */
- dccp_ctl_socket->sk->sk_prot->unhash(dccp_ctl_socket->sk);
- }
-
- return rc;
-}
-
-#ifdef CONFIG_IP_DCCP_UNLOAD_HACK
-void dccp_ctl_sock_exit(void)
-{
- if (dccp_ctl_socket != NULL) {
- sock_release(dccp_ctl_socket);
- dccp_ctl_socket = NULL;
- }
-}
-
-EXPORT_SYMBOL_GPL(dccp_ctl_sock_exit);
-#endif
-
-static int __init init_dccp_v4_mibs(void)
+static int __init dccp_mib_init(void)
{
int rc = -ENOMEM;
@@ -778,6 +957,13 @@ out_free_one:
}
+static void dccp_mib_exit(void)
+{
+ free_percpu(dccp_statistics[0]);
+ free_percpu(dccp_statistics[1]);
+ dccp_statistics[0] = dccp_statistics[1] = NULL;
+}
+
static int thash_entries;
module_param(thash_entries, int, 0444);
MODULE_PARM_DESC(thash_entries, "Number of ehash buckets");
@@ -794,17 +980,14 @@ static int __init dccp_init(void)
{
unsigned long goal;
int ehash_order, bhash_order, i;
- int rc = proto_register(&dccp_prot, 1);
-
- if (rc)
- goto out;
+ int rc = -ENOBUFS;
dccp_hashinfo.bind_bucket_cachep =
kmem_cache_create("dccp_bind_bucket",
sizeof(struct inet_bind_bucket), 0,
SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!dccp_hashinfo.bind_bucket_cachep)
- goto out_proto_unregister;
+ goto out;
/*
* Size and allocate the main established and bind bucket
@@ -866,27 +1049,23 @@ static int __init dccp_init(void)
INIT_HLIST_HEAD(&dccp_hashinfo.bhash[i].chain);
}
- if (init_dccp_v4_mibs())
+ rc = dccp_mib_init();
+ if (rc)
goto out_free_dccp_bhash;
- rc = -EAGAIN;
- if (inet_add_protocol(&dccp_protocol, IPPROTO_DCCP))
- goto out_free_dccp_v4_mibs;
-
- inet_register_protosw(&dccp_v4_protosw);
+ rc = dccp_ackvec_init();
+ if (rc)
+ goto out_free_dccp_mib;
- rc = dccp_ctl_sock_init();
+ rc = dccp_sysctl_init();
if (rc)
- goto out_unregister_protosw;
+ goto out_ackvec_exit;
out:
return rc;
-out_unregister_protosw:
- inet_unregister_protosw(&dccp_v4_protosw);
- inet_del_protocol(&dccp_protocol, IPPROTO_DCCP);
-out_free_dccp_v4_mibs:
- free_percpu(dccp_statistics[0]);
- free_percpu(dccp_statistics[1]);
- dccp_statistics[0] = dccp_statistics[1] = NULL;
+out_ackvec_exit:
+ dccp_ackvec_exit();
+out_free_dccp_mib:
+ dccp_mib_exit();
out_free_dccp_bhash:
free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order);
dccp_hashinfo.bhash = NULL;
@@ -896,23 +1075,12 @@ out_free_dccp_ehash:
out_free_bind_bucket_cachep:
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
dccp_hashinfo.bind_bucket_cachep = NULL;
-out_proto_unregister:
- proto_unregister(&dccp_prot);
goto out;
}
-static const char dccp_del_proto_err_msg[] __exitdata =
- KERN_ERR "can't remove dccp net_protocol\n";
-
static void __exit dccp_fini(void)
{
- inet_unregister_protosw(&dccp_v4_protosw);
-
- if (inet_del_protocol(&dccp_protocol, IPPROTO_DCCP) < 0)
- printk(dccp_del_proto_err_msg);
-
- free_percpu(dccp_statistics[0]);
- free_percpu(dccp_statistics[1]);
+ dccp_mib_exit();
free_pages((unsigned long)dccp_hashinfo.bhash,
get_order(dccp_hashinfo.bhash_size *
sizeof(struct inet_bind_hashbucket)));
@@ -920,19 +1088,13 @@ static void __exit dccp_fini(void)
get_order(dccp_hashinfo.ehash_size *
sizeof(struct inet_ehash_bucket)));
kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep);
- proto_unregister(&dccp_prot);
+ dccp_ackvec_exit();
+ dccp_sysctl_exit();
}
module_init(dccp_init);
module_exit(dccp_fini);
-/*
- * __stringify doesn't likes enums, so use SOCK_DCCP (6) and IPPROTO_DCCP (33)
- * values directly, Also cover the case where the protocol is not specified,
- * i.e. net-pf-PF_INET-proto-0-type-SOCK_DCCP
- */
-MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-33-type-6");
-MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-0-type-6");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arnaldo Carvalho de Melo <acme@conectiva.com.br>");
MODULE_DESCRIPTION("DCCP - Datagram Congestion Controlled Protocol");
diff --git a/net/dccp/sysctl.c b/net/dccp/sysctl.c
new file mode 100644
index 000000000000..64c89e9c229e
--- /dev/null
+++ b/net/dccp/sysctl.c
@@ -0,0 +1,124 @@
+/*
+ * net/dccp/sysctl.c
+ *
+ * An implementation of the DCCP protocol
+ * Arnaldo Carvalho de Melo <acme@mandriva.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+#ifndef CONFIG_SYSCTL
+#error This file should not be compiled without CONFIG_SYSCTL defined
+#endif
+
+extern int dccp_feat_default_sequence_window;
+extern int dccp_feat_default_rx_ccid;
+extern int dccp_feat_default_tx_ccid;
+extern int dccp_feat_default_ack_ratio;
+extern int dccp_feat_default_send_ack_vector;
+extern int dccp_feat_default_send_ndp_count;
+
+static struct ctl_table dccp_default_table[] = {
+ {
+ .ctl_name = NET_DCCP_DEFAULT_SEQ_WINDOW,
+ .procname = "seq_window",
+ .data = &dccp_feat_default_sequence_window,
+ .maxlen = sizeof(dccp_feat_default_sequence_window),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .ctl_name = NET_DCCP_DEFAULT_RX_CCID,
+ .procname = "rx_ccid",
+ .data = &dccp_feat_default_rx_ccid,
+ .maxlen = sizeof(dccp_feat_default_rx_ccid),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .ctl_name = NET_DCCP_DEFAULT_TX_CCID,
+ .procname = "tx_ccid",
+ .data = &dccp_feat_default_tx_ccid,
+ .maxlen = sizeof(dccp_feat_default_tx_ccid),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .ctl_name = NET_DCCP_DEFAULT_ACK_RATIO,
+ .procname = "ack_ratio",
+ .data = &dccp_feat_default_ack_ratio,
+ .maxlen = sizeof(dccp_feat_default_ack_ratio),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .ctl_name = NET_DCCP_DEFAULT_SEND_ACKVEC,
+ .procname = "send_ackvec",
+ .data = &dccp_feat_default_send_ack_vector,
+ .maxlen = sizeof(dccp_feat_default_send_ack_vector),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .ctl_name = NET_DCCP_DEFAULT_SEND_NDP,
+ .procname = "send_ndp",
+ .data = &dccp_feat_default_send_ndp_count,
+ .maxlen = sizeof(dccp_feat_default_send_ndp_count),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ { .ctl_name = 0, }
+};
+
+static struct ctl_table dccp_table[] = {
+ {
+ .ctl_name = NET_DCCP_DEFAULT,
+ .procname = "default",
+ .mode = 0555,
+ .child = dccp_default_table,
+ },
+ { .ctl_name = 0, },
+};
+
+static struct ctl_table dccp_dir_table[] = {
+ {
+ .ctl_name = NET_DCCP,
+ .procname = "dccp",
+ .mode = 0555,
+ .child = dccp_table,
+ },
+ { .ctl_name = 0, },
+};
+
+static struct ctl_table dccp_root_table[] = {
+ {
+ .ctl_name = CTL_NET,
+ .procname = "net",
+ .mode = 0555,
+ .child = dccp_dir_table,
+ },
+ { .ctl_name = 0, },
+};
+
+static struct ctl_table_header *dccp_table_header;
+
+int __init dccp_sysctl_init(void)
+{
+ dccp_table_header = register_sysctl_table(dccp_root_table, 1);
+
+ return dccp_table_header != NULL ? 0 : -ENOMEM;
+}
+
+void dccp_sysctl_exit(void)
+{
+ if (dccp_table_header != NULL) {
+ unregister_sysctl_table(dccp_table_header);
+ dccp_table_header = NULL;
+ }
+}
diff --git a/net/dccp/timer.c b/net/dccp/timer.c
index aa34b576e228..5244415e5f18 100644
--- a/net/dccp/timer.c
+++ b/net/dccp/timer.c
@@ -31,7 +31,7 @@ static void dccp_write_err(struct sock *sk)
sk->sk_err = sk->sk_err_soft ? : ETIMEDOUT;
sk->sk_error_report(sk);
- dccp_v4_send_reset(sk, DCCP_RESET_CODE_ABORTED);
+ dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
dccp_done(sk);
DCCP_INC_STATS_BH(DCCP_MIB_ABORTONTIMEOUT);
}
@@ -141,6 +141,17 @@ static void dccp_retransmit_timer(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
+ /* retransmit timer is used for feature negotiation throughout
+ * connection. In this case, no packet is re-transmitted, but rather an
+ * ack is generated and pending changes are splaced into its options.
+ */
+ if (sk->sk_send_head == NULL) {
+ dccp_pr_debug("feat negotiation retransmit timeout %p\n", sk);
+ if (sk->sk_state == DCCP_OPEN)
+ dccp_send_ack(sk);
+ goto backoff;
+ }
+
/*
* sk->sk_send_head has to have one skb with
* DCCP_SKB_CB(skb)->dccpd_type set to one of the retransmittable DCCP
@@ -177,6 +188,7 @@ static void dccp_retransmit_timer(struct sock *sk)
goto out;
}
+backoff:
icsk->icsk_backoff++;
icsk->icsk_retransmits++;
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index ce4aaf94860d..2b289ef20ab3 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -172,7 +172,7 @@ static struct hlist_head *dn_find_list(struct sock *sk)
/*
* Valid ports are those greater than zero and not already in use.
*/
-static int check_port(unsigned short port)
+static int check_port(__le16 port)
{
struct sock *sk;
struct hlist_node *node;
@@ -661,7 +661,7 @@ disc_reject:
}
}
-char *dn_addr2asc(dn_address addr, char *buf)
+char *dn_addr2asc(__u16 addr, char *buf)
{
unsigned short node, area;
@@ -801,7 +801,7 @@ static int dn_auto_bind(struct socket *sock)
/* End of compatibility stuff */
scp->addr.sdn_add.a_len = dn_htons(2);
- rv = dn_dev_bind_default((dn_address *)scp->addr.sdn_add.a_addr);
+ rv = dn_dev_bind_default((__le16 *)scp->addr.sdn_add.a_addr);
if (rv == 0) {
rv = dn_hash_sock(sk);
if (rv)
@@ -1021,7 +1021,7 @@ static void dn_user_copy(struct sk_buff *skb, struct optdata_dn *opt)
opt->opt_optl = *ptr++;
opt->opt_status = 0;
memcpy(opt->opt_data, ptr, opt->opt_optl);
- skb_pull(skb, opt->opt_optl + 1);
+ skb_pull(skb, dn_ntohs(opt->opt_optl) + 1);
}
@@ -1121,8 +1121,8 @@ static int dn_accept(struct socket *sock, struct socket *newsock, int flags)
skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->addr), &type));
skb_pull(skb, dn_username2sockaddr(skb->data, skb->len, &(DN_SK(newsk)->peer), &type));
- *(dn_address *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src;
- *(dn_address *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst;
+ *(__le16 *)(DN_SK(newsk)->peer.sdn_add.a_addr) = cb->src;
+ *(__le16 *)(DN_SK(newsk)->addr.sdn_add.a_addr) = cb->dst;
menuver = *skb->data;
skb_pull(skb, 1);
@@ -1365,7 +1365,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char __us
if (optlen != sizeof(struct optdata_dn))
return -EINVAL;
- if (u.opt.opt_optl > 16)
+ if (dn_ntohs(u.opt.opt_optl) > 16)
return -EINVAL;
memcpy(&scp->conndata_out, &u.opt, optlen);
@@ -1378,7 +1378,7 @@ static int __dn_setsockopt(struct socket *sock, int level,int optname, char __us
if (optlen != sizeof(struct optdata_dn))
return -EINVAL;
- if (u.opt.opt_optl > 16)
+ if (dn_ntohs(u.opt.opt_optl) > 16)
return -EINVAL;
memcpy(&scp->discdata_out, &u.opt, optlen);
@@ -1693,7 +1693,7 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock,
if (rv)
goto out;
- if (flags & ~(MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT|MSG_NOSIGNAL)) {
+ if (flags & ~(MSG_CMSG_COMPAT|MSG_PEEK|MSG_OOB|MSG_WAITALL|MSG_DONTWAIT|MSG_NOSIGNAL)) {
rv = -EOPNOTSUPP;
goto out;
}
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index efbead83ba7f..cc7b9d9255ef 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -64,7 +64,7 @@ extern struct neigh_table dn_neigh_table;
/*
* decnet_address is kept in network order.
*/
-dn_address decnet_address = 0;
+__le16 decnet_address = 0;
static DEFINE_RWLOCK(dndev_lock);
static struct net_device *decnet_default_device;
@@ -439,7 +439,7 @@ static void dn_dev_del_ifa(struct dn_dev *dn_db, struct dn_ifaddr **ifap, int de
*ifap = ifa1->ifa_next;
if (dn_db->dev->type == ARPHRD_ETHER) {
- if (ifa1->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) {
+ if (ifa1->ifa_local != dn_eth2dn(dev->dev_addr)) {
dn_dn2eth(mac_addr, ifa1->ifa_local);
dev_mc_delete(dev, mac_addr, ETH_ALEN, 0);
}
@@ -470,7 +470,7 @@ static int dn_dev_insert_ifa(struct dn_dev *dn_db, struct dn_ifaddr *ifa)
}
if (dev->type == ARPHRD_ETHER) {
- if (ifa->ifa_local != dn_htons(dn_eth2dn(dev->dev_addr))) {
+ if (ifa->ifa_local != dn_eth2dn(dev->dev_addr)) {
dn_dn2eth(mac_addr, ifa->ifa_local);
dev_mc_add(dev, mac_addr, ETH_ALEN, 0);
dev_mc_upload(dev);
@@ -561,7 +561,7 @@ int dn_dev_ioctl(unsigned int cmd, void __user *arg)
switch(cmd) {
case SIOCGIFADDR:
- *((dn_address *)sdn->sdn_nodeaddr) = ifa->ifa_local;
+ *((__le16 *)sdn->sdn_nodeaddr) = ifa->ifa_local;
goto rarok;
case SIOCSIFADDR:
@@ -804,7 +804,7 @@ done:
return skb->len;
}
-static int dn_dev_get_first(struct net_device *dev, dn_address *addr)
+static int dn_dev_get_first(struct net_device *dev, __le16 *addr)
{
struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
struct dn_ifaddr *ifa;
@@ -830,7 +830,7 @@ out:
* a sensible default. Eventually the routing code will take care of all the
* nasties for us I hope.
*/
-int dn_dev_bind_default(dn_address *addr)
+int dn_dev_bind_default(__le16 *addr)
{
struct net_device *dev;
int rv;
@@ -853,7 +853,7 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
{
struct endnode_hello_message *msg;
struct sk_buff *skb = NULL;
- unsigned short int *pktlen;
+ __le16 *pktlen;
struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
if ((skb = dn_alloc_skb(NULL, sizeof(*msg), GFP_ATOMIC)) == NULL)
@@ -882,7 +882,7 @@ static void dn_send_endnode_hello(struct net_device *dev, struct dn_ifaddr *ifa)
msg->datalen = 0x02;
memset(msg->data, 0xAA, 2);
- pktlen = (unsigned short *)skb_push(skb,2);
+ pktlen = (__le16 *)skb_push(skb,2);
*pktlen = dn_htons(skb->len - 2);
skb->nh.raw = skb->data;
@@ -926,7 +926,7 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
size_t size;
unsigned char *ptr;
unsigned char *i1, *i2;
- unsigned short *pktlen;
+ __le16 *pktlen;
char *src;
if (mtu2blksize(dev) < (26 + 7))
@@ -955,11 +955,11 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
ptr += ETH_ALEN;
*ptr++ = dn_db->parms.forwarding == 1 ?
DN_RT_INFO_L1RT : DN_RT_INFO_L2RT;
- *((unsigned short *)ptr) = dn_htons(mtu2blksize(dev));
+ *((__le16 *)ptr) = dn_htons(mtu2blksize(dev));
ptr += 2;
*ptr++ = dn_db->parms.priority; /* Priority */
*ptr++ = 0; /* Area: Reserved */
- *((unsigned short *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
+ *((__le16 *)ptr) = dn_htons((unsigned short)dn_db->parms.t3);
ptr += 2;
*ptr++ = 0; /* MPD: Reserved */
i1 = ptr++;
@@ -974,7 +974,7 @@ static void dn_send_router_hello(struct net_device *dev, struct dn_ifaddr *ifa)
skb_trim(skb, (27 + *i2));
- pktlen = (unsigned short *)skb_push(skb, 2);
+ pktlen = (__le16 *)skb_push(skb, 2);
*pktlen = dn_htons(skb->len - 2);
skb->nh.raw = skb->data;
@@ -1016,7 +1016,7 @@ static void dn_send_ptp_hello(struct net_device *dev, struct dn_ifaddr *ifa)
ptr = skb_put(skb, 2 + 4 + tdlen);
*ptr++ = DN_RT_PKT_HELO;
- *((dn_address *)ptr) = ifa->ifa_local;
+ *((__le16 *)ptr) = ifa->ifa_local;
ptr += 2;
*ptr++ = tdlen;
@@ -1150,7 +1150,7 @@ struct dn_dev *dn_dev_create(struct net_device *dev, int *err)
void dn_dev_up(struct net_device *dev)
{
struct dn_ifaddr *ifa;
- dn_address addr = decnet_address;
+ __le16 addr = decnet_address;
int maybe_default = 0;
struct dn_dev *dn_db = (struct dn_dev *)dev->dn_ptr;
@@ -1173,7 +1173,7 @@ void dn_dev_up(struct net_device *dev)
if (dev->type == ARPHRD_ETHER) {
if (memcmp(dev->dev_addr, dn_hiord, 4) != 0)
return;
- addr = dn_htons(dn_eth2dn(dev->dev_addr));
+ addr = dn_eth2dn(dev->dev_addr);
maybe_default = 1;
}
@@ -1385,8 +1385,8 @@ static int dn_dev_seq_show(struct seq_file *seq, void *v)
mtu2blksize(dev),
dn_db->parms.priority,
dn_db->parms.state, dn_db->parms.name,
- dn_db->router ? dn_addr2asc(dn_ntohs(*(dn_address *)dn_db->router->primary_key), router_buf) : "",
- dn_db->peer ? dn_addr2asc(dn_ntohs(*(dn_address *)dn_db->peer->primary_key), peer_buf) : "");
+ dn_db->router ? dn_addr2asc(dn_ntohs(*(__le16 *)dn_db->router->primary_key), router_buf) : "",
+ dn_db->peer ? dn_addr2asc(dn_ntohs(*(__le16 *)dn_db->peer->primary_key), peer_buf) : "");
}
return 0;
}
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
index 99bc061759c3..bd4ce8681a12 100644
--- a/net/decnet/dn_fib.c
+++ b/net/decnet/dn_fib.c
@@ -143,11 +143,11 @@ static inline struct dn_fib_info *dn_fib_find_info(const struct dn_fib_info *nfi
return NULL;
}
-u16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
+__le16 dn_fib_get_attr16(struct rtattr *attr, int attrlen, int type)
{
while(RTA_OK(attr,attrlen)) {
if (attr->rta_type == type)
- return *(u16*)RTA_DATA(attr);
+ return *(__le16*)RTA_DATA(attr);
attr = RTA_NEXT(attr, attrlen);
}
@@ -565,7 +565,7 @@ int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-static void fib_magic(int cmd, int type, __u16 dst, int dst_len, struct dn_ifaddr *ifa)
+static void fib_magic(int cmd, int type, __le16 dst, int dst_len, struct dn_ifaddr *ifa)
{
struct dn_fib_table *tb;
struct {
@@ -684,7 +684,7 @@ static int dn_fib_dnaddr_event(struct notifier_block *this, unsigned long event,
return NOTIFY_DONE;
}
-int dn_fib_sync_down(dn_address local, struct net_device *dev, int force)
+int dn_fib_sync_down(__le16 local, struct net_device *dev, int force)
{
int ret = 0;
int scope = RT_SCOPE_NOWHERE;
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index 33ab256cfd4a..7c8692c26bfe 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -95,7 +95,7 @@ static struct neigh_ops dn_phase3_ops = {
struct neigh_table dn_neigh_table = {
.family = PF_DECnet,
.entry_size = sizeof(struct dn_neigh),
- .key_len = sizeof(dn_address),
+ .key_len = sizeof(__le16),
.hash = dn_neigh_hash,
.constructor = dn_neigh_construct,
.id = "dn_neigh_cache",
@@ -123,7 +123,7 @@ struct neigh_table dn_neigh_table = {
static u32 dn_neigh_hash(const void *pkey, const struct net_device *dev)
{
- return jhash_2words(*(dn_address *)pkey, 0, dn_neigh_table.hash_rnd);
+ return jhash_2words(*(__u16 *)pkey, 0, dn_neigh_table.hash_rnd);
}
static int dn_neigh_construct(struct neighbour *neigh)
@@ -249,14 +249,14 @@ static int dn_long_output(struct sk_buff *skb)
data = skb_push(skb, sizeof(struct dn_long_packet) + 3);
lp = (struct dn_long_packet *)(data+3);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
*(data + 2) = 1 | DN_RT_F_PF; /* Padding */
lp->msgflg = DN_RT_PKT_LONG|(cb->rt_flags&(DN_RT_F_IE|DN_RT_F_RQR|DN_RT_F_RTS));
lp->d_area = lp->d_subarea = 0;
- dn_dn2eth(lp->d_id, dn_ntohs(cb->dst));
+ dn_dn2eth(lp->d_id, cb->dst);
lp->s_area = lp->s_subarea = 0;
- dn_dn2eth(lp->s_id, dn_ntohs(cb->src));
+ dn_dn2eth(lp->s_id, cb->src);
lp->nl2 = 0;
lp->visit_ct = cb->hops & 0x3f;
lp->s_class = 0;
@@ -293,7 +293,7 @@ static int dn_short_output(struct sk_buff *skb)
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
sp = (struct dn_short_packet *)(data+2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
@@ -335,7 +335,7 @@ static int dn_phase3_output(struct sk_buff *skb)
}
data = skb_push(skb, sizeof(struct dn_short_packet) + 2);
- *((unsigned short *)data) = dn_htons(skb->len - 2);
+ *((__le16 *)data) = dn_htons(skb->len - 2);
sp = (struct dn_short_packet *)(data + 2);
sp->msgflg = DN_RT_PKT_SHORT|(cb->rt_flags&(DN_RT_F_RQR|DN_RT_F_RTS));
@@ -373,9 +373,9 @@ int dn_neigh_router_hello(struct sk_buff *skb)
struct neighbour *neigh;
struct dn_neigh *dn;
struct dn_dev *dn_db;
- dn_address src;
+ __le16 src;
- src = dn_htons(dn_eth2dn(msg->id));
+ src = dn_eth2dn(msg->id);
neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
@@ -409,7 +409,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
}
/* Only use routers in our area */
- if ((dn_ntohs(src)>>10) == dn_ntohs((decnet_address)>>10)) {
+ if ((dn_ntohs(src)>>10) == (dn_ntohs((decnet_address))>>10)) {
if (!dn_db->router) {
dn_db->router = neigh_clone(neigh);
} else {
@@ -433,9 +433,9 @@ int dn_neigh_endnode_hello(struct sk_buff *skb)
struct endnode_hello_message *msg = (struct endnode_hello_message *)skb->data;
struct neighbour *neigh;
struct dn_neigh *dn;
- dn_address src;
+ __le16 src;
- src = dn_htons(dn_eth2dn(msg->id));
+ src = dn_eth2dn(msg->id);
neigh = __neigh_lookup(&dn_neigh_table, &src, skb->dev, 1);
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 44bda85e678f..547523b41c81 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -85,7 +85,7 @@ static void dn_log_martian(struct sk_buff *skb, const char *msg)
if (decnet_log_martians && net_ratelimit()) {
char *devname = skb->dev ? skb->dev->name : "???";
struct dn_skb_cb *cb = DN_SKB_CB(skb);
- printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port);
+ printk(KERN_INFO "DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, dn_ntohs(cb->src), dn_ntohs(cb->dst), dn_ntohs(cb->src_port), dn_ntohs(cb->dst_port));
}
}
@@ -128,7 +128,7 @@ static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
*/
static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
{
- unsigned short *ptr = (unsigned short *)skb->data;
+ __le16 *ptr = (__le16 *)skb->data;
int len = 0;
unsigned short ack;
@@ -346,7 +346,7 @@ static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
ptr = skb->data;
cb->services = *ptr++;
cb->info = *ptr++;
- cb->segsize = dn_ntohs(*(__u16 *)ptr);
+ cb->segsize = dn_ntohs(*(__le16 *)ptr);
if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
scp->persist = 0;
@@ -363,7 +363,7 @@ static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
if (skb->len > 0) {
unsigned char dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
- scp->conndata_in.opt_optl = dlen;
+ scp->conndata_in.opt_optl = dn_htons((__u16)dlen);
memcpy(scp->conndata_in.opt_data, skb->data + 1, dlen);
}
}
@@ -397,17 +397,17 @@ static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- reason = dn_ntohs(*(__u16 *)skb->data);
+ reason = dn_ntohs(*(__le16 *)skb->data);
skb_pull(skb, 2);
- scp->discdata_in.opt_status = reason;
+ scp->discdata_in.opt_status = dn_htons(reason);
scp->discdata_in.opt_optl = 0;
memset(scp->discdata_in.opt_data, 0, 16);
if (skb->len > 0) {
unsigned char dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
- scp->discdata_in.opt_optl = dlen;
+ scp->discdata_in.opt_optl = dn_htons((__u16)dlen);
memcpy(scp->discdata_in.opt_data, skb->data + 1, dlen);
}
}
@@ -464,7 +464,7 @@ static void dn_nsp_disc_conf(struct sock *sk, struct sk_buff *skb)
if (skb->len != 2)
goto out;
- reason = dn_ntohs(*(__u16 *)skb->data);
+ reason = dn_ntohs(*(__le16 *)skb->data);
sk->sk_state = TCP_CLOSE;
@@ -513,7 +513,7 @@ static void dn_nsp_linkservice(struct sock *sk, struct sk_buff *skb)
if (skb->len != 4)
goto out;
- segnum = dn_ntohs(*(__u16 *)ptr);
+ segnum = dn_ntohs(*(__le16 *)ptr);
ptr += 2;
lsflags = *(unsigned char *)ptr++;
fcval = *ptr;
@@ -621,7 +621,7 @@ static void dn_nsp_otherdata(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+ cb->segnum = segnum = dn_ntohs(*(__le16 *)skb->data);
skb_pull(skb, 2);
if (seq_next(scp->numoth_rcv, segnum)) {
@@ -649,7 +649,7 @@ static void dn_nsp_data(struct sock *sk, struct sk_buff *skb)
if (skb->len < 2)
goto out;
- cb->segnum = segnum = dn_ntohs(*(__u16 *)skb->data);
+ cb->segnum = segnum = dn_ntohs(*(__le16 *)skb->data);
skb_pull(skb, 2);
if (seq_next(scp->numdat_rcv, segnum)) {
@@ -760,7 +760,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
/*
* Grab the destination address.
*/
- cb->dst_port = *(unsigned short *)ptr;
+ cb->dst_port = *(__le16 *)ptr;
cb->src_port = 0;
ptr += 2;
@@ -768,7 +768,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
* If not a connack, grab the source address too.
*/
if (pskb_may_pull(skb, 5)) {
- cb->src_port = *(unsigned short *)ptr;
+ cb->src_port = *(__le16 *)ptr;
ptr += 2;
skb_pull(skb, 5);
}
@@ -778,7 +778,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
* Swap src & dst and look up in the normal way.
*/
if (unlikely(cb->rt_flags & DN_RT_F_RTS)) {
- unsigned short tmp = cb->dst_port;
+ __le16 tmp = cb->dst_port;
cb->dst_port = cb->src_port;
cb->src_port = tmp;
tmp = cb->dst;
diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c
index c96c767b1f74..c2e21cd89b3c 100644
--- a/net/decnet/dn_nsp_out.c
+++ b/net/decnet/dn_nsp_out.c
@@ -287,26 +287,26 @@ int dn_nsp_xmit_timeout(struct sock *sk)
return 0;
}
-static inline unsigned char *dn_mk_common_header(struct dn_scp *scp, struct sk_buff *skb, unsigned char msgflag, int len)
+static inline __le16 *dn_mk_common_header(struct dn_scp *scp, struct sk_buff *skb, unsigned char msgflag, int len)
{
unsigned char *ptr = skb_push(skb, len);
BUG_ON(len < 5);
*ptr++ = msgflag;
- *((unsigned short *)ptr) = scp->addrrem;
+ *((__le16 *)ptr) = scp->addrrem;
ptr += 2;
- *((unsigned short *)ptr) = scp->addrloc;
+ *((__le16 *)ptr) = scp->addrloc;
ptr += 2;
- return ptr;
+ return (__le16 __force *)ptr;
}
-static unsigned short *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned char msgflag, int hlen, int other)
+static __le16 *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, unsigned char msgflag, int hlen, int other)
{
struct dn_scp *scp = DN_SK(sk);
unsigned short acknum = scp->numdat_rcv & 0x0FFF;
unsigned short ackcrs = scp->numoth_rcv & 0x0FFF;
- unsigned short *ptr;
+ __le16 *ptr;
BUG_ON(hlen < 9);
@@ -325,7 +325,7 @@ static unsigned short *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, un
/* Set "cross subchannel" bit in ackcrs */
ackcrs |= 0x2000;
- ptr = (unsigned short *)dn_mk_common_header(scp, skb, msgflag, hlen);
+ ptr = (__le16 *)dn_mk_common_header(scp, skb, msgflag, hlen);
*ptr++ = dn_htons(acknum);
*ptr++ = dn_htons(ackcrs);
@@ -333,11 +333,11 @@ static unsigned short *dn_mk_ack_header(struct sock *sk, struct sk_buff *skb, un
return ptr;
}
-static unsigned short *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth)
+static __le16 *dn_nsp_mk_data_header(struct sock *sk, struct sk_buff *skb, int oth)
{
struct dn_scp *scp = DN_SK(sk);
struct dn_skb_cb *cb = DN_SKB_CB(skb);
- unsigned short *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth);
+ __le16 *ptr = dn_mk_ack_header(sk, skb, cb->nsp_flags, 11, oth);
if (unlikely(oth)) {
cb->segnum = scp->numoth;
@@ -524,9 +524,9 @@ void dn_send_conn_conf(struct sock *sk, gfp_t gfp)
struct dn_scp *scp = DN_SK(sk);
struct sk_buff *skb = NULL;
struct nsp_conn_init_msg *msg;
- unsigned char len = scp->conndata_out.opt_optl;
+ __u8 len = (__u8)dn_ntohs(scp->conndata_out.opt_optl);
- if ((skb = dn_alloc_skb(sk, 50 + scp->conndata_out.opt_optl, gfp)) == NULL)
+ if ((skb = dn_alloc_skb(sk, 50 + dn_ntohs(scp->conndata_out.opt_optl), gfp)) == NULL)
return;
msg = (struct nsp_conn_init_msg *)skb_put(skb, sizeof(*msg));
@@ -553,7 +553,7 @@ void dn_send_conn_conf(struct sock *sk, gfp_t gfp)
static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
unsigned short reason, gfp_t gfp,
struct dst_entry *dst,
- int ddl, unsigned char *dd, __u16 rem, __u16 loc)
+ int ddl, unsigned char *dd, __le16 rem, __le16 loc)
{
struct sk_buff *skb = NULL;
int size = 7 + ddl + ((msgflg == NSP_DISCINIT) ? 1 : 0);
@@ -561,7 +561,7 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
if ((dst == NULL) || (rem == 0)) {
if (net_ratelimit())
- printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", (unsigned)rem, dst);
+ printk(KERN_DEBUG "DECnet: dn_nsp_do_disc: BUG! Please report this to SteveW@ACM.org rem=%u dst=%p\n", dn_ntohs(rem), dst);
return;
}
@@ -570,11 +570,11 @@ static __inline__ void dn_nsp_do_disc(struct sock *sk, unsigned char msgflg,
msg = skb_put(skb, size);
*msg++ = msgflg;
- *(__u16 *)msg = rem;
+ *(__le16 *)msg = rem;
msg += 2;
- *(__u16 *)msg = loc;
+ *(__le16 *)msg = loc;
msg += 2;
- *(__u16 *)msg = dn_htons(reason);
+ *(__le16 *)msg = dn_htons(reason);
msg += 2;
if (msgflg == NSP_DISCINIT)
*msg++ = ddl;
@@ -600,10 +600,10 @@ void dn_nsp_send_disc(struct sock *sk, unsigned char msgflg,
int ddl = 0;
if (msgflg == NSP_DISCINIT)
- ddl = scp->discdata_out.opt_optl;
+ ddl = dn_ntohs(scp->discdata_out.opt_optl);
if (reason == 0)
- reason = scp->discdata_out.opt_status;
+ reason = dn_ntohs(scp->discdata_out.opt_status);
dn_nsp_do_disc(sk, msgflg, reason, gfp, sk->sk_dst_cache, ddl,
scp->discdata_out.opt_data, scp->addrrem, scp->addrloc);
@@ -708,7 +708,7 @@ void dn_nsp_send_conninit(struct sock *sk, unsigned char msgflg)
if (aux > 0)
memcpy(skb_put(skb, aux), scp->accessdata.acc_acc, aux);
- aux = scp->conndata_out.opt_optl;
+ aux = (__u8)dn_ntohs(scp->conndata_out.opt_optl);
*skb_put(skb, 1) = aux;
if (aux > 0)
memcpy(skb_put(skb,aux), scp->conndata_out.opt_data, aux);
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 3407f190afe8..e172cf98d7fc 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -133,9 +133,9 @@ static struct dst_ops dn_dst_ops = {
.entries = ATOMIC_INIT(0),
};
-static __inline__ unsigned dn_hash(unsigned short src, unsigned short dst)
+static __inline__ unsigned dn_hash(__le16 src, __le16 dst)
{
- unsigned short tmp = src ^ dst;
+ __u16 tmp = (__u16 __force)(src ^ dst);
tmp ^= (tmp >> 3);
tmp ^= (tmp >> 5);
tmp ^= (tmp >> 10);
@@ -149,8 +149,7 @@ static inline void dnrt_free(struct dn_route *rt)
static inline void dnrt_drop(struct dn_route *rt)
{
- if (rt)
- dst_release(&rt->u.dst);
+ dst_release(&rt->u.dst);
call_rcu_bh(&rt->u.dst.rcu_head, dst_rcu_free);
}
@@ -379,9 +378,9 @@ static int dn_return_short(struct sk_buff *skb)
{
struct dn_skb_cb *cb;
unsigned char *ptr;
- dn_address *src;
- dn_address *dst;
- dn_address tmp;
+ __le16 *src;
+ __le16 *dst;
+ __le16 tmp;
/* Add back headers */
skb_push(skb, skb->data - skb->nh.raw);
@@ -394,9 +393,9 @@ static int dn_return_short(struct sk_buff *skb)
ptr = skb->data + 2;
*ptr++ = (cb->rt_flags & ~DN_RT_F_RQR) | DN_RT_F_RTS;
- dst = (dn_address *)ptr;
+ dst = (__le16 *)ptr;
ptr += 2;
- src = (dn_address *)ptr;
+ src = (__le16 *)ptr;
ptr += 2;
*ptr = 0; /* Zero hop count */
@@ -475,7 +474,8 @@ static int dn_route_rx_packet(struct sk_buff *skb)
struct dn_skb_cb *cb = DN_SKB_CB(skb);
printk(KERN_DEBUG
"DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n",
- (int)cb->rt_flags, devname, skb->len, cb->src, cb->dst,
+ (int)cb->rt_flags, devname, skb->len,
+ dn_ntohs(cb->src), dn_ntohs(cb->dst),
err, skb->pkt_type);
}
@@ -505,7 +505,7 @@ static int dn_route_rx_long(struct sk_buff *skb)
/* Destination info */
ptr += 2;
- cb->dst = dn_htons(dn_eth2dn(ptr));
+ cb->dst = dn_eth2dn(ptr);
if (memcmp(ptr, dn_hiord_addr, 4) != 0)
goto drop_it;
ptr += 6;
@@ -513,7 +513,7 @@ static int dn_route_rx_long(struct sk_buff *skb)
/* Source info */
ptr += 2;
- cb->src = dn_htons(dn_eth2dn(ptr));
+ cb->src = dn_eth2dn(ptr);
if (memcmp(ptr, dn_hiord_addr, 4) != 0)
goto drop_it;
ptr += 6;
@@ -541,9 +541,9 @@ static int dn_route_rx_short(struct sk_buff *skb)
skb_pull(skb, 5);
skb->h.raw = skb->data;
- cb->dst = *(dn_address *)ptr;
+ cb->dst = *(__le16 *)ptr;
ptr += 2;
- cb->src = *(dn_address *)ptr;
+ cb->src = *(__le16 *)ptr;
ptr += 2;
cb->hops = *ptr & 0x3f;
@@ -575,7 +575,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
{
struct dn_skb_cb *cb;
unsigned char flags = 0;
- __u16 len = dn_ntohs(*(__u16 *)skb->data);
+ __u16 len = dn_ntohs(*(__le16 *)skb->data);
struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr;
unsigned char padlen = 0;
@@ -782,7 +782,7 @@ static int dn_rt_bug(struct sk_buff *skb)
struct dn_skb_cb *cb = DN_SKB_CB(skb);
printk(KERN_DEBUG "dn_rt_bug: skb from:%04x to:%04x\n",
- cb->src, cb->dst);
+ dn_ntohs(cb->src), dn_ntohs(cb->dst));
}
kfree_skb(skb);
@@ -823,7 +823,7 @@ static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
return 0;
}
-static inline int dn_match_addr(__u16 addr1, __u16 addr2)
+static inline int dn_match_addr(__le16 addr1, __le16 addr2)
{
__u16 tmp = dn_ntohs(addr1) ^ dn_ntohs(addr2);
int match = 16;
@@ -834,9 +834,9 @@ static inline int dn_match_addr(__u16 addr1, __u16 addr2)
return match;
}
-static __u16 dnet_select_source(const struct net_device *dev, __u16 daddr, int scope)
+static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
{
- __u16 saddr = 0;
+ __le16 saddr = 0;
struct dn_dev *dn_db = dev->dn_ptr;
struct dn_ifaddr *ifa;
int best_match = 0;
@@ -861,14 +861,14 @@ static __u16 dnet_select_source(const struct net_device *dev, __u16 daddr, int s
return saddr;
}
-static inline __u16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
+static inline __le16 __dn_fib_res_prefsrc(struct dn_fib_res *res)
{
return dnet_select_source(DN_FIB_RES_DEV(*res), DN_FIB_RES_GW(*res), res->scope);
}
-static inline __u16 dn_fib_rules_map_destination(__u16 daddr, struct dn_fib_res *res)
+static inline __le16 dn_fib_rules_map_destination(__le16 daddr, struct dn_fib_res *res)
{
- __u16 mask = dnet_make_mask(res->prefixlen);
+ __le16 mask = dnet_make_mask(res->prefixlen);
return (daddr&~mask)|res->fi->fib_nh->nh_gw;
}
@@ -892,12 +892,13 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old
struct dn_fib_res res = { .fi = NULL, .type = RTN_UNICAST };
int err;
int free_res = 0;
- __u16 gateway = 0;
+ __le16 gateway = 0;
if (decnet_debug_level & 16)
printk(KERN_DEBUG
"dn_route_output_slow: dst=%04x src=%04x mark=%d"
- " iif=%d oif=%d\n", oldflp->fld_dst, oldflp->fld_src,
+ " iif=%d oif=%d\n", dn_ntohs(oldflp->fld_dst),
+ dn_ntohs(oldflp->fld_src),
oldflp->fld_fwmark, loopback_dev.ifindex, oldflp->oif);
/* If we have an output interface, verify its a DECnet device */
@@ -961,8 +962,9 @@ source_ok:
if (decnet_debug_level & 16)
printk(KERN_DEBUG
"dn_route_output_slow: initial checks complete."
- " dst=%o4x src=%04x oif=%d try_hard=%d\n", fl.fld_dst,
- fl.fld_src, fl.oif, try_hard);
+ " dst=%o4x src=%04x oif=%d try_hard=%d\n",
+ dn_ntohs(fl.fld_dst), dn_ntohs(fl.fld_src),
+ fl.oif, try_hard);
/*
* N.B. If the kernel is compiled without router support then
@@ -1218,8 +1220,8 @@ static int dn_route_input_slow(struct sk_buff *skb)
struct neighbour *neigh = NULL;
unsigned hash;
int flags = 0;
- __u16 gateway = 0;
- __u16 local_src = 0;
+ __le16 gateway = 0;
+ __le16 local_src = 0;
struct flowi fl = { .nl_u = { .dn_u =
{ .daddr = cb->dst,
.saddr = cb->src,
@@ -1266,7 +1268,7 @@ static int dn_route_input_slow(struct sk_buff *skb)
res.type = RTN_LOCAL;
flags |= RTCF_DIRECTSRC;
} else {
- __u16 src_map = fl.fld_src;
+ __le16 src_map = fl.fld_src;
free_res = 1;
out_dev = DN_FIB_RES_DEV(res);
diff --git a/net/decnet/dn_rules.c b/net/decnet/dn_rules.c
index 1060de70bc0c..446faafe2065 100644
--- a/net/decnet/dn_rules.c
+++ b/net/decnet/dn_rules.c
@@ -27,6 +27,8 @@
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/in_route.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
#include <asm/atomic.h>
#include <asm/uaccess.h>
#include <net/neighbour.h>
@@ -39,18 +41,18 @@
struct dn_fib_rule
{
- struct dn_fib_rule *r_next;
+ struct hlist_node r_hlist;
atomic_t r_clntref;
u32 r_preference;
unsigned char r_table;
unsigned char r_action;
unsigned char r_dst_len;
unsigned char r_src_len;
- dn_address r_src;
- dn_address r_srcmask;
- dn_address r_dst;
- dn_address r_dstmask;
- dn_address r_srcmap;
+ __le16 r_src;
+ __le16 r_srcmask;
+ __le16 r_dst;
+ __le16 r_dstmask;
+ __le16 r_srcmap;
u8 r_flags;
#ifdef CONFIG_DECNET_ROUTE_FWMARK
u32 r_fwmark;
@@ -58,6 +60,7 @@ struct dn_fib_rule
int r_ifindex;
char r_ifname[IFNAMSIZ];
int r_dead;
+ struct rcu_head rcu;
};
static struct dn_fib_rule default_rule = {
@@ -67,18 +70,17 @@ static struct dn_fib_rule default_rule = {
.r_action = RTN_UNICAST
};
-static struct dn_fib_rule *dn_fib_rules = &default_rule;
-static DEFINE_RWLOCK(dn_fib_rules_lock);
-
+static struct hlist_head dn_fib_rules;
int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct rtattr **rta = arg;
struct rtmsg *rtm = NLMSG_DATA(nlh);
- struct dn_fib_rule *r, **rp;
+ struct dn_fib_rule *r;
+ struct hlist_node *node;
int err = -ESRCH;
- for(rp=&dn_fib_rules; (r=*rp) != NULL; rp = &r->r_next) {
+ hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) {
if ((!rta[RTA_SRC-1] || memcmp(RTA_DATA(rta[RTA_SRC-1]), &r->r_src, 2) == 0) &&
rtm->rtm_src_len == r->r_src_len &&
rtm->rtm_dst_len == r->r_dst_len &&
@@ -95,10 +97,8 @@ int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (r == &default_rule)
break;
- write_lock_bh(&dn_fib_rules_lock);
- *rp = r->r_next;
+ hlist_del_rcu(&r->r_hlist);
r->r_dead = 1;
- write_unlock_bh(&dn_fib_rules_lock);
dn_fib_rule_put(r);
err = 0;
break;
@@ -108,11 +108,17 @@ int dn_fib_rtm_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return err;
}
+static inline void dn_fib_rule_put_rcu(struct rcu_head *head)
+{
+ struct dn_fib_rule *r = container_of(head, struct dn_fib_rule, rcu);
+ kfree(r);
+}
+
void dn_fib_rule_put(struct dn_fib_rule *r)
{
if (atomic_dec_and_test(&r->r_clntref)) {
if (r->r_dead)
- kfree(r);
+ call_rcu(&r->rcu, dn_fib_rule_put_rcu);
else
printk(KERN_DEBUG "Attempt to free alive dn_fib_rule\n");
}
@@ -123,7 +129,8 @@ int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct rtattr **rta = arg;
struct rtmsg *rtm = NLMSG_DATA(nlh);
- struct dn_fib_rule *r, *new_r, **rp;
+ struct dn_fib_rule *r, *new_r, *last = NULL;
+ struct hlist_node *node = NULL;
unsigned char table_id;
if (rtm->rtm_src_len > 16 || rtm->rtm_dst_len > 16)
@@ -149,6 +156,7 @@ int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (!new_r)
return -ENOMEM;
memset(new_r, 0, sizeof(*new_r));
+
if (rta[RTA_SRC-1])
memcpy(&new_r->r_src, RTA_DATA(rta[RTA_SRC-1]), 2);
if (rta[RTA_DST-1])
@@ -179,27 +187,26 @@ int dn_fib_rtm_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
}
}
- rp = &dn_fib_rules;
+ r = container_of(dn_fib_rules.first, struct dn_fib_rule, r_hlist);
if (!new_r->r_preference) {
- r = dn_fib_rules;
- if (r && (r = r->r_next) != NULL) {
- rp = &dn_fib_rules->r_next;
+ if (r && r->r_hlist.next != NULL) {
+ r = container_of(r->r_hlist.next, struct dn_fib_rule, r_hlist);
if (r->r_preference)
new_r->r_preference = r->r_preference - 1;
}
}
- while((r=*rp) != NULL) {
+ hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) {
if (r->r_preference > new_r->r_preference)
break;
- rp = &r->r_next;
+ last = r;
}
-
- new_r->r_next = r;
atomic_inc(&new_r->r_clntref);
- write_lock_bh(&dn_fib_rules_lock);
- *rp = new_r;
- write_unlock_bh(&dn_fib_rules_lock);
+
+ if (last)
+ hlist_add_after_rcu(&last->r_hlist, &new_r->r_hlist);
+ else
+ hlist_add_before_rcu(&new_r->r_hlist, &r->r_hlist);
return 0;
}
@@ -208,12 +215,14 @@ int dn_fib_lookup(const struct flowi *flp, struct dn_fib_res *res)
{
struct dn_fib_rule *r, *policy;
struct dn_fib_table *tb;
- dn_address saddr = flp->fld_src;
- dn_address daddr = flp->fld_dst;
+ __le16 saddr = flp->fld_src;
+ __le16 daddr = flp->fld_dst;
+ struct hlist_node *node;
int err;
- read_lock(&dn_fib_rules_lock);
- for(r = dn_fib_rules; r; r = r->r_next) {
+ rcu_read_lock();
+
+ hlist_for_each_entry_rcu(r, node, &dn_fib_rules, r_hlist) {
if (((saddr^r->r_src) & r->r_srcmask) ||
((daddr^r->r_dst) & r->r_dstmask) ||
#ifdef CONFIG_DECNET_ROUTE_FWMARK
@@ -228,14 +237,14 @@ int dn_fib_lookup(const struct flowi *flp, struct dn_fib_res *res)
policy = r;
break;
case RTN_UNREACHABLE:
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return -ENETUNREACH;
default:
case RTN_BLACKHOLE:
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return -EINVAL;
case RTN_PROHIBIT:
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return -EACCES;
}
@@ -246,20 +255,20 @@ int dn_fib_lookup(const struct flowi *flp, struct dn_fib_res *res)
res->r = policy;
if (policy)
atomic_inc(&policy->r_clntref);
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return 0;
}
if (err < 0 && err != -EAGAIN) {
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return err;
}
}
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
return -ESRCH;
}
-unsigned dnet_addr_type(__u16 addr)
+unsigned dnet_addr_type(__le16 addr)
{
struct flowi fl = { .nl_u = { .dn_u = { .daddr = addr } } };
struct dn_fib_res res;
@@ -277,7 +286,7 @@ unsigned dnet_addr_type(__u16 addr)
return ret;
}
-__u16 dn_fib_rules_policy(__u16 saddr, struct dn_fib_res *res, unsigned *flags)
+__le16 dn_fib_rules_policy(__le16 saddr, struct dn_fib_res *res, unsigned *flags)
{
struct dn_fib_rule *r = res->r;
@@ -297,27 +306,23 @@ __u16 dn_fib_rules_policy(__u16 saddr, struct dn_fib_res *res, unsigned *flags)
static void dn_fib_rules_detach(struct net_device *dev)
{
+ struct hlist_node *node;
struct dn_fib_rule *r;
- for(r = dn_fib_rules; r; r = r->r_next) {
- if (r->r_ifindex == dev->ifindex) {
- write_lock_bh(&dn_fib_rules_lock);
+ hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) {
+ if (r->r_ifindex == dev->ifindex)
r->r_ifindex = -1;
- write_unlock_bh(&dn_fib_rules_lock);
- }
}
}
static void dn_fib_rules_attach(struct net_device *dev)
{
+ struct hlist_node *node;
struct dn_fib_rule *r;
- for(r = dn_fib_rules; r; r = r->r_next) {
- if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0) {
- write_lock_bh(&dn_fib_rules_lock);
+ hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) {
+ if (r->r_ifindex == -1 && strcmp(dev->name, r->r_ifname) == 0)
r->r_ifindex = dev->ifindex;
- write_unlock_bh(&dn_fib_rules_lock);
- }
}
}
@@ -387,18 +392,20 @@ rtattr_failure:
int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
{
- int idx;
+ int idx = 0;
int s_idx = cb->args[0];
struct dn_fib_rule *r;
+ struct hlist_node *node;
- read_lock(&dn_fib_rules_lock);
- for(r = dn_fib_rules, idx = 0; r; r = r->r_next, idx++) {
+ rcu_read_lock();
+ hlist_for_each_entry(r, node, &dn_fib_rules, r_hlist) {
if (idx < s_idx)
continue;
if (dn_fib_fill_rule(skb, r, cb, NLM_F_MULTI) < 0)
break;
+ idx++;
}
- read_unlock(&dn_fib_rules_lock);
+ rcu_read_unlock();
cb->args[0] = idx;
return skb->len;
@@ -406,6 +413,8 @@ int dn_fib_dump_rules(struct sk_buff *skb, struct netlink_callback *cb)
void __init dn_fib_rules_init(void)
{
+ INIT_HLIST_HEAD(&dn_fib_rules);
+ hlist_add_head(&default_rule.r_hlist, &dn_fib_rules);
register_netdevice_notifier(&dn_fib_rules_notifier);
}
diff --git a/net/decnet/dn_table.c b/net/decnet/dn_table.c
index 6f8b5658cb4e..0ebc46af1bdd 100644
--- a/net/decnet/dn_table.c
+++ b/net/decnet/dn_table.c
@@ -46,7 +46,7 @@ struct dn_zone
u32 dz_hashmask;
#define DZ_HASHMASK(dz) ((dz)->dz_hashmask)
int dz_order;
- u16 dz_mask;
+ __le16 dz_mask;
#define DZ_MASK(dz) ((dz)->dz_mask)
};
@@ -84,14 +84,14 @@ static int dn_fib_hash_zombies;
static inline dn_fib_idx_t dn_hash(dn_fib_key_t key, struct dn_zone *dz)
{
- u16 h = ntohs(key.datum)>>(16 - dz->dz_order);
+ u16 h = dn_ntohs(key.datum)>>(16 - dz->dz_order);
h ^= (h >> 10);
h ^= (h >> 6);
h &= DZ_HASHMASK(dz);
return *(dn_fib_idx_t *)&h;
}
-static inline dn_fib_key_t dz_key(u16 dst, struct dn_zone *dz)
+static inline dn_fib_key_t dz_key(__le16 dst, struct dn_zone *dz)
{
dn_fib_key_t k;
k.datum = dst & DZ_MASK(dz);
@@ -250,7 +250,7 @@ static int dn_fib_nh_match(struct rtmsg *r, struct nlmsghdr *nlh, struct dn_kern
for_nexthops(fi) {
int attrlen = nhlen - sizeof(struct rtnexthop);
- dn_address gw;
+ __le16 gw;
if (attrlen < 0 || (nhlen -= nhp->rtnh_len) < 0)
return -EINVAL;
@@ -457,7 +457,7 @@ static int dn_fib_table_insert(struct dn_fib_table *tb, struct rtmsg *r, struct
dz_key_0(key);
if (rta->rta_dst) {
- dn_address dst;
+ __le16 dst;
memcpy(&dst, rta->rta_dst, 2);
if (dst & ~DZ_MASK(dz))
return -EINVAL;
@@ -593,7 +593,7 @@ static int dn_fib_table_delete(struct dn_fib_table *tb, struct rtmsg *r, struct
dz_key_0(key);
if (rta->rta_dst) {
- dn_address dst;
+ __le16 dst;
memcpy(&dst, rta->rta_dst, 2);
if (dst & ~DZ_MASK(dz))
return -EINVAL;
diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c
index 0e9d2c571165..bda5920215fd 100644
--- a/net/decnet/sysctl_net_decnet.c
+++ b/net/decnet/sysctl_net_decnet.c
@@ -86,9 +86,9 @@ static void strip_it(char *str)
* Simple routine to parse an ascii DECnet address
* into a network order address.
*/
-static int parse_addr(dn_address *addr, char *str)
+static int parse_addr(__le16 *addr, char *str)
{
- dn_address area, node;
+ __u16 area, node;
while(*str && !ISNUM(*str)) str++;
@@ -139,7 +139,7 @@ static int dn_node_address_strategy(ctl_table *table, int __user *name, int nlen
void **context)
{
size_t len;
- dn_address addr;
+ __le16 addr;
if (oldval && oldlenp) {
if (get_user(len, oldlenp))
@@ -147,14 +147,14 @@ static int dn_node_address_strategy(ctl_table *table, int __user *name, int nlen
if (len) {
if (len != sizeof(unsigned short))
return -EINVAL;
- if (put_user(decnet_address, (unsigned short __user *)oldval))
+ if (put_user(decnet_address, (__le16 __user *)oldval))
return -EFAULT;
}
}
if (newval && newlen) {
if (newlen != sizeof(unsigned short))
return -EINVAL;
- if (get_user(addr, (unsigned short __user *)newval))
+ if (get_user(addr, (__le16 __user *)newval))
return -EFAULT;
dn_dev_devices_off();
@@ -173,7 +173,7 @@ static int dn_node_address_handler(ctl_table *table, int write,
{
char addr[DN_ASCBUF_LEN];
size_t len;
- dn_address dnaddr;
+ __le16 dnaddr;
if (!*lenp || (*ppos && !write)) {
*lenp = 0;
diff --git a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig
index d18ccba3ea9e..dbb08528ddf5 100644
--- a/net/ieee80211/Kconfig
+++ b/net/ieee80211/Kconfig
@@ -66,3 +66,4 @@ config IEEE80211_CRYPT_TKIP
This can be compiled as a modules and it will be called
"ieee80211_crypt_tkip".
+source "net/ieee80211/softmac/Kconfig"
diff --git a/net/ieee80211/Makefile b/net/ieee80211/Makefile
index f988417121da..796a7c76ee48 100644
--- a/net/ieee80211/Makefile
+++ b/net/ieee80211/Makefile
@@ -10,3 +10,4 @@ ieee80211-objs := \
ieee80211_wx.o \
ieee80211_geo.o
+obj-$(CONFIG_IEEE80211_SOFTMAC) += softmac/
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index a7f2a642a512..604b7b0097bc 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -780,6 +780,80 @@ int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
return 0;
}
+/* Filter out unrelated packets, call ieee80211_rx[_mgt] */
+int ieee80211_rx_any(struct ieee80211_device *ieee,
+ struct sk_buff *skb, struct ieee80211_rx_stats *stats)
+{
+ struct ieee80211_hdr_4addr *hdr;
+ int is_packet_for_us;
+ u16 fc;
+
+ if (ieee->iw_mode == IW_MODE_MONITOR)
+ return ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL;
+
+ hdr = (struct ieee80211_hdr_4addr *)skb->data;
+ fc = le16_to_cpu(hdr->frame_ctl);
+
+ if ((fc & IEEE80211_FCTL_VERS) != 0)
+ return -EINVAL;
+
+ switch (fc & IEEE80211_FCTL_FTYPE) {
+ case IEEE80211_FTYPE_MGMT:
+ ieee80211_rx_mgt(ieee, hdr, stats);
+ return 0;
+ case IEEE80211_FTYPE_DATA:
+ break;
+ case IEEE80211_FTYPE_CTL:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ is_packet_for_us = 0;
+ switch (ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ /* our BSS and not from/to DS */
+ if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0)
+ if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) {
+ /* promisc: get all */
+ if (ieee->dev->flags & IFF_PROMISC)
+ is_packet_for_us = 1;
+ /* to us */
+ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
+ is_packet_for_us = 1;
+ /* mcast */
+ else if (is_multicast_ether_addr(hdr->addr1))
+ is_packet_for_us = 1;
+ }
+ break;
+ case IW_MODE_INFRA:
+ /* our BSS (== from our AP) and from DS */
+ if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0)
+ if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) {
+ /* promisc: get all */
+ if (ieee->dev->flags & IFF_PROMISC)
+ is_packet_for_us = 1;
+ /* to us */
+ else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0)
+ is_packet_for_us = 1;
+ /* mcast */
+ else if (is_multicast_ether_addr(hdr->addr1)) {
+ /* not our own packet bcasted from AP */
+ if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN))
+ is_packet_for_us = 1;
+ }
+ }
+ break;
+ default:
+ /* ? */
+ break;
+ }
+
+ if (is_packet_for_us)
+ return (ieee80211_rx(ieee, skb, stats) ? 0 : -EINVAL);
+ return 0;
+}
+
#define MGMT_FRAME_FIXED_PART_LENGTH 0x24
static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 };
diff --git a/net/ieee80211/softmac/Kconfig b/net/ieee80211/softmac/Kconfig
new file mode 100644
index 000000000000..6cd9f3427be6
--- /dev/null
+++ b/net/ieee80211/softmac/Kconfig
@@ -0,0 +1,10 @@
+config IEEE80211_SOFTMAC
+ tristate "Software MAC add-on to the IEEE 802.11 networking stack"
+ depends on IEEE80211 && EXPERIMENTAL
+ ---help---
+ This option enables the hardware independent software MAC addon
+ for the IEEE 802.11 networking stack.
+
+config IEEE80211_SOFTMAC_DEBUG
+ bool "Enable full debugging output"
+ depends on IEEE80211_SOFTMAC
diff --git a/net/ieee80211/softmac/Makefile b/net/ieee80211/softmac/Makefile
new file mode 100644
index 000000000000..bfcb391bb2c7
--- /dev/null
+++ b/net/ieee80211/softmac/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_IEEE80211_SOFTMAC) += ieee80211softmac.o
+ieee80211softmac-objs := \
+ ieee80211softmac_io.o \
+ ieee80211softmac_auth.o \
+ ieee80211softmac_module.o \
+ ieee80211softmac_scan.o \
+ ieee80211softmac_wx.o \
+ ieee80211softmac_assoc.o \
+ ieee80211softmac_event.o
diff --git a/net/ieee80211/softmac/ieee80211softmac_assoc.c b/net/ieee80211/softmac/ieee80211softmac_assoc.c
new file mode 100644
index 000000000000..be61de78dfa4
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_assoc.c
@@ -0,0 +1,396 @@
+/*
+ * This file contains the softmac's association logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Overview
+ *
+ * Before you can associate, you have to authenticate.
+ *
+ */
+
+/* Sends out an association request to the desired AP */
+static void
+ieee80211softmac_assoc(struct ieee80211softmac_device *mac, struct ieee80211softmac_network *net)
+{
+ unsigned long flags;
+
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Send association request */
+ ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_ASSOC_REQ, 0);
+
+ dprintk(KERN_INFO PFX "sent association request!\n");
+
+ /* Change the state to associating */
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.associating = 1;
+ mac->associated = 0; /* just to make sure */
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Set a timer for timeout */
+ /* FIXME: make timeout configurable */
+ schedule_delayed_work(&mac->associnfo.timeout, 5 * HZ);
+}
+
+void
+ieee80211softmac_assoc_timeout(void *d)
+{
+ struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ /* we might race against ieee80211softmac_handle_assoc_response,
+ * so make sure only one of us does something */
+ if (!mac->associnfo.associating) {
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return;
+ }
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associated = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ dprintk(KERN_INFO PFX "assoc request timed out!\n");
+ /* FIXME: we need to know the network here. that requires a bit of restructuring */
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_TIMEOUT, NULL);
+}
+
+/* Sends out a disassociation request to the desired AP */
+static void
+ieee80211softmac_disassoc(struct ieee80211softmac_device *mac, u16 reason)
+{
+ unsigned long flags;
+ struct ieee80211softmac_network *found;
+
+ if (mac->associnfo.bssvalid && mac->associated) {
+ found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+ if (found)
+ ieee80211softmac_send_mgt_frame(mac, found, IEEE80211_STYPE_DISASSOC, reason);
+ } else if (mac->associnfo.associating) {
+ cancel_delayed_work(&mac->associnfo.timeout);
+ }
+
+ /* Change our state */
+ spin_lock_irqsave(&mac->lock, flags);
+ /* Do NOT clear bssvalid as that will break ieee80211softmac_assoc_work! */
+ mac->associated = 0;
+ mac->associnfo.associating = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+static inline int
+we_support_all_basic_rates(struct ieee80211softmac_device *mac, u8 *from, u8 from_len)
+{
+ int idx, search, found;
+ u8 rate, search_rate;
+
+ for (idx = 0; idx < (from_len); idx++) {
+ rate = (from)[idx];
+ if (!(rate & IEEE80211_BASIC_RATE_MASK))
+ continue;
+ found = 0;
+ rate &= ~IEEE80211_BASIC_RATE_MASK;
+ for (search = 0; search < mac->ratesinfo.count; search++) {
+ search_rate = mac->ratesinfo.rates[search];
+ search_rate &= ~IEEE80211_BASIC_RATE_MASK;
+ if (rate == search_rate) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+network_matches_request(struct ieee80211softmac_device *mac, struct ieee80211_network *net)
+{
+ /* we cannot associate to networks whose name we don't know */
+ if (ieee80211_is_empty_essid(net->ssid, net->ssid_len))
+ return 0;
+ /* do not associate to a network whose BSSBasicRateSet we cannot support */
+ if (!we_support_all_basic_rates(mac, net->rates, net->rates_len))
+ return 0;
+ /* do we really need to check the ex rates? */
+ if (!we_support_all_basic_rates(mac, net->rates_ex, net->rates_ex_len))
+ return 0;
+
+ /* if 'ANY' network requested, take any that doesn't have privacy enabled */
+ if (mac->associnfo.req_essid.len == 0
+ && !(net->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ if (net->ssid_len != mac->associnfo.req_essid.len)
+ return 0;
+ if (!memcmp(net->ssid, mac->associnfo.req_essid.data, mac->associnfo.req_essid.len))
+ return 1;
+ return 0;
+}
+
+static void
+ieee80211softmac_assoc_notify(struct net_device *dev, void *context)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ ieee80211softmac_assoc_work((void*)mac);
+}
+
+/* This function is called to handle userspace requests (asynchronously) */
+void
+ieee80211softmac_assoc_work(void *d)
+{
+ struct ieee80211softmac_device *mac = (struct ieee80211softmac_device *)d;
+ struct ieee80211softmac_network *found = NULL;
+ struct ieee80211_network *net = NULL, *best = NULL;
+ unsigned long flags;
+
+ /* meh */
+ if (mac->associated)
+ ieee80211softmac_disassoc(mac, WLAN_REASON_DISASSOC_STA_HAS_LEFT);
+
+ /* try to find the requested network in our list, if we found one already */
+ if (mac->associnfo.bssvalid)
+ found = ieee80211softmac_get_network_by_bssid(mac, mac->associnfo.bssid);
+
+ /* Search the ieee80211 networks for this network if we didn't find it by bssid,
+ * but only if we've scanned at least once (to get a better list of networks to
+ * select from). If we have not scanned before, the !found logic below will be
+ * invoked and will scan. */
+ if (!found && (mac->associnfo.scan_retry < IEEE80211SOFTMAC_ASSOC_SCAN_RETRY_LIMIT))
+ {
+ s8 rssi = -128; /* if I don't initialise, gcc emits an invalid warning
+ because it cannot follow the best pointer logic. */
+ spin_lock_irqsave(&mac->ieee->lock, flags);
+ list_for_each_entry(net, &mac->ieee->network_list, list) {
+ /* we're supposed to find the network with
+ * the best signal here, as we're asked to join
+ * any network with a specific ESSID, and many
+ * different ones could have that.
+ *
+ * I'll for now just go with the reported rssi.
+ *
+ * We also should take into account the rateset
+ * here to find the best BSSID to try.
+ */
+ if (network_matches_request(mac, net)) {
+ if (!best) {
+ best = net;
+ rssi = best->stats.rssi;
+ continue;
+ }
+ /* we already had a matching network, so
+ * compare their properties to get the
+ * better of the two ... (see above)
+ */
+ if (rssi < net->stats.rssi) {
+ best = net;
+ rssi = best->stats.rssi;
+ }
+ }
+ }
+ /* if we unlock here, we might get interrupted and the `best'
+ * pointer could go stale */
+ if (best) {
+ found = ieee80211softmac_create_network(mac, best);
+ /* if found is still NULL, then we got -ENOMEM somewhere */
+ if (found)
+ ieee80211softmac_add_network(mac, found);
+ }
+ spin_unlock_irqrestore(&mac->ieee->lock, flags);
+ }
+
+ if (!found) {
+ if (mac->associnfo.scan_retry > 0) {
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.scan_retry--;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* We know of no such network. Let's scan.
+ * NB: this also happens if we had no memory to copy the network info...
+ * Maybe we can hope to have more memory after scanning finishes ;)
+ */
+ dprintk(KERN_INFO PFX "Associate: Scanning for networks first.\n");
+ ieee80211softmac_notify(mac->dev, IEEE80211SOFTMAC_EVENT_SCAN_FINISHED, ieee80211softmac_assoc_notify, NULL);
+ if (ieee80211softmac_start_scan(mac))
+ dprintk(KERN_INFO PFX "Associate: failed to initiate scan. Is device up?\n");
+ return;
+ }
+ else {
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.associating = 0;
+ mac->associated = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ dprintk(KERN_INFO PFX "Unable to find matching network after scan!\n");
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_NET_NOT_FOUND, NULL);
+ return;
+ }
+ }
+
+ mac->associnfo.bssvalid = 1;
+ memcpy(mac->associnfo.bssid, found->bssid, ETH_ALEN);
+ /* copy the ESSID for displaying it */
+ mac->associnfo.associate_essid.len = found->essid.len;
+ memcpy(mac->associnfo.associate_essid.data, found->essid.data, IW_ESSID_MAX_SIZE + 1);
+
+ /* we found a network! authenticate (if necessary) and associate to it. */
+ if (!found->authenticated) {
+ /* This relies on the fact that _auth_req only queues the work,
+ * otherwise adding the notification would be racy. */
+ if (!ieee80211softmac_auth_req(mac, found)) {
+ dprintk(KERN_INFO PFX "cannot associate without being authenticated, requested authentication\n");
+ ieee80211softmac_notify_internal(mac, IEEE80211SOFTMAC_EVENT_ANY, found, ieee80211softmac_assoc_notify, NULL, GFP_KERNEL);
+ } else {
+ printkl(KERN_WARNING PFX "Not authenticated, but requesting authentication failed. Giving up to associate\n");
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, found);
+ }
+ return;
+ }
+ /* finally! now we can start associating */
+ ieee80211softmac_assoc(mac, found);
+}
+
+/* call this to do whatever is necessary when we're associated */
+static void
+ieee80211softmac_associated(struct ieee80211softmac_device *mac,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211softmac_network *net)
+{
+ mac->associnfo.associating = 0;
+ mac->associated = 1;
+ if (mac->set_bssid_filter)
+ mac->set_bssid_filter(mac->dev, net->bssid);
+ memcpy(mac->ieee->bssid, net->bssid, ETH_ALEN);
+ netif_carrier_on(mac->dev);
+
+ mac->association_id = le16_to_cpup(&resp->aid);
+}
+
+/* received frame handling functions */
+int
+ieee80211softmac_handle_assoc_response(struct net_device * dev,
+ struct ieee80211_assoc_response * resp,
+ struct ieee80211_network * _ieee80211_network_do_not_use)
+{
+ /* NOTE: the network parameter has to be ignored by
+ * this code because it is the ieee80211's pointer
+ * to the struct, not ours (we made a copy)
+ */
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ u16 status = le16_to_cpup(&resp->status);
+ struct ieee80211softmac_network *network = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->lock, flags);
+
+ if (!mac->associnfo.associating) {
+ /* we race against the timeout function, so make sure
+ * only one of us can do work */
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+ network = ieee80211softmac_get_network_by_bssid_locked(mac, resp->header.addr3);
+
+ /* someone sending us things without us knowing him? Ignore. */
+ if (!network) {
+ dprintk(KERN_INFO PFX "Received unrequested assocation response from " MAC_FMT "\n", MAC_ARG(resp->header.addr3));
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+ }
+
+ /* now that we know it was for us, we can cancel the timeout */
+ cancel_delayed_work(&mac->associnfo.timeout);
+
+ switch (status) {
+ case 0:
+ dprintk(KERN_INFO PFX "associated!\n");
+ ieee80211softmac_associated(mac, resp, network);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATED, network);
+ break;
+ case WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH:
+ if (!network->auth_desynced_once) {
+ /* there seem to be a few rare cases where our view of
+ * the world is obscured, or buggy APs that don't DEAUTH
+ * us properly. So we handle that, but allow it only once.
+ */
+ printkl(KERN_INFO PFX "We were not authenticated during association, retrying...\n");
+ network->authenticated = 0;
+ /* we don't want to do this more than once ... */
+ network->auth_desynced_once = 1;
+ schedule_work(&mac->associnfo.work);
+ break;
+ }
+ default:
+ dprintk(KERN_INFO PFX "associating failed (reason: 0x%x)!\n", status);
+ mac->associnfo.associating = 0;
+ mac->associnfo.bssvalid = 0;
+ mac->associated = 0;
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_ASSOCIATE_FAILED, network);
+ }
+
+ spin_unlock_irqrestore(&mac->lock, flags);
+ return 0;
+}
+
+int
+ieee80211softmac_handle_disassoc(struct net_device * dev,
+ struct ieee80211_disassoc *disassoc)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ unsigned long flags;
+ if (memcmp(disassoc->header.addr2, mac->associnfo.bssid, ETH_ALEN))
+ return 0;
+ if (memcmp(disassoc->header.addr1, mac->dev->dev_addr, ETH_ALEN))
+ return 0;
+ dprintk(KERN_INFO PFX "got disassoc frame\n");
+ netif_carrier_off(dev);
+ spin_lock_irqsave(&mac->lock, flags);
+ mac->associnfo.bssvalid = 0;
+ mac->associated = 0;
+ schedule_work(&mac->associnfo.work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+int
+ieee80211softmac_handle_reassoc_req(struct net_device * dev,
+ struct ieee80211_reassoc_request * resp)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_network *network;
+
+ network = ieee80211softmac_get_network_by_bssid(mac, resp->header.addr3);
+ if (!network) {
+ dprintkl(KERN_INFO PFX "reassoc request from unknown network\n");
+ return 0;
+ }
+ ieee80211softmac_assoc(mac, network);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_auth.c b/net/ieee80211/softmac/ieee80211softmac_auth.c
new file mode 100644
index 000000000000..9a0eac6c61eb
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_auth.c
@@ -0,0 +1,364 @@
+/*
+ * This file contains the softmac's authentication logic.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+static void ieee80211softmac_auth_queue(void *data);
+
+/* Queues an auth request to the desired AP */
+int
+ieee80211softmac_auth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *auth;
+ unsigned long flags;
+
+ if (net->authenticating)
+ return 0;
+
+ /* Add the network if it's not already added */
+ ieee80211softmac_add_network(mac, net);
+
+ dprintk(KERN_NOTICE PFX "Queueing Authentication Request to "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Queue the auth request */
+ auth = (struct ieee80211softmac_auth_queue_item *)
+ kmalloc(sizeof(struct ieee80211softmac_auth_queue_item), GFP_KERNEL);
+ if(auth == NULL)
+ return -ENOMEM;
+
+ auth->net = net;
+ auth->mac = mac;
+ auth->retry = IEEE80211SOFTMAC_AUTH_RETRY_LIMIT;
+ auth->state = IEEE80211SOFTMAC_AUTH_OPEN_REQUEST;
+ INIT_WORK(&auth->work, &ieee80211softmac_auth_queue, (void *)auth);
+
+ /* Lock (for list) */
+ spin_lock_irqsave(&mac->lock, flags);
+
+ /* add to list */
+ list_add_tail(&auth->list, &mac->auth_queue);
+ schedule_work(&auth->work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+
+/* Sends an auth request to the desired AP and handles timeouts */
+static void
+ieee80211softmac_auth_queue(void *data)
+{
+ struct ieee80211softmac_device *mac;
+ struct ieee80211softmac_auth_queue_item *auth;
+ struct ieee80211softmac_network *net;
+ unsigned long flags;
+
+ auth = (struct ieee80211softmac_auth_queue_item *)data;
+ net = auth->net;
+ mac = auth->mac;
+
+ if(auth->retry > 0) {
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Lock and set flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticated = 0;
+ net->authenticating = 1;
+ /* add a timeout call so we eventually give up waiting for an auth reply */
+ schedule_delayed_work(&auth->work, IEEE80211SOFTMAC_AUTH_TIMEOUT);
+ auth->retry--;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ if (ieee80211softmac_send_mgt_frame(mac, auth->net, IEEE80211_STYPE_AUTH, auth->state))
+ dprintk(KERN_NOTICE PFX "Sending Authentication Request to "MAC_FMT" failed (this shouldn't happen, wait for the timeout).\n", MAC_ARG(net->bssid));
+ else
+ dprintk(KERN_NOTICE PFX "Sent Authentication Request to "MAC_FMT".\n", MAC_ARG(net->bssid));
+ return;
+ }
+
+ printkl(KERN_WARNING PFX "Authentication timed out with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ /* Remove this item from the queue */
+ spin_lock_irqsave(&mac->lock, flags);
+ ieee80211softmac_call_events_locked(mac, IEEE80211SOFTMAC_EVENT_AUTH_TIMEOUT, net);
+ cancel_delayed_work(&auth->work); /* just to make sure... */
+ list_del(&auth->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Free it */
+ kfree(auth);
+}
+
+/* Handle the auth response from the AP
+ * This should be registered with ieee80211 as handle_auth
+ */
+int
+ieee80211softmac_auth_resp(struct net_device *dev, struct ieee80211_auth *auth)
+{
+
+ struct list_head *list_ptr;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct ieee80211softmac_network *net = NULL;
+ unsigned long flags;
+ u8 * data;
+
+ /* Find correct auth queue item */
+ spin_lock_irqsave(&mac->lock, flags);
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ net = aq->net;
+ if (!memcmp(net->bssid, auth->header.addr2, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Make sure that we've got an auth queue item for this request */
+ if(aq == NULL)
+ {
+ printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but no queue item exists.\n", MAC_ARG(auth->header.addr2));
+ /* Error #? */
+ return -1;
+ }
+
+ /* Check for out of order authentication */
+ if(!net->authenticating)
+ {
+ printkl(KERN_DEBUG PFX "Authentication response received from "MAC_FMT" but did not request authentication.\n",MAC_ARG(auth->header.addr2));
+ return -1;
+ }
+
+ /* Parse the auth packet */
+ switch(auth->algorithm) {
+ case WLAN_AUTH_OPEN:
+ /* Check the status code of the response */
+
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Send event */
+ printkl(KERN_NOTICE PFX "Open Authentication completed with "MAC_FMT"\n", MAC_ARG(net->bssid));
+ ieee80211softmac_call_events(mac, IEEE80211SOFTMAC_EVENT_AUTHENTICATED, net);
+ break;
+ default:
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticated = 0;
+ net->authenticating = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ printkl(KERN_NOTICE PFX "Open Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ case WLAN_AUTH_SHARED_KEY:
+ /* Figure out where we are in the process */
+ switch(auth->transaction) {
+ case IEEE80211SOFTMAC_AUTH_SHARED_CHALLENGE:
+ /* Check to make sure we have a challenge IE */
+ data = (u8 *)auth->info_element;
+ if(*data++ != MFIE_TYPE_CHALLENGE){
+ printkl(KERN_NOTICE PFX "Shared Key Authentication failed due to a missing challenge.\n");
+ break;
+ }
+ /* Save the challenge */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->challenge_len = *data++;
+ if(net->challenge_len > WLAN_AUTH_CHALLENGE_LEN)
+ net->challenge_len = WLAN_AUTH_CHALLENGE_LEN;
+ if(net->challenge != NULL)
+ kfree(net->challenge);
+ net->challenge = kmalloc(net->challenge_len, GFP_ATOMIC);
+ memcpy(net->challenge, data, net->challenge_len);
+ aq->state = IEEE80211SOFTMAC_AUTH_SHARED_RESPONSE;
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Switch to correct channel for this network */
+ mac->set_channel(mac->dev, net->channel);
+
+ /* Send our response (How to encrypt?) */
+ ieee80211softmac_send_mgt_frame(mac, aq->net, IEEE80211_STYPE_AUTH, aq->state);
+ break;
+ case IEEE80211SOFTMAC_AUTH_SHARED_PASS:
+ /* Check the status code of the response */
+ switch(auth->status) {
+ case WLAN_STATUS_SUCCESS:
+ /* Update the status to Authenticated */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 1;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ printkl(KERN_NOTICE PFX "Shared Key Authentication completed with "MAC_FMT"\n",
+ MAC_ARG(net->bssid));
+ break;
+ default:
+ printkl(KERN_NOTICE PFX "Shared Key Authentication with "MAC_FMT" failed, error code: %i\n",
+ MAC_ARG(net->bssid), le16_to_cpup(&auth->status));
+ /* Lock and reset flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+ spin_unlock_irqrestore(&mac->lock, flags);
+ /* Count the error? */
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ printkl(KERN_WARNING PFX "Unhandled Authentication Step: %i\n", auth->transaction);
+ break;
+ }
+ goto free_aq;
+ break;
+ default:
+ /* ERROR */
+ goto free_aq;
+ break;
+ }
+ return 0;
+free_aq:
+ /* Cancel the timeout */
+ spin_lock_irqsave(&mac->lock, flags);
+ cancel_delayed_work(&aq->work);
+ /* Remove this item from the queue */
+ list_del(&aq->list);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ /* Free it */
+ kfree(aq);
+ return 0;
+}
+
+/*
+ * Handle deauthorization
+ */
+static void
+ieee80211softmac_deauth_from_net(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net)
+{
+ struct ieee80211softmac_auth_queue_item *aq = NULL;
+ struct list_head *list_ptr;
+ unsigned long flags;
+
+ /* Lock and reset status flags */
+ spin_lock_irqsave(&mac->lock, flags);
+ net->authenticating = 0;
+ net->authenticated = 0;
+
+ /* Find correct auth queue item, if it exists */
+ list_for_each(list_ptr, &mac->auth_queue) {
+ aq = list_entry(list_ptr, struct ieee80211softmac_auth_queue_item, list);
+ if (!memcmp(net->bssid, aq->net->bssid, ETH_ALEN))
+ break;
+ else
+ aq = NULL;
+ }
+
+ /* Cancel pending work */
+ if(aq != NULL)
+ /* Not entirely safe? What about running work? */
+ cancel_delayed_work(&aq->work);
+
+ /* Free our network ref */
+ ieee80211softmac_del_network_locked(mac, net);
+ if(net->challenge != NULL)
+ kfree(net->challenge);
+ kfree(net);
+
+ /* can't transmit data right now... */
+ netif_carrier_off(mac->dev);
+ /* let's try to re-associate */
+ schedule_work(&mac->associnfo.work);
+ spin_unlock_irqrestore(&mac->lock, flags);
+}
+
+/*
+ * Sends a deauth request to the desired AP
+ */
+int
+ieee80211softmac_deauth_req(struct ieee80211softmac_device *mac,
+ struct ieee80211softmac_network *net, int reason)
+{
+ int ret;
+
+ /* Make sure the network is authenticated */
+ if (!net->authenticated)
+ {
+ printkl(KERN_DEBUG PFX "Can't send deauthentication packet, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ /* Send the de-auth packet */
+ if((ret = ieee80211softmac_send_mgt_frame(mac, net, IEEE80211_STYPE_DEAUTH, reason)))
+ return ret;
+
+ ieee80211softmac_deauth_from_net(mac, net);
+ return 0;
+}
+
+/*
+ * This should be registered with ieee80211 as handle_deauth
+ */
+int
+ieee80211softmac_deauth_resp(struct net_device *dev, struct ieee80211_deauth *deauth)
+{
+
+ struct ieee80211softmac_network *net = NULL;
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (!deauth) {
+ dprintk("deauth without deauth packet. eek!\n");
+ return 0;
+ }
+
+ net = ieee80211softmac_get_network_by_bssid(mac, deauth->header.addr2);
+
+ if (net == NULL) {
+ printkl(KERN_DEBUG PFX "Received deauthentication packet from "MAC_FMT", but that network is unknown.\n",
+ MAC_ARG(deauth->header.addr2));
+ return 0;
+ }
+
+ /* Make sure the network is authenticated */
+ if(!net->authenticated)
+ {
+ printkl(KERN_DEBUG PFX "Can't perform deauthentication, network is not authenticated.\n");
+ /* Error okay? */
+ return -EPERM;
+ }
+
+ ieee80211softmac_deauth_from_net(mac, net);
+ return 0;
+}
diff --git a/net/ieee80211/softmac/ieee80211softmac_event.c b/net/ieee80211/softmac/ieee80211softmac_event.c
new file mode 100644
index 000000000000..0a52bbda1e4c
--- /dev/null
+++ b/net/ieee80211/softmac/ieee80211softmac_event.c
@@ -0,0 +1,159 @@
+/*
+ * Event system
+ * Also see comments in public header file and longer explanation below.
+ *
+ * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net>
+ * Joseph Jezak <josejx@gentoo.org>
+ * Larry Finger <Larry.Finger@lwfinger.net>
+ * Danny van Dyk <kugelfang@gentoo.org>
+ * Michael Buesch <mbuesch@freenet.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called COPYING.
+ */
+
+#include "ieee80211softmac_priv.h"
+
+/*
+ * Each event has associated to it
+ * - an event type (see constants in public header)
+ * - an event context (see below)
+ * - the function to be called
+ * - a context (extra parameter to call the function with)
+ * - and the softmac struct
+ *
+ * The event context is private and can only be used from
+ * within this module. Its meaning varies with the event
+ * type:
+ * SCAN_FINISHED: no special meaning
+ * ASSOCIATED,
+ * ASSOCIATE_FAILED,
+ * ASSOCIATE_TIMEOUT,
+ * AUTHENTICATED,
+ * AUTH_FAILED,
+ * AUTH_TIMEOUT: a pointer to the network struct
+ * ...
+ * Code within this module can use the event context to be only
+ * called when the event is true for that specific context
+ * as per above table.
+ * If the event context is NULL, then the notification is always called,
+ * regardless of the event context. The event context is not passed to
+ * the callback, it is assumed that the context suffices.
+ *
+ * You can also use the event context only by setting the event type
+ * to -1 (private use only), in which case you'll be notified
+ * whenever the event context matches.
+ */
+
+static char *event_descriptions[IEEE80211SOFTMAC_EVENT_LAST+1] = {
+ "scan finished",
+ "associated",
+ "associating failed",
+ "associating timed out",
+ "authenticated",
+ "authenticating failed",
+ "authenticating timed out",
+ "associating failed because no suitable network was found",
+};
+
+
+static void
+ieee80211softmac_notify_callback(void *d)
+{
+ struct ieee80211softmac_event event = *(struct ieee80211softmac_event*) d;
+ kfree(d);
+
+ event.fun(event.mac->dev, event.context);
+}
+
+int
+ieee80211softmac_notify_internal(struct ieee80211softmac_device *mac,
+ int event, void *event_context, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_event *eventptr;
+ unsigned long flags;
+
+ if (event < -1 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ if (!fun)
+ return -EINVAL;
+
+ eventptr = kmalloc(sizeof(struct ieee80211softmac_event), gfp_mask);
+ if (!eventptr)
+ return -ENOMEM;
+
+ eventptr->event_type = event;
+ INIT_WORK(&eventptr->work, ieee80211softmac_notify_callback, eventptr);
+ eventptr->fun = fun;
+ eventptr->context = context;
+ eventptr->mac = mac;
+ eventptr->event_context = event_context;
+
+ spin_lock_irqsave(&mac->lock, flags);
+ list_add(&eventptr->list, &mac->events);
+ spin_unlock_irqrestore(&mac->lock, flags);
+
+ return 0;
+}
+
+int
+ieee80211softmac_notify_gfp(struct net_device *dev,
+ int event, notify_function_ptr fun, void *context, gfp_t gfp_mask)
+{
+ struct ieee80211softmac_device *mac = ieee80211_priv(dev);
+
+ if (event < 0 || event > IEEE80211SOFTMAC_EVENT_LAST)
+ return -ENOSYS;
+
+ return ieee80211softmac_notify_internal(mac, event, NULL, fun, context, gfp_mask);
+}
+EXPORT_SYMBOL_GPL(ieee80211softmac_notify_gfp);
+
+/* private -- calling all callbacks that were specified */
+void
+ieee80211softmac_call_events_locked(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ struct ieee80211softmac_event *eventptr, *tmp;
+ union iwreq_data wrqu;
+ char *msg;
+
+ if (event >= 0) {
+ msg = event_descriptions[event];
+ wrqu.data.length = strlen(msg);
+ wireless_send_event(mac->dev, IWEVCUSTOM, &wrqu, msg);
+ }
+
+ if (!list_empty(&mac->events))
+ list_for_each_entry_safe(eventptr, tmp, &mac->events, list) {
+ if ((eventptr->event_type == event || eventptr->event_type == -1)
+ && (eventptr->event_context == NULL || eventptr->event_context == event_ctx)) {
+ list_del(&eventptr->list);
+ schedule_work(&eventptr->work);
+ }
+ }
+}
+
+void
+ieee80211softmac_call_events(struct ieee80211softmac_device *mac, int event, void *event_ctx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mac->l