mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
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:
commit
d0b952a983
220 changed files with 12047 additions and 5799 deletions
22
net/Kconfig
22
net/Kconfig
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
199
net/bridge/br_netlink.c
Normal 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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
185
net/core/dev.c
185
net/core/dev.c
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
131
net/core/user_dma.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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]>");
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
471
net/ipv4/netfilter/ip_conntrack_sip.c
Normal file
471
net/ipv4/netfilter/ip_conntrack_sip.c
Normal 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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
249
net/ipv4/netfilter/ip_nat_sip.c
Normal file
249
net/ipv4/netfilter/ip_nat_sip.c
Normal 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);
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
|||
127
net/ipv4/tcp.c
127
net/ipv4/tcp.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
448
net/ipv4/tcp_compound.c
Normal 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");
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
338
net/ipv4/tcp_lp.c
Normal 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");
|
||||
|
|
@ -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
181
net/ipv4/tcp_probe.c
Normal 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
231
net/ipv4/tcp_veno.c
Normal 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");
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
83
net/ipv4/xfrm4_mode_transport.c
Normal file
83
net/ipv4/xfrm4_mode_transport.c
Normal 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);
|
||||
125
net/ipv4/xfrm4_mode_tunnel.c
Normal file
125
net/ipv4/xfrm4_mode_tunnel.c
Normal 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);
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
88
net/ipv6/xfrm6_mode_transport.c
Normal file
88
net/ipv6/xfrm6_mode_transport.c
Normal 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);
|
||||
121
net/ipv6/xfrm6_mode_tunnel.c
Normal file
121
net/ipv6/xfrm6_mode_tunnel.c
Normal 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);
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in a new issue