Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6

* master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6: (109 commits)
  [ETHTOOL]: Fix UFO typo
  [SCTP]: Fix persistent slowdown in sctp when a gap ack consumes rx buffer.
  [SCTP]: Send only 1 window update SACK per message.
  [SCTP]: Don't do CRC32C checksum over loopback.
  [SCTP] Reset rtt_in_progress for the chunk when processing its sack.
  [SCTP]: Reject sctp packets with broadcast addresses.
  [SCTP]: Limit association max_retrans setting in setsockopt.
  [PFKEYV2]: Fix inconsistent typing in struct sadb_x_kmprivate.
  [IPV6]: Sum real space for RTAs.
  [IRDA]: Use put_unaligned() in irlmp_do_discovery().
  [BRIDGE]: Add support for NETIF_F_HW_CSUM devices
  [NET]: Add NETIF_F_GEN_CSUM and NETIF_F_ALL_CSUM
  [TG3]: Convert to non-LLTX
  [TG3]: Remove unnecessary tx_lock
  [TCP]: Add tcp_slow_start_after_idle sysctl.
  [BNX2]: Update version and reldate
  [BNX2]: Use CPU native page size
  [BNX2]: Use compressed firmware
  [BNX2]: Add firmware decompression
  [BNX2]: Allow WoL settings on new 5708 chips
  ...

Manual fixup for conflict in drivers/net/tulip/winbond-840.c
This commit is contained in:
Linus Torvalds 2006-06-19 18:55:56 -07:00
commit d0b952a983
220 changed files with 12047 additions and 5799 deletions

View file

@ -66,6 +66,13 @@ source "net/ipv6/Kconfig"
endif # if INET
config NETWORK_SECMARK
bool "Security Marking"
help
This enables security marking of network packets, similar
to nfmark, but designated for security purposes.
If you are unsure how to answer this question, answer N.
menuconfig NETFILTER
bool "Network packet filtering (replaces ipchains)"
---help---
@ -215,6 +222,21 @@ config NET_PKTGEN
To compile this code as a module, choose M here: the
module will be called pktgen.
config NET_TCPPROBE
tristate "TCP connection probing"
depends on INET && EXPERIMENTAL && PROC_FS && KPROBES
---help---
This module allows for capturing the changes to TCP connection
state in response to incoming packets. It is used for debugging
TCP congestion avoidance modules. If you don't understand
what was just said, you don't need it: say N.
Documentation on how to use the packet generator can be found
at http://linux-net.osdl.org/index.php/TcpProbe
To compile this code as a module, choose M here: the
module will be called tcp_probe.
endmenu
endmenu

View file

@ -98,7 +98,7 @@ static void unlink_clip_vcc(struct clip_vcc *clip_vcc)
printk(KERN_CRIT "!clip_vcc->entry (clip_vcc %p)\n", clip_vcc);
return;
}
spin_lock_bh(&entry->neigh->dev->xmit_lock); /* block clip_start_xmit() */
netif_tx_lock_bh(entry->neigh->dev); /* block clip_start_xmit() */
entry->neigh->used = jiffies;
for (walk = &entry->vccs; *walk; walk = &(*walk)->next)
if (*walk == clip_vcc) {
@ -122,7 +122,7 @@ static void unlink_clip_vcc(struct clip_vcc *clip_vcc)
printk(KERN_CRIT "ATMARP: unlink_clip_vcc failed (entry %p, vcc "
"0x%p)\n", entry, clip_vcc);
out:
spin_unlock_bh(&entry->neigh->dev->xmit_lock);
netif_tx_unlock_bh(entry->neigh->dev);
}
/* The neighbour entry n->lock is held. */

View file

@ -6,7 +6,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o
bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \
br_ioctl.o br_notify.o br_stp.o br_stp_bpdu.o \
br_stp_if.o br_stp_timer.o
br_stp_if.o br_stp_timer.o br_netlink.o
bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o

View file

@ -30,36 +30,46 @@ static struct llc_sap *br_stp_sap;
static int __init br_init(void)
{
int err;
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;
return -EADDRINUSE;
}
br_fdb_init();
#ifdef CONFIG_BRIDGE_NETFILTER
if (br_netfilter_init())
return 1;
#endif
err = br_netfilter_init();
if (err)
goto err_out1;
err = register_netdevice_notifier(&br_device_notifier);
if (err)
goto err_out2;
br_netlink_init();
brioctl_set(br_ioctl_deviceless_stub);
br_handle_frame_hook = br_handle_frame;
br_fdb_get_hook = br_fdb_get;
br_fdb_put_hook = br_fdb_put;
register_netdevice_notifier(&br_device_notifier);
return 0;
err_out2:
br_netfilter_fini();
err_out1:
llc_sap_put(br_stp_sap);
return err;
}
static void __exit br_deinit(void)
{
rcu_assign_pointer(br_stp_sap->rcv_func, NULL);
#ifdef CONFIG_BRIDGE_NETFILTER
br_netlink_fini();
br_netfilter_fini();
#endif
unregister_netdevice_notifier(&br_device_notifier);
brioctl_set(NULL);

View file

@ -145,9 +145,9 @@ static int br_set_tx_csum(struct net_device *dev, u32 data)
struct net_bridge *br = netdev_priv(dev);
if (data)
br->feature_mask |= NETIF_F_IP_CSUM;
br->feature_mask |= NETIF_F_NO_CSUM;
else
br->feature_mask &= ~NETIF_F_IP_CSUM;
br->feature_mask &= ~NETIF_F_ALL_CSUM;
br_features_recompute(br);
return 0;
@ -185,5 +185,5 @@ void br_dev_setup(struct net_device *dev)
dev->priv_flags = IFF_EBRIDGE;
dev->features = NETIF_F_SG | NETIF_F_FRAGLIST
| NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_IP_CSUM;
| NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_NO_CSUM;
}

View file

@ -20,14 +20,11 @@
#include <linux/netfilter_bridge.h>
#include "br_private.h"
/* Don't forward packets to originating port or forwarding diasabled */
static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
{
if (skb->dev == p->dev ||
p->state != BR_STATE_FORWARDING)
return 0;
return 1;
return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING);
}
static inline unsigned packet_length(const struct sk_buff *skb)
@ -55,10 +52,9 @@ int br_dev_queue_push_xmit(struct sk_buff *skb)
int br_forward_finish(struct sk_buff *skb)
{
NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
br_dev_queue_push_xmit);
return NF_HOOK(PF_BRIDGE, NF_BR_POST_ROUTING, skb, NULL, skb->dev,
br_dev_queue_push_xmit);
return 0;
}
static void __br_deliver(const struct net_bridge_port *to, struct sk_buff *skb)

View file

@ -372,12 +372,17 @@ void br_features_recompute(struct net_bridge *br)
struct net_bridge_port *p;
unsigned long features, checksum;
features = br->feature_mask &~ NETIF_F_IP_CSUM;
checksum = br->feature_mask & NETIF_F_IP_CSUM;
checksum = br->feature_mask & NETIF_F_ALL_CSUM ? NETIF_F_NO_CSUM : 0;
features = br->feature_mask & ~NETIF_F_ALL_CSUM;
list_for_each_entry(p, &br->port_list, list) {
if (!(p->dev->features
& (NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM)))
if (checksum & NETIF_F_NO_CSUM &&
!(p->dev->features & NETIF_F_NO_CSUM))
checksum ^= NETIF_F_NO_CSUM | NETIF_F_HW_CSUM;
if (checksum & NETIF_F_HW_CSUM &&
!(p->dev->features & NETIF_F_HW_CSUM))
checksum ^= NETIF_F_HW_CSUM | NETIF_F_IP_CSUM;
if (!(p->dev->features & NETIF_F_IP_CSUM))
checksum = 0;
features &= p->dev->features;
}

View file

@ -407,12 +407,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook,
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
goto inhdr_error;
if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)))
goto inhdr_error;
if (skb->ip_summed == CHECKSUM_HW)
skb->ip_summed = CHECKSUM_NONE;
}
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
goto inhdr_error;
}
if (hdr->nexthdr == NEXTHDR_HOP && check_hbh_len(skb))
goto inhdr_error;
@ -495,11 +491,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
if (skb->len < len || len < 4 * iph->ihl)
goto inhdr_error;
if (skb->len > len) {
__pskb_trim(skb, len);
if (skb->ip_summed == CHECKSUM_HW)
skb->ip_summed = CHECKSUM_NONE;
}
pskb_trim_rcsum(skb, len);
nf_bridge_put(skb->nf_bridge);
if (!nf_bridge_alloc(skb))

199
net/bridge/br_netlink.c Normal file
View file

@ -0,0 +1,199 @@
/*
* Bridge netlink control interface
*
* Authors:
* Stephen Hemminger <shemminger@osdl.org>
*
* 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/kernel.h>
#include <linux/rtnetlink.h>
#include "br_private.h"
/*
* Create one netlink message for one interface
* Contains port and master info as well as carrier and bridge state.
*/
static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port,
u32 pid, u32 seq, int event, unsigned int flags)
{
const struct net_bridge *br = port->br;
const struct net_device *dev = port->dev;
struct ifinfomsg *r;
struct nlmsghdr *nlh;
unsigned char *b = skb->tail;
u32 mtu = dev->mtu;
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
u8 portstate = port->state;
pr_debug("br_fill_info event %d port %s master %s\n",
event, dev->name, br->dev->name);
nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*r), flags);
r = NLMSG_DATA(nlh);
r->ifi_family = AF_BRIDGE;
r->__ifi_pad = 0;
r->ifi_type = dev->type;
r->ifi_index = dev->ifindex;
r->ifi_flags = dev_get_flags(dev);
r->ifi_change = 0;
RTA_PUT(skb, IFLA_IFNAME, strlen(dev->name)+1, dev->name);
RTA_PUT(skb, IFLA_MASTER, sizeof(int), &br->dev->ifindex);
if (dev->addr_len)
RTA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr);
RTA_PUT(skb, IFLA_MTU, sizeof(mtu), &mtu);
if (dev->ifindex != dev->iflink)
RTA_PUT(skb, IFLA_LINK, sizeof(int), &dev->iflink);
RTA_PUT(skb, IFLA_OPERSTATE, sizeof(operstate), &operstate);
if (event == RTM_NEWLINK)
RTA_PUT(skb, IFLA_PROTINFO, sizeof(portstate), &portstate);
nlh->nlmsg_len = skb->tail - b;
return skb->len;
nlmsg_failure:
rtattr_failure:
skb_trim(skb, b - skb->data);
return -EINVAL;
}
/*
* Notify listeners of a change in port information
*/
void br_ifinfo_notify(int event, struct net_bridge_port *port)
{
struct sk_buff *skb;
int err = -ENOMEM;
pr_debug("bridge notify event=%d\n", event);
skb = alloc_skb(NLMSG_SPACE(sizeof(struct ifinfomsg) + 128),
GFP_ATOMIC);
if (!skb)
goto err_out;
err = br_fill_ifinfo(skb, port, current->pid, 0, event, 0);
if (err)
goto err_kfree;
NETLINK_CB(skb).dst_group = RTNLGRP_LINK;
netlink_broadcast(rtnl, skb, 0, RTNLGRP_LINK, GFP_ATOMIC);
return;
err_kfree:
kfree_skb(skb);
err_out:
netlink_set_err(rtnl, 0, RTNLGRP_LINK, err);
}
/*
* Dump information about all ports, in response to GETLINK
*/
static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net_device *dev;
int idx;
int s_idx = cb->args[0];
int err = 0;
read_lock(&dev_base_lock);
for (dev = dev_base, idx = 0; dev; dev = dev->next) {
struct net_bridge_port *p = dev->br_port;
/* not a bridge port */
if (!p)
continue;
if (idx < s_idx)
continue;
err = br_fill_ifinfo(skb, p, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI);
if (err <= 0)
break;
++idx;
}
read_unlock(&dev_base_lock);
cb->args[0] = idx;
return skb->len;
}
/*
* Change state of port (ie from forwarding to blocking etc)
* Used by spanning tree in user space.
*/
static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{
struct rtattr **rta = arg;
struct ifinfomsg *ifm = NLMSG_DATA(nlh);
struct net_device *dev;
struct net_bridge_port *p;
u8 new_state;
if (ifm->ifi_family != AF_BRIDGE)
return -EPFNOSUPPORT;
/* Must pass valid state as PROTINFO */
if (rta[IFLA_PROTINFO-1]) {
u8 *pstate = RTA_DATA(rta[IFLA_PROTINFO-1]);
new_state = *pstate;
} else
return -EINVAL;
if (new_state > BR_STATE_BLOCKING)
return -EINVAL;
/* Find bridge port */
dev = __dev_get_by_index(ifm->ifi_index);
if (!dev)
return -ENODEV;
p = dev->br_port;
if (!p)
return -EINVAL;
/* if kernel STP is running, don't allow changes */
if (p->br->stp_enabled)
return -EBUSY;
if (!netif_running(dev))
return -ENETDOWN;
if (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)
return -ENETDOWN;
p->state = new_state;
br_log_state(p);
return 0;
}
static struct rtnetlink_link bridge_rtnetlink_table[RTM_NR_MSGTYPES] = {
[RTM_GETLINK - RTM_BASE] = { .dumpit = br_dump_ifinfo, },
[RTM_SETLINK - RTM_BASE] = { .doit = br_rtm_setlink, },
};
void __init br_netlink_init(void)
{
rtnetlink_links[PF_BRIDGE] = bridge_rtnetlink_table;
}
void __exit br_netlink_fini(void)
{
rtnetlink_links[PF_BRIDGE] = NULL;
}

View file

@ -14,6 +14,7 @@
*/
#include <linux/kernel.h>
#include <linux/rtnetlink.h>
#include "br_private.h"
@ -49,6 +50,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
case NETDEV_CHANGEADDR:
br_fdb_changeaddr(p, dev->dev_addr);
br_ifinfo_notify(RTM_NEWLINK, p);
br_stp_recalculate_bridge_id(br);
break;

View file

@ -29,7 +29,7 @@
#define BR_PORT_DEBOUNCE (HZ/10)
#define BR_VERSION "2.1"
#define BR_VERSION "2.2"
typedef struct bridge_id bridge_id;
typedef struct mac_addr mac_addr;
@ -192,8 +192,13 @@ extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern int br_ioctl_deviceless_stub(unsigned int cmd, void __user *arg);
/* br_netfilter.c */
#ifdef CONFIG_BRIDGE_NETFILTER
extern int br_netfilter_init(void);
extern void br_netfilter_fini(void);
#else
#define br_netfilter_init() (0)
#define br_netfilter_fini() do { } while(0)
#endif
/* br_stp.c */
extern void br_log_state(const struct net_bridge_port *p);
@ -232,6 +237,11 @@ extern struct net_bridge_fdb_entry *(*br_fdb_get_hook)(struct net_bridge *br,
extern void (*br_fdb_put_hook)(struct net_bridge_fdb_entry *ent);
/* br_netlink.c */
extern void br_netlink_init(void);
extern void br_netlink_fini(void);
extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
#ifdef CONFIG_SYSFS
/* br_sysfs_if.c */
extern struct sysfs_ops brport_sysfs_ops;

View file

@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include "br_private.h"
#include "br_private_stp.h"
@ -86,6 +87,7 @@ void br_stp_disable_bridge(struct net_bridge *br)
void br_stp_enable_port(struct net_bridge_port *p)
{
br_init_port(p);
br_ifinfo_notify(RTM_NEWLINK, p);
br_port_state_selection(p->br);
}
@ -99,6 +101,8 @@ void br_stp_disable_port(struct net_bridge_port *p)
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
br->dev->name, p->port_no, p->dev->name, "disabled");
br_ifinfo_notify(RTM_DELLINK, p);
wasroot = br_is_root_bridge(br);
br_become_designated_port(p);
p->state = BR_STATE_DISABLED;

View file

@ -16,3 +16,4 @@ obj-$(CONFIG_NET_DIVERT) += dv.o
obj-$(CONFIG_NET_PKTGEN) += pktgen.o
obj-$(CONFIG_WIRELESS_EXT) += wireless.o
obj-$(CONFIG_NETPOLL) += netpoll.o
obj-$(CONFIG_NET_DMA) += user_dma.o

View file

@ -115,6 +115,7 @@
#include <net/iw_handler.h>
#include <asm/current.h>
#include <linux/audit.h>
#include <linux/dmaengine.h>
/*
* The list of packet types we will receive (as opposed to discard)
@ -148,6 +149,12 @@ static DEFINE_SPINLOCK(ptype_lock);
static struct list_head ptype_base[16]; /* 16 way hashed list */
static struct list_head ptype_all; /* Taps */
#ifdef CONFIG_NET_DMA
static struct dma_client *net_dma_client;
static unsigned int net_dma_count;
static spinlock_t net_dma_event_lock;
#endif
/*
* The @dev_base list is protected by @dev_base_lock and the rtnl
* semaphore.
@ -1215,75 +1222,15 @@ static inline int illegal_highdma(struct net_device *dev, struct sk_buff *skb)
#define illegal_highdma(dev, skb) (0)
#endif
/* Keep head the same: replace data */
int __skb_linearize(struct sk_buff *skb, gfp_t gfp_mask)
{
unsigned int size;
u8 *data;
long offset;
struct skb_shared_info *ninfo;
int headerlen = skb->data - skb->head;
int expand = (skb->tail + skb->data_len) - skb->end;
if (skb_shared(skb))
BUG();
if (expand <= 0)
expand = 0;
size = skb->end - skb->head + expand;
size = SKB_DATA_ALIGN(size);
data = kmalloc(size + sizeof(struct skb_shared_info), gfp_mask);
if (!data)
return -ENOMEM;
/* Copy entire thing */
if (skb_copy_bits(skb, -headerlen, data, headerlen + skb->len))
BUG();
/* Set up shinfo */
ninfo = (struct skb_shared_info*)(data + size);
atomic_set(&ninfo->dataref, 1);
ninfo->tso_size = skb_shinfo(skb)->tso_size;
ninfo->tso_segs = skb_shinfo(skb)->tso_segs;
ninfo->nr_frags = 0;
ninfo->frag_list = NULL;
/* Offset between the two in bytes */
offset = data - skb->head;
/* Free old data. */
skb_release_data(skb);
skb->head = data;
skb->end = data + size;
/* Set up new pointers */
skb->h.raw += offset;
skb->nh.raw += offset;
skb->mac.raw += offset;
skb->tail += offset;
skb->data += offset;
/* We are no longer a clone, even if we were. */
skb->cloned = 0;
skb->tail += skb->data_len;
skb->data_len = 0;
return 0;
}
#define HARD_TX_LOCK(dev, cpu) { \
if ((dev->features & NETIF_F_LLTX) == 0) { \
spin_lock(&dev->xmit_lock); \
dev->xmit_lock_owner = cpu; \
netif_tx_lock(dev); \
} \
}
#define HARD_TX_UNLOCK(dev) { \
if ((dev->features & NETIF_F_LLTX) == 0) { \
dev->xmit_lock_owner = -1; \
spin_unlock(&dev->xmit_lock); \
netif_tx_unlock(dev); \
} \
}
@ -1321,7 +1268,7 @@ int dev_queue_xmit(struct sk_buff *skb)
if (skb_shinfo(skb)->frag_list &&
!(dev->features & NETIF_F_FRAGLIST) &&
__skb_linearize(skb, GFP_ATOMIC))
__skb_linearize(skb))
goto out_kfree_skb;
/* Fragmented skb is linearized if device does not support SG,
@ -1330,14 +1277,14 @@ int dev_queue_xmit(struct sk_buff *skb)
*/
if (skb_shinfo(skb)->nr_frags &&
(!(dev->features & NETIF_F_SG) || illegal_highdma(dev, skb)) &&
__skb_linearize(skb, GFP_ATOMIC))
__skb_linearize(skb))
goto out_kfree_skb;
/* If packet is not checksummed and device does not support
* checksumming for this protocol, complete checksumming here.
*/
if (skb->ip_summed == CHECKSUM_HW &&
(!(dev->features & (NETIF_F_HW_CSUM | NETIF_F_NO_CSUM)) &&
(!(dev->features & NETIF_F_GEN_CSUM) &&
(!(dev->features & NETIF_F_IP_CSUM) ||
skb->protocol != htons(ETH_P_IP))))
if (skb_checksum_help(skb, 0))
@ -1382,8 +1329,8 @@ int dev_queue_xmit(struct sk_buff *skb)
/* The device has no queue. Common case for software devices:
loopback, all the sorts of tunnels...
Really, it is unlikely that xmit_lock protection is necessary here.
(f.e. loopback and IP tunnels are clean ignoring statistics
Really, it is unlikely that netif_tx_lock protection is necessary
here. (f.e. loopback and IP tunnels are clean ignoring statistics
counters.)
However, it is possible, that they rely on protection
made by us here.
@ -1846,6 +1793,19 @@ static void net_rx_action(struct softirq_action *h)
}
}
out:
#ifdef CONFIG_NET_DMA
/*
* There may not be any more sk_buffs coming right now, so push
* any pending DMA copies to hardware
*/
if (net_dma_client) {
struct dma_chan *chan;
rcu_read_lock();
list_for_each_entry_rcu(chan, &net_dma_client->channels, client_node)
dma_async_memcpy_issue_pending(chan);
rcu_read_unlock();
}
#endif
local_irq_enable();
return;
@ -2785,7 +2745,7 @@ int register_netdevice(struct net_device *dev)
BUG_ON(dev->reg_state != NETREG_UNINITIALIZED);
spin_lock_init(&dev->queue_lock);
spin_lock_init(&dev->xmit_lock);
spin_lock_init(&dev->_xmit_lock);
dev->xmit_lock_owner = -1;
#ifdef CONFIG_NET_CLS_ACT
spin_lock_init(&dev->ingress_lock);
@ -2829,9 +2789,7 @@ int register_netdevice(struct net_device *dev)
/* Fix illegal SG+CSUM combinations. */
if ((dev->features & NETIF_F_SG) &&
!(dev->features & (NETIF_F_IP_CSUM |
NETIF_F_NO_CSUM |
NETIF_F_HW_CSUM))) {
!(dev->features & NETIF_F_ALL_CSUM)) {
printk("%s: Dropping NETIF_F_SG since no checksum feature.\n",
dev->name);
dev->features &= ~NETIF_F_SG;
@ -3300,6 +3258,88 @@ static int dev_cpu_callback(struct notifier_block *nfb,
}
#endif /* CONFIG_HOTPLUG_CPU */
#ifdef CONFIG_NET_DMA
/**
* net_dma_rebalance -
* This is called when the number of channels allocated to the net_dma_client
* changes. The net_dma_client tries to have one DMA channel per CPU.
*/
static void net_dma_rebalance(void)
{
unsigned int cpu, i, n;
struct dma_chan *chan;
lock_cpu_hotplug();
if (net_dma_count == 0) {
for_each_online_cpu(cpu)
rcu_assign_pointer(per_cpu(softnet_data.net_dma, cpu), NULL);
unlock_cpu_hotplug();
return;
}
i = 0;
cpu = first_cpu(cpu_online_map);
rcu_read_lock();
list_for_each_entry(chan, &net_dma_client->channels, client_node) {
n = ((num_online_cpus() / net_dma_count)
+ (i < (num_online_cpus() % net_dma_count) ? 1 : 0));
while(n) {
per_cpu(softnet_data.net_dma, cpu) = chan;
cpu = next_cpu(cpu, cpu_online_map);
n--;
}
i++;
}
rcu_read_unlock();
unlock_cpu_hotplug();
}
/**
* netdev_dma_event - event callback for the net_dma_client
* @client: should always be net_dma_client
* @chan:
* @event:
*/
static void netdev_dma_event(struct dma_client *client, struct dma_chan *chan,
enum dma_event event)
{
spin_lock(&net_dma_event_lock);
switch (event) {
case DMA_RESOURCE_ADDED:
net_dma_count++;
net_dma_rebalance();
break;
case DMA_RESOURCE_REMOVED:
net_dma_count--;
net_dma_rebalance();
break;
default:
break;
}
spin_unlock(&net_dma_event_lock);
}
/**
* netdev_dma_regiser - register the networking subsystem as a DMA client
*/
static int __init netdev_dma_register(void)
{
spin_lock_init(&net_dma_event_lock);
net_dma_client = dma_async_client_register(netdev_dma_event);
if (net_dma_client == NULL)
return -ENOMEM;
dma_async_client_chan_request(net_dma_client, num_online_cpus());
return 0;
}
#else
static int __init netdev_dma_register(void) { return -ENODEV; }
#endif /* CONFIG_NET_DMA */
/*
* Initialize the DEV module. At boot time this walks the device list and
@ -3353,6 +3393,8 @@ static int __init net_dev_init(void)
atomic_set(&queue->backlog_dev.refcnt, 1);
}
netdev_dma_register();
dev_boot_phase = 0;
open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
@ -3371,7 +3413,6 @@ subsys_initcall(net_dev_init);
EXPORT_SYMBOL(__dev_get_by_index);
EXPORT_SYMBOL(__dev_get_by_name);
EXPORT_SYMBOL(__dev_remove_pack);
EXPORT_SYMBOL(__skb_linearize);
EXPORT_SYMBOL(dev_valid_name);
EXPORT_SYMBOL(dev_add_pack);
EXPORT_SYMBOL(dev_alloc_name);

View file

@ -62,7 +62,7 @@
* Device mc lists are changed by bh at least if IPv6 is enabled,
* so that it must be bh protected.
*
* We block accesses to device mc filters with dev->xmit_lock.
* We block accesses to device mc filters with netif_tx_lock.
*/
/*
@ -93,9 +93,9 @@ static void __dev_mc_upload(struct net_device *dev)
void dev_mc_upload(struct net_device *dev)
{
spin_lock_bh(&dev->xmit_lock);
netif_tx_lock_bh(dev);
__dev_mc_upload(dev);
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
}
/*
@ -107,7 +107,7 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl)
int err = 0;
struct dev_mc_list *dmi, **dmip;
spin_lock_bh(&dev->xmit_lock);
netif_tx_lock_bh(dev);
for (dmip = &dev->mc_list; (dmi = *dmip) != NULL; dmip = &dmi->next) {
/*
@ -139,13 +139,13 @@ int dev_mc_delete(struct net_device *dev, void *addr, int alen, int glbl)
*/
__dev_mc_upload(dev);
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
return 0;
}
}
err = -ENOENT;
done:
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
return err;
}
@ -160,7 +160,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
dmi1 = kmalloc(sizeof(*dmi), GFP_ATOMIC);
spin_lock_bh(&dev->xmit_lock);
netif_tx_lock_bh(dev);
for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) {
if (memcmp(dmi->dmi_addr, addr, dmi->dmi_addrlen) == 0 &&
dmi->dmi_addrlen == alen) {
@ -176,7 +176,7 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
}
if ((dmi = dmi1) == NULL) {
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
return -ENOMEM;
}
memcpy(dmi->dmi_addr, addr, alen);
@ -189,11 +189,11 @@ int dev_mc_add(struct net_device *dev, void *addr, int alen, int glbl)
__dev_mc_upload(dev);
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
return 0;
done:
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
kfree(dmi1);
return err;
}
@ -204,7 +204,7 @@ done:
void dev_mc_discard(struct net_device *dev)
{
spin_lock_bh(&dev->xmit_lock);
netif_tx_lock_bh(dev);
while (dev->mc_list != NULL) {
struct dev_mc_list *tmp = dev->mc_list;
@ -215,7 +215,7 @@ void dev_mc_discard(struct net_device *dev)
}
dev->mc_count = 0;
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
}
#ifdef CONFIG_PROC_FS
@ -250,7 +250,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v)
struct dev_mc_list *m;
struct net_device *dev = v;
spin_lock_bh(&dev->xmit_lock);
netif_tx_lock_bh(dev);
for (m = dev->mc_list; m; m = m->next) {
int i;
@ -262,7 +262,7 @@ static int dev_mc_seq_show(struct seq_file *seq, void *v)
seq_putc(seq, '\n');
}
spin_unlock_bh(&dev->xmit_lock);
netif_tx_unlock_bh(dev);
return 0;
}

View file

@ -30,7 +30,7 @@ u32 ethtool_op_get_link(struct net_device *dev)
u32 ethtool_op_get_tx_csum(struct net_device *dev)
{
return (dev->features & (NETIF_F_IP_CSUM | NETIF_F_HW_CSUM)) != 0;
return (dev->features & NETIF_F_ALL_CSUM) != 0;
}
int ethtool_op_set_tx_csum(struct net_device *dev, u32 data)
@ -551,9 +551,7 @@ static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
return -EFAULT;
if (edata.data &&
!(dev->features & (NETIF_F_IP_CSUM |
NETIF_F_NO_CSUM |
NETIF_F_HW_CSUM)))
!(dev->features & NETIF_F_ALL_CSUM))
return -EINVAL;
return __ethtool_set_sg(dev, edata.data);
@ -591,7 +589,7 @@ static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr)
{
struct ethtool_value edata = { ETHTOOL_GTSO };
struct ethtool_value edata = { ETHTOOL_GUFO };
if (!dev->ethtool_ops->get_ufo)
return -EOPNOTSUPP;
@ -600,6 +598,7 @@ static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr)
return -EFAULT;
return 0;
}
static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr)
{
struct ethtool_value edata;

View file

@ -273,24 +273,21 @@ static void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
do {
npinfo->tries--;
spin_lock(&np->dev->xmit_lock);
np->dev->xmit_lock_owner = smp_processor_id();
netif_tx_lock(np->dev);
/*
* network drivers do not expect to be called if the queue is
* stopped.
*/
if (netif_queue_stopped(np->dev)) {
np->dev->xmit_lock_owner = -1;
spin_unlock(&np->dev->xmit_lock);
netif_tx_unlock(np->dev);
netpoll_poll(np);
udelay(50);
continue;
}
status = np->dev->hard_start_xmit(skb, np->dev);
np->dev->xmit_lock_owner = -1;
spin_unlock(&np->dev->xmit_lock);
netif_tx_unlock(np->dev);
/* success */
if(!status) {

View file

@ -2897,7 +2897,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
}
}
spin_lock_bh(&odev->xmit_lock);
netif_tx_lock_bh(odev);
if (!netif_queue_stopped(odev)) {
atomic_inc(&(pkt_dev->skb->users));
@ -2942,7 +2942,7 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
pkt_dev->next_tx_ns = 0;
}
spin_unlock_bh(&odev->xmit_lock);
netif_tx_unlock_bh(odev);
/* If pkt_dev->count is zero, then run forever */
if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {

View file

@ -464,7 +464,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
n->tc_verd = CLR_TC_MUNGED(n->tc_verd);
C(input_dev);
#endif
skb_copy_secmark(n, skb);
#endif
C(truesize);
atomic_set(&n->users, 1);
@ -526,6 +526,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
#endif
new->tc_index = old->tc_index;
#endif
skb_copy_secmark(new, old);
atomic_set(&new->users, 1);
skb_shinfo(new)->tso_size = skb_shinfo(old)->tso_size;
skb_shinfo(new)->tso_segs = skb_shinfo(old)->tso_segs;
@ -800,12 +801,10 @@ struct sk_buff *skb_pad(struct sk_buff *skb, int pad)
return nskb;
}
/* Trims skb to length len. It can change skb pointers, if "realloc" is 1.
* If realloc==0 and trimming is impossible without change of data,
* it is BUG().
/* Trims skb to length len. It can change skb pointers.
*/
int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc)
int ___pskb_trim(struct sk_buff *skb, unsigned int len)
{
int offset = skb_headlen(skb);
int nfrags = skb_shinfo(skb)->nr_frags;
@ -815,7 +814,6 @@ int ___pskb_trim(struct sk_buff *skb, unsigned int len, int realloc)
int end = offset + skb_shinfo(skb)->frags[i].size;
if (end > len) {
if (skb_cloned(skb)) {
BUG_ON(!realloc);
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
return -ENOMEM;
}

View file

@ -832,6 +832,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
atomic_set(&newsk->sk_omem_alloc, 0);
skb_queue_head_init(&newsk->sk_receive_queue);
skb_queue_head_init(&newsk->sk_write_queue);
#ifdef CONFIG_NET_DMA
skb_queue_head_init(&newsk->sk_async_wait_queue);
#endif
rwlock_init(&newsk->sk_dst_lock);
rwlock_init(&newsk->sk_callback_lock);
@ -1383,6 +1386,9 @@ void sock_init_data(struct socket *sock, struct sock *sk)
skb_queue_head_init(&sk->sk_receive_queue);
skb_queue_head_init(&sk->sk_write_queue);
skb_queue_head_init(&sk->sk_error_queue);
#ifdef CONFIG_NET_DMA
skb_queue_head_init(&sk->sk_async_wait_queue);
#endif
sk->sk_send_head = NULL;

131
net/core/user_dma.c Normal file
View file

@ -0,0 +1,131 @@
/*
* Copyright(c) 2004 - 2006 Intel Corporation. All rights reserved.
* Portions based on net/core/datagram.c and copyrighted by their authors.
*
* 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., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called COPYING.
*/
/*
* This code allows the net stack to make use of a DMA engine for
* skb to iovec copies.
*/
#include <linux/dmaengine.h>
#include <linux/socket.h>
#include <linux/rtnetlink.h> /* for BUG_TRAP */
#include <net/tcp.h>
#define NET_DMA_DEFAULT_COPYBREAK 4096
int sysctl_tcp_dma_copybreak = NET_DMA_DEFAULT_COPYBREAK;
/**
* dma_skb_copy_datagram_iovec - Copy a datagram to an iovec.
* @skb - buffer to copy
* @offset - offset in the buffer to start copying from
* @iovec - io vector to copy to
* @len - amount of data to copy from buffer to iovec
* @pinned_list - locked iovec buffer data
*
* Note: the iovec is modified during the copy.
*/
int dma_skb_copy_datagram_iovec(struct dma_chan *chan,
struct sk_buff *skb, int offset, struct iovec *to,
size_t len, struct dma_pinned_list *pinned_list)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
dma_cookie_t cookie = 0;
/* Copy header. */
if (copy > 0) {
if (copy > len)
copy = len;
cookie = dma_memcpy_to_iovec(chan, to, pinned_list,
skb->data + offset, copy);
if (cookie < 0)
goto fault;
len -= copy;
if (len == 0)
goto end;
offset += copy;
}
/* Copy paged appendix. Hmm... why does this look so complicated? */
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
int end;
BUG_TRAP(start <= offset + len);
end = start + skb_shinfo(skb)->frags[i].size;
copy = end - offset;
if ((copy = end - offset) > 0) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
struct page *page = frag->page;
if (copy > len)
copy = len;
cookie = dma_memcpy_pg_to_iovec(chan, to, pinned_list, page,
frag->page_offset + offset - start, copy);
if (cookie < 0)
goto fault;
len -= copy;
if (len == 0)
goto end;
offset += copy;
}
start = end;
}
if (skb_shinfo(skb)->frag_list) {
struct sk_buff *list = skb_shinfo(skb)->frag_list;
for (; list; list = list->next) {
int end;
BUG_TRAP(start <= offset + len);
end = start + list->len;
copy = end - offset;
if (copy > 0) {
if (copy > len)
copy = len;
cookie = dma_skb_copy_datagram_iovec(chan, list,
offset - start, to, copy,
pinned_list);
if (cookie < 0)
goto fault;
len -= copy;
if (len == 0)
goto end;
offset += copy;
}
start = end;
}
}
end:
if (!len) {
skb->dma_cookie = cookie;
return cookie;
}
fault:
return -EFAULT;
}

View file

@ -719,7 +719,7 @@ int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
dccp_pr_debug("packet_type=%s\n",
dccp_packet_name(dh->dccph_type));
sk_eat_skb(sk, skb);
sk_eat_skb(sk, skb, 0);
verify_sock_status:
if (sock_flag(sk, SOCK_DONE)) {
len = 0;
@ -773,7 +773,7 @@ verify_sock_status:
}
found_fin_ok:
if (!(flags & MSG_PEEK))
sk_eat_skb(sk, skb);
sk_eat_skb(sk, skb, 0);
break;
} while (1);
out:

View file

@ -801,8 +801,7 @@ got_it:
* We linearize everything except data segments here.
*/
if (cb->nsp_flags & ~0x60) {
if (unlikely(skb_is_nonlinear(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0)
if (unlikely(skb_linearize(skb)))
goto free_out;
}

View file

@ -629,8 +629,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
padlen);
if (flags & DN_RT_PKT_CNTL) {
if (unlikely(skb_is_nonlinear(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0)
if (unlikely(skb_linearize(skb)))
goto dump_it;
switch(flags & DN_RT_CNTL_MSK) {

View file

@ -414,6 +414,24 @@ config INET_TUNNEL
tristate
default n
config INET_XFRM_MODE_TRANSPORT
tristate "IP: IPsec transport mode"
default y
select XFRM
---help---
Support for IPsec transport mode.
If unsure, say Y.
config INET_XFRM_MODE_TUNNEL
tristate "IP: IPsec tunnel mode"
default y
select XFRM
---help---
Support for IPsec tunnel mode.
If unsure, say Y.
config INET_DIAG
tristate "INET: socket monitoring interface"
default y
@ -532,6 +550,38 @@ config TCP_CONG_SCALABLE
properties, though is known to have fairness issues.
See http://www-lce.eng.cam.ac.uk/~ctk21/scalable/
config TCP_CONG_LP
tristate "TCP Low Priority"
depends on EXPERIMENTAL
default n
---help---
TCP Low Priority (TCP-LP), a distributed algorithm whose goal is
to utiliza only the excess network bandwidth as compared to the
``fair share`` of bandwidth as targeted by TCP.
See http://www-ece.rice.edu/networks/TCP-LP/
config TCP_CONG_VENO
tristate "TCP Veno"
depends on EXPERIMENTAL
default n
---help---
TCP Veno is a sender-side only enhancement of TCP to obtain better
throughput over wireless networks. TCP Veno makes use of state
distinguishing to circumvent the difficult judgment of the packet loss
type. TCP Veno cuts down less congestion window in response to random
loss packets.
See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf
config TCP_CONG_COMPOUND
tristate "TCP Compound"
depends on EXPERIMENTAL
default n
---help---
TCP Compound is a sender-side only change to TCP that uses
a mixed Reno/Vegas approach to calculate the cwnd.
For further details look here:
ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf
endmenu
config TCP_CONG_BIC

View file

@ -24,6 +24,8 @@ obj-$(CONFIG_INET_ESP) += esp4.o
obj-$(CONFIG_INET_IPCOMP) += ipcomp.o
obj-$(CONFIG_INET_XFRM_TUNNEL) += xfrm4_tunnel.o
obj-$(CONFIG_INET_TUNNEL) += tunnel4.o
obj-$(CONFIG_INET_XFRM_MODE_TRANSPORT) += xfrm4_mode_transport.o
obj-$(CONFIG_INET_XFRM_MODE_TUNNEL) += xfrm4_mode_tunnel.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_RR) += multipath_rr.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_RANDOM) += multipath_random.o
@ -34,6 +36,7 @@ obj-$(CONFIG_IP_VS) += ipvs/
obj-$(CONFIG_INET_DIAG) += inet_diag.o
obj-$(CONFIG_IP_ROUTE_MULTIPATH_CACHED) += multipath.o
obj-$(CONFIG_INET_TCP_DIAG) += tcp_diag.o
obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
@ -41,7 +44,10 @@ obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o
obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o
obj-$(CONFIG_TCP_CONG_HTCP) += tcp_htcp.o
obj-$(CONFIG_TCP_CONG_VEGAS) += tcp_vegas.o
obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o
obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
obj-$(CONFIG_TCP_CONG_COMPOUND) += tcp_compound.o
obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
xfrm4_output.o

View file

@ -119,6 +119,7 @@ error:
static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ah_hlen;
int ihl;
struct iphdr *iph;
struct ip_auth_hdr *ah;
struct ah_data *ahp;
@ -149,13 +150,14 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
ah = (struct ip_auth_hdr*)skb->data;
iph = skb->nh.iph;
memcpy(work_buf, iph, iph->ihl*4);
ihl = skb->data - skb->nh.raw;
memcpy(work_buf, iph, ihl);
iph->ttl = 0;
iph->tos = 0;
iph->frag_off = 0;
iph->check = 0;
if (iph->ihl != 5) {
if (ihl > sizeof(*iph)) {
u32 dummy;
if (ip_clear_mutable_options(iph, &dummy))
goto out;
@ -164,7 +166,7 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
u8 auth_data[MAX_AH_AUTH_LEN];
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
skb_push(skb, skb->data - skb->nh.raw);
skb_push(skb, ihl);
ahp->icv(ahp, skb, ah->auth_data);
if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
x->stats.integrity_failed++;
@ -172,11 +174,8 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
}
}
((struct iphdr*)work_buf)->protocol = ah->nexthdr;
skb->nh.raw = skb_pull(skb, ah_hlen);
memcpy(skb->nh.raw, work_buf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len);
skb_pull(skb, skb->nh.iph->ihl*4);
skb->h.raw = skb->data;
skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
__skb_pull(skb, ah_hlen + ihl);
return 0;

View file

@ -143,10 +143,9 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
int alen = esp->auth.icv_trunc_len;
int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
int nfrags;
int encap_len = 0;
int ihl;
u8 nexthdr[2];
struct scatterlist *sg;
u8 workbuf[60];
int padlen;
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
@ -177,7 +176,6 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
skb->ip_summed = CHECKSUM_NONE;
esph = (struct ip_esp_hdr*)skb->data;
iph = skb->nh.iph;
/* Get ivec. This can be wrong, check against another impls. */
if (esp->conf.ivlen)
@ -204,12 +202,12 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... check padding bits here. Silly. :-) */
iph = skb->nh.iph;
ihl = iph->ihl * 4;
if (x->encap) {
struct xfrm_encap_tmpl *encap = x->encap;
struct udphdr *uh;
uh = (struct udphdr *)(iph + 1);
encap_len = (void*)esph - (void*)uh;
struct udphdr *uh = (void *)(skb->nh.raw + ihl);
/*
* 1) if the NAT-T peer's IP or port changed then
@ -246,11 +244,7 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
iph->protocol = nexthdr[1];
pskb_trim(skb, skb->len - alen - padlen - 2);
memcpy(workbuf, skb->nh.raw, iph->ihl*4);
skb->h.raw = skb_pull(skb, sizeof(struct ip_esp_hdr) + esp->conf.ivlen);
skb->nh.raw += encap_len + sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
memcpy(skb->nh.raw, workbuf, iph->ihl*4);
skb->nh.iph->tot_len = htons(skb->len);
skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;
return 0;

View file

@ -730,7 +730,6 @@ out_err:
static void icmp_redirect(struct sk_buff *skb)
{
struct iphdr *iph;
unsigned long ip;
if (skb->len < sizeof(struct iphdr))
goto out_err;
@ -742,7 +741,6 @@ static void icmp_redirect(struct sk_buff *skb)
goto out;
iph = (struct iphdr *)skb->data;
ip = iph->daddr;
switch (skb->h.icmph->code & 7) {
case ICMP_REDIR_NET:
@ -752,7 +750,8 @@ static void icmp_redirect(struct sk_buff *skb)
*/
case ICMP_REDIR_HOST:
case ICMP_REDIR_HOSTTOS:
ip_rt_redirect(skb->nh.iph->saddr, ip, skb->h.icmph->un.gateway,
ip_rt_redirect(skb->nh.iph->saddr, iph->daddr,
skb->h.icmph->un.gateway,
iph->saddr, skb->dev);
break;
}

View file

@ -2361,7 +2361,7 @@ static int igmp_mc_seq_show(struct seq_file *seq, void *v)
}
seq_printf(seq,
"\t\t\t\t%08lX %5d %d:%08lX\t\t%d\n",
"\t\t\t\t%08X %5d %d:%08lX\t\t%d\n",
im->multiaddr, im->users,
im->tm_running, im->tm_running ?
jiffies_to_clock_t(im->timer.expires-jiffies) : 0,

View file

@ -410,6 +410,7 @@ static void ip_copy_metadata(struct sk_buff *to, struct sk_buff *from)
nf_bridge_get(to->nf_bridge);
#endif
#endif
skb_copy_secmark(to, from);
}
/*
@ -839,7 +840,7 @@ int ip_append_data(struct sock *sk,
*/
if (transhdrlen &&
length + fragheaderlen <= mtu &&
rt->u.dst.dev->features&(NETIF_F_IP_CSUM|NETIF_F_NO_CSUM|NETIF_F_HW_CSUM) &&
rt->u.dst.dev->features & NETIF_F_ALL_CSUM &&
!exthdrlen)
csummode = CHECKSUM_HW;

View file

@ -45,7 +45,6 @@ static LIST_HEAD(ipcomp_tfms_list);
static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
{
int err, plen, dlen;
struct iphdr *iph;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
struct crypto_tfm *tfm;
@ -74,8 +73,6 @@ static int ipcomp_decompress(struct xfrm_state *x, struct sk_buff *skb)
skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
iph = skb->nh.iph;
iph->tot_len = htons(dlen + iph->ihl * 4);
out:
put_cpu();
return err;
@ -83,34 +80,21 @@ out:
static int ipcomp_input(struct xfrm_state *x, struct sk_buff *skb)
{
u8 nexthdr;
int err = 0;
int err = -ENOMEM;
struct iphdr *iph;
union {
struct iphdr iph;
char buf[60];
} tmp_iph;
struct ip_comp_hdr *ipch;
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
err = -ENOMEM;
if (skb_linearize_cow(skb))
goto out;
}
skb->ip_summed = CHECKSUM_NONE;
/* Remove ipcomp header and decompress original payload */
iph = skb->nh.iph;
memcpy(&tmp_iph, iph, iph->ihl * 4);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ip_comp_hdr));
skb->nh.raw += sizeof(struct ip_comp_hdr);
memcpy(skb->nh.raw, &tmp_iph, tmp_iph.iph.ihl * 4);
iph = skb->nh.iph;
iph->tot_len = htons(ntohs(iph->tot_len) - sizeof(struct ip_comp_hdr));
iph->protocol = nexthdr;
skb->h.raw = skb->data;
ipch = (void *)skb->data;
iph->protocol = ipch->nexthdr;
skb->h.raw = skb->nh.raw + sizeof(*ipch);
__skb_pull(skb, sizeof(*ipch));
err = ipcomp_decompress(x, skb);
out:
@ -171,10 +155,8 @@ static int ipcomp_output(struct xfrm_state *x, struct sk_buff *skb)
goto out_ok;
}
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
if (skb_linearize_cow(skb))
goto out_ok;
}
err = ipcomp_compress(x, skb);
iph = skb->nh.iph;

View file

@ -55,6 +55,18 @@ config IP_NF_CONNTRACK_MARK
of packets, but this mark value is kept in the conntrack session
instead of the individual packets.
config IP_NF_CONNTRACK_SECMARK
bool 'Connection tracking security mark support'
depends on IP_NF_CONNTRACK && NETWORK_SECMARK
help
This option enables security markings to be applied to
connections. Typically they are copied to connections from
packets using the CONNSECMARK target and copied back from
connections to packets with the same target, with the packets
being originally labeled via SECMARK.
If unsure, say 'N'.
config IP_NF_CONNTRACK_EVENTS
bool "Connection tracking events (EXPERIMENTAL)"
depends on EXPERIMENTAL && IP_NF_CONNTRACK
@ -142,6 +154,8 @@ config IP_NF_TFTP
config IP_NF_AMANDA
tristate "Amanda backup protocol support"
depends on IP_NF_CONNTRACK
select TEXTSEARCH
select TEXTSEARCH_KMP
help
If you are running the Amanda backup package <http://www.amanda.org/>
on this machine or machines that will be MASQUERADED through this
@ -181,14 +195,26 @@ config IP_NF_H323
With this module you can support H.323 on a connection tracking/NAT
firewall.
This module supports RAS, Fast-start, H.245 tunnelling, RTP/RTCP
and T.120 based data and applications including audio, video, FAX,
chat, whiteboard, file transfer, etc. For more information, please
see http://nath323.sourceforge.net/.
This module supports RAS, Fast Start, H.245 Tunnelling, Call
Forwarding, RTP/RTCP and T.120 based audio, video, fax, chat,
whiteboard, file transfer, etc. For more information, please
visit http://nath323.sourceforge.net/.
If you want to compile it as a module, say 'M' here and read
Documentation/modules.txt. If unsure, say 'N'.
config IP_NF_SIP
tristate "SIP protocol support (EXPERIMENTAL)"
depends on IP_NF_CONNTRACK && EXPERIMENTAL
help
SIP is an application-layer control protocol that can establish,
modify, and terminate multimedia sessions (conferences) such as
Internet telephony calls. With the ip_conntrack_sip and
the ip_nat_sip modules you can support the protocol on a connection
tracking/NATing firewall.
To compile it as a module, choose M here. If unsure, say Y.
config IP_NF_QUEUE
tristate "IP Userspace queueing via NETLINK (OBSOLETE)"
help
@ -501,6 +527,12 @@ config IP_NF_NAT_H323
default IP_NF_NAT if IP_NF_H323=y
default m if IP_NF_H323=m
config IP_NF_NAT_SIP
tristate
depends on IP_NF_IPTABLES!=n && IP_NF_CONNTRACK!=n && IP_NF_NAT!=n
default IP_NF_NAT if IP_NF_SIP=y
default m if IP_NF_SIP=m
# mangle + specific targets
config IP_NF_MANGLE
tristate "Packet mangling"

View file

@ -31,6 +31,7 @@ obj-$(CONFIG_IP_NF_AMANDA) += ip_conntrack_amanda.o
obj-$(CONFIG_IP_NF_TFTP) += ip_conntrack_tftp.o
obj-$(CONFIG_IP_NF_FTP) += ip_conntrack_ftp.o
obj-$(CONFIG_IP_NF_IRC) += ip_conntrack_irc.o
obj-$(CONFIG_IP_NF_SIP) += ip_conntrack_sip.o
obj-$(CONFIG_IP_NF_NETBIOS_NS) += ip_conntrack_netbios_ns.o
# NAT helpers
@ -40,6 +41,7 @@ obj-$(CONFIG_IP_NF_NAT_AMANDA) += ip_nat_amanda.o
obj-$(CONFIG_IP_NF_NAT_TFTP) += ip_nat_tftp.o
obj-$(CONFIG_IP_NF_NAT_FTP) += ip_nat_ftp.o
obj-$(CONFIG_IP_NF_NAT_IRC) += ip_nat_irc.o
obj-$(CONFIG_IP_NF_NAT_SIP) += ip_nat_sip.o
# generic IP tables
obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o

View file

@ -17,33 +17,29 @@
* this value.
*
*/
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/moduleparam.h>
#include <linux/textsearch.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <net/checksum.h>
#include <net/udp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_amanda.h>
static unsigned int master_timeout = 300;
static char *ts_algo = "kmp";
MODULE_AUTHOR("Brian J. Murrell <[email protected]>");
MODULE_DESCRIPTION("Amanda connection tracking module");
MODULE_LICENSE("GPL");
module_param(master_timeout, uint, 0600);
MODULE_PARM_DESC(master_timeout, "timeout for the master connection");
static const char *conns[] = { "DATA ", "MESG ", "INDEX " };
/* This is slow, but it's simple. --RR */
static char *amanda_buffer;
static DEFINE_SPINLOCK(amanda_buffer_lock);
module_param(ts_algo, charp, 0400);
MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)");
unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
@ -52,12 +48,48 @@ unsigned int (*ip_nat_amanda_hook)(struct sk_buff **pskb,
struct ip_conntrack_expect *exp);
EXPORT_SYMBOL_GPL(ip_nat_amanda_hook);
enum amanda_strings {
SEARCH_CONNECT,
SEARCH_NEWLINE,
SEARCH_DATA,
SEARCH_MESG,
SEARCH_INDEX,
};
static struct {
char *string;
size_t len;
struct ts_config *ts;
} search[] = {
[SEARCH_CONNECT] = {
.string = "CONNECT ",
.len = 8,
},
[SEARCH_NEWLINE] = {
.string = "\n",
.len = 1,
},
[SEARCH_DATA] = {
.string = "DATA ",
.len = 5,
},
[SEARCH_MESG] = {
.string = "MESG ",
.len = 5,
},
[SEARCH_INDEX] = {
.string = "INDEX ",
.len = 6,
},
};
static int help(struct sk_buff **pskb,
struct ip_conntrack *ct, enum ip_conntrack_info ctinfo)
{
struct ts_state ts;
struct ip_conntrack_expect *exp;
char *data, *data_limit, *tmp;
unsigned int dataoff, i;
unsigned int dataoff, start, stop, off, i;
char pbuf[sizeof("65535")], *tmp;
u_int16_t port, len;
int ret = NF_ACCEPT;
@ -77,29 +109,34 @@ static int help(struct sk_buff **pskb,
return NF_ACCEPT;
}
spin_lock_bh(&amanda_buffer_lock);
skb_copy_bits(*pskb, dataoff, amanda_buffer, (*pskb)->len - dataoff);
data = amanda_buffer;
data_limit = amanda_buffer + (*pskb)->len - dataoff;
*data_limit = '\0';
/* Search for the CONNECT string */
data = strstr(data, "CONNECT ");
if (!data)
memset(&ts, 0, sizeof(ts));
start = skb_find_text(*pskb, dataoff, (*pskb)->len,
search[SEARCH_CONNECT].ts, &ts);
if (start == UINT_MAX)
goto out;
data += strlen("CONNECT ");
start += dataoff + search[SEARCH_CONNECT].len;
/* Only search first line. */
if ((tmp = strchr(data, '\n')))
*tmp = '\0';
memset(&ts, 0, sizeof(ts));
stop = skb_find_text(*pskb, start, (*pskb)->len,
search[SEARCH_NEWLINE].ts, &ts);
if (stop == UINT_MAX)
goto out;
stop += start;
for (i = 0; i < ARRAY_SIZE(conns); i++) {
char *match = strstr(data, conns[i]);
if (!match)
for (i = SEARCH_DATA; i <= SEARCH_INDEX; i++) {
memset(&ts, 0, sizeof(ts));
off = skb_find_text(*pskb, start, stop, search[i].ts, &ts);
if (off == UINT_MAX)
continue;
tmp = data = match + strlen(conns[i]);
port = simple_strtoul(data, &data, 10);
len = data - tmp;
off += start + search[i].len;
len = min_t(unsigned int, sizeof(pbuf) - 1, stop - off);
if (skb_copy_bits(*pskb, off, pbuf, len))
break;
pbuf[len] = '\0';
port = simple_strtoul(pbuf, &tmp, 10);
len = tmp - pbuf;
if (port == 0 || len > 5)
break;
@ -125,8 +162,7 @@ static int help(struct sk_buff **pskb,
exp->mask.dst.u.tcp.port = 0xFFFF;
if (ip_nat_amanda_hook)
ret = ip_nat_amanda_hook(pskb, ctinfo,
tmp - amanda_buffer,
ret = ip_nat_amanda_hook(pskb, ctinfo, off - dataoff,
len, exp);
else if (ip_conntrack_expect_related(exp) != 0)
ret = NF_DROP;
@ -134,12 +170,11 @@ static int help(struct sk_buff **pskb,
}
out:
spin_unlock_bh(&amanda_buffer_lock);
return ret;
}
static struct ip_conntrack_helper amanda_helper = {
.max_expected = ARRAY_SIZE(conns),
.max_expected = 3,
.timeout = 180,
.me = THIS_MODULE,
.help = help,
@ -155,26 +190,36 @@ static struct ip_conntrack_helper amanda_helper = {
static void __exit ip_conntrack_amanda_fini(void)
{
int i;
ip_conntrack_helper_unregister(&amanda_helper);
kfree(amanda_buffer);
for (i = 0; i < ARRAY_SIZE(search); i++)
textsearch_destroy(search[i].ts);
}
static int __init ip_conntrack_amanda_init(void)
{
int ret;
int ret, i;
amanda_buffer = kmalloc(65536, GFP_KERNEL);
if (!amanda_buffer)
return -ENOMEM;
ret = ip_conntrack_helper_register(&amanda_helper);
if (ret < 0) {
kfree(amanda_buffer);
return ret;
ret = -ENOMEM;
for (i = 0; i < ARRAY_SIZE(search); i++) {
search[i].ts = textsearch_prepare(ts_algo, search[i].string,
search[i].len,
GFP_KERNEL, TS_AUTOLOAD);
if (search[i].ts == NULL)
goto err;
}
ret = ip_conntrack_helper_register(&amanda_helper);
if (ret < 0)
goto err;
return 0;
err:
for (; i >= 0; i--) {
if (search[i].ts)
textsearch_destroy(search[i].ts);
}
return ret;
}
module_init(ip_conntrack_amanda_init);

View file

@ -723,6 +723,9 @@ init_conntrack(struct ip_conntrack_tuple *tuple,
defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
/* this is ugly, but there is no other place where to put it */
conntrack->nat.masq_index = exp->master->nat.masq_index;
#endif
#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
conntrack->secmark = exp->master->secmark;
#endif
nf_conntrack_get(&conntrack->master->ct_general);
CONNTRACK_STAT_INC(expect_new);
@ -1130,6 +1133,12 @@ void __ip_ct_refresh_acct(struct ip_conntrack *ct,
write_lock_bh(&ip_conntrack_lock);
/* Only update if this is not a fixed timeout */
if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) {
write_unlock_bh(&ip_conntrack_lock);
return;
}
/* If not in hash table, timer will not be active yet */
if (!is_confirmed(ct)) {
ct->timeout.expires = extra_jiffies;

View file

@ -56,37 +56,48 @@ static int try_eprt(const char *, size_t, u_int32_t [], char);
static int try_epsv_response(const char *, size_t, u_int32_t [], char);
static const struct ftp_search {
enum ip_conntrack_dir dir;
const char *pattern;
size_t plen;
char skip;
char term;
enum ip_ct_ftp_type ftptype;
int (*getnum)(const char *, size_t, u_int32_t[], char);
} search[] = {
{
IP_CT_DIR_ORIGINAL,
"PORT", sizeof("PORT") - 1, ' ', '\r',
IP_CT_FTP_PORT,
try_rfc959,
} search[IP_CT_DIR_MAX][2] = {
[IP_CT_DIR_ORIGINAL] = {
{
.pattern = "PORT",
.plen = sizeof("PORT") - 1,
.skip = ' ',
.term = '\r',
.ftptype = IP_CT_FTP_PORT,
.getnum = try_rfc959,
},
{
.pattern = "EPRT",
.plen = sizeof("EPRT") - 1,
.skip = ' ',
.term = '\r',
.ftptype = IP_CT_FTP_EPRT,
.getnum = try_eprt,
},
},
{
IP_CT_DIR_REPLY,
"227 ", sizeof("227 ") - 1, '(', ')',
IP_CT_FTP_PASV,
try_rfc959,
},
{
IP_CT_DIR_ORIGINAL,
"EPRT", sizeof("EPRT") - 1, ' ', '\r',
IP_CT_FTP_EPRT,
try_eprt,
},
{
IP_CT_DIR_REPLY,
"229 ", sizeof("229 ") - 1, '(', ')',
IP_CT_FTP_EPSV,
try_epsv_response,
[IP_CT_DIR_REPLY] = {
{
.pattern = "227 ",
.plen = sizeof("227 ") - 1,
.skip = '(',
.term = ')',
.ftptype = IP_CT_FTP_PASV,
.getnum = try_rfc959,
},
{
.pattern = "229 ",
.plen = sizeof("229 ") - 1,
.skip = '(',
.term = ')',
.ftptype = IP_CT_FTP_EPSV,
.getnum = try_epsv_response,
},
},
};
@ -346,17 +357,15 @@ static int help(struct sk_buff **pskb,
array[2] = (ntohl(ct->tuplehash[dir].tuple.src.ip) >> 8) & 0xFF;
array[3] = ntohl(ct->tuplehash[dir].tuple.src.ip) & 0xFF;
for (i = 0; i < ARRAY_SIZE(search); i++) {
if (search[i].dir != dir) continue;
for (i = 0; i < ARRAY_SIZE(search[dir]); i++) {
found = find_pattern(fb_ptr, (*pskb)->len - dataoff,
search[i].pattern,
search[i].plen,
search[i].skip,
search[i].term,
search[dir][i].pattern,
search[dir][i].plen,
search[dir][i].skip,
search[dir][i].term,
&matchoff, &matchlen,
array,
search[i].getnum);
search[dir][i].getnum);
if (found) break;
}
if (found == -1) {
@ -366,7 +375,7 @@ static int help(struct sk_buff **pskb,
this case. */
if (net_ratelimit())
printk("conntrack_ftp: partial %s %u+%u\n",
search[i].pattern,
search[dir][i].pattern,
ntohl(th->seq), datalen);
ret = NF_DROP;
goto out;
@ -426,7 +435,7 @@ static int help(struct sk_buff **pskb,
/* Now, NAT might want to mangle the packet, and register the
* (possibly changed) expectation itself. */
if (ip_nat_ftp_hook)
ret = ip_nat_ftp_hook(pskb, ctinfo, search[i].ftptype,
ret = ip_nat_ftp_hook(pskb, ctinfo, search[dir][i].ftptype,
matchoff, matchlen, exp, &seq);
else {
/* Can't expect this? Best to drop packet now. */

View file

@ -22,6 +22,8 @@
#include <linux/netfilter_ipv4/ip_conntrack_tuple.h>
#include <linux/netfilter_ipv4/ip_conntrack_h323.h>
#include <linux/moduleparam.h>
#include <linux/ctype.h>
#include <linux/inet.h>
#if 0
#define DEBUGP printk
@ -38,6 +40,12 @@ static int gkrouted_only = 1;
module_param(gkrouted_only, int, 0600);
MODULE_PARM_DESC(gkrouted_only, "only accept calls from gatekeeper");
static int callforward_filter = 1;
module_param(callforward_filter, bool, 0600);
MODULE_PARM_DESC(callforward_filter, "only create call forwarding expectations "
"if both endpoints are on different sides "
"(determined by routing information)");
/* Hooks for NAT */
int (*set_h245_addr_hook) (struct sk_buff ** pskb,
unsigned char **data, int dataoff,
@ -77,6 +85,12 @@ int (*nat_h245_hook) (struct sk_buff ** pskb,
unsigned char **data, int dataoff,
TransportAddress * addr, u_int16_t port,
struct ip_conntrack_expect * exp);
int (*nat_callforwarding_hook) (struct sk_buff ** pskb,
struct ip_conntrack * ct,
enum ip_conntrack_info ctinfo,
unsigned char **data, int dataoff,
TransportAddress * addr, u_int16_t port,
struct ip_conntrack_expect * exp);
int (*nat_q931_hook) (struct sk_buff ** pskb,
struct ip_conntrack * ct,
enum ip_conntrack_info ctinfo,
@ -683,6 +697,92 @@ static int expect_h245(struct sk_buff **pskb, struct ip_conntrack *ct,
return ret;
}
/* Forwarding declaration */
void ip_conntrack_q931_expect(struct ip_conntrack *new,
struct ip_conntrack_expect *this);
/****************************************************************************/
static int expect_callforwarding(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
unsigned char **data, int dataoff,
TransportAddress * addr)
{
int dir = CTINFO2DIR(ctinfo);
int ret = 0;
u_int32_t ip;
u_int16_t port;
struct ip_conntrack_expect *exp = NULL;
/* Read alternativeAddress */
if (!get_h225_addr(*data, addr, &ip, &port) || port == 0)
return 0;
/* If the calling party is on the same side of the forward-to party,
* we don't need to track the second call */
if (callforward_filter) {
struct rtable *rt1, *rt2;
struct flowi fl1 = {
.fl4_dst = ip,
};
struct flowi fl2 = {
.fl4_dst = ct->tuplehash[!dir].tuple.src.ip,
};
if (ip_route_output_key(&rt1, &fl1) == 0) {
if (ip_route_output_key(&rt2, &fl2) == 0) {
if (rt1->rt_gateway == rt2->rt_gateway &&
rt1->u.dst.dev == rt2->u.dst.dev)
ret = 1;
dst_release(&rt2->u.dst);
}
dst_release(&rt1->u.dst);
}
if (ret) {
DEBUGP("ip_ct_q931: Call Forwarding not tracked\n");
return 0;
}
}
/* Create expect for the second call leg */
if ((exp = ip_conntrack_expect_alloc(ct)) == NULL)
return -1;
exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
exp->tuple.src.u.tcp.port = 0;
exp->tuple.dst.ip = ip;
exp->tuple.dst.u.tcp.port = htons(port);
exp->tuple.dst.protonum = IPPROTO_TCP;
exp->mask.src.ip = 0xFFFFFFFF;
exp->mask.src.u.tcp.port = 0;
exp->mask.dst.ip = 0xFFFFFFFF;
exp->mask.dst.u.tcp.port = 0xFFFF;
exp->mask.dst.protonum = 0xFF;
exp->flags = 0;
if (ct->tuplehash[dir].tuple.src.ip !=
ct->tuplehash[!dir].tuple.dst.ip && nat_callforwarding_hook) {
/* Need NAT */
ret = nat_callforwarding_hook(pskb, ct, ctinfo, data, dataoff,
addr, port, exp);
} else { /* Conntrack only */
exp->expectfn = ip_conntrack_q931_expect;
if (ip_conntrack_expect_related(exp) == 0) {
DEBUGP("ip_ct_q931: expect Call Forwarding "
"%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
NIPQUAD(exp->tuple.src.ip),
ntohs(exp->tuple.src.u.tcp.port),
NIPQUAD(exp->tuple.dst.ip),
ntohs(exp->tuple.dst.u.tcp.port));
} else
ret = -1;
}
ip_conntrack_expect_put(exp);
return ret;
}
/****************************************************************************/
static int process_setup(struct sk_buff **pskb, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
@ -878,6 +978,15 @@ static int process_facility(struct sk_buff **pskb, struct ip_conntrack *ct,
DEBUGP("ip_ct_q931: Facility\n");
if (facility->reason.choice == eFacilityReason_callForwarded) {
if (facility->options & eFacility_UUIE_alternativeAddress)
return expect_callforwarding(pskb, ct, ctinfo, data,
dataoff,
&facility->
alternativeAddress);
return 0;
}
if (facility->options & eFacility_UUIE_h245Address) {
ret = expect_h245(pskb, ct, ctinfo, data, dataoff,
&facility->h245Address);
@ -1677,7 +1786,6 @@ static int __init init(void)
fini();
return ret;
}
DEBUGP("ip_ct_h323: init success\n");
return 0;
}
@ -1696,6 +1804,7 @@ EXPORT_SYMBOL_GPL(set_ras_addr_hook);
EXPORT_SYMBOL_GPL(nat_rtp_rtcp_hook);
EXPORT_SYMBOL_GPL(nat_t120_hook);
EXPORT_SYMBOL_GPL(nat_h245_hook);
EXPORT_SYMBOL_GPL(nat_callforwarding_hook);
EXPORT_SYMBOL_GPL(nat_q931_hook);
MODULE_AUTHOR("Jing Min Zhao <[email protected]>");

View file

@ -1,4 +1,4 @@
/* Generated by Jing Min Zhao's ASN.1 parser, Mar 15 2006
/* Generated by Jing Min Zhao's ASN.1 parser, Apr 20 2006
*
* Copyright (c) 2006 Jing Min Zhao <zhaojingmin@users.sourceforge.net>
*
@ -1069,8 +1069,8 @@ static field_t _Facility_UUIE_fastStart[] = { /* SEQUENCE OF */
static field_t _Facility_UUIE[] = { /* SEQUENCE */
{FNAME("protocolIdentifier") OID, BYTE, 0, 0, SKIP, 0, NULL},
{FNAME("alternativeAddress") CHOICE, 3, 7, 7, SKIP | EXT | OPT, 0,
_TransportAddress},
{FNAME("alternativeAddress") CHOICE, 3, 7, 7, DECODE | EXT | OPT,
offsetof(Facility_UUIE, alternativeAddress), _TransportAddress},
{FNAME("alternativeAliasAddress") SEQOF, SEMI, 0, 0, SKIP | OPT, 0,
_Facility_UUIE_alternativeAliasAddress},
{FNAME("conferenceID") OCTSTR, FIXD, 16, 0, SKIP | OPT, 0, NULL},

View file

@ -399,38 +399,54 @@ nfattr_failure:
static int ctnetlink_done(struct netlink_callback *cb)
{
DEBUGP("entered %s\n", __FUNCTION__);
if (cb->args[1])
ip_conntrack_put((struct ip_conntrack *)cb->args[1]);
return 0;
}
static int
ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
{
struct ip_conntrack *ct = NULL;
struct ip_conntrack *ct, *last;
struct ip_conntrack_tuple_hash *h;
struct list_head *i;
u_int32_t *id = (u_int32_t *) &cb->args[1];
DEBUGP("entered %s, last bucket=%lu id=%u\n", __FUNCTION__,
cb->args[0], *id);
read_lock_bh(&ip_conntrack_lock);
for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++, *id = 0) {
for (; cb->args[0] < ip_conntrack_htable_size; cb->args[0]++) {
restart:
last = (struct ip_conntrack *)cb->args[1];
list_for_each_prev(i, &ip_conntrack_hash[cb->args[0]]) {
h = (struct ip_conntrack_tuple_hash *) i;
if (DIRECTION(h) != IP_CT_DIR_ORIGINAL)
continue;
ct = tuplehash_to_ctrack(h);
if (ct->id <= *id)
continue;
if (last != NULL) {
if (ct == last) {
ip_conntrack_put(last);
cb->args[1] = 0;
last = NULL;
} else
continue;
}
if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
cb->nlh->nlmsg_seq,
IPCTNL_MSG_CT_NEW,
1, ct) < 0)
1, ct) < 0) {
nf_conntrack_get(&ct->ct_general);
cb->args[1] = (unsigned long)ct;
goto out;
*id = ct->id;
}
}
if (last != NULL) {
ip_conntrack_put(last);
cb->args[1] = 0;
goto restart;
}
}
out:
out:
read_unlock_bh(&ip_conntrack_lock);
DEBUGP("leaving, last bucket=%lu id=%u\n", cb->args[0], *id);
@ -629,7 +645,7 @@ static const size_t cta_min_nat[CTA_NAT_MAX] = {
};
static inline int
ctnetlink_parse_nat(struct nfattr *cda[],
ctnetlink_parse_nat(struct nfattr *nat,
const struct ip_conntrack *ct, struct ip_nat_range *range)
{
struct nfattr *tb[CTA_NAT_MAX];
@ -639,7 +655,7 @@ ctnetlink_parse_nat(struct nfattr *cda[],
memset(range, 0, sizeof(*range));
nfattr_parse_nested(tb, CTA_NAT_MAX, cda[CTA_NAT-1]);
nfattr_parse_nested(tb, CTA_NAT_MAX, nat);
if (nfattr_bad_size(tb, CTA_NAT_MAX, cta_min_nat))
return -EINVAL;
@ -854,39 +870,30 @@ ctnetlink_change_status(struct ip_conntrack *ct, struct nfattr *cda[])
/* ASSURED bit can only be set */
return -EINVAL;
if (cda[CTA_NAT-1]) {
if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
#ifndef CONFIG_IP_NF_NAT_NEEDED
return -EINVAL;
#else
unsigned int hooknum;
struct ip_nat_range range;
if (ctnetlink_parse_nat(cda, ct, &range) < 0)
return -EINVAL;
DEBUGP("NAT: %u.%u.%u.%u-%u.%u.%u.%u:%u-%u\n",
NIPQUAD(range.min_ip), NIPQUAD(range.max_ip),
htons(range.min.all), htons(range.max.all));
/* This is tricky but it works. ip_nat_setup_info needs the
* hook number as parameter, so let's do the correct
* conversion and run away */
if (status & IPS_SRC_NAT_DONE)
hooknum = NF_IP_POST_ROUTING; /* IP_NAT_MANIP_SRC */
else if (status & IPS_DST_NAT_DONE)
hooknum = NF_IP_PRE_ROUTING; /* IP_NAT_MANIP_DST */
else
return -EINVAL; /* Missing NAT flags */
DEBUGP("NAT status: %lu\n",
status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
if (ip_nat_initialized(ct, HOOK2MANIP(hooknum)))
return -EEXIST;
ip_nat_setup_info(ct, &range, hooknum);
DEBUGP("NAT status after setup_info: %lu\n",
ct->status & (IPS_NAT_MASK | IPS_NAT_DONE_MASK));
if (cda[CTA_NAT_DST-1]) {
if (ctnetlink_parse_nat(cda[CTA_NAT_DST-1], ct,
&range) < 0)
return -EINVAL;
if (ip_nat_initialized(ct,
HOOK2MANIP(NF_IP_PRE_ROUTING)))
return -EEXIST;
ip_nat_setup_info(ct, &range, NF_IP_PRE_ROUTING);
}
if (cda[CTA_NAT_SRC-1]) {
if (ctnetlink_parse_nat(cda[CTA_NAT_SRC-1], ct,
&range) < 0)
return -EINVAL;
if (ip_nat_initialized(ct,
HOOK2MANIP(NF_IP_POST_ROUTING)))
return -EEXIST;
ip_nat_setup_info(ct, &range, NF_IP_POST_ROUTING);
}
#endif
}
@ -1106,7 +1113,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
/* implicit 'else' */
/* we only allow nat config for new conntracks */
if (cda[CTA_NAT-1]) {
if (cda[CTA_NAT_SRC-1] || cda[CTA_NAT_DST-1]) {
err = -EINVAL;
goto out_unlock;
}

View file

@ -77,10 +77,10 @@ static inline int gre_key_cmpfn(const struct ip_ct_gre_keymap *km,
}
/* look up the source key for a given tuple */
static u_int32_t gre_keymap_lookup(struct ip_conntrack_tuple *t)
static __be16 gre_keymap_lookup(struct ip_conntrack_tuple *t)
{
struct ip_ct_gre_keymap *km;
u_int32_t key = 0;
__be16 key = 0;
read_lock_bh(&ip_ct_gre_lock);
km = LIST_FIND(&gre_keymap_list, gre_key_cmpfn,
@ -190,7 +190,7 @@ static int gre_pkt_to_tuple(const struct sk_buff *skb,
struct ip_conntrack_tuple *tuple)
{
struct gre_hdr_pptp _pgrehdr, *pgrehdr;
u_int32_t srckey;
__be16 srckey;
struct gre_hdr _grehdr, *grehdr;
/* first only delinearize old RFC1701 GRE header */

View file

@ -224,7 +224,7 @@ icmp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
}
/* See ip_conntrack_proto_tcp.c */
if (hooknum == NF_IP_PRE_ROUTING &&
if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
nf_ip_checksum(skb, hooknum, skb->nh.iph->ihl * 4, 0)) {
if (LOG_INVALID(IPPROTO_ICMP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,

View file

@ -870,7 +870,7 @@ static int tcp_error(struct sk_buff *skb,
* and moreover root might send raw packets.
*/
/* FIXME: Source route IP option packets --RR */
if (hooknum == NF_IP_PRE_ROUTING &&
if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_TCP)) {
if (LOG_INVALID(IPPROTO_TCP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,

View file

@ -120,7 +120,7 @@ static int udp_error(struct sk_buff *skb, enum ip_conntrack_info *ctinfo,
* because the semantic of CHECKSUM_HW is different there
* and moreover root might send raw packets.
* FIXME: Source route IP option packets --RR */
if (hooknum == NF_IP_PRE_ROUTING &&
if (ip_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
nf_ip_checksum(skb, hooknum, iph->ihl * 4, IPPROTO_UDP)) {
if (LOG_INVALID(IPPROTO_UDP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,

View file

@ -0,0 +1,471 @@
/* SIP extension for IP connection tracking.
*
* (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
* based on RR's ip_conntrack_ftp.c and other modules.
*
* 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/config.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/skbuff.h>
#include <linux/in.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_sip.h>
#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Hentschel <[email protected]>");
MODULE_DESCRIPTION("SIP connection tracking helper");
#define MAX_PORTS 8
static unsigned short ports[MAX_PORTS];
static int ports_c;
module_param_array(ports, ushort, &ports_c, 0400);
MODULE_PARM_DESC(ports, "port numbers of sip servers");
static unsigned int sip_timeout = SIP_TIMEOUT;
module_param(sip_timeout, uint, 0600);
MODULE_PARM_DESC(sip_timeout, "timeout for the master SIP session");
unsigned int (*ip_nat_sip_hook)(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack *ct,
const char **dptr);
EXPORT_SYMBOL_GPL(ip_nat_sip_hook);
unsigned int (*ip_nat_sdp_hook)(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *exp,
const char *dptr);
EXPORT_SYMBOL_GPL(ip_nat_sdp_hook);
int ct_sip_get_info(const char *dptr, size_t dlen,
unsigned int *matchoff,
unsigned int *matchlen,
struct sip_header_nfo *hnfo);
EXPORT_SYMBOL_GPL(ct_sip_get_info);
static int digits_len(const char *dptr, const char *limit, int *shift);
static int epaddr_len(const char *dptr, const char *limit, int *shift);
static int skp_digits_len(const char *dptr, const char *limit, int *shift);
static int skp_epaddr_len(const char *dptr, const char *limit, int *shift);
struct sip_header_nfo ct_sip_hdrs[] = {
{ /* Via header */
.lname = "Via:",
.lnlen = sizeof("Via:") - 1,
.sname = "\r\nv:",
.snlen = sizeof("\r\nv:") - 1, /* rfc3261 "\r\n" */
.ln_str = "UDP ",
.ln_strlen = sizeof("UDP ") - 1,
.match_len = epaddr_len,
},
{ /* Contact header */
.lname = "Contact:",
.lnlen = sizeof("Contact:") - 1,
.sname = "\r\nm:",
.snlen = sizeof("\r\nm:") - 1,
.ln_str = "sip:",
.ln_strlen = sizeof("sip:") - 1,
.match_len = skp_epaddr_len
},
{ /* Content length header */
.lname = "Content-Length:",
.lnlen = sizeof("Content-Length:") - 1,
.sname = "\r\nl:",
.snlen = sizeof("\r\nl:") - 1,
.ln_str = ":",
.ln_strlen = sizeof(":") - 1,
.match_len = skp_digits_len
},
{ /* SDP media info */
.lname = "\nm=",
.lnlen = sizeof("\nm=") - 1,
.sname = "\rm=",
.snlen = sizeof("\rm=") - 1,
.ln_str = "audio ",
.ln_strlen = sizeof("audio ") - 1,
.match_len = digits_len
},
{ /* SDP owner address*/
.lname = "\no=",
.lnlen = sizeof("\no=") - 1,
.sname = "\ro=",
.snlen = sizeof("\ro=") - 1,
.ln_str = "IN IP4 ",
.ln_strlen = sizeof("IN IP4 ") - 1,
.match_len = epaddr_len
},
{ /* SDP connection info */
.lname = "\nc=",
.lnlen = sizeof("\nc=") - 1,
.sname = "\rc=",
.snlen = sizeof("\rc=") - 1,
.ln_str = "IN IP4 ",
.ln_strlen = sizeof("IN IP4 ") - 1,
.match_len = epaddr_len
},
{ /* Requests headers */
.lname = "sip:",
.lnlen = sizeof("sip:") - 1,
.sname = "sip:",
.snlen = sizeof("sip:") - 1, /* yes, i know.. ;) */
.ln_str = "@",
.ln_strlen = sizeof("@") - 1,
.match_len = epaddr_len
},
{ /* SDP version header */
.lname = "\nv=",
.lnlen = sizeof("\nv=") - 1,
.sname = "\rv=",
.snlen = sizeof("\rv=") - 1,
.ln_str = "=",
.ln_strlen = sizeof("=") - 1,
.match_len = digits_len
}
};
EXPORT_SYMBOL_GPL(ct_sip_hdrs);
/* get line lenght until first CR or LF seen. */
int ct_sip_lnlen(const char *line, const char *limit)
{
const char *k = line;
while ((line <= limit) && (*line == '\r' || *line == '\n'))
line++;
while (line <= limit) {
if (*line == '\r' || *line == '\n')
break;
line++;
}
return line - k;
}
EXPORT_SYMBOL_GPL(ct_sip_lnlen);
/* Linear string search, case sensitive. */
const char *ct_sip_search(const char *needle, const char *haystack,
size_t needle_len, size_t haystack_len)
{
const char *limit = haystack + (haystack_len - needle_len);
while (haystack <= limit) {
if (memcmp(haystack, needle, needle_len) == 0)
return haystack;
haystack++;
}
return NULL;
}
EXPORT_SYMBOL_GPL(ct_sip_search);
static int digits_len(const char *dptr, const char *limit, int *shift)
{
int len = 0;
while (dptr <= limit && isdigit(*dptr)) {
dptr++;
len++;
}
return len;
}
/* get digits lenght, skiping blank spaces. */
static int skp_digits_len(const char *dptr, const char *limit, int *shift)
{
for (; dptr <= limit && *dptr == ' '; dptr++)
(*shift)++;
return digits_len(dptr, limit, shift);
}
/* Simple ipaddr parser.. */
static int parse_ipaddr(const char *cp, const char **endp,
u_int32_t *ipaddr, const char *limit)
{
unsigned long int val;
int i, digit = 0;
for (i = 0, *ipaddr = 0; cp <= limit && i < 4; i++) {
digit = 0;
if (!isdigit(*cp))
break;
val = simple_strtoul(cp, (char **)&cp, 10);
if (val > 0xFF)
return -1;
((u_int8_t *)ipaddr)[i] = val;
digit = 1;
if (*cp != '.')
break;
cp++;
}
if (!digit)
return -1;
if (endp)
*endp = cp;
return 0;
}
/* skip ip address. returns it lenght. */
static int epaddr_len(const char *dptr, const char *limit, int *shift)
{
const char *aux = dptr;
u_int32_t ip;
if (parse_ipaddr(dptr, &dptr, &ip, limit) < 0) {
DEBUGP("ip: %s parse failed.!\n", dptr);
return 0;
}
/* Port number */
if (*dptr == ':') {
dptr++;
dptr += digits_len(dptr, limit, shift);
}
return dptr - aux;
}
/* get address length, skiping user info. */
static int skp_epaddr_len(const char *dptr, const char *limit, int *shift)
{
int s = *shift;
for (; dptr <= limit && *dptr != '@'; dptr++)
(*shift)++;
if (*dptr == '@') {
dptr++;
(*shift)++;
} else
*shift = s;
return epaddr_len(dptr, limit, shift);
}
/* Returns 0 if not found, -1 error parsing. */
int ct_sip_get_info(const char *dptr, size_t dlen,
unsigned int *matchoff,
unsigned int *matchlen,
struct sip_header_nfo *hnfo)
{
const char *limit, *aux, *k = dptr;
int shift = 0;
limit = dptr + (dlen - hnfo->lnlen);
while (dptr <= limit) {
if ((strncmp(dptr, hnfo->lname, hnfo->lnlen) != 0) &&
(strncmp(dptr, hnfo->sname, hnfo->snlen) != 0)) {
dptr++;
continue;
}
aux = ct_sip_search(hnfo->ln_str, dptr, hnfo->ln_strlen,
ct_sip_lnlen(dptr, limit));
if (!aux) {
DEBUGP("'%s' not found in '%s'.\n", hnfo->ln_str,
hnfo->lname);
return -1;
}
aux += hnfo->ln_strlen;
*matchlen = hnfo->match_len(aux, limit, &shift);
if (!*matchlen)
return -1;
*matchoff = (aux - k) + shift;
DEBUGP("%s match succeeded! - len: %u\n", hnfo->lname,
*matchlen);
return 1;
}
DEBUGP("%s header not found.\n", hnfo->lname);
return 0;
}
static int set_expected_rtp(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
u_int32_t ipaddr, u_int16_t port,
const char *dptr)
{
struct ip_conntrack_expect *exp;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
int ret;
exp = ip_conntrack_expect_alloc(ct);
if (exp == NULL)
return NF_DROP;
exp->tuple.src.ip = ct->tuplehash[!dir].tuple.src.ip;
exp->tuple.src.u.udp.port = 0;
exp->tuple.dst.ip = ipaddr;
exp->tuple.dst.u.udp.port = htons(port);
exp->tuple.dst.protonum = IPPROTO_UDP;
exp->mask.src.ip = 0xFFFFFFFF;
exp->mask.src.u.udp.port = 0;
exp->mask.dst.ip = 0xFFFFFFFF;
exp->mask.dst.u.udp.port = 0xFFFF;
exp->mask.dst.protonum = 0xFF;
exp->expectfn = NULL;
exp->flags = 0;
if (ip_nat_sdp_hook)
ret = ip_nat_sdp_hook(pskb, ctinfo, exp, dptr);
else {
if (ip_conntrack_expect_related(exp) != 0)
ret = NF_DROP;
else
ret = NF_ACCEPT;
}
ip_conntrack_expect_put(exp);
return ret;
}
static int sip_help(struct sk_buff **pskb,
struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo)
{
unsigned int dataoff, datalen;
const char *dptr;
int ret = NF_ACCEPT;
int matchoff, matchlen;
u_int32_t ipaddr;
u_int16_t port;
/* No Data ? */
dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
if (dataoff >= (*pskb)->len) {
DEBUGP("skb->len = %u\n", (*pskb)->len);
return NF_ACCEPT;
}
ip_ct_refresh(ct, *pskb, sip_timeout * HZ);
if (!skb_is_nonlinear(*pskb))
dptr = (*pskb)->data + dataoff;
else {
DEBUGP("Copy of skbuff not supported yet.\n");
goto out;
}
if (ip_nat_sip_hook) {
if (!ip_nat_sip_hook(pskb, ctinfo, ct, &dptr)) {
ret = NF_DROP;
goto out;
}
}
/* After this point NAT, could have mangled skb, so
we need to recalculate payload lenght. */
datalen = (*pskb)->len - dataoff;
if (datalen < (sizeof("SIP/2.0 200") - 1))
goto out;
/* RTP info only in some SDP pkts */
if (memcmp(dptr, "INVITE", sizeof("INVITE") - 1) != 0 &&
memcmp(dptr, "SIP/2.0 200", sizeof("SIP/2.0 200") - 1) != 0) {
goto out;
}
/* Get ip and port address from SDP packet. */
if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
&ct_sip_hdrs[POS_CONNECTION]) > 0) {
/* We'll drop only if there are parse problems. */
if (parse_ipaddr(dptr + matchoff, NULL, &ipaddr,
dptr + datalen) < 0) {
ret = NF_DROP;
goto out;
}
if (ct_sip_get_info(dptr, datalen, &matchoff, &matchlen,
&ct_sip_hdrs[POS_MEDIA]) > 0) {
port = simple_strtoul(dptr + matchoff, NULL, 10);
if (port < 1024) {
ret = NF_DROP;
goto out;
}
ret = set_expected_rtp(pskb, ct, ctinfo,
ipaddr, port, dptr);
}
}
out:
return ret;
}
static struct ip_conntrack_helper sip[MAX_PORTS];
static char sip_names[MAX_PORTS][10];
static void fini(void)
{
int i;
for (i = 0; i < ports_c; i++) {
DEBUGP("unregistering helper for port %d\n", ports[i]);
ip_conntrack_helper_unregister(&sip[i]);
}
}
static int __init init(void)
{
int i, ret;
char *tmpname;
if (ports_c == 0)
ports[ports_c++] = SIP_PORT;
for (i = 0; i < ports_c; i++) {
/* Create helper structure */
memset(&sip[i], 0, sizeof(struct ip_conntrack_helper));
sip[i].tuple.dst.protonum = IPPROTO_UDP;
sip[i].tuple.src.u.udp.port = htons(ports[i]);
sip[i].mask.src.u.udp.port = 0xFFFF;
sip[i].mask.dst.protonum = 0xFF;
sip[i].max_expected = 1;
sip[i].timeout = 3 * 60; /* 3 minutes */
sip[i].me = THIS_MODULE;
sip[i].help = sip_help;
tmpname = &sip_names[i][0];
if (ports[i] == SIP_PORT)
sprintf(tmpname, "sip");
else
sprintf(tmpname, "sip-%d", i);
sip[i].name = tmpname;
DEBUGP("port #%d: %d\n", i, ports[i]);
ret = ip_conntrack_helper_register(&sip[i]);
if (ret) {
printk("ERROR registering helper for port %d\n",
ports[i]);
fini();
return ret;
}
}
return 0;
}
module_init(init);
module_exit(fini);

View file

@ -189,6 +189,11 @@ static int ct_seq_show(struct seq_file *s, void *v)
return -ENOSPC;
#endif
#ifdef CONFIG_IP_NF_CONNTRACK_SECMARK
if (seq_printf(s, "secmark=%u ", conntrack->secmark))
return -ENOSPC;
#endif
if (seq_printf(s, "use=%u\n", atomic_read(&conntrack->ct_general.use)))
return -ENOSPC;
@ -417,7 +422,7 @@ static unsigned int ip_conntrack_help(unsigned int hooknum,
/* This is where we call the helper: as the packet goes out. */
ct = ip_conntrack_get(*pskb, &ctinfo);
if (ct && ct->helper) {
if (ct && ct->helper && ctinfo != IP_CT_RELATED + IP_CT_IS_REPLY) {
unsigned int ret;
ret = ct->helper->help(pskb, ct, ctinfo);
if (ret != NF_ACCEPT)
@ -564,6 +569,8 @@ extern unsigned int ip_ct_generic_timeout;
static int log_invalid_proto_min = 0;
static int log_invalid_proto_max = 255;
int ip_conntrack_checksum = 1;
static struct ctl_table_header *ip_ct_sysctl_header;
static ctl_table ip_ct_sysctl_table[] = {
@ -591,6 +598,14 @@ static ctl_table ip_ct_sysctl_table[] = {
.mode = 0444,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_CHECKSUM,
.procname = "ip_conntrack_checksum",
.data = &ip_conntrack_checksum,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = NET_IPV4_NF_CONNTRACK_TCP_TIMEOUT_SYN_SENT,
.procname = "ip_conntrack_tcp_timeout_syn_sent",
@ -946,6 +961,7 @@ EXPORT_SYMBOL_GPL(__ip_conntrack_helper_find_byname);
EXPORT_SYMBOL_GPL(ip_conntrack_proto_find_get);
EXPORT_SYMBOL_GPL(ip_conntrack_proto_put);
EXPORT_SYMBOL_GPL(__ip_conntrack_proto_find);
EXPORT_SYMBOL_GPL(ip_conntrack_checksum);
#if defined(CONFIG_IP_NF_CONNTRACK_NETLINK) || \
defined(CONFIG_IP_NF_CONNTRACK_NETLINK_MODULE)
EXPORT_SYMBOL_GPL(ip_ct_port_tuple_to_nfattr);

View file

@ -486,6 +486,80 @@ static int nat_q931(struct sk_buff **pskb, struct ip_conntrack *ct,
return 0;
}
/****************************************************************************/
static void ip_nat_callforwarding_expect(struct ip_conntrack *new,
struct ip_conntrack_expect *this)
{
struct ip_nat_range range;
/* This must be a fresh one. */
BUG_ON(new->status & IPS_NAT_DONE_MASK);
/* Change src to where master sends to */
range.flags = IP_NAT_RANGE_MAP_IPS;
range.min_ip = range.max_ip = new->tuplehash[!this->dir].tuple.src.ip;
/* hook doesn't matter, but it has to do source manip */
ip_nat_setup_info(new, &range, NF_IP_POST_ROUTING);
/* For DST manip, map port here to where it's expected. */
range.flags = (IP_NAT_RANGE_MAP_IPS | IP_NAT_RANGE_PROTO_SPECIFIED);
range.min = range.max = this->saved_proto;
range.min_ip = range.max_ip = this->saved_ip;
/* hook doesn't matter, but it has to do destination manip */
ip_nat_setup_info(new, &range, NF_IP_PRE_ROUTING);
ip_conntrack_q931_expect(new, this);
}
/****************************************************************************/
static int nat_callforwarding(struct sk_buff **pskb, struct ip_conntrack *ct,
enum ip_conntrack_info ctinfo,
unsigned char **data, int dataoff,
TransportAddress * addr, u_int16_t port,
struct ip_conntrack_expect *exp)
{
int dir = CTINFO2DIR(ctinfo);
u_int16_t nated_port;
/* Set expectations for NAT */
exp->saved_ip = exp->tuple.dst.ip;
exp->tuple.dst.ip = ct->tuplehash[!dir].tuple.dst.ip;
exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
exp->expectfn = ip_nat_callforwarding_expect;
exp->dir = !dir;
/* Try to get same port: if not, try to change it. */
for (nated_port = port; nated_port != 0; nated_port++) {
exp->tuple.dst.u.tcp.port = htons(nated_port);
if (ip_conntrack_expect_related(exp) == 0)
break;
}
if (nated_port == 0) { /* No port available */
if (net_ratelimit())
printk("ip_nat_q931: out of TCP ports\n");
return 0;
}
/* Modify signal */
if (!set_h225_addr(pskb, data, dataoff, addr,
ct->tuplehash[!dir].tuple.dst.ip,
nated_port) == 0) {
ip_conntrack_unexpect_related(exp);
return -1;
}
/* Success */
DEBUGP("ip_nat_q931: expect Call Forwarding "
"%u.%u.%u.%u:%hu->%u.%u.%u.%u:%hu\n",
NIPQUAD(exp->tuple.src.ip), ntohs(exp->tuple.src.u.tcp.port),
NIPQUAD(exp->tuple.dst.ip), ntohs(exp->tuple.dst.u.tcp.port));
return 0;
}
/****************************************************************************/
static int __init init(void)
{
@ -496,6 +570,7 @@ static int __init init(void)
BUG_ON(nat_rtp_rtcp_hook != NULL);
BUG_ON(nat_t120_hook != NULL);
BUG_ON(nat_h245_hook != NULL);
BUG_ON(nat_callforwarding_hook != NULL);
BUG_ON(nat_q931_hook != NULL);
set_h245_addr_hook = set_h245_addr;
@ -505,6 +580,7 @@ static int __init init(void)
nat_rtp_rtcp_hook = nat_rtp_rtcp;
nat_t120_hook = nat_t120;
nat_h245_hook = nat_h245;
nat_callforwarding_hook = nat_callforwarding;
nat_q931_hook = nat_q931;
DEBUGP("ip_nat_h323: init success\n");
@ -521,6 +597,7 @@ static void __exit fini(void)
nat_rtp_rtcp_hook = NULL;
nat_t120_hook = NULL;
nat_h245_hook = NULL;
nat_callforwarding_hook = NULL;
nat_q931_hook = NULL;
synchronize_net();
}

View file

@ -0,0 +1,249 @@
/* SIP extension for UDP NAT alteration.
*
* (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
* based on RR's ip_nat_ftp.c and other modules.
*
* 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/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv4/ip_nat.h>
#include <linux/netfilter_ipv4/ip_nat_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_helper.h>
#include <linux/netfilter_ipv4/ip_conntrack_sip.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Hentschel <[email protected]>");
MODULE_DESCRIPTION("SIP NAT helper");
#if 0
#define DEBUGP printk
#else
#define DEBUGP(format, args...)
#endif
extern struct sip_header_nfo ct_sip_hdrs[];
static unsigned int mangle_sip_packet(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack *ct,
const char **dptr, size_t dlen,
char *buffer, int bufflen,
struct sip_header_nfo *hnfo)
{
unsigned int matchlen, matchoff;
if (ct_sip_get_info(*dptr, dlen, &matchoff, &matchlen, hnfo) <= 0)
return 0;
if (!ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
matchoff, matchlen, buffer, bufflen))
return 0;
/* We need to reload this. Thanks Patrick. */
*dptr = (*pskb)->data + (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
return 1;
}
static unsigned int ip_nat_sip(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack *ct,
const char **dptr)
{
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
unsigned int bufflen, dataoff;
u_int32_t ip;
u_int16_t port;
dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
ip = ct->tuplehash[!dir].tuple.dst.ip;
port = ct->tuplehash[!dir].tuple.dst.u.udp.port;
bufflen = sprintf(buffer, "%u.%u.%u.%u:%u", NIPQUAD(ip), ntohs(port));
/* short packet ? */
if (((*pskb)->len - dataoff) < (sizeof("SIP/2.0") - 1))
return 0;
/* Basic rules: requests and responses. */
if (memcmp(*dptr, "SIP/2.0", sizeof("SIP/2.0") - 1) == 0) {
const char *aux;
if ((ctinfo) < IP_CT_IS_REPLY) {
mangle_sip_packet(pskb, ctinfo, ct, dptr,
(*pskb)->len - dataoff,
buffer, bufflen,
&ct_sip_hdrs[POS_CONTACT]);
return 1;
}
if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
(*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_VIA]))
return 0;
/* This search should ignore case, but later.. */
aux = ct_sip_search("CSeq:", *dptr, sizeof("CSeq:") - 1,
(*pskb)->len - dataoff);
if (!aux)
return 0;
if (!ct_sip_search("REGISTER", aux, sizeof("REGISTER"),
ct_sip_lnlen(aux, *dptr + (*pskb)->len - dataoff)))
return 1;
return mangle_sip_packet(pskb, ctinfo, ct, dptr,
(*pskb)->len - dataoff,
buffer, bufflen,
&ct_sip_hdrs[POS_CONTACT]);
}
if ((ctinfo) < IP_CT_IS_REPLY) {
if (!mangle_sip_packet(pskb, ctinfo, ct, dptr,
(*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_VIA]))
return 0;
/* Mangle Contact if exists only. - watch udp_nat_mangle()! */
mangle_sip_packet(pskb, ctinfo, ct, dptr, (*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_CONTACT]);
return 1;
}
/* This mangle requests headers. */
return mangle_sip_packet(pskb, ctinfo, ct, dptr,
ct_sip_lnlen(*dptr,
*dptr + (*pskb)->len - dataoff),
buffer, bufflen, &ct_sip_hdrs[POS_REQ_HEADER]);
}
static int mangle_content_len(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack *ct,
const char *dptr)
{
unsigned int dataoff, matchoff, matchlen;
char buffer[sizeof("65536")];
int bufflen;
dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
/* Get actual SDP lenght */
if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
&matchlen, &ct_sip_hdrs[POS_SDP_HEADER]) > 0) {
/* since ct_sip_get_info() give us a pointer passing 'v='
we need to add 2 bytes in this count. */
int c_len = (*pskb)->len - dataoff - matchoff + 2;
/* Now, update SDP lenght */
if (ct_sip_get_info(dptr, (*pskb)->len - dataoff, &matchoff,
&matchlen, &ct_sip_hdrs[POS_CONTENT]) > 0) {
bufflen = sprintf(buffer, "%u", c_len);
return ip_nat_mangle_udp_packet(pskb, ct, ctinfo,
matchoff, matchlen,
buffer, bufflen);
}
}
return 0;
}
static unsigned int mangle_sdp(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack *ct,
u_int32_t newip, u_int16_t port,
const char *dptr)
{
char buffer[sizeof("nnn.nnn.nnn.nnn")];
unsigned int dataoff, bufflen;
dataoff = (*pskb)->nh.iph->ihl*4 + sizeof(struct udphdr);
/* Mangle owner and contact info. */
bufflen = sprintf(buffer, "%u.%u.%u.%u", NIPQUAD(newip));
if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_OWNER]))
return 0;
if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_CONNECTION]))
return 0;
/* Mangle media port. */
bufflen = sprintf(buffer, "%u", port);
if (!mangle_sip_packet(pskb, ctinfo, ct, &dptr, (*pskb)->len - dataoff,
buffer, bufflen, &ct_sip_hdrs[POS_MEDIA]))
return 0;
return mangle_content_len(pskb, ctinfo, ct, dptr);
}
/* So, this packet has hit the connection tracking matching code.
Mangle it, and change the expectation to match the new version. */
static unsigned int ip_nat_sdp(struct sk_buff **pskb,
enum ip_conntrack_info ctinfo,
struct ip_conntrack_expect *exp,
const char *dptr)
{
struct ip_conntrack *ct = exp->master;
enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
u_int32_t newip;
u_int16_t port;
DEBUGP("ip_nat_sdp():\n");
/* Connection will come from reply */
newip = ct->tuplehash[!dir].tuple.dst.ip;
exp->tuple.dst.ip = newip;
exp->saved_proto.udp.port = exp->tuple.dst.u.udp.port;
exp->dir = !dir;
/* When you see the packet, we need to NAT it the same as the
this one. */
exp->expectfn = ip_nat_follow_master;
/* Try to get same port: if not, try to change it. */
for (port = ntohs(exp->saved_proto.udp.port); port != 0; port++) {
exp->tuple.dst.u.udp.port = htons(port);
if (ip_conntrack_expect_related(exp) == 0)
break;
}
if (port == 0)
return NF_DROP;
if (!mangle_sdp(pskb, ctinfo, ct, newip, port, dptr)) {
ip_conntrack_unexpect_related(exp);
return NF_DROP;
}
return NF_ACCEPT;
}
static void __exit fini(void)
{
ip_nat_sip_hook = NULL;
ip_nat_sdp_hook = NULL;
/* Make sure noone calls it, meanwhile. */
synchronize_net();
}
static int __init init(void)
{
BUG_ON(ip_nat_sip_hook);
BUG_ON(ip_nat_sdp_hook);
ip_nat_sip_hook = ip_nat_sip;
ip_nat_sdp_hook = ip_nat_sdp;
return 0;
}
module_init(init);
module_exit(fini);

View file

@ -1348,4 +1348,4 @@ static void __exit ip_nat_snmp_basic_fini(void)
module_init(ip_nat_snmp_basic_init);
module_exit(ip_nat_snmp_basic_fini);
module_param(debug, bool, 0600);
module_param(debug, int, 0600);

View file

@ -241,25 +241,17 @@ clusterip_hashfn(struct sk_buff *skb, struct clusterip_config *config)
struct iphdr *iph = skb->nh.iph;
unsigned long hashval;
u_int16_t sport, dport;
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *ih;
u_int16_t *ports;
switch (iph->protocol) {
case IPPROTO_TCP:
th = (void *)iph+iph->ihl*4;
sport = ntohs(th->source);
dport = ntohs(th->dest);
break;
case IPPROTO_UDP:
uh = (void *)iph+iph->ihl*4;
sport = ntohs(uh->source);
dport = ntohs(uh->dest);
break;
case IPPROTO_SCTP:
case IPPROTO_DCCP:
case IPPROTO_ICMP:
ih = (void *)iph+iph->ihl*4;
sport = ntohs(ih->un.echo.id);
dport = (ih->type<<8)|ih->code;
ports = (void *)iph+iph->ihl*4;
sport = ports[0];
dport = ports[1];
break;
default:
if (net_ratelimit()) {

View file

@ -147,6 +147,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
/* This packet will not be the same as the other: clear nf fields */
nf_reset(nskb);
nskb->nfmark = 0;
skb_init_secmark(nskb);
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);

View file

@ -28,9 +28,6 @@
#include <linux/jhash.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/sctp.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/list.h>
@ -83,6 +80,7 @@ struct ipt_hashlimit_htable {
/* used internally */
spinlock_t lock; /* lock for list_head */
u_int32_t rnd; /* random seed for hash */
int rnd_initialized;
struct timer_list timer; /* timer for gc */
atomic_t count; /* number entries in table */
@ -137,8 +135,10 @@ __dsthash_alloc_init(struct ipt_hashlimit_htable *ht, struct dsthash_dst *dst)
/* initialize hash with random val at the time we allocate
* the first hashtable entry */
if (!ht->rnd)
if (!ht->rnd_initialized) {
get_random_bytes(&ht->rnd, 4);
ht->rnd_initialized = 1;
}
if (ht->cfg.max &&
atomic_read(&ht->count) >= ht->cfg.max) {
@ -217,7 +217,7 @@ static int htable_create(struct ipt_hashlimit_info *minfo)
atomic_set(&hinfo->count, 0);
atomic_set(&hinfo->use, 1);
hinfo->rnd = 0;
hinfo->rnd_initialized = 0;
spin_lock_init(&hinfo->lock);
hinfo->pde = create_proc_entry(minfo->name, 0, hashlimit_procdir);
if (!hinfo->pde) {
@ -381,49 +381,6 @@ static inline void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now)
dh->rateinfo.credit = dh->rateinfo.credit_cap;
}
static inline int get_ports(const struct sk_buff *skb, int offset,
u16 ports[2])
{
union {
struct tcphdr th;
struct udphdr uh;
sctp_sctphdr_t sctph;
} hdr_u, *ptr_u;
/* Must not be a fragment. */
if (offset)
return 1;
/* Must be big enough to read ports (both UDP and TCP have
them at the start). */
ptr_u = skb_header_pointer(skb, skb->nh.iph->ihl*4, 8, &hdr_u);
if (!ptr_u)
return 1;
switch (skb->nh.iph->protocol) {
case IPPROTO_TCP:
ports[0] = ptr_u->th.source;
ports[1] = ptr_u->th.dest;
break;
case IPPROTO_UDP:
ports[0] = ptr_u->uh.source;
ports[1] = ptr_u->uh.dest;
break;
case IPPROTO_SCTP:
ports[0] = ptr_u->sctph.source;
ports[1] = ptr_u->sctph.dest;
break;
default:
/* all other protocols don't supprot per-port hash
* buckets */
ports[0] = ports[1] = 0;
break;
}
return 0;
}
static int
hashlimit_match(const struct sk_buff *skb,
const struct net_device *in,
@ -449,8 +406,22 @@ hashlimit_match(const struct sk_buff *skb,
dst.src_ip = skb->nh.iph->saddr;
if (hinfo->cfg.mode & IPT_HASHLIMIT_HASH_DPT
||hinfo->cfg.mode & IPT_HASHLIMIT_HASH_SPT) {
u_int16_t ports[2];
if (get_ports(skb, offset, ports)) {
u_int16_t _ports[2], *ports;
switch (skb->nh.iph->protocol) {
case IPPROTO_TCP:
case IPPROTO_UDP:
case IPPROTO_SCTP:
case IPPROTO_DCCP:
ports = skb_header_pointer(skb, skb->nh.iph->ihl*4,
sizeof(_ports), &_ports);
break;
default:
_ports[0] = _ports[1] = 0;
ports = _ports;
break;
}
if (!ports) {
/* We've been asked to examine this packet, and we
can't. Hence, no choice but to drop. */
*hotdrop = 1;
@ -561,7 +532,7 @@ static void
hashlimit_destroy(const struct xt_match *match, void *matchinfo,
unsigned int matchsize)
{
struct ipt_hashlimit_info *r = (struct ipt_hashlimit_info *) matchinfo;
struct ipt_hashlimit_info *r = matchinfo;
htable_put(r->hinfo);
}

File diff suppressed because it is too large Load diff

View file

@ -145,7 +145,7 @@ static unsigned int ipv4_conntrack_help(unsigned int hooknum,
/* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(*pskb, &ctinfo);
if (!ct)
if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)
return NF_ACCEPT;
help = nfct_help(ct);

View file

@ -235,7 +235,7 @@ icmp_error(struct sk_buff *skb, unsigned int dataoff,
}
/* See ip_conntrack_proto_tcp.c */
if (hooknum == NF_IP_PRE_ROUTING &&
if (nf_conntrack_checksum && hooknum == NF_IP_PRE_ROUTING &&
nf_ip_checksum(skb, hooknum, dataoff, 0)) {
if (LOG_INVALID(IPPROTO_ICMP))
nf_log_packet(PF_INET, 0, skb, NULL, NULL, NULL,

View file

@ -103,7 +103,7 @@ static void raw_v4_unhash(struct sock *sk)
}
struct sock *__raw_v4_lookup(struct sock *sk, unsigned short num,
unsigned long raddr, unsigned long laddr,
__be32 raddr, __be32 laddr,
int dif)
{
struct hlist_node *node;

View file

@ -181,14 +181,6 @@ ctl_table ipv4_table[] = {
.proc_handler = &ipv4_doint_and_flush,
.strategy = &ipv4_doint_and_flush_strategy,
},
{
.ctl_name = NET_IPV4_AUTOCONFIG,
.procname = "ip_autoconfig",
.data = &ipv4_config.autoconfig,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_NO_PMTU_DISC,
.procname = "ip_no_pmtu_disc",
@ -688,6 +680,24 @@ ctl_table ipv4_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec
},
#ifdef CONFIG_NET_DMA
{
.ctl_name = NET_TCP_DMA_COPYBREAK,
.procname = "tcp_dma_copybreak",
.data = &sysctl_tcp_dma_copybreak,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{
.ctl_name = NET_TCP_SLOW_START_AFTER_IDLE,
.procname = "tcp_slow_start_after_idle",
.data = &sysctl_tcp_slow_start_after_idle,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 }
};

View file

@ -263,7 +263,7 @@
#include <net/tcp.h>
#include <net/xfrm.h>
#include <net/ip.h>
#include <net/netdma.h>
#include <asm/uaccess.h>
#include <asm/ioctls.h>
@ -622,14 +622,10 @@ ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset,
ssize_t res;
struct sock *sk = sock->sk;
#define TCP_ZC_CSUM_FLAGS (NETIF_F_IP_CSUM | NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
if (!(sk->sk_route_caps & NETIF_F_SG) ||
!(sk->sk_route_caps & TCP_ZC_CSUM_FLAGS))
!(sk->sk_route_caps & NETIF_F_ALL_CSUM))
return sock_no_sendpage(sock, page, offset, size, flags);
#undef TCP_ZC_CSUM_FLAGS
lock_sock(sk);
TCP_CHECK_TIMER(sk);
res = do_tcp_sendpages(sk, &page, offset, size, flags);
@ -726,9 +722,7 @@ new_segment:
/*
* Check whether we can use HW checksum.
*/
if (sk->sk_route_caps &
(NETIF_F_IP_CSUM | NETIF_F_NO_CSUM |
NETIF_F_HW_CSUM))
if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
skb->ip_summed = CHECKSUM_HW;
skb_entail(sk, tp, skb);
@ -937,7 +931,7 @@ static int tcp_recv_urg(struct sock *sk, long timeo,
* calculation of whether or not we must ACK for the sake of
* a window update.
*/
static void cleanup_rbuf(struct sock *sk, int copied)
void tcp_cleanup_rbuf(struct sock *sk, int copied)
{
struct tcp_sock *tp = tcp_sk(sk);
int time_to_ack = 0;
@ -1072,11 +1066,11 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
break;
}
if (skb->h.th->fin) {
sk_eat_skb(sk, skb);
sk_eat_skb(sk, skb, 0);
++seq;
break;
}
sk_eat_skb(sk, skb);
sk_eat_skb(sk, skb, 0);
if (!desc->count)
break;
}
@ -1086,7 +1080,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
/* Clean up data we have read: This will do ACK frames. */
if (copied)
cleanup_rbuf(sk, copied);
tcp_cleanup_rbuf(sk, copied);
return copied;
}
@ -1110,6 +1104,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
int target; /* Read at least this many bytes */
long timeo;
struct task_struct *user_recv = NULL;
int copied_early = 0;
lock_sock(sk);
@ -1133,6 +1128,17 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
target = sock_rcvlowat(sk, flags & MSG_WAITALL, len);
#ifdef CONFIG_NET_DMA
tp->ucopy.dma_chan = NULL;
preempt_disable();
if ((len > sysctl_tcp_dma_copybreak) && !(flags & MSG_PEEK) &&
!sysctl_tcp_low_latency && __get_cpu_var(softnet_data.net_dma)) {
preempt_enable_no_resched();
tp->ucopy.pinned_list = dma_pin_iovec_pages(msg->msg_iov, len);
} else
preempt_enable_no_resched();
#endif
do {
struct sk_buff *skb;
u32 offset;
@ -1220,7 +1226,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
}
cleanup_rbuf(sk, copied);
tcp_cleanup_rbuf(sk, copied);
if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) {
/* Install new reader */
@ -1274,6 +1280,10 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
} else
sk_wait_data(sk, &timeo);
#ifdef CONFIG_NET_DMA
tp->ucopy.wakeup = 0;
#endif
if (user_recv) {
int chunk;
@ -1329,13 +1339,39 @@ do_prequeue:
}
if (!(flags & MSG_TRUNC)) {
err = skb_copy_datagram_iovec(skb, offset,
msg->msg_iov, used);
if (err) {
/* Exception. Bailout! */
if (!copied)
copied = -EFAULT;
break;
#ifdef CONFIG_NET_DMA
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
if (tp->ucopy.dma_chan) {
tp->ucopy.dma_cookie = dma_skb_copy_datagram_iovec(
tp->ucopy.dma_chan, skb, offset,
msg->msg_iov, used,
tp->ucopy.pinned_list);
if (tp->ucopy.dma_cookie < 0) {
printk(KERN_ALERT "dma_cookie < 0\n");
/* Exception. Bailout! */
if (!copied)
copied = -EFAULT;
break;
}
if ((offset + used) == skb->len)
copied_early = 1;
} else
#endif
{
err = skb_copy_datagram_iovec(skb, offset,
msg->msg_iov, used);
if (err) {
/* Exception. Bailout! */
if (!copied)
copied = -EFAULT;
break;
}
}
}
@ -1355,15 +1391,19 @@ skip_copy:
if (skb->h.th->fin)
goto found_fin_ok;
if (!(flags & MSG_PEEK))
sk_eat_skb(sk, skb);
if (!(flags & MSG_PEEK)) {
sk_eat_skb(sk, skb, copied_early);
copied_early = 0;
}
continue;
found_fin_ok:
/* Process the FIN. */
++*seq;
if (!(flags & MSG_PEEK))
sk_eat_skb(sk, skb);
if (!(flags & MSG_PEEK)) {
sk_eat_skb(sk, skb, copied_early);
copied_early = 0;
}
break;
} while (len > 0);
@ -1386,12 +1426,42 @@ skip_copy:
tp->ucopy.len = 0;
}
#ifdef CONFIG_NET_DMA
if (tp->ucopy.dma_chan) {
struct sk_buff *skb;
dma_cookie_t done, used;
dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
while (dma_async_memcpy_complete(tp->ucopy.dma_chan,
tp->ucopy.dma_cookie, &done,
&used) == DMA_IN_PROGRESS) {
/* do partial cleanup of sk_async_wait_queue */
while ((skb = skb_peek(&sk->sk_async_wait_queue)) &&
(dma_async_is_complete(skb->dma_cookie, done,
used) == DMA_SUCCESS)) {
__skb_dequeue(&sk->sk_async_wait_queue);
kfree_skb(skb);
}
}
/* Safe to free early-copied skbs now */
__skb_queue_purge(&sk->sk_async_wait_queue);
dma_chan_put(tp->ucopy.dma_chan);
tp->ucopy.dma_chan = NULL;
}
if (tp->ucopy.pinned_list) {
dma_unpin_iovec_pages(tp->ucopy.pinned_list);
tp->ucopy.pinned_list = NULL;
}
#endif
/* According to UNIX98, msg_name/msg_namelen are ignored
* on connected socket. I was just happy when found this 8) --ANK
*/
/* Clean up data we have read: This will do ACK frames. */
cleanup_rbuf(sk, copied);
tcp_cleanup_rbuf(sk, copied);
TCP_CHECK_TIMER(sk);
release_sock(sk);
@ -1658,6 +1728,9 @@ int tcp_disconnect(struct sock *sk, int flags)
__skb_queue_purge(&sk->sk_receive_queue);
sk_stream_writequeue_purge(sk);
__skb_queue_purge(&tp->out_of_order_queue);
#ifdef CONFIG_NET_DMA
__skb_queue_purge(&sk->sk_async_wait_queue);
#endif
inet->dport = 0;
@ -1858,7 +1931,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level,
(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) &&
inet_csk_ack_scheduled(sk)) {
icsk->icsk_ack.pending |= ICSK_ACK_PUSHED;
cleanup_rbuf(sk, 1);
tcp_cleanup_rbuf(sk, 1);
if (!(val & 1))
icsk->icsk_ack.pingpong = 1;
}

View file

@ -198,12 +198,6 @@ static u32 bictcp_undo_cwnd(struct sock *sk)
return max(tp->snd_cwnd, ca->last_max_cwnd);
}
static u32 bictcp_min_cwnd(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
return tp->snd_ssthresh;
}
static void bictcp_state(struct sock *sk, u8 new_state)
{
if (new_state == TCP_CA_Loss)
@ -231,7 +225,6 @@ static struct tcp_congestion_ops bictcp = {
.cong_avoid = bictcp_cong_avoid,
.set_state = bictcp_state,
.undo_cwnd = bictcp_undo_cwnd,
.min_cwnd = bictcp_min_cwnd,
.pkts_acked = bictcp_acked,
.owner = THIS_MODULE,
.name = "bic",

448
net/ipv4/tcp_compound.c Normal file
View file

@ -0,0 +1,448 @@
/*
* TCP Vegas congestion control
*
* This is based on the congestion detection/avoidance scheme described in
* Lawrence S. Brakmo and Larry L. Peterson.
* "TCP Vegas: End to end congestion avoidance on a global internet."
* IEEE Journal on Selected Areas in Communication, 13(8):1465--1480,
* October 1995. Available from:
* ftp://ftp.cs.arizona.edu/xkernel/Papers/jsac.ps
*
* See http://www.cs.arizona.edu/xkernel/ for their implementation.
* The main aspects that distinguish this implementation from the
* Arizona Vegas implementation are:
* o We do not change the loss detection or recovery mechanisms of
* Linux in any way. Linux already recovers from losses quite well,
* using fine-grained timers, NewReno, and FACK.
* o To avoid the performance penalty imposed by increasing cwnd
* only every-other RTT during slow start, we increase during
* every RTT during slow start, just like Reno.
* o Largely to allow continuous cwnd growth during slow start,
* we use the rate at which ACKs come back as the "actual"
* rate, rather than the rate at which data is sent.
* o To speed convergence to the right rate, we set the cwnd
* to achieve the right ("actual") rate when we exit slow start.
* o To filter out the noise caused by delayed ACKs, we use the
* minimum RTT sample observed during the last RTT to calculate
* the actual rate.
* o When the sender re-starts from idle, it waits until it has
* received ACKs for an entire flight of new data before making
* a cwnd adjustment decision. The original Vegas implementation
* assumed senders never went idle.
*
*
* TCP Compound based on TCP Vegas
*
* further details can be found here:
* ftp://ftp.research.microsoft.com/pub/tr/TR-2005-86.pdf
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/inet_diag.h>
#include <net/tcp.h>
/* Default values of the Vegas variables, in fixed-point representation
* with V_PARAM_SHIFT bits to the right of the binary point.
*/
#define V_PARAM_SHIFT 1
#define TCP_COMPOUND_ALPHA 3U
#define TCP_COMPOUND_BETA 1U
#define TCP_COMPOUND_GAMMA 30
#define TCP_COMPOUND_ZETA 1
/* TCP compound variables */
struct compound {
u32 beg_snd_nxt; /* right edge during last RTT */
u32 beg_snd_una; /* left edge during last RTT */
u32 beg_snd_cwnd; /* saves the size of the cwnd */
u8 doing_vegas_now; /* if true, do vegas for this RTT */
u16 cntRTT; /* # of RTTs measured within last RTT */
u32 minRTT; /* min of RTTs measured within last RTT (in usec) */
u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */
u32 cwnd;
u32 dwnd;
};
/* There are several situations when we must "re-start" Vegas:
*
* o when a connection is established
* o after an RTO
* o after fast recovery
* o when we send a packet and there is no outstanding
* unacknowledged data (restarting an idle connection)
*
* In these circumstances we cannot do a Vegas calculation at the
* end of the first RTT, because any calculation we do is using
* stale info -- both the saved cwnd and congestion feedback are
* stale.
*
* Instead we must wait until the completion of an RTT during
* which we actually receive ACKs.
*/
static inline void vegas_enable(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
struct compound *vegas = inet_csk_ca(sk);
/* Begin taking Vegas samples next time we send something. */
vegas->doing_vegas_now = 1;
/* Set the beginning of the next send window. */
vegas->beg_snd_nxt = tp->snd_nxt;
vegas->cntRTT = 0;
vegas->minRTT = 0x7fffffff;
}
/* Stop taking Vegas samples for now. */
static inline void vegas_disable(struct sock *sk)
{
struct compound *vegas = inet_csk_ca(sk);
vegas->doing_vegas_now = 0;
}
static void tcp_compound_init(struct sock *sk)
{
struct compound *vegas = inet_csk_ca(sk);
const struct tcp_sock *tp = tcp_sk(sk);
vegas->baseRTT = 0x7fffffff;
vegas_enable(sk);
vegas->dwnd = 0;
vegas->cwnd = tp->snd_cwnd;
}
/* Do RTT sampling needed for Vegas.
* Basically we:
* o min-filter RTT samples from within an RTT to get the current
* propagation delay + queuing delay (we are min-filtering to try to
* avoid the effects of delayed ACKs)
* o min-filter RTT samples from a much longer window (forever for now)
* to find the propagation delay (baseRTT)
*/
static void tcp_compound_rtt_calc(struct sock *sk, u32 usrtt)
{
struct compound *vegas = inet_csk_ca(sk);
u32 vrtt = usrtt + 1; /* Never allow zero rtt or baseRTT */
/* Filter to find propagation delay: */
if (vrtt < vegas->baseRTT)
vegas->baseRTT = vrtt;
/* Find the min RTT during the last RTT to find
* the current prop. delay + queuing delay:
*/
vegas->minRTT = min(vegas->minRTT, vrtt);
vegas->cntRTT++;
}
static void tcp_compound_state(struct sock *sk, u8 ca_state)
{
if (ca_state == TCP_CA_Open)
vegas_enable(sk);
else
vegas_disable(sk);
}
/* 64bit divisor, dividend and result. dynamic precision */
static inline u64 div64_64(u64 dividend, u64 divisor)
{
u32 d = divisor;
if (divisor > 0xffffffffULL) {
unsigned int shift = fls(divisor >> 32);
d = divisor >> shift;
dividend >>= shift;
}
/* avoid 64 bit division if possible */
if (dividend >> 32)
do_div(dividend, d);
else
dividend = (u32) dividend / d;
return dividend;
}
/* calculate the quartic root of "a" using Newton-Raphson */
static u32 qroot(u64 a)
{
u32 x, x1;
/* Initial estimate is based on:
* qrt(x) = exp(log(x) / 4)
*/
x = 1u << (fls64(a) >> 2);
/*
* Iteration based on:
* 3
* x = ( 3 * x + a / x ) / 4
* k+1 k k
*/
do {
u64 x3 = x;
x1 = x;
x3 *= x;
x3 *= x;
x = (3 * x + (u32) div64_64(a, x3)) / 4;
} while (abs(x1 - x) > 1);
return x;
}
/*
* If the connection is idle and we are restarting,
* then we don't want to do any Vegas calculations
* until we get fresh RTT samples. So when we
* restart, we reset our Vegas state to a clean
* slate. After we get acks for this flight of
* packets, _then_ we can make Vegas calculations
* again.
*/
static void tcp_compound_cwnd_event(struct sock *sk, enum tcp_ca_event event)
{
if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START)
tcp_compound_init(sk);
}
static void tcp_compound_cong_avoid(struct sock *sk, u32 ack,
u32 seq_rtt, u32 in_flight, int flag)
{
struct tcp_sock *tp = tcp_sk(sk);
struct compound *vegas = inet_csk_ca(sk);
u8 inc = 0;
if (vegas->cwnd + vegas->dwnd > tp->snd_cwnd) {
if (vegas->cwnd > tp->snd_cwnd || vegas->dwnd > tp->snd_cwnd) {
vegas->cwnd = tp->snd_cwnd;
vegas->dwnd = 0;
} else
vegas->cwnd = tp->snd_cwnd - vegas->dwnd;
}
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
if (vegas->cwnd <= tp->snd_ssthresh)
inc = 1;
else if (tp->snd_cwnd_cnt < tp->snd_cwnd)
tp->snd_cwnd_cnt++;
if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
inc = 1;
tp->snd_cwnd_cnt = 0;
}
if (inc && tp->snd_cwnd < tp->snd_cwnd_clamp)
vegas->cwnd++;
/* The key players are v_beg_snd_una and v_beg_snd_nxt.
*
* These are so named because they represent the approximate values
* of snd_una and snd_nxt at the beginning of the current RTT. More
* precisely, they represent the amount of data sent during the RTT.
* At the end of the RTT, when we receive an ACK for v_beg_snd_nxt,
* we will calculate that (v_beg_snd_nxt - v_beg_snd_una) outstanding
* bytes of data have been ACKed during the course of the RTT, giving
* an "actual" rate of:
*
* (v_beg_snd_nxt - v_beg_snd_una) / (rtt duration)
*
* Unfortunately, v_beg_snd_una is not exactly equal to snd_una,
* because delayed ACKs can cover more than one segment, so they
* don't line up nicely with the boundaries of RTTs.
*
* Another unfortunate fact of life is that delayed ACKs delay the
* advance of the left edge of our send window, so that the number
* of bytes we send in an RTT is often less than our cwnd will allow.
* So we keep track of our cwnd separately, in v_beg_snd_cwnd.
*/
if (after(ack, vegas->beg_snd_nxt)) {
/* Do the Vegas once-per-RTT cwnd adjustment. */
u32 old_wnd, old_snd_cwnd;
/* Here old_wnd is essentially the window of data that was
* sent during the previous RTT, and has all
* been acknowledged in the course of the RTT that ended
* with the ACK we just received. Likewise, old_snd_cwnd
* is the cwnd during the previous RTT.
*/
if (!tp->mss_cache)
return;
old_wnd = (vegas->beg_snd_nxt - vegas->beg_snd_una) /
tp->mss_cache;
old_snd_cwnd = vegas->beg_snd_cwnd;
/* Save the extent of the current window so we can use this
* at the end of the next RTT.
*/
vegas->beg_snd_una = vegas->beg_snd_nxt;
vegas->beg_snd_nxt = tp->snd_nxt;
vegas->beg_snd_cwnd = tp->snd_cwnd;
/* We do the Vegas calculations only if we got enough RTT
* samples that we can be reasonably sure that we got
* at least one RTT sample that wasn't from a delayed ACK.
* If we only had 2 samples total,
* then that means we're getting only 1 ACK per RTT, which
* means they're almost certainly delayed ACKs.
* If we have 3 samples, we should be OK.
*/
if (vegas->cntRTT > 2) {
u32 rtt, target_cwnd, diff;
u32 brtt, dwnd;
/* We have enough RTT samples, so, using the Vegas
* algorithm, we determine if we should increase or
* decrease cwnd, and by how much.
*/
/* Pluck out the RTT we are using for the Vegas
* calculations. This is the min RTT seen during the
* last RTT. Taking the min filters out the effects
* of delayed ACKs, at the cost of noticing congestion
* a bit later.
*/
rtt = vegas->minRTT;
/* Calculate the cwnd we should have, if we weren't
* going too fast.
*
* This is:
* (actual rate in segments) * baseRTT
* We keep it as a fixed point number with
* V_PARAM_SHIFT bits to the right of the binary point.
*/
if (!rtt)
return;
brtt = vegas->baseRTT;
target_cwnd = ((old_wnd * brtt)
<< V_PARAM_SHIFT) / rtt;
/* Calculate the difference between the window we had,
* and the window we would like to have. This quantity
* is the "Diff" from the Arizona Vegas papers.
*
* Again, this is a fixed point number with
* V_PARAM_SHIFT bits to the right of the binary
* point.
*/
diff = (old_wnd << V_PARAM_SHIFT) - target_cwnd;
dwnd = vegas->dwnd;
if (diff < (TCP_COMPOUND_GAMMA << V_PARAM_SHIFT)) {
u64 v;
u32 x;
/*
* The TCP Compound paper describes the choice
* of "k" determines the agressiveness,
* ie. slope of the response function.
*
* For same value as HSTCP would be 0.8
* but for computaional reasons, both the
* original authors and this implementation
* use 0.75.
*/
v = old_wnd;
x = qroot(v * v * v) >> TCP_COMPOUND_ALPHA;
if (x > 1)
dwnd = x - 1;
else
dwnd = 0;
dwnd += vegas->dwnd;
} else if ((dwnd << V_PARAM_SHIFT) <
(diff * TCP_COMPOUND_BETA))
dwnd = 0;
else
dwnd =
((dwnd << V_PARAM_SHIFT) -
(diff *
TCP_COMPOUND_BETA)) >> V_PARAM_SHIFT;
vegas->dwnd = dwnd;
}
/* Wipe the slate clean for the next RTT. */
vegas->cntRTT = 0;
vegas->minRTT = 0x7fffffff;
}
tp->snd_cwnd = vegas->cwnd + vegas->dwnd;
}
/* Extract info for Tcp socket info provided via netlink. */
static void tcp_compound_get_info(struct sock *sk, u32 ext, struct sk_buff *skb)
{
const struct compound *ca = inet_csk_ca(sk);
if (ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
struct tcpvegas_info *info;
info = RTA_DATA(__RTA_PUT(skb, INET_DIAG_VEGASINFO,
sizeof(*info)));
info->tcpv_enabled = ca->doing_vegas_now;
info->tcpv_rttcnt = ca->cntRTT;
info->tcpv_rtt = ca->baseRTT;
info->tcpv_minrtt = ca->minRTT;
rtattr_failure:;
}
}
static struct tcp_congestion_ops tcp_compound = {
.init = tcp_compound_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_compound_cong_avoid,
.rtt_sample = tcp_compound_rtt_calc,
.set_state = tcp_compound_state,
.cwnd_event = tcp_compound_cwnd_event,
.get_info = tcp_compound_get_info,
.owner = THIS_MODULE,
.name = "compound",
};
static int __init tcp_compound_register(void)
{
BUG_ON(sizeof(struct compound) > ICSK_CA_PRIV_SIZE);
tcp_register_congestion_control(&tcp_compound);
return 0;
}
static void __exit tcp_compound_unregister(void)
{
tcp_unregister_congestion_control(&tcp_compound);
}
module_init(tcp_compound_register);
module_exit(tcp_compound_unregister);
MODULE_AUTHOR("Angelo P. Castellani, Stephen Hemminger");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TCP Compound");

View file

@ -38,7 +38,7 @@ int tcp_register_congestion_control(struct tcp_congestion_ops *ca)
int ret = 0;
/* all algorithms must implement ssthresh and cong_avoid ops */
if (!ca->ssthresh || !ca->cong_avoid || !ca->min_cwnd) {
if (!ca->ssthresh || !ca->cong_avoid) {
printk(KERN_ERR "TCP %s does not implement required ops\n",
ca->name);
return -EINVAL;
@ -251,8 +251,8 @@ u32 tcp_reno_ssthresh(struct sock *sk)
}
EXPORT_SYMBOL_GPL(tcp_reno_ssthresh);
/* Lower bound on congestion window. */
u32 tcp_reno_min_cwnd(struct sock *sk)
/* Lower bound on congestion window with halving. */
u32 tcp_reno_min_cwnd(const struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
return tp->snd_ssthresh/2;

View file

@ -325,11 +325,6 @@ static u32 bictcp_undo_cwnd(struct sock *sk)
return max(tcp_sk(sk)->snd_cwnd, ca->last_max_cwnd);
}
static u32 bictcp_min_cwnd(struct sock *sk)
{
return tcp_sk(sk)->snd_ssthresh;
}
static void bictcp_state(struct sock *sk, u8 new_state)
{
if (new_state == TCP_CA_Loss)
@ -357,7 +352,6 @@ static struct tcp_congestion_ops cubictcp = {
.cong_avoid = bictcp_cong_avoid,
.set_state = bictcp_state,
.undo_cwnd = bictcp_undo_cwnd,
.min_cwnd = bictcp_min_cwnd,
.pkts_acked = bictcp_acked,
.owner = THIS_MODULE,
.name = "cubic",

View file

@ -98,6 +98,10 @@ struct hstcp {
u32 ai;
};
static int max_ssthresh = 100;
module_param(max_ssthresh, int, 0644);
MODULE_PARM_DESC(max_ssthresh, "limited slow start threshold (RFC3742)");
static void hstcp_init(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
@ -119,9 +123,23 @@ static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 rtt,
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
if (tp->snd_cwnd <= tp->snd_ssthresh)
tcp_slow_start(tp);
else {
if (tp->snd_cwnd <= tp->snd_ssthresh) {
/* RFC3742: limited slow start
* the window is increased by 1/K MSS for each arriving ACK,
* for K = int(cwnd/(0.5 max_ssthresh))
*/
if (max_ssthresh > 0 && tp->snd_cwnd > max_ssthresh) {
u32 k = max(tp->snd_cwnd / (max_ssthresh >> 1), 1U);
if (++tp->snd_cwnd_cnt >= k) {
if (tp->snd_cwnd < tp->snd_cwnd_clamp)
tp->snd_cwnd++;
tp->snd_cwnd_cnt = 0;
}
} else {
if (tp->snd_cwnd < tp->snd_cwnd_clamp)
tp->snd_cwnd++;
}
} else {
/* Update AIMD parameters */
if (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd) {
while (tp->snd_cwnd > hstcp_aimd_vals[ca->ai].cwnd &&

View file

@ -246,14 +246,6 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt,
}
}
/* Lower bound on congestion window. */
static u32 htcp_min_cwnd(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
return tp->snd_ssthresh;
}
static void htcp_init(struct sock *sk)
{
struct htcp *ca = inet_csk_ca(sk);
@ -285,7 +277,6 @@ static void htcp_state(struct sock *sk, u8 new_state)
static struct tcp_congestion_ops htcp = {
.init = htcp_init,
.ssthresh = htcp_recalc_ssthresh,
.min_cwnd = htcp_min_cwnd,
.cong_avoid = htcp_cong_avoid,
.set_state = htcp_state,
.undo_cwnd = htcp_cwnd_undo,

View file

@ -71,6 +71,7 @@
#include <net/inet_common.h>
#include <linux/ipsec.h>
#include <asm/unaligned.h>
#include <net/netdma.h>
int sysctl_tcp_timestamps = 1;
int sysctl_tcp_window_scaling = 1;
@ -1688,17 +1689,26 @@ static inline void tcp_moderate_cwnd(struct tcp_sock *tp)
tp->snd_cwnd_stamp = tcp_time_stamp;
}
/* Lower bound on congestion window is slow start threshold
* unless congestion avoidance choice decides to overide it.
*/
static inline u32 tcp_cwnd_min(const struct sock *sk)
{
const struct tcp_congestion_ops *ca_ops = inet_csk(sk)->icsk_ca_ops;
return ca_ops->min_cwnd ? ca_ops->min_cwnd(sk) : tcp_sk(sk)->snd_ssthresh;
}
/* Decrease cwnd each second ack. */
static void tcp_cwnd_down(struct sock *sk)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
int decr = tp->snd_cwnd_cnt + 1;
tp->snd_cwnd_cnt = decr&1;
decr >>= 1;
if (decr && tp->snd_cwnd > icsk->icsk_ca_ops->min_cwnd(sk))
if (decr && tp->snd_cwnd > tcp_cwnd_min(sk))
tp->snd_cwnd -= decr;
tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)+1);
@ -3785,6 +3795,50 @@ static inline int tcp_checksum_complete_user(struct sock *sk, struct sk_buff *sk
__tcp_checksum_complete_user(sk, skb);
}
#ifdef CONFIG_NET_DMA
static int tcp_dma_try_early_copy(struct sock *sk, struct sk_buff *skb, int hlen)
{
struct tcp_sock *tp = tcp_sk(sk);
int chunk = skb->len - hlen;
int dma_cookie;
int copied_early = 0;
if (tp->ucopy.wakeup)
return 0;
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
if (tp->ucopy.dma_chan && skb->ip_summed == CHECKSUM_UNNECESSARY) {
dma_cookie = dma_skb_copy_datagram_iovec(tp->ucopy.dma_chan,
skb, hlen, tp->ucopy.iov, chunk, tp->ucopy.pinned_list);
if (dma_cookie < 0)
goto out;
tp->ucopy.dma_cookie = dma_cookie;
copied_early = 1;
tp->ucopy.len -= chunk;
tp->copied_seq += chunk;
tcp_rcv_space_adjust(sk);
if ((tp->ucopy.len == 0) ||
(tcp_flag_word(skb->h.th) & TCP_FLAG_PSH) ||
(atomic_read(&sk->sk_rmem_alloc) > (sk->sk_rcvbuf >> 1))) {
tp->ucopy.wakeup = 1;
sk->sk_data_ready(sk, 0);
}
} else if (chunk > 0) {
tp->ucopy.wakeup = 1;
sk->sk_data_ready(sk, 0);
}
out:
return copied_early;
}
#endif /* CONFIG_NET_DMA */
/*
* TCP receive function for the ESTABLISHED state.
*
@ -3886,8 +3940,6 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp);
tcp_rcv_rtt_measure_ts(sk, skb);
/* We know that such packets are checksummed
* on entry.
*/
@ -3901,14 +3953,23 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
}
} else {
int eaten = 0;
int copied_early = 0;
if (tp->ucopy.task == current &&
tp->copied_seq == tp->rcv_nxt &&
len - tcp_header_len <= tp->ucopy.len &&
sock_owned_by_user(sk)) {
__set_current_state(TASK_RUNNING);
if (tp->copied_seq == tp->rcv_nxt &&
len - tcp_header_len <= tp->ucopy.len) {
#ifdef CONFIG_NET_DMA
if (tcp_dma_try_early_copy(sk, skb, tcp_header_len)) {
copied_early = 1;
eaten = 1;
}
#endif
if (tp->ucopy.task == current && sock_owned_by_user(sk) && !copied_early) {
__set_current_state(TASK_RUNNING);
if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) {
if (!tcp_copy_to_iovec(sk, skb, tcp_header_len))
eaten = 1;
}
if (eaten) {
/* Predicted packet is in window by definition.
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
@ -3924,8 +3985,9 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
__skb_pull(skb, tcp_header_len);
tp->rcv_nxt = TCP_SKB_CB(skb)->end_seq;
NET_INC_STATS_BH(LINUX_MIB_TCPHPHITSTOUSER);
eaten = 1;
}
if (copied_early)
tcp_cleanup_rbuf(sk, skb->len);
}
if (!eaten) {
if (tcp_checksum_complete_user(sk, skb))
@ -3966,6 +4028,11 @@ int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
__tcp_ack_snd_check(sk, 0);
no_ack:
#ifdef CONFIG_NET_DMA
if (copied_early)
__skb_queue_tail(&sk->sk_async_wait_queue, skb);
else
#endif
if (eaten)
__kfree_skb(skb);
else

View file

@ -71,6 +71,7 @@
#include <net/inet_common.h>
#include <net/timewait_sock.h>
#include <net/xfrm.h>
#include <net/netdma.h>
#include <linux/inet.h>
#include <linux/ipv6.h>
@ -1091,8 +1092,18 @@ process:
bh_lock_sock(sk);
ret = 0;
if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb))
#ifdef CONFIG_NET_DMA
struct tcp_sock *tp = tcp_sk(sk);
if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
tp->ucopy.dma_chan = get_softnet_dma();
if (tp->ucopy.dma_chan)
ret = tcp_v4_do_rcv(sk, skb);
else
#endif
{
if (!tcp_prequeue(sk, skb))
ret = tcp_v4_do_rcv(sk, skb);
}
} else
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);
@ -1296,6 +1307,11 @@ int tcp_v4_destroy_sock(struct sock *sk)
/* Cleans up our, hopefully empty, out_of_order_queue. */
__skb_queue_purge(&tp->out_of_order_queue);
#ifdef CONFIG_NET_DMA
/* Cleans up our sk_async_wait_queue */
__skb_queue_purge(&sk->sk_async_wait_queue);
#endif
/* Clean prequeue, it must be empty really */
__skb_queue_purge(&tp->ucopy.prequeue);

338
net/ipv4/tcp_lp.c Normal file
View file

@ -0,0 +1,338 @@
/*
* TCP Low Priority (TCP-LP)
*
* TCP Low Priority is a distributed algorithm whose goal is to utilize only
* the excess network bandwidth as compared to the ``fair share`` of
* bandwidth as targeted by TCP. Available from:
* http://www.ece.rice.edu/~akuzma/Doc/akuzma/TCP-LP.pdf
*
* Original Author:
* Aleksandar Kuzmanovic <akuzma@northwestern.edu>
*
* See http://www-ece.rice.edu/networks/TCP-LP/ for their implementation.
* As of 2.6.13, Linux supports pluggable congestion control algorithms.
* Due to the limitation of the API, we take the following changes from
* the original TCP-LP implementation:
* o We use newReno in most core CA handling. Only add some checking
* within cong_avoid.
* o Error correcting in remote HZ, therefore remote HZ will be keeped
* on checking and updating.
* o Handling calculation of One-Way-Delay (OWD) within rtt_sample, sicne
* OWD have a similar meaning as RTT. Also correct the buggy formular.
* o Handle reaction for Early Congestion Indication (ECI) within
* pkts_acked, as mentioned within pseudo code.
* o OWD is handled in relative format, where local time stamp will in
* tcp_time_stamp format.
*
* Port from 2.4.19 to 2.6.16 as module by:
* Wong Hoi Sing Edison <hswong3i@gmail.com>
* Hung Hing Lun <hlhung3i@gmail.com>
*
* Version: $Id: tcp_lp.c,v 1.22 2006-05-02 18:18:19 hswong3i Exp $
*/
#include <linux/config.h>
#include <linux/module.h>
#include <net/tcp.h>
/* resolution of owd */
#define LP_RESOL 1000
/**
* enum tcp_lp_state
* @LP_VALID_RHZ: is remote HZ valid?
* @LP_VALID_OWD: is OWD valid?
* @LP_WITHIN_THR: are we within threshold?
* @LP_WITHIN_INF: are we within inference?
*
* TCP-LP's state flags.
* We create this set of state flag mainly for debugging.
*/
enum tcp_lp_state {
LP_VALID_RHZ = (1 << 0),
LP_VALID_OWD = (1 << 1),
LP_WITHIN_THR = (1 << 3),
LP_WITHIN_INF = (1 << 4),
};
/**
* struct lp
* @flag: TCP-LP state flag
* @sowd: smoothed OWD << 3
* @owd_min: min OWD
* @owd_max: max OWD
* @owd_max_rsv: resrved max owd
* @remote_hz: estimated remote HZ
* @remote_ref_time: remote reference time
* @local_ref_time: local reference time
* @last_drop: time for last active drop
* @inference: current inference
*
* TCP-LP's private struct.
* We get the idea from original TCP-LP implementation where only left those we
* found are really useful.
*/
struct lp {
u32 flag;
u32 sowd;
u32 owd_min;
u32 owd_max;
u32 owd_max_rsv;
u32 remote_hz;
u32 remote_ref_time;
u32 local_ref_time;
u32 last_drop;
u32 inference;
};
/**
* tcp_lp_init
*
* Init all required variables.
* Clone the handling from Vegas module implementation.
*/
static void tcp_lp_init(struct sock *sk)
{
struct lp *lp = inet_csk_ca(sk);
lp->flag = 0;
lp->sowd = 0;
lp->owd_min = 0xffffffff;
lp->owd_max = 0;
lp->owd_max_rsv = 0;
lp->remote_hz = 0;
lp->remote_ref_time = 0;
lp->local_ref_time = 0;
lp->last_drop = 0;
lp->inference = 0;
}
/**
* tcp_lp_cong_avoid
*
* Implementation of cong_avoid.
* Will only call newReno CA when away from inference.
* From TCP-LP's paper, this will be handled in additive increasement.
*/
static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight,
int flag)
{
struct lp *lp = inet_csk_ca(sk);
if (!(lp->flag & LP_WITHIN_INF))
tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag);
}
/**
* tcp_lp_remote_hz_estimator
*
* Estimate remote HZ.
* We keep on updating the estimated value, where original TCP-LP
* implementation only guest it for once and use forever.
*/
static u32 tcp_lp_remote_hz_estimator(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct lp *lp = inet_csk_ca(sk);
s64 rhz = lp->remote_hz << 6; /* remote HZ << 6 */
s64 m = 0;
/* not yet record reference time
* go away!! record it before come back!! */
if (lp->remote_ref_time == 0 || lp->local_ref_time == 0)
goto out;
/* we can't calc remote HZ with no different!! */
if (tp->rx_opt.rcv_tsval == lp->remote_ref_time
|| tp->rx_opt.rcv_tsecr == lp->local_ref_time)
goto out;
m = HZ * (tp->rx_opt.rcv_tsval -
lp->remote_ref_time) / (tp->rx_opt.rcv_tsecr -
lp->local_ref_time);
if (m < 0)
m = -m;
if (rhz != 0) {
m -= rhz >> 6; /* m is now error in remote HZ est */
rhz += m; /* 63/64 old + 1/64 new */
} else
rhz = m << 6;
/* record time for successful remote HZ calc */
lp->flag |= LP_VALID_RHZ;
out:
/* record reference time stamp */
lp->remote_ref_time = tp->rx_opt.rcv_tsval;
lp->local_ref_time = tp->rx_opt.rcv_tsecr;
return rhz >> 6;
}
/**
* tcp_lp_owd_calculator
*
* Calculate one way delay (in relative format).
* Original implement OWD as minus of remote time difference to local time
* difference directly. As this time difference just simply equal to RTT, when
* the network status is stable, remote RTT will equal to local RTT, and result
* OWD into zero.
* It seems to be a bug and so we fixed it.
*/
static u32 tcp_lp_owd_calculator(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct lp *lp = inet_csk_ca(sk);
s64 owd = 0;
lp->remote_hz = tcp_lp_remote_hz_estimator(sk);
if (lp->flag & LP_VALID_RHZ) {
owd =
tp->rx_opt.rcv_tsval * (LP_RESOL / lp->remote_hz) -
tp->rx_opt.rcv_tsecr * (LP_RESOL / HZ);
if (owd < 0)
owd = -owd;
}
if (owd > 0)
lp->flag |= LP_VALID_OWD;
else
lp->flag &= ~LP_VALID_OWD;
return owd;
}
/**
* tcp_lp_rtt_sample
*
* Implementation or rtt_sample.
* Will take the following action,
* 1. calc OWD,
* 2. record the min/max OWD,
* 3. calc smoothed OWD (SOWD).
* Most ideas come from the original TCP-LP implementation.
*/
static void tcp_lp_rtt_sample(struct sock *sk, u32 usrtt)
{
struct lp *lp = inet_csk_ca(sk);
s64 mowd = tcp_lp_owd_calculator(sk);
/* sorry that we don't have valid data */
if (!(lp->flag & LP_VALID_RHZ) || !(lp->flag & LP_VALID_OWD))
return;
/* record the next min owd */
if (mowd < lp->owd_min)
lp->owd_min = mowd;
/* always forget the max of the max
* we just set owd_max as one below it */
if (mowd > lp->owd_max) {
if (mowd > lp->owd_max_rsv) {
if (lp->owd_max_rsv == 0)
lp->owd_max = mowd;
else
lp->owd_max = lp->owd_max_rsv;
lp->owd_max_rsv = mowd;
} else
lp->owd_max = mowd;
}
/* calc for smoothed owd */
if (lp->sowd != 0) {
mowd -= lp->sowd >> 3; /* m is now error in owd est */
lp->sowd += mowd; /* owd = 7/8 owd + 1/8 new */
} else
lp->sowd = mowd << 3; /* take the measured time be owd */
}
/**
* tcp_lp_pkts_acked
*
* Implementation of pkts_acked.
* Deal with active drop under Early Congestion Indication.
* Only drop to half and 1 will be handle, because we hope to use back
* newReno in increase case.
* We work it out by following the idea from TCP-LP's paper directly
*/
static void tcp_lp_pkts_acked(struct sock *sk, u32 num_acked)
{
struct tcp_sock *tp = tcp_sk(sk);
struct lp *lp = inet_csk_ca(sk);
/* calc inference */
if (tcp_time_stamp > tp->rx_opt.rcv_tsecr)
lp->inference = 3 * (tcp_time_stamp - tp->rx_opt.rcv_tsecr);
/* test if within inference */
if (lp->last_drop && (tcp_time_stamp - lp->last_drop < lp->inference))
lp->flag |= LP_WITHIN_INF;
else
lp->flag &= ~LP_WITHIN_INF;
/* test if within threshold */
if (lp->sowd >> 3 <
lp->owd_min + 15 * (lp->owd_max - lp->owd_min) / 100)
lp->flag |= LP_WITHIN_THR;
else
lp->flag &= ~LP_WITHIN_THR;
pr_debug("TCP-LP: %05o|%5u|%5u|%15u|%15u|%15u\n", lp->flag,
tp->snd_cwnd, lp->remote_hz, lp->owd_min, lp->owd_max,
lp->sowd >> 3);
if (lp->flag & LP_WITHIN_THR)
return;
/* FIXME: try to reset owd_min and owd_max here
* so decrease the chance the min/max is no longer suitable
* and will usually within threshold when whithin inference */
lp->owd_min = lp->sowd >> 3;
lp->owd_max = lp->sowd >> 2;
lp->owd_max_rsv = lp->sowd >> 2;
/* happened within inference
* drop snd_cwnd into 1 */
if (lp->flag & LP_WITHIN_INF)
tp->snd_cwnd = 1U;
/* happened after inference
* cut snd_cwnd into half */
else
tp->snd_cwnd = max(tp->snd_cwnd >> 1U, 1U);
/* record this drop time */
lp->last_drop = tcp_time_stamp;
}
static struct tcp_congestion_ops tcp_lp = {
.init = tcp_lp_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_lp_cong_avoid,
.min_cwnd = tcp_reno_min_cwnd,
.rtt_sample = tcp_lp_rtt_sample,
.pkts_acked = tcp_lp_pkts_acked,
.owner = THIS_MODULE,
.name = "lp"
};
static int __init tcp_lp_register(void)
{
BUG_ON(sizeof(struct lp) > ICSK_CA_PRIV_SIZE);
return tcp_register_congestion_control(&tcp_lp);
}
static void __exit tcp_lp_unregister(void)
{
tcp_unregister_congestion_control(&tcp_lp);
}
module_init(tcp_lp_register);
module_exit(tcp_lp_unregister);
MODULE_AUTHOR("Wong Hoi Sing Edison, Hung Hing Lun");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TCP Low Priority");

View file

@ -59,6 +59,9 @@ int sysctl_tcp_tso_win_divisor = 3;
int sysctl_tcp_mtu_probing = 0;
int sysctl_tcp_base_mss = 512;
/* By default, RFC2861 behavior. */
int sysctl_tcp_slow_start_after_idle = 1;
static void update_send_head(struct sock *sk, struct tcp_sock *tp,
struct sk_buff *skb)
{
@ -138,7 +141,8 @@ static void tcp_event_data_sent(struct tcp_sock *tp,
struct inet_connection_sock *icsk = inet_csk(sk);
const u32 now = tcp_time_stamp;
if (!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto)
if (sysctl_tcp_slow_start_after_idle &&
(!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto))
tcp_cwnd_restart(sk, __sk_dst_get(sk));
tp->lsndtime = now;

181
net/ipv4/tcp_probe.c Normal file
View file

@ -0,0 +1,181 @@
/*
* tcpprobe - Observe the TCP flow with kprobes.
*
* The idea for this came from Werner Almesberger's umlsim
* Copyright (C) 2004, Stephen Hemminger <shemminger@osdl.org>
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/socket.h>
#include <linux/tcp.h>
#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/kfifo.h>
#include <linux/vmalloc.h>
#include <net/tcp.h>
MODULE_AUTHOR("Stephen Hemminger <[email protected]>");
MODULE_DESCRIPTION("TCP cwnd snooper");
MODULE_LICENSE("GPL");
static int port = 0;
MODULE_PARM_DESC(port, "Port to match (0=all)");
module_param(port, int, 0);
static int bufsize = 64*1024;
MODULE_PARM_DESC(bufsize, "Log buffer size (default 64k)");
module_param(bufsize, int, 0);
static const char procname[] = "tcpprobe";
struct {
struct kfifo *fifo;
spinlock_t lock;
wait_queue_head_t wait;
struct timeval tstart;
} tcpw;
static void printl(const char *fmt, ...)
{
va_list args;
int len;
struct timeval now;
char tbuf[256];
va_start(args, fmt);
do_gettimeofday(&now);
now.tv_sec -= tcpw.tstart.tv_sec;
now.tv_usec -= tcpw.tstart.tv_usec;
if (now.tv_usec < 0) {
--now.tv_sec;
now.tv_usec += 1000000;
}
len = sprintf(tbuf, "%lu.%06lu ",
(unsigned long) now.tv_sec,
(unsigned long) now.tv_usec);
len += vscnprintf(tbuf+len, sizeof(tbuf)-len, fmt, args);
va_end(args);
kfifo_put(tcpw.fifo, tbuf, len);
wake_up(&tcpw.wait);
}
static int jtcp_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t size)
{
const struct tcp_sock *tp = tcp_sk(sk);
const struct inet_sock *inet = inet_sk(sk);
if (port == 0 || ntohs(inet->dport) == port ||
ntohs(inet->sport) == port) {
printl("%d.%d.%d.%d:%u %d.%d.%d.%d:%u %d %#x %#x %u %u %u\n",
NIPQUAD(inet->saddr), ntohs(inet->sport),
NIPQUAD(inet->daddr), ntohs(inet->dport),
size, tp->snd_nxt, tp->snd_una,
tp->snd_cwnd, tcp_current_ssthresh(sk),
tp->snd_wnd);
}
jprobe_return();
return 0;
}
static struct jprobe tcp_send_probe = {
.kp = { .addr = (kprobe_opcode_t *) &tcp_sendmsg, },
.entry = (kprobe_opcode_t *) &jtcp_sendmsg,
};
static int tcpprobe_open(struct inode * inode, struct file * file)
{
kfifo_reset(tcpw.fifo);
do_gettimeofday(&tcpw.tstart);
return 0;
}
static ssize_t tcpprobe_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
int error = 0, cnt;
unsigned char *tbuf;
if (!buf || len < 0)
return -EINVAL;
if (len == 0)
return 0;
tbuf = vmalloc(len);
if (!tbuf)
return -ENOMEM;
error = wait_event_interruptible(tcpw.wait,
__kfifo_len(tcpw.fifo) != 0);
if (error)
return error;
cnt = kfifo_get(tcpw.fifo, tbuf, len);
error = copy_to_user(buf, tbuf, cnt);
vfree(tbuf);
return error ? error : cnt;
}
static struct file_operations tcpprobe_fops = {
.owner = THIS_MODULE,
.open = tcpprobe_open,
.read = tcpprobe_read,
};
static __init int tcpprobe_init(void)
{
int ret = -ENOMEM;
init_waitqueue_head(&tcpw.wait);
spin_lock_init(&tcpw.lock);
tcpw.fifo = kfifo_alloc(bufsize, GFP_KERNEL, &tcpw.lock);
if (!proc_net_fops_create(procname, S_IRUSR, &tcpprobe_fops))
goto err0;
ret = register_jprobe(&tcp_send_probe);
if (ret)
goto err1;
pr_info("TCP watch registered (port=%d)\n", port);
return 0;
err1:
proc_net_remove(procname);
err0:
kfifo_free(tcpw.fifo);
return ret;
}
module_init(tcpprobe_init);
static __exit void tcpprobe_exit(void)
{
kfifo_free(tcpw.fifo);
proc_net_remove(procname);
unregister_jprobe(&tcp_send_probe);
}
module_exit(tcpprobe_exit);

231
net/ipv4/tcp_veno.c Normal file
View file

@ -0,0 +1,231 @@
/*
* TCP Veno congestion control
*
* This is based on the congestion detection/avoidance scheme described in
* C. P. Fu, S. C. Liew.
* "TCP Veno: TCP Enhancement for Transmission over Wireless Access Networks."
* IEEE Journal on Selected Areas in Communication,
* Feb. 2003.
* See http://www.ntu.edu.sg/home5/ZHOU0022/papers/CPFu03a.pdf
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/inet_diag.h>
#include <net/tcp.h>
/* Default values of the Veno variables, in fixed-point representation
* with V_PARAM_SHIFT bits to the right of the binary point.
*/
#define V_PARAM_SHIFT 1
static const int beta = 3 << V_PARAM_SHIFT;
/* Veno variables */
struct veno {
u8 doing_veno_now; /* if true, do veno for this rtt */
u16 cntrtt; /* # of rtts measured within last rtt */
u32 minrtt; /* min of rtts measured within last rtt (in usec) */
u32 basertt; /* the min of all Veno rtt measurements seen (in usec) */
u32 inc; /* decide whether to increase cwnd */
u32 diff; /* calculate the diff rate */
};
/* There are several situations when we must "re-start" Veno:
*
* o when a connection is established
* o after an RTO
* o after fast recovery
* o when we send a packet and there is no outstanding
* unacknowledged data (restarting an idle connection)
*
*/
static inline void veno_enable(struct sock *sk)
{
struct veno *veno = inet_csk_ca(sk);
/* turn on Veno */
veno->doing_veno_now = 1;
veno->minrtt = 0x7fffffff;
}
static inline void veno_disable(struct sock *sk)
{
struct veno *veno = inet_csk_ca(sk);
/* turn off Veno */
veno->doing_veno_now = 0;
}
static void tcp_veno_init(struct sock *sk)
{
struct veno *veno = inet_csk_ca(sk);
veno->basertt = 0x7fffffff;
veno->inc = 1;
veno_enable(sk);
}
/* Do rtt sampling needed for Veno. */
static void tcp_veno_rtt_calc(struct sock *sk, u32 usrtt)
{
struct veno *veno = inet_csk_ca(sk);
u32 vrtt = usrtt + 1; /* Never allow zero rtt or basertt */
/* Filter to find propagation delay: */
if (vrtt < veno->basertt)
veno->basertt = vrtt;
/* Find the min rtt during the last rtt to find
* the current prop. delay + queuing delay:
*/
veno->minrtt = min(veno->minrtt, vrtt);
veno->cntrtt++;
}
static void tcp_veno_state(struct sock *sk, u8 ca_state)
{
if (ca_state == TCP_CA_Open)
veno_enable(sk);
else
veno_disable(sk);
}
/*
* If the connection is idle and we are restarting,
* then we don't want to do any Veno calculations
* until we get fresh rtt samples. So when we
* restart, we reset our Veno state to a clean
* state. After we get acks for this flight of
* packets, _then_ we can make Veno calculations
* again.
*/
static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event)
{
if (event == CA_EVENT_CWND_RESTART || event == CA_EVENT_TX_START)
tcp_veno_init(sk);
}
static void tcp_veno_cong_avoid(struct sock *sk, u32 ack,
u32 seq_rtt, u32 in_flight, int flag)
{
struct tcp_sock *tp = tcp_sk(sk);
struct veno *veno = inet_csk_ca(sk);
if (!veno->doing_veno_now)
return tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag);
/* limited by applications */
if (!tcp_is_cwnd_limited(sk, in_flight))
return;
/* We do the Veno calculations only if we got enough rtt samples */
if (veno->cntrtt <= 2) {
/* We don't have enough rtt samples to do the Veno
* calculation, so we'll behave like Reno.
*/
tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag);
} else {
u32 rtt, target_cwnd;
/* We have enough rtt samples, so, using the Veno
* algorithm, we determine the state of the network.
*/
rtt = veno->minrtt;
target_cwnd = ((tp->snd_cwnd * veno->basertt)
<< V_PARAM_SHIFT) / rtt;
veno->diff = (tp->snd_cwnd << V_PARAM_SHIFT) - target_cwnd;
if (tp->snd_cwnd <= tp->snd_ssthresh) {
/* Slow start. */
tcp_slow_start(tp);
} else {
/* Congestion avoidance. */
if (veno->diff < beta) {
/* In the "non-congestive state", increase cwnd
* every rtt.
*/
if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
if (tp->snd_cwnd < tp->snd_cwnd_clamp)
tp->snd_cwnd++;
tp->snd_cwnd_cnt = 0;
} else
tp->snd_cwnd_cnt++;
} else {
/* In the "congestive state", increase cwnd
* every other rtt.
*/
if (tp->snd_cwnd_cnt >= tp->snd_cwnd) {
if (veno->inc
&& tp->snd_cwnd <
tp->snd_cwnd_clamp) {
tp->snd_cwnd++;
veno->inc = 0;
} else
veno->inc = 1;
tp->snd_cwnd_cnt = 0;
} else
tp->snd_cwnd_cnt++;
}
}
if (tp->snd_cwnd < 2)
tp->snd_cwnd = 2;
else if (tp->snd_cwnd > tp->snd_cwnd_clamp)
tp->snd_cwnd = tp->snd_cwnd_clamp;
}
/* Wipe the slate clean for the next rtt. */
/* veno->cntrtt = 0; */
veno->minrtt = 0x7fffffff;
}
/* Veno MD phase */
static u32 tcp_veno_ssthresh(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
struct veno *veno = inet_csk_ca(sk);
if (veno->diff < beta)
/* in "non-congestive state", cut cwnd by 1/5 */
return max(tp->snd_cwnd * 4 / 5, 2U);
else
/* in "congestive state", cut cwnd by 1/2 */
return max(tp->snd_cwnd >> 1U, 2U);
}
static struct tcp_congestion_ops tcp_veno = {
.init = tcp_veno_init,
.ssthresh = tcp_veno_ssthresh,
.cong_avoid = tcp_veno_cong_avoid,
.rtt_sample = tcp_veno_rtt_calc,
.set_state = tcp_veno_state,
.cwnd_event = tcp_veno_cwnd_event,
.owner = THIS_MODULE,
.name = "veno",
};
static int __init tcp_veno_register(void)
{
BUG_ON(sizeof(struct veno) > ICSK_CA_PRIV_SIZE);
tcp_register_congestion_control(&tcp_veno);
return 0;
}
static void __exit tcp_veno_unregister(void)
{
tcp_unregister_congestion_control(&tcp_veno);
}
module_init(tcp_veno_register);
module_exit(tcp_veno_unregister);
MODULE_AUTHOR("Bin Zhou, Cheng Peng Fu");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TCP Veno");

View file

@ -1,7 +1,24 @@
/*
* TCP Westwood+
* TCP Westwood+: end-to-end bandwidth estimation for TCP
*
* Angelo Dell'Aera: TCP Westwood+ support
* Angelo Dell'Aera: author of the first version of TCP Westwood+ in Linux 2.4
*
* Support at http://c3lab.poliba.it/index.php/Westwood
* Main references in literature:
*
* - Mascolo S, Casetti, M. Gerla et al.
* "TCP Westwood: bandwidth estimation for TCP" Proc. ACM Mobicom 2001
*
* - A. Grieco, s. Mascolo
* "Performance evaluation of New Reno, Vegas, Westwood+ TCP" ACM Computer
* Comm. Review, 2004
*
* - A. Dell'Aera, L. Grieco, S. Mascolo.
* "Linux 2.4 Implementation of Westwood+ TCP with Rate-Halving :
* A Performance Evaluation Over the Internet" (ICC 2004), Paris, June 2004
*
* Westwood+ employs end-to-end bandwidth measurement to set cwnd and
* ssthresh after packet loss. The probing phase is as the original Reno.
*/
#include <linux/config.h>
@ -22,6 +39,8 @@ struct westwood {
u32 accounted;
u32 rtt;
u32 rtt_min; /* minimum observed RTT */
u8 first_ack; /* flag which infers that this is the first ack */
u8 reset_rtt_min; /* Reset RTT min to next RTT sample*/
};
@ -49,9 +68,11 @@ static void tcp_westwood_init(struct sock *sk)
w->bw_est = 0;
w->accounted = 0;
w->cumul_ack = 0;
w->reset_rtt_min = 1;
w->rtt_min = w->rtt = TCP_WESTWOOD_INIT_RTT;
w->rtt_win_sx = tcp_time_stamp;
w->snd_una = tcp_sk(sk)->snd_una;
w->first_ack = 1;
}
/*
@ -63,10 +84,16 @@ static inline u32 westwood_do_filter(u32 a, u32 b)
return (((7 * a) + b) >> 3);
}
static inline void westwood_filter(struct westwood *w, u32 delta)
static void westwood_filter(struct westwood *w, u32 delta)
{
w->bw_ns_est = westwood_do_filter(w->bw_ns_est, w->bk / delta);
w->bw_est = westwood_do_filter(w->bw_est, w->bw_ns_est);
/* If the filter is empty fill it with the first sample of bandwidth */
if (w->bw_ns_est == 0 && w->bw_est == 0) {
w->bw_ns_est = w->bk / delta;
w->bw_est = w->bw_ns_est;
} else {
w->bw_ns_est = westwood_do_filter(w->bw_ns_est, w->bk / delta);
w->bw_est = westwood_do_filter(w->bw_est, w->bw_ns_est);
}
}
/*
@ -91,6 +118,15 @@ static void westwood_update_window(struct sock *sk)
struct westwood *w = inet_csk_ca(sk);
s32 delta = tcp_time_stamp - w->rtt_win_sx;
/* Initialize w->snd_una with the first acked sequence number in order
* to fix mismatch between tp->snd_una and w->snd_una for the first
* bandwidth sample
*/
if (w->first_ack) {
w->snd_una = tcp_sk(sk)->snd_una;
w->first_ack = 0;
}
/*
* See if a RTT-window has passed.
* Be careful since if RTT is less than
@ -108,6 +144,16 @@ static void westwood_update_window(struct sock *sk)
}
}
static inline void update_rtt_min(struct westwood *w)
{
if (w->reset_rtt_min) {
w->rtt_min = w->rtt;
w->reset_rtt_min = 0;
} else
w->rtt_min = min(w->rtt, w->rtt_min);
}
/*
* @westwood_fast_bw
* It is called when we are in fast path. In particular it is called when
@ -123,7 +169,7 @@ static inline void westwood_fast_bw(struct sock *sk)
w->bk += tp->snd_una - w->snd_una;
w->snd_una = tp->snd_una;
w->rtt_min = min(w->rtt, w->rtt_min);
update_rtt_min(w);
}
/*
@ -162,12 +208,6 @@ static inline u32 westwood_acked_count(struct sock *sk)
return w->cumul_ack;
}
static inline u32 westwood_bw_rttmin(const struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
const struct westwood *w = inet_csk_ca(sk);
return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2);
}
/*
* TCP Westwood
@ -175,9 +215,11 @@ static inline u32 westwood_bw_rttmin(const struct sock *sk)
* in packets we use mss_cache). Rttmin is guaranteed to be >= 2
* so avoids ever returning 0.
*/
static u32 tcp_westwood_cwnd_min(struct sock *sk)
static u32 tcp_westwood_bw_rttmin(const struct sock *sk)
{
return westwood_bw_rttmin(sk);
const struct tcp_sock *tp = tcp_sk(sk);
const struct westwood *w = inet_csk_ca(sk);
return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2);
}
static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)
@ -191,17 +233,19 @@ static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)
break;
case CA_EVENT_COMPLETE_CWR:
tp->snd_cwnd = tp->snd_ssthresh = westwood_bw_rttmin(sk);
tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
break;
case CA_EVENT_FRTO:
tp->snd_ssthresh = westwood_bw_rttmin(sk);
tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
/* Update RTT_min when next ack arrives */
w->reset_rtt_min = 1;
break;
case CA_EVENT_SLOW_ACK:
westwood_update_window(sk);
w->bk += westwood_acked_count(sk);
w->rtt_min = min(w->rtt, w->rtt_min);
update_rtt_min(w);
break;
default:
@ -235,7 +279,7 @@ static struct tcp_congestion_ops tcp_westwood = {
.init = tcp_westwood_init,
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
.min_cwnd = tcp_westwood_cwnd_min,
.min_cwnd = tcp_westwood_bw_rttmin,
.cwnd_event = tcp_westwood_event,
.get_info = tcp_westwood_info,
.pkts_acked = tcp_westwood_pkts_acked,

View file

@ -13,7 +13,6 @@
#include <linux/string.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
@ -24,15 +23,6 @@ int xfrm4_rcv(struct sk_buff *skb)
EXPORT_SYMBOL(xfrm4_rcv);
static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
{
struct iphdr *outer_iph = skb->nh.iph;
struct iphdr *inner_iph = skb->h.ipiph;
if (INET_ECN_is_ce(outer_iph->tos))
IP_ECN_set_ce(inner_iph);
}
static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
{
switch (nexthdr) {
@ -113,24 +103,10 @@ int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
xfrm_vec[xfrm_nr++] = x;
iph = skb->nh.iph;
if (x->mode->input(x, skb))
goto drop;
if (x->props.mode) {
if (iph->protocol != IPPROTO_IPIP)
goto drop;
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto drop;
if (skb_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto drop;
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
ipv4_copy_dscp(iph, skb->h.ipiph);
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip_ecn_decapsulate(skb);
skb->mac.raw = memmove(skb->data - skb->mac_len,
skb->mac.raw, skb->mac_len);
skb->nh.raw = skb->data;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
decaps = 1;
break;
}

View file

@ -0,0 +1,83 @@
/*
* xfrm4_mode_transport.c - Transport mode encapsulation for IPv4.
*
* Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/stringify.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/xfrm.h>
/* Add encapsulation header.
*
* The IP header will be moved forward to make space for the encapsulation
* header.
*
* On exit, skb->h will be set to the start of the payload to be processed
* by x->type->output and skb->nh will be set to the top IP header.
*/
static int xfrm4_transport_output(struct sk_buff *skb)
{
struct xfrm_state *x;
struct iphdr *iph;
int ihl;
iph = skb->nh.iph;
skb->h.ipiph = iph;
ihl = iph->ihl * 4;
skb->h.raw += ihl;
x = skb->dst->xfrm;
skb->nh.raw = memmove(skb_push(skb, x->props.header_len), iph, ihl);
return 0;
}
/* Remove encapsulation header.
*
* The IP header will be moved over the top of the encapsulation header.
*
* On entry, skb->h shall point to where the IP header should be and skb->nh
* shall be set to where the IP header currently is. skb->data shall point
* to the start of the payload.
*/
static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ihl = skb->data - skb->h.raw;
if (skb->h.raw != skb->nh.raw)
skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
skb->nh.iph->tot_len = htons(skb->len + ihl);
skb->h.raw = skb->data;
return 0;
}
static struct xfrm_mode xfrm4_transport_mode = {
.input = xfrm4_transport_input,
.output = xfrm4_transport_output,
.owner = THIS_MODULE,
.encap = XFRM_MODE_TRANSPORT,
};
static int __init xfrm4_transport_init(void)
{
return xfrm_register_mode(&xfrm4_transport_mode, AF_INET);
}
static void __exit xfrm4_transport_exit(void)
{
int err;
err = xfrm_unregister_mode(&xfrm4_transport_mode, AF_INET);
BUG_ON(err);
}
module_init(xfrm4_transport_init);
module_exit(xfrm4_transport_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TRANSPORT);

View file

@ -0,0 +1,125 @@
/*
* xfrm4_mode_tunnel.c - Tunnel mode encapsulation for IPv4.
*
* Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/stringify.h>
#include <net/dst.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
static inline void ipip_ecn_decapsulate(struct sk_buff *skb)
{
struct iphdr *outer_iph = skb->nh.iph;
struct iphdr *inner_iph = skb->h.ipiph;
if (INET_ECN_is_ce(outer_iph->tos))
IP_ECN_set_ce(inner_iph);
}
/* Add encapsulation header.
*
* The top IP header will be constructed per RFC 2401. The following fields
* in it shall be filled in by x->type->output:
* tot_len
* check
*
* On exit, skb->h will be set to the start of the payload to be processed
* by x->type->output and skb->nh will be set to the top IP header.
*/
static int xfrm4_tunnel_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct iphdr *iph, *top_iph;
int flags;
iph = skb->nh.iph;
skb->h.ipiph = iph;
skb->nh.raw = skb_push(skb, x->props.header_len);
top_iph = skb->nh.iph;
top_iph->ihl = 5;
top_iph->version = 4;
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos);
flags = x->props.flags;
if (flags & XFRM_STATE_NOECN)
IP_ECN_clear(top_iph);
top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
0 : (iph->frag_off & htons(IP_DF));
if (!top_iph->frag_off)
__ip_select_ident(top_iph, dst->child, 0);
top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4;
top_iph->protocol = IPPROTO_IPIP;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
return 0;
}
static int xfrm4_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
struct iphdr *iph = skb->nh.iph;
int err = -EINVAL;
if (iph->protocol != IPPROTO_IPIP)
goto out;
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto out;
if (skb_cloned(skb) &&
(err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
goto out;
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
ipv4_copy_dscp(iph, skb->h.ipiph);
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip_ecn_decapsulate(skb);
skb->mac.raw = memmove(skb->data - skb->mac_len,
skb->mac.raw, skb->mac_len);
skb->nh.raw = skb->data;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
err = 0;
out:
return err;
}
static struct xfrm_mode xfrm4_tunnel_mode = {
.input = xfrm4_tunnel_input,
.output = xfrm4_tunnel_output,
.owner = THIS_MODULE,
.encap = XFRM_MODE_TUNNEL,
};
static int __init xfrm4_tunnel_init(void)
{
return xfrm_register_mode(&xfrm4_tunnel_mode, AF_INET);
}
static void __exit xfrm4_tunnel_exit(void)
{
int err;
err = xfrm_unregister_mode(&xfrm4_tunnel_mode, AF_INET);
BUG_ON(err);
}
module_init(xfrm4_tunnel_init);
module_exit(xfrm4_tunnel_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_XFRM_MODE(AF_INET, XFRM_MODE_TUNNEL);

View file

@ -12,67 +12,10 @@
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/netfilter_ipv4.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/xfrm.h>
#include <net/icmp.h>
/* Add encapsulation header.
*
* In transport mode, the IP header will be moved forward to make space
* for the encapsulation header.
*
* In tunnel mode, the top IP header will be constructed per RFC 2401.
* The following fields in it shall be filled in by x->type->output:
* tot_len
* check
*
* On exit, skb->h will be set to the start of the payload to be processed
* by x->type->output and skb->nh will be set to the top IP header.
*/
static void xfrm4_encap(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct iphdr *iph, *top_iph;
int flags;
iph = skb->nh.iph;
skb->h.ipiph = iph;
skb->nh.raw = skb_push(skb, x->props.header_len);
top_iph = skb->nh.iph;
if (!x->props.mode) {
skb->h.raw += iph->ihl*4;
memmove(top_iph, iph, iph->ihl*4);
return;
}
top_iph->ihl = 5;
top_iph->version = 4;
/* DS disclosed */
top_iph->tos = INET_ECN_encapsulate(iph->tos, iph->tos);
flags = x->props.flags;
if (flags & XFRM_STATE_NOECN)
IP_ECN_clear(top_iph);
top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
0 : (iph->frag_off & htons(IP_DF));
if (!top_iph->frag_off)
__ip_select_ident(top_iph, dst->child, 0);
top_iph->ttl = dst_metric(dst->child, RTAX_HOPLIMIT);
top_iph->saddr = x->props.saddr.a4;
top_iph->daddr = x->id.daddr.a4;
top_iph->protocol = IPPROTO_IPIP;
memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
}
static int xfrm4_tunnel_check_size(struct sk_buff *skb)
{
int mtu, ret = 0;
@ -121,7 +64,9 @@ static int xfrm4_output_one(struct sk_buff *skb)
if (err)
goto error;
xfrm4_encap(skb);
err = x->mode->output(skb);
if (err)
goto error;
err = x->type->output(x, skb);
if (err)

View file

@ -17,8 +17,6 @@
static struct dst_ops xfrm4_dst_ops;
static struct xfrm_policy_afinfo xfrm4_policy_afinfo;
static struct xfrm_type_map xfrm4_type_map = { .lock = RW_LOCK_UNLOCKED };
static int xfrm4_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
{
return __ip_route_output_key((struct rtable**)dst, fl);
@ -237,9 +235,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
static inline int xfrm4_garbage_collect(void)
{
read_lock(&xfrm4_policy_afinfo.lock);
xfrm4_policy_afinfo.garbage_collect();
read_unlock(&xfrm4_policy_afinfo.lock);
return (atomic_read(&xfrm4_dst_ops.entries) > xfrm4_dst_ops.gc_thresh*2);
}
@ -299,8 +295,6 @@ static struct dst_ops xfrm4_dst_ops = {
static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
.family = AF_INET,
.lock = RW_LOCK_UNLOCKED,
.type_map = &xfrm4_type_map,
.dst_ops = &xfrm4_dst_ops,
.dst_lookup = xfrm4_dst_lookup,
.find_bundle = __xfrm4_find_bundle,

View file

@ -131,7 +131,6 @@ __xfrm4_find_acq(u8 mode, u32 reqid, u8 proto,
static struct xfrm_state_afinfo xfrm4_state_afinfo = {
.family = AF_INET,
.lock = RW_LOCK_UNLOCKED,
.init_flags = xfrm4_init_flags,
.init_tempsel = __xfrm4_init_tempsel,
.state_lookup = __xfrm4_state_lookup,

View file

@ -106,6 +106,26 @@ config INET6_TUNNEL
tristate
default n
config INET6_XFRM_MODE_TRANSPORT
tristate "IPv6: IPsec transport mode"
depends on IPV6
default IPV6
select XFRM
---help---
Support for IPsec transport mode.
If unsure, say Y.
config INET6_XFRM_MODE_TUNNEL
tristate "IPv6: IPsec tunnel mode"
depends on IPV6
default IPV6
select XFRM
---help---
Support for IPsec tunnel mode.
If unsure, say Y.
config IPV6_TUNNEL
tristate "IPv6: IPv6-in-IPv6 tunnel"
select INET6_TUNNEL

View file

@ -20,6 +20,8 @@ obj-$(CONFIG_INET6_ESP) += esp6.o
obj-$(CONFIG_INET6_IPCOMP) += ipcomp6.o
obj-$(CONFIG_INET6_XFRM_TUNNEL) += xfrm6_tunnel.o
obj-$(CONFIG_INET6_TUNNEL) += tunnel6.o
obj-$(CONFIG_INET6_XFRM_MODE_TRANSPORT) += xfrm6_mode_transport.o
obj-$(CONFIG_INET6_XFRM_MODE_TUNNEL) += xfrm6_mode_tunnel.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o

View file

@ -2860,6 +2860,11 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);
}
/* Maximum length of ifa_cacheinfo attributes */
#define INET6_IFADDR_RTA_SPACE \
RTA_SPACE(16) /* IFA_ADDRESS */ + \
RTA_SPACE(sizeof(struct ifa_cacheinfo)) /* CACHEINFO */
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
u32 pid, u32 seq, int event, unsigned int flags)
{
@ -3092,7 +3097,7 @@ static int inet6_dump_ifacaddr(struct sk_buff *skb, struct netlink_callback *cb)
static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa)
{
struct sk_buff *skb;
int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128);
int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + INET6_IFADDR_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
@ -3142,6 +3147,17 @@ static void inline ipv6_store_devconf(struct ipv6_devconf *cnf,
#endif
}
/* Maximum length of ifinfomsg attributes */
#define INET6_IFINFO_RTA_SPACE \
RTA_SPACE(IFNAMSIZ) /* IFNAME */ + \
RTA_SPACE(MAX_ADDR_LEN) /* ADDRESS */ + \
RTA_SPACE(sizeof(u32)) /* MTU */ + \
RTA_SPACE(sizeof(int)) /* LINK */ + \
RTA_SPACE(0) /* PROTINFO */ + \
RTA_SPACE(sizeof(u32)) /* FLAGS */ + \
RTA_SPACE(sizeof(struct ifla_cacheinfo)) /* CACHEINFO */ + \
RTA_SPACE(sizeof(__s32[DEVCONF_MAX])) /* CONF */
static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
u32 pid, u32 seq, int event, unsigned int flags)
{
@ -3235,8 +3251,7 @@ static int inet6_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
{
struct sk_buff *skb;
/* 128 bytes ?? */
int size = NLMSG_SPACE(sizeof(struct ifinfomsg)+128);
int size = NLMSG_SPACE(sizeof(struct ifinfomsg) + INET6_IFINFO_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {
@ -3252,6 +3267,11 @@ void inet6_ifinfo_notify(int event, struct inet6_dev *idev)
netlink_broadcast(rtnl, skb, 0, RTNLGRP_IPV6_IFINFO, GFP_ATOMIC);
}
/* Maximum length of prefix_cacheinfo attributes */
#define INET6_PREFIX_RTA_SPACE \
RTA_SPACE(sizeof(((struct prefix_info *)NULL)->prefix)) /* ADDRESS */ + \
RTA_SPACE(sizeof(struct prefix_cacheinfo)) /* CACHEINFO */
static int inet6_fill_prefix(struct sk_buff *skb, struct inet6_dev *idev,
struct prefix_info *pinfo, u32 pid, u32 seq,
int event, unsigned int flags)
@ -3296,7 +3316,7 @@ static void inet6_prefix_notify(int event, struct inet6_dev *idev,
struct prefix_info *pinfo)
{
struct sk_buff *skb;
int size = NLMSG_SPACE(sizeof(struct prefixmsg)+128);
int size = NLMSG_SPACE(sizeof(struct prefixmsg) + INET6_PREFIX_RTA_SPACE);
skb = alloc_skb(size, GFP_ATOMIC);
if (!skb) {

View file

@ -292,7 +292,7 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
memset(ah->auth_data, 0, ahp->icv_trunc_len);
skb_push(skb, skb->data - skb->nh.raw);
skb_push(skb, hdr_len);
ahp->icv(ahp, skb, ah->auth_data);
if (memcmp(ah->auth_data, auth_data, ahp->icv_trunc_len)) {
LIMIT_NETDEBUG(KERN_WARNING "ipsec ah authentication error\n");
@ -301,12 +301,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
}
}
skb->nh.raw = skb_pull(skb, ah_hlen);
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
skb_pull(skb, hdr_len);
skb->h.raw = skb->data;
skb->h.raw = memcpy(skb->nh.raw += ah_hlen, tmp_hdr, hdr_len);
__skb_pull(skb, ah_hlen + hdr_len);
kfree(tmp_hdr);

View file

@ -142,26 +142,18 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
int hdr_len = skb->h.raw - skb->nh.raw;
int nfrags;
unsigned char *tmp_hdr = NULL;
int ret = 0;
if (!pskb_may_pull(skb, sizeof(struct ipv6_esp_hdr))) {
ret = -EINVAL;
goto out_nofree;
goto out;
}
if (elen <= 0 || (elen & (blksize-1))) {
ret = -EINVAL;
goto out_nofree;
goto out;
}
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr) {
ret = -ENOMEM;
goto out_nofree;
}
memcpy(tmp_hdr, skb->nh.raw, hdr_len);
/* If integrity check is required, do this. */
if (esp->auth.icv_full_len) {
u8 sum[esp->auth.icv_full_len];
@ -222,16 +214,12 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
/* ... check padding bits here. Silly. :-) */
pskb_trim(skb, skb->len - alen - padlen - 2);
skb->h.raw = skb_pull(skb, sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen);
skb->nh.raw += sizeof(struct ipv6_esp_hdr) + esp->conf.ivlen;
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
ret = nexthdr[1];
}
skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - hdr_len;
out:
kfree(tmp_hdr);
out_nofree:
return ret;
}

View file

@ -39,6 +39,7 @@
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/route.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
@ -458,6 +459,7 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
nf_bridge_get(to->nf_bridge);
#endif
#endif
skb_copy_secmark(to, from);
}
int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
@ -488,6 +490,7 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
return offset;
}
EXPORT_SYMBOL_GPL(ip6_find_1stfragopt);
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{

View file

@ -65,38 +65,25 @@ static LIST_HEAD(ipcomp6_tfms_list);
static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
{
int err = 0;
u8 nexthdr = 0;
int hdr_len = skb->h.raw - skb->nh.raw;
unsigned char *tmp_hdr = NULL;
int err = -ENOMEM;
struct ipv6hdr *iph;
struct ipv6_comp_hdr *ipch;
int plen, dlen;
struct ipcomp_data *ipcd = x->data;
u8 *start, *scratch;
struct crypto_tfm *tfm;
int cpu;
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
err = -ENOMEM;
if (skb_linearize_cow(skb))
goto out;
}
skb->ip_summed = CHECKSUM_NONE;
/* Remove ipcomp header and decompress original payload */
iph = skb->nh.ipv6h;
tmp_hdr = kmalloc(hdr_len, GFP_ATOMIC);
if (!tmp_hdr)
goto out;
memcpy(tmp_hdr, iph, hdr_len);
nexthdr = *(u8 *)skb->data;
skb_pull(skb, sizeof(struct ipv6_comp_hdr));
skb->nh.raw += sizeof(struct ipv6_comp_hdr);
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
iph = skb->nh.ipv6h;
iph->payload_len = htons(ntohs(iph->payload_len) - sizeof(struct ipv6_comp_hdr));
skb->h.raw = skb->data;
ipch = (void *)skb->data;
skb->h.raw = skb->nh.raw + sizeof(*ipch);
__skb_pull(skb, sizeof(*ipch));
/* decompression */
plen = skb->len;
@ -125,18 +112,11 @@ static int ipcomp6_input(struct xfrm_state *x, struct sk_buff *skb)
skb_put(skb, dlen - plen);
memcpy(skb->data, scratch, dlen);
err = ipch->nexthdr;
iph = skb->nh.ipv6h;
iph->payload_len = htons(skb->len);
out_put_cpu:
put_cpu();
out:
kfree(tmp_hdr);
if (err)
goto error_out;
return nexthdr;
error_out:
return err;
}
@ -159,10 +139,8 @@ static int ipcomp6_output(struct xfrm_state *x, struct sk_buff *skb)
goto out_ok;
}
if ((skb_is_nonlinear(skb) || skb_cloned(skb)) &&
skb_linearize(skb, GFP_ATOMIC) != 0) {
if (skb_linearize_cow(skb))
goto out_ok;
}
/* compression */
plen = skb->len - hdr_len;

View file

@ -189,7 +189,7 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
/* This is where we call the helper: as the packet goes out. */
ct = nf_ct_get(*pskb, &ctinfo);
if (!ct)
if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)
goto out;
help = nfct_help(ct);

View file

@ -233,7 +233,7 @@ icmpv6_error(struct sk_buff *skb, unsigned int dataoff,
return -NF_ACCEPT;
}
if (hooknum == NF_IP6_PRE_ROUTING &&
if (nf_conntrack_checksum && hooknum == NF_IP6_PRE_ROUTING &&
nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) {
nf_log_packet(PF_INET6, 0, skb, NULL, NULL, NULL,
"nf_ct_icmpv6: ICMPv6 checksum failed\n");

View file

@ -456,13 +456,9 @@ static int nf_ct_frag6_queue(struct nf_ct_frag6_queue *fq, struct sk_buff *skb,
DEBUGP("queue: message is too short.\n");
goto err;
}
if (end-offset < skb->len) {
if (pskb_trim(skb, end - offset)) {
DEBUGP("Can't trim\n");
goto err;
}
if (skb->ip_summed != CHECKSUM_UNNECESSARY)
skb->ip_summed = CHECKSUM_NONE;
if (pskb_trim_rcsum(skb, end - offset)) {
DEBUGP("Can't trim\n");
goto err;
}
/* Find out which fragments are in front and at the back of us

View file

@ -1218,8 +1218,16 @@ process:
bh_lock_sock(sk);
ret = 0;
if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb))
ret = tcp_v6_do_rcv(sk, skb);
#ifdef CONFIG_NET_DMA
struct tcp_sock *tp = tcp_sk(sk);
if (tp->ucopy.dma_chan)
ret = tcp_v6_do_rcv(sk, skb);
else
#endif
{
if (!tcp_prequeue(sk, skb))
ret = tcp_v6_do_rcv(sk, skb);
}
} else
sk_add_backlog(sk, skb);
bh_unlock_sock(sk);

View file

@ -13,21 +13,9 @@
#include <linux/string.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <net/dsfield.h>
#include <net/inet_ecn.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
{
struct ipv6hdr *outer_iph = skb->nh.ipv6h;
struct ipv6hdr *inner_iph = skb->h.ipv6h;
if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
IP6_ECN_set_ce(inner_iph);
}
int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
{
int err;
@ -81,21 +69,10 @@ int xfrm6_rcv_spi(struct sk_buff *skb, u32 spi)
xfrm_vec[xfrm_nr++] = x;
if (x->mode->input(x, skb))
goto drop;
if (x->props.mode) { /* XXX */
if (nexthdr != IPPROTO_IPV6)
goto drop;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto drop;
if (skb_cloned(skb) &&
pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
goto drop;
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h);
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(skb);
skb->mac.raw = memmove(skb->data - skb->mac_len,
skb->mac.raw, skb->mac_len);
skb->nh.raw = skb->data;
decaps = 1;
break;
}

View file

@ -0,0 +1,88 @@
/*
* xfrm6_mode_transport.c - Transport mode encapsulation for IPv6.
*
* Copyright (C) 2002 USAGI/WIDE Project
* Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/stringify.h>
#include <net/dst.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
/* Add encapsulation header.
*
* The IP header and mutable extension headers will be moved forward to make
* space for the encapsulation header.
*
* On exit, skb->h will be set to the start of the encapsulation header to be
* filled in by x->type->output and skb->nh will be set to the nextheader field
* of the extension header directly preceding the encapsulation header, or in
* its absence, that of the top IP header. The value of skb->data will always
* point to the top IP header.
*/
static int xfrm6_transport_output(struct sk_buff *skb)
{
struct xfrm_state *x = skb->dst->xfrm;
struct ipv6hdr *iph;
u8 *prevhdr;
int hdr_len;
skb_push(skb, x->props.header_len);
iph = skb->nh.ipv6h;
hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
skb->nh.raw = prevhdr - x->props.header_len;
skb->h.raw = skb->data + hdr_len;
memmove(skb->data, iph, hdr_len);
return 0;
}
/* Remove encapsulation header.
*
* The IP header will be moved over the top of the encapsulation header.
*
* On entry, skb->h shall point to where the IP header should be and skb->nh
* shall be set to where the IP header currently is. skb->data shall point
* to the start of the payload.
*/
static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
{
int ihl = skb->data - skb->h.raw;
if (skb->h.raw != skb->nh.raw)
skb->nh.raw = memmove(skb->h.raw, skb->nh.raw, ihl);
skb->nh.ipv6h->payload_len = htons(skb->len + ihl -
sizeof(struct ipv6hdr));
skb->h.raw = skb->data;
return 0;
}
static struct xfrm_mode xfrm6_transport_mode = {
.input = xfrm6_transport_input,
.output = xfrm6_transport_output,
.owner = THIS_MODULE,
.encap = XFRM_MODE_TRANSPORT,
};
static int __init xfrm6_transport_init(void)
{
return xfrm_register_mode(&xfrm6_transport_mode, AF_INET6);
}
static void __exit xfrm6_transport_exit(void)
{
int err;
err = xfrm_unregister_mode(&xfrm6_transport_mode, AF_INET6);
BUG_ON(err);
}
module_init(xfrm6_transport_init);
module_exit(xfrm6_transport_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TRANSPORT);

View file

@ -0,0 +1,121 @@
/*
* xfrm6_mode_tunnel.c - Tunnel mode encapsulation for IPv6.
*
* Copyright (C) 2002 USAGI/WIDE Project
* Copyright (c) 2004-2006 Herbert Xu <herbert@gondor.apana.org.au>
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/stringify.h>
#include <net/dsfield.h>
#include <net/dst.h>
#include <net/inet_ecn.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
static inline void ipip6_ecn_decapsulate(struct sk_buff *skb)
{
struct ipv6hdr *outer_iph = skb->nh.ipv6h;
struct ipv6hdr *inner_iph = skb->h.ipv6h;
if (INET_ECN_is_ce(ipv6_get_dsfield(outer_iph)))
IP6_ECN_set_ce(inner_iph);
}
/* Add encapsulation header.
*
* The top IP header will be constructed per RFC 2401. The following fields
* in it shall be filled in by x->type->output:
* payload_len
*
* On exit, skb->h will be set to the start of the encapsulation header to be
* filled in by x->type->output and skb->nh will be set to the nextheader field
* of the extension header directly preceding the encapsulation header, or in
* its absence, that of the top IP header. The value of skb->data will always
* point to the top IP header.
*/
static int xfrm6_tunnel_output(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph, *top_iph;
int dsfield;
skb_push(skb, x->props.header_len);
iph = skb->nh.ipv6h;
skb->nh.raw = skb->data;
top_iph = skb->nh.ipv6h;
skb->nh.raw = &top_iph->nexthdr;
skb->h.ipv6h = top_iph + 1;
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
dsfield = ipv6_get_dsfield(top_iph);
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
if (x->props.flags & XFRM_STATE_NOECN)
dsfield &= ~INET_ECN_MASK;
ipv6_change_dsfield(top_iph, 0, dsfield);
top_iph->nexthdr = IPPROTO_IPV6;
top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
return 0;
}
static int xfrm6_tunnel_input(struct xfrm_state *x, struct sk_buff *skb)
{
int err = -EINVAL;
if (skb->nh.raw[IP6CB(skb)->nhoff] != IPPROTO_IPV6)
goto out;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto out;
if (skb_cloned(skb) &&
(err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
goto out;
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
ipv6_copy_dscp(skb->nh.ipv6h, skb->h.ipv6h);
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(skb);
skb->mac.raw = memmove(skb->data - skb->mac_len,
skb->mac.raw, skb->mac_len);
skb->nh.raw = skb->data;
err = 0;
out:
return err;
}
static struct xfrm_mode xfrm6_tunnel_mode = {
.input = xfrm6_tunnel_input,
.output = xfrm6_tunnel_output,
.owner = THIS_MODULE,
.encap = XFRM_MODE_TUNNEL,
};
static int __init xfrm6_tunnel_init(void)
{
return xfrm_register_mode(&xfrm6_tunnel_mode, AF_INET6);
}
static void __exit xfrm6_tunnel_exit(void)
{
int err;
err = xfrm_unregister_mode(&xfrm6_tunnel_mode, AF_INET6);
BUG_ON(err);
}
module_init(xfrm6_tunnel_init);
module_exit(xfrm6_tunnel_exit);
MODULE_LICENSE("GPL");
MODULE_ALIAS_XFRM_MODE(AF_INET6, XFRM_MODE_TUNNEL);

View file

@ -14,68 +14,9 @@
#include <linux/spinlock.h>
#include <linux/icmpv6.h>
#include <linux/netfilter_ipv6.h>
#include <net/dsfield.h>
#include <net/inet_ecn.h>
#include <net/ipv6.h>
#include <net/xfrm.h>
/* Add encapsulation header.
*
* In transport mode, the IP header and mutable extension headers will be moved
* forward to make space for the encapsulation header.
*
* In tunnel mode, the top IP header will be constructed per RFC 2401.
* The following fields in it shall be filled in by x->type->output:
* payload_len
*
* On exit, skb->h will be set to the start of the encapsulation header to be
* filled in by x->type->output and skb->nh will be set to the nextheader field
* of the extension header directly preceding the encapsulation header, or in
* its absence, that of the top IP header. The value of skb->data will always
* point to the top IP header.
*/
static void xfrm6_encap(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
struct ipv6hdr *iph, *top_iph;
int dsfield;
skb_push(skb, x->props.header_len);
iph = skb->nh.ipv6h;
if (!x->props.mode) {
u8 *prevhdr;
int hdr_len;
hdr_len = ip6_find_1stfragopt(skb, &prevhdr);
skb->nh.raw = prevhdr - x->props.header_len;
skb->h.raw = skb->data + hdr_len;
memmove(skb->data, iph, hdr_len);
return;
}
skb->nh.raw = skb->data;
top_iph = skb->nh.ipv6h;
skb->nh.raw = &top_iph->nexthdr;
skb->h.ipv6h = top_iph + 1;
top_iph->version = 6;
top_iph->priority = iph->priority;
top_iph->flow_lbl[0] = iph->flow_lbl[0];
top_iph->flow_lbl[1] = iph->flow_lbl[1];
top_iph->flow_lbl[2] = iph->flow_lbl[2];
dsfield = ipv6_get_dsfield(top_iph);
dsfield = INET_ECN_encapsulate(dsfield, dsfield);
if (x->props.flags & XFRM_STATE_NOECN)
dsfield &= ~INET_ECN_MASK;
ipv6_change_dsfield(top_iph, 0, dsfield);
top_iph->nexthdr = IPPROTO_IPV6;
top_iph->hop_limit = dst_metric(dst->child, RTAX_HOPLIMIT);
ipv6_addr_copy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr);
ipv6_addr_copy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr);
}
static int xfrm6_tunnel_check_size(struct sk_buff *skb)
{
int mtu, ret = 0;
@ -118,7 +59,9 @@ static int xfrm6_output_one(struct sk_buff *skb)
if (err)
goto error;
xfrm6_encap(skb);
err = x->mode->output(skb);
if (err)
goto error;
err = x->type->output(x, skb);
if (err)

View file

@ -23,8 +23,6 @@
static struct dst_ops xfrm6_dst_ops;
static struct xfrm_policy_afinfo xfrm6_policy_afinfo;
static struct xfrm_type_map xfrm6_type_map = { .lock = RW_LOCK_UNLOCKED };
static int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
{
int err = 0;
@ -249,9 +247,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
static inline int xfrm6_garbage_collect(void)
{
read_lock(&xfrm6_policy_afinfo.lock);
xfrm6_policy_afinfo.garbage_collect();
read_unlock(&xfrm6_policy_afinfo.lock);
return (atomic_read(&xfrm6_dst_ops.entries) > xfrm6_dst_ops.gc_thresh*2);
}
@ -311,8 +307,6 @@ static struct dst_ops xfrm6_dst_ops = {
static struct xfrm_policy_afinfo xfrm6_policy_afinfo = {
.family = AF_INET6,
.lock = RW_LOCK_UNLOCKED,
.type_map = &xfrm6_type_map,
.dst_ops = &xfrm6_dst_ops,
.dst_lookup = xfrm6_dst_lookup,
.find_bundle = __xfrm6_find_bundle,

View file

@ -135,7 +135,6 @@ __xfrm6_find_acq(u8 mode, u32 reqid, u8 proto,
static struct xfrm_state_afinfo xfrm6_state_afinfo = {
.family = AF_INET6,
.lock = RW_LOCK_UNLOCKED,
.init_tempsel = __xfrm6_init_tempsel,
.state_lookup = __xfrm6_state_lookup,
.find_acq = __xfrm6_find_acq,

View file

@ -238,7 +238,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
}
/* Apply checksum. Not allowed on 802.3 links. */
if (sk->sk_no_check || intrfc->if_dlink_type == IPX_FRAME_8023)
if (sk->sk_no_check || intrfc->if_dlink_type == htons(IPX_FRAME_8023))
ipx->ipx_checksum = 0xFFFF;
else
ipx->ipx_checksum = ipx_cksum(ipx, len + sizeof(struct ipxhdr));

View file

@ -44,6 +44,8 @@
#include <net/irda/irlmp.h>
#include <net/irda/irlmp_frame.h>
#include <asm/unaligned.h>
static __u8 irlmp_find_free_slsap(void);
static int irlmp_slsap_inuse(__u8 slsap_sel);
@ -840,6 +842,7 @@ void irlmp_do_expiry(void)
void irlmp_do_discovery(int nslots)
{
struct lap_cb *lap;
__u16 *data_hintsp;
/* Make sure the value is sane */
if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){
@ -849,7 +852,8 @@ void irlmp_do_discovery(int nslots)
}
/* Construct new discovery info to be used by IrLAP, */
u16ho(irlmp->discovery_cmd.data.hints) = irlmp->hints.word;
data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints;
put_unaligned(irlmp->hints.word, data_hintsp);
/*
* Set character set for device name (we use ASCII), and

View file

@ -1454,21 +1454,23 @@ static int pfkey_delete(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
if (x == NULL)
return -ESRCH;
if ((err = security_xfrm_state_delete(x)))
goto out;
if (xfrm_state_kern(x)) {
xfrm_state_put(x);
return -EPERM;
err = -EPERM;
goto out;
}
err = xfrm_state_delete(x);
if (err < 0) {
xfrm_state_put(x);
return err;
}
if (err < 0)
goto out;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
c.event = XFRM_MSG_DELSA;
km_state_notify(x, &c);
out:
xfrm_state_put(x);
return err;
@ -2274,11 +2276,14 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
err = 0;
if ((err = security_xfrm_policy_delete(xp)))
goto out;
c.seq = hdr->sadb_msg_seq;
c.pid = hdr->sadb_msg_pid;
c.event = XFRM_MSG_DELPOLICY;
km_policy_notify(xp, pol->sadb_x_policy_dir-1, &c);
out:
xfrm_pol_put(xp);
return err;
}

View file

@ -674,7 +674,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
lock_sock(sk);
copied = -ENOTCONN;
if (sk->sk_state == TCP_LISTEN)
if (unlikely(sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_LISTEN))
goto out;
timeo = sock_rcvtimeo(sk, nonblock);
@ -733,7 +733,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
if (sk->sk_state == TCP_CLOSE) {
if (sk->sk_type == SOCK_STREAM && sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
/*
* This occurs when user tries to read
@ -789,7 +789,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
continue;
if (!(flags & MSG_PEEK)) {
sk_eat_skb(sk, skb);
sk_eat_skb(sk, skb, 0);
*seq = 0;
}
} while (len > 0);

View file

@ -26,8 +26,6 @@
#include <net/llc_c_st.h>
#include <net/tcp_states.h>
u8 llc_mac_null_var[IFHWADDRLEN];
/**
* llc_build_and_send_pkt - Connection data sending for upper layers.
* @sk: connection

View file

@ -142,6 +142,8 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
struct llc_sap *sap;
struct llc_pdu_sn *pdu;
int dest;
int (*rcv)(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
/*
* When the interface is in promisc. mode, drop all the crap that it
@ -169,9 +171,11 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
* First the upper layer protocols that don't need the full
* LLC functionality
*/
if (sap->rcv_func) {
sap->rcv_func(skb, dev, pt, orig_dev);
goto out_put;
rcv = rcu_dereference(sap->rcv_func);
if (rcv) {
struct sk_buff *cskb = skb_clone(skb, GFP_ATOMIC);
if (cskb)
rcv(cskb, dev, pt, orig_dev);
}
dest = llc_pdu_type(skb);
if (unlikely(!dest || !llc_type_handlers[dest - 1]))

Some files were not shown because too many files have changed in this diff Show more