Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@home.transmeta.com>2003-06-13 21:52:52 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-06-13 21:52:52 -0700
commit2eb80fd261bb3f37b76284e516422f79cb5b5331 (patch)
tree789a760810e01512bcd0f76584fcd0fe259728da
parent758361cb4addbd37bcb45cc15b3c3fff1ca661c9 (diff)
parent968876cce2a04c5c9fd84143523b3c71d4727114 (diff)
Merge bk://kernel.bkbits.net/davem/net-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
-rw-r--r--drivers/net/acenic.c16
-rw-r--r--drivers/net/dummy.c41
-rw-r--r--drivers/net/net_init.c3
-rw-r--r--drivers/net/ppp_deflate.c4
-rw-r--r--drivers/net/slip.c18
-rw-r--r--drivers/net/sungem.c2
-rw-r--r--drivers/net/sungem.h30
-rw-r--r--drivers/net/tg3.c67
-rw-r--r--drivers/net/tun.c90
-rw-r--r--include/linux/etherdevice.h1
-rw-r--r--include/linux/if_tun.h2
-rw-r--r--include/linux/netdevice.h2
-rw-r--r--include/linux/netfilter_arp/arp_tables.h2
-rw-r--r--include/net/flow.h2
-rw-r--r--include/net/sctp/sctp.h43
-rw-r--r--include/net/sctp/structs.h76
-rw-r--r--include/net/sctp/user.h5
-rw-r--r--net/8021q/vlan.c90
-rw-r--r--net/8021q/vlan.h2
-rw-r--r--net/8021q/vlan_dev.c22
-rw-r--r--net/bridge/br_device.c9
-rw-r--r--net/bridge/br_if.c21
-rw-r--r--net/bridge/br_input.c6
-rw-r--r--net/bridge/br_netfilter.c2
-rw-r--r--net/bridge/br_notify.c4
-rw-r--r--net/bridge/br_private.h2
-rw-r--r--net/bridge/br_stp.c10
-rw-r--r--net/bridge/br_stp_bpdu.c2
-rw-r--r--net/bridge/br_stp_if.c4
-rw-r--r--net/bridge/br_stp_timer.c16
-rw-r--r--net/bridge/netfilter/ebt_redirect.c2
-rw-r--r--net/bridge/netfilter/ebtables.c4
-rw-r--r--net/core/dev.c45
-rw-r--r--net/core/flow.c139
-rw-r--r--net/core/neighbour.c7
-rw-r--r--net/core/net-sysfs.c163
-rw-r--r--net/ipv4/devinet.c4
-rw-r--r--net/ipv4/ip_gre.c110
-rw-r--r--net/ipv4/ipip.c112
-rw-r--r--net/ipv4/netfilter/iptable_filter.c4
-rw-r--r--net/ipv4/netfilter/iptable_mangle.c4
-rw-r--r--net/ipv4/xfrm4_policy.c3
-rw-r--r--net/ipv6/addrconf.c3
-rw-r--r--net/ipv6/mcast.c4
-rw-r--r--net/ipv6/ndisc.c3
-rw-r--r--net/ipv6/reassembly.c13
-rw-r--r--net/ipv6/sit.c117
-rw-r--r--net/ipv6/udp.c2
-rw-r--r--net/ipv6/xfrm6_policy.c1
-rw-r--r--net/llc/llc_proc.c2
-rw-r--r--net/netsyms.c1
-rw-r--r--net/sctp/associola.c35
-rw-r--r--net/sctp/bind_addr.c4
-rw-r--r--net/sctp/input.c34
-rw-r--r--net/sctp/ipv6.c9
-rw-r--r--net/sctp/objcnt.c2
-rw-r--r--net/sctp/proc.c159
-rw-r--r--net/sctp/protocol.c206
-rw-r--r--net/sctp/sm_make_chunk.c26
-rw-r--r--net/sctp/sm_sideeffect.c3
-rw-r--r--net/sctp/sm_statefuns.c71
-rw-r--r--net/sctp/sm_statetable.c57
-rw-r--r--net/sctp/socket.c410
-rw-r--r--net/sctp/sysctl.c26
-rw-r--r--net/sctp/transport.c18
-rw-r--r--net/xfrm/xfrm_policy.c2
66 files changed, 1399 insertions, 1000 deletions
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index d9745bd31855..555ccaadb239 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -642,8 +642,7 @@ int __devinit acenic_probe (ACE_PROBE_ARG)
(pdev->device == PCI_DEVICE_ID_SGI_ACENIC)))
continue;
- dev = init_etherdev(NULL, sizeof(struct ace_private));
-
+ dev = alloc_etherdev(sizeof(struct ace_private));
if (dev == NULL) {
printk(KERN_ERR "acenic: Unable to allocate "
"net_device structure!\n");
@@ -653,13 +652,6 @@ int __devinit acenic_probe (ACE_PROBE_ARG)
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
- if (!dev->priv)
- dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL);
- if (!dev->priv) {
- printk(KERN_ERR "acenic: Unable to allocate memory\n");
- return -ENOMEM;
- }
-
ap = dev->priv;
ap->pdev = pdev;
@@ -739,6 +731,12 @@ int __devinit acenic_probe (ACE_PROBE_ARG)
break;
}
+ if (register_netdev(dev)) {
+ printk(KERN_ERR "acenic: device registration failed\n");
+ kfree(dev);
+ continue;
+ }
+
switch(pdev->vendor) {
case PCI_VENDOR_ID_ALTEON:
if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9100T) {
diff --git a/drivers/net/dummy.c b/drivers/net/dummy.c
index bd9c573c7362..7739a846cc19 100644
--- a/drivers/net/dummy.c
+++ b/drivers/net/dummy.c
@@ -51,15 +51,9 @@ static int dummy_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
}
#endif
-static int __init dummy_init(struct net_device *dev)
+static void __init dummy_setup(struct net_device *dev)
{
/* Initialize the device structure. */
-
- dev->priv = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
- if (dev->priv == NULL)
- return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct net_device_stats));
-
dev->get_stats = dummy_get_stats;
dev->hard_start_xmit = dummy_xmit;
dev->set_multicast_list = set_multicast_list;
@@ -72,8 +66,7 @@ static int __init dummy_init(struct net_device *dev)
dev->tx_queue_len = 0;
dev->flags |= IFF_NOARP;
dev->flags &= ~IFF_MULTICAST;
-
- return 0;
+ SET_MODULE_OWNER(dev);
}
static int dummy_xmit(struct sk_buff *skb, struct net_device *dev)
@@ -92,32 +85,30 @@ static struct net_device_stats *dummy_get_stats(struct net_device *dev)
return dev->priv;
}
-static struct net_device dev_dummy;
+static struct net_device *dev_dummy;
static int __init dummy_init_module(void)
{
int err;
- dev_dummy.init = dummy_init;
- SET_MODULE_OWNER(&dev_dummy);
+ dev_dummy = alloc_netdev(sizeof(struct net_device_stats),
+ "dummy%d", dummy_setup);
- /* Find a name for this unit */
- err=dev_alloc_name(&dev_dummy,"dummy%d");
- if(err<0)
- return err;
- err = register_netdev(&dev_dummy);
- if (err<0)
- return err;
- return 0;
+ if (!dev_dummy)
+ return -ENOMEM;
+
+ if ((err = register_netdev(dev_dummy))) {
+ kfree(dev_dummy);
+ dev_dummy = NULL;
+ }
+ return err;
}
static void __exit dummy_cleanup_module(void)
{
- unregister_netdev(&dev_dummy);
- kfree(dev_dummy.priv);
-
- memset(&dev_dummy, 0, sizeof(dev_dummy));
- dev_dummy.init = dummy_init;
+ unregister_netdev(dev_dummy);
+ kfree(dev_dummy);
+ dev_dummy = NULL;
}
module_init(dummy_init_module);
diff --git a/drivers/net/net_init.c b/drivers/net/net_init.c
index 1a7650a87df2..9979fbf91dd5 100644
--- a/drivers/net/net_init.c
+++ b/drivers/net/net_init.c
@@ -70,7 +70,7 @@
*/
-static struct net_device *alloc_netdev(int sizeof_priv, const char *mask,
+struct net_device *alloc_netdev(int sizeof_priv, const char *mask,
void (*setup)(struct net_device *))
{
struct net_device *dev;
@@ -96,6 +96,7 @@ static struct net_device *alloc_netdev(int sizeof_priv, const char *mask,
return dev;
}
+EXPORT_SYMBOL(alloc_netdev);
static struct net_device *init_alloc_dev(int sizeof_priv)
{
diff --git a/drivers/net/ppp_deflate.c b/drivers/net/ppp_deflate.c
index 14657acad571..7b029939be24 100644
--- a/drivers/net/ppp_deflate.c
+++ b/drivers/net/ppp_deflate.c
@@ -456,10 +456,10 @@ int z_decompress(void *arg, unsigned char *ibuf, int isize,
/* Check the sequence number. */
seq = (ibuf[PPP_HDRLEN] << 8) + ibuf[PPP_HDRLEN+1];
- if (seq != state->seqno) {
+ if (seq != (state->seqno & 0xffff)) {
if (state->debug)
printk(KERN_DEBUG "z_decompress%d: bad seq # %d, expected %d\n",
- state->unit, seq, state->seqno);
+ state->unit, seq, state->seqno & 0xffff);
return DECOMP_ERROR;
}
++state->seqno;
diff --git a/drivers/net/slip.c b/drivers/net/slip.c
index fd8ca72d62eb..a93ed6dc739e 100644
--- a/drivers/net/slip.c
+++ b/drivers/net/slip.c
@@ -1381,27 +1381,23 @@ static void __exit slip_exit(void)
local_bh_enable();
} while (busy && time_before(jiffies, timeout));
- busy = 0;
for (i = 0; i < slip_maxdev; i++) {
struct slip_ctrl *slc = slip_ctrls[i];
if (slc) {
unregister_netdev(&slc->dev);
if (slc->ctrl.tty) {
printk(KERN_ERR "%s: tty discipline is still running\n", slc->dev.name);
- /* Pin module forever */
- MOD_INC_USE_COUNT;
- busy++;
- continue;
+ /* Intentionally leak the control block. */
+ } else {
+ sl_free_bufs(&slc->ctrl);
+ kfree(slc);
}
- sl_free_bufs(&slc->ctrl);
- kfree(slc);
slip_ctrls[i] = NULL;
}
}
- if (!busy) {
- kfree(slip_ctrls);
- slip_ctrls = NULL;
- }
+
+ kfree(slip_ctrls);
+ slip_ctrls = NULL;
}
if ((i = tty_register_ldisc(N_SLIP, NULL)))
{
diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c
index 9975c84d0b98..a005eb8d53ad 100644
--- a/drivers/net/sungem.c
+++ b/drivers/net/sungem.c
@@ -2676,7 +2676,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev,
*/
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
pdev->device == PCI_DEVICE_ID_SUN_GEM &&
- !pci_set_dma_mask(pdev, (u64) 0xffffffffffffffff)) {
+ !pci_set_dma_mask(pdev, (u64) 0xffffffffffffffffULL)) {
pci_using_dac = 1;
} else {
err = pci_set_dma_mask(pdev, (u64) 0xffffffff);
diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h
index 5a94bb12099d..73045d630f43 100644
--- a/drivers/net/sungem.h
+++ b/drivers/net/sungem.h
@@ -805,14 +805,14 @@ struct gem_txd {
u64 buffer;
};
-#define TXDCTRL_BUFSZ 0x0000000000007fff /* Buffer Size */
-#define TXDCTRL_CSTART 0x00000000001f8000 /* CSUM Start Offset */
-#define TXDCTRL_COFF 0x000000001fe00000 /* CSUM Stuff Offset */
-#define TXDCTRL_CENAB 0x0000000020000000 /* CSUM Enable */
-#define TXDCTRL_EOF 0x0000000040000000 /* End of Frame */
-#define TXDCTRL_SOF 0x0000000080000000 /* Start of Frame */
-#define TXDCTRL_INTME 0x0000000100000000 /* "Interrupt Me" */
-#define TXDCTRL_NOCRC 0x0000000200000000 /* No CRC Present */
+#define TXDCTRL_BUFSZ 0x0000000000007fffULL /* Buffer Size */
+#define TXDCTRL_CSTART 0x00000000001f8000ULL /* CSUM Start Offset */
+#define TXDCTRL_COFF 0x000000001fe00000ULL /* CSUM Stuff Offset */
+#define TXDCTRL_CENAB 0x0000000020000000ULL /* CSUM Enable */
+#define TXDCTRL_EOF 0x0000000040000000ULL /* End of Frame */
+#define TXDCTRL_SOF 0x0000000080000000ULL /* Start of Frame */
+#define TXDCTRL_INTME 0x0000000100000000ULL /* "Interrupt Me" */
+#define TXDCTRL_NOCRC 0x0000000200000000ULL /* No CRC Present */
/* GEM requires that RX descriptors are provided four at a time,
* aligned. Also, the RX ring may not wrap around. This means that
@@ -840,13 +840,13 @@ struct gem_rxd {
u64 buffer;
};
-#define RXDCTRL_TCPCSUM 0x000000000000ffff /* TCP Pseudo-CSUM */
-#define RXDCTRL_BUFSZ 0x000000007fff0000 /* Buffer Size */
-#define RXDCTRL_OWN 0x0000000080000000 /* GEM owns this entry */
-#define RXDCTRL_HASHVAL 0x0ffff00000000000 /* Hash Value */
-#define RXDCTRL_HPASS 0x1000000000000000 /* Passed Hash Filter */
-#define RXDCTRL_ALTMAC 0x2000000000000000 /* Matched ALT MAC */
-#define RXDCTRL_BAD 0x4000000000000000 /* Frame has bad CRC */
+#define RXDCTRL_TCPCSUM 0x000000000000ffffULL /* TCP Pseudo-CSUM */
+#define RXDCTRL_BUFSZ 0x000000007fff0000ULL /* Buffer Size */
+#define RXDCTRL_OWN 0x0000000080000000ULL /* GEM owns this entry */
+#define RXDCTRL_HASHVAL 0x0ffff00000000000ULL /* Hash Value */
+#define RXDCTRL_HPASS 0x1000000000000000ULL /* Passed Hash Filter */
+#define RXDCTRL_ALTMAC 0x2000000000000000ULL /* Matched ALT MAC */
+#define RXDCTRL_BAD 0x4000000000000000ULL /* Frame has bad CRC */
#define RXDCTRL_FRESH(gp) \
((((RX_BUF_ALLOC_SIZE(gp) - RX_OFFSET) << 16) & RXDCTRL_BUFSZ) | \
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 3a9ff2f4445e..3787261c6ef5 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -32,10 +32,6 @@
#include <asm/byteorder.h>
#include <asm/uaccess.h>
-#ifndef PCI_DMA_BUS_IS_PHYS
-#define PCI_DMA_BUS_IS_PHYS 1
-#endif
-
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
#define TG3_VLAN_TAG_USED 1
#else
@@ -55,8 +51,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.5"
-#define DRV_MODULE_RELDATE "March 21, 2003"
+#define DRV_MODULE_VERSION "1.6"
+#define DRV_MODULE_RELDATE "June 11, 2003"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -2234,73 +2230,17 @@ static void tg3_tx_timeout(struct net_device *dev)
schedule_work(&tp->reset_task);
}
-#if !PCI_DMA_BUS_IS_PHYS
-static void tg3_set_txd_addr(struct tg3 *tp, int entry, dma_addr_t mapping)
-{
- if (tp->tg3_flags & TG3_FLAG_HOST_TXDS) {
- struct tg3_tx_buffer_desc *txd = &tp->tx_ring[entry];
-
- txd->addr_hi = ((u64) mapping >> 32);
- txd->addr_lo = ((u64) mapping & 0xffffffff);
- } else {
- unsigned long txd;
-
- txd = (tp->regs +
- NIC_SRAM_WIN_BASE +
- NIC_SRAM_TX_BUFFER_DESC);
- txd += (entry * TXD_SIZE);
-
- if (sizeof(dma_addr_t) != sizeof(u32))
- writel(((u64) mapping >> 32),
- txd + TXD_ADDR + TG3_64BIT_REG_HIGH);
-
- writel(((u64) mapping & 0xffffffff),
- txd + TXD_ADDR + TG3_64BIT_REG_LOW);
- }
-}
-#endif
-
static void tg3_set_txd(struct tg3 *, int, dma_addr_t, int, u32, u32);
static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
u32 guilty_entry, int guilty_len,
u32 last_plus_one, u32 *start, u32 mss)
{
+ struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
dma_addr_t new_addr;
u32 entry = *start;
int i;
-#if !PCI_DMA_BUS_IS_PHYS
- /* IOMMU, just map the guilty area again which is guaranteed to
- * use different addresses.
- */
-
- i = 0;
- while (entry != guilty_entry) {
- entry = NEXT_TX(entry);
- i++;
- }
- if (i == 0) {
- new_addr = pci_map_single(tp->pdev, skb->data, guilty_len,
- PCI_DMA_TODEVICE);
- } else {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
-
- new_addr = pci_map_page(tp->pdev,
- frag->page, frag->page_offset,
- guilty_len, PCI_DMA_TODEVICE);
- }
- pci_unmap_single(tp->pdev, pci_unmap_addr(&tp->tx_buffers[guilty_entry],
- mapping),
- guilty_len, PCI_DMA_TODEVICE);
- tg3_set_txd_addr(tp, guilty_entry, new_addr);
- pci_unmap_addr_set(&tp->tx_buffers[guilty_entry], mapping,
- new_addr);
- *start = last_plus_one;
-#else
- /* Oh well, no IOMMU, have to allocate a whole new SKB. */
- struct sk_buff *new_skb = skb_copy(skb, GFP_ATOMIC);
-
if (!new_skb) {
dev_kfree_skb(skb);
return -1;
@@ -2337,7 +2277,6 @@ static int tigon3_4gb_hwbug_workaround(struct tg3 *tp, struct sk_buff *skb,
}
dev_kfree_skb(skb);
-#endif
return 0;
}
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 8e159834ed06..352cdf3c1716 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -122,12 +122,6 @@ int tun_net_init(struct net_device *dev)
DBG(KERN_INFO "%s: tun_net_init\n", tun->name);
- SET_MODULE_OWNER(dev);
- dev->open = tun_net_open;
- dev->hard_start_xmit = tun_net_xmit;
- dev->stop = tun_net_close;
- dev->get_stats = tun_net_stats;
-
switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TUN_DEV:
/* Point-to-Point TUN Device */
@@ -199,14 +193,14 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv,
skb_reserve(skb, 2);
memcpy_fromiovec(skb_put(skb, len), iv, len);
- skb->dev = &tun->dev;
+ skb->dev = tun->dev;
switch (tun->flags & TUN_TYPE_MASK) {
case TUN_TUN_DEV:
skb->mac.raw = skb->data;
skb->protocol = pi.proto;
break;
case TUN_TAP_DEV:
- skb->protocol = eth_type_trans(skb, &tun->dev);
+ skb->protocol = eth_type_trans(skb, tun->dev);
break;
};
@@ -325,7 +319,7 @@ static ssize_t tun_chr_readv(struct file *file, const struct iovec *iv,
schedule();
continue;
}
- netif_start_queue(&tun->dev);
+ netif_start_queue(tun->dev);
ret = tun_put_user(tun, skb, (struct iovec *) iv, len);
@@ -347,6 +341,24 @@ static ssize_t tun_chr_read(struct file * file, char * buf,
return tun_chr_readv(file, &iv, 1, pos);
}
+static void tun_setup(struct net_device *dev)
+{
+ struct tun_struct *tun = dev->priv;
+
+ skb_queue_head_init(&tun->readq);
+ init_waitqueue_head(&tun->read_wait);
+
+ tun->owner = -1;
+ dev->init = tun_net_init;
+ tun->name = dev->name;
+ SET_MODULE_OWNER(dev);
+ dev->open = tun_net_open;
+ dev->hard_start_xmit = tun_net_xmit;
+ dev->stop = tun_net_close;
+ dev->get_stats = tun_net_stats;
+ dev->destructor = (void (*)(struct net_device *))kfree;
+}
+
static int tun_set_iff(struct file *file, struct ifreq *ifr)
{
struct tun_struct *tun;
@@ -367,30 +379,18 @@ static int tun_set_iff(struct file *file, struct ifreq *ifr)
return -EPERM;
} else {
char *name;
-
- /* Allocate new device */
- if (!(tun = kmalloc(sizeof(struct tun_struct), GFP_KERNEL)) )
- return -ENOMEM;
- memset(tun, 0, sizeof(struct tun_struct));
-
- skb_queue_head_init(&tun->readq);
- init_waitqueue_head(&tun->read_wait);
-
- tun->owner = -1;
- tun->dev.init = tun_net_init;
- tun->dev.priv = tun;
- SET_MODULE_OWNER(&tun->dev);
+ unsigned long flags = 0;
err = -EINVAL;
/* Set dev type */
if (ifr->ifr_flags & IFF_TUN) {
/* TUN device */
- tun->flags |= TUN_TUN_DEV;
+ flags |= TUN_TUN_DEV;
name = "tun%d";
} else if (ifr->ifr_flags & IFF_TAP) {
/* TAP device */
- tun->flags |= TUN_TAP_DEV;
+ flags |= TUN_TAP_DEV;
name = "tap%d";
} else
goto failed;
@@ -398,12 +398,27 @@ static int tun_set_iff(struct file *file, struct ifreq *ifr)
if (*ifr->ifr_name)
name = ifr->ifr_name;
- if ((err = dev_alloc_name(&tun->dev, name)) < 0)
- goto failed;
- if ((err = register_netdevice(&tun->dev)))
+ dev = alloc_netdev(sizeof(struct tun_struct), name,
+ tun_setup);
+ if (!dev)
+ return -ENOMEM;
+
+ tun = dev->priv;
+ tun->flags = flags;
+
+ if (strchr(dev->name, '%')) {
+ err = dev_alloc_name(dev, dev->name);
+ if (err < 0) {
+ kfree(dev);
+ goto failed;
+ }
+ }
+
+ if ((err = register_netdevice(tun->dev))) {
+ kfree(dev);
goto failed;
+ }
- tun->name = tun->dev.name;
}
DBG(KERN_INFO "%s: tun_set_iff\n", tun->name);
@@ -419,9 +434,7 @@ static int tun_set_iff(struct file *file, struct ifreq *ifr)
strcpy(ifr->ifr_name, tun->name);
return 0;
-
-failed:
- kfree(tun);
+ failed:
return err;
}
@@ -548,10 +561,8 @@ static int tun_chr_close(struct inode *inode, struct file *file)
/* Drop read queue */
skb_queue_purge(&tun->readq);
- if (!(tun->flags & TUN_PERSIST)) {
- dev_close(&tun->dev);
- unregister_netdevice(&tun->dev);
- }
+ if (!(tun->flags & TUN_PERSIST))
+ unregister_netdevice(tun->dev);
rtnl_unlock();
@@ -574,11 +585,10 @@ static struct file_operations tun_fops = {
.fasync = tun_chr_fasync
};
-static struct miscdevice tun_miscdev=
-{
- TUN_MINOR,
- "net/tun",
- &tun_fops
+static struct miscdevice tun_miscdev = {
+ .minor = TUN_MINOR,
+ .name = "net/tun",
+ .fops = &tun_fops
};
int __init tun_init(void)
diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h
index bac9b4d5ad03..d3b49237ed50 100644
--- a/include/linux/etherdevice.h
+++ b/include/linux/etherdevice.h
@@ -40,7 +40,6 @@ extern int eth_header_parse(struct sk_buff *skb,
unsigned char *haddr);
extern struct net_device *init_etherdev(struct net_device *dev, int sizeof_priv);
extern struct net_device *alloc_etherdev(int sizeof_priv);
-
static inline void eth_copy_and_sum (struct sk_buff *dest, unsigned char *src, int len, int base)
{
memcpy (dest->data, src, len);
diff --git a/include/linux/if_tun.h b/include/linux/if_tun.h
index 2af535344624..f8459e01c896 100644
--- a/include/linux/if_tun.h
+++ b/include/linux/if_tun.h
@@ -40,7 +40,7 @@ struct tun_struct {
wait_queue_head_t read_wait;
struct sk_buff_head readq;
- struct net_device dev;
+ struct net_device *dev;
struct net_device_stats stats;
struct fasync_struct *fasync;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d1f54f5930e6..ecc2225939b9 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -816,6 +816,8 @@ extern void tr_setup(struct net_device *dev);
extern void fc_setup(struct net_device *dev);
extern void fc_freedev(struct net_device *dev);
/* Support for loadable net-drivers */
+extern struct net_device *alloc_netdev(int sizeof_priv, const char *name,
+ void (*setup)(struct net_device *));
extern int register_netdev(struct net_device *dev);
extern void unregister_netdev(struct net_device *dev);
/* Functions used for multicast support */
diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h
index 7b11e236c7c9..d951537bab60 100644
--- a/include/linux/netfilter_arp/arp_tables.h
+++ b/include/linux/netfilter_arp/arp_tables.h
@@ -111,7 +111,7 @@ struct arpt_counters
#define ARPT_INV_ARPHRD 0x0080 /* Invert the sense of ARP HRD. */
#define ARPT_INV_ARPPRO 0x0100 /* Invert the sense of ARP PRO. */
#define ARPT_INV_ARPHLN 0x0200 /* Invert the sense of ARP HLN. */
-#define ARPT_INV_MASK 0x007F /* All possible flag bits mask. */
+#define ARPT_INV_MASK 0x03FF /* All possible flag bits mask. */
/* This structure defines each of the firewall rules. Consists of 3
parts which are 1) general ARP header stuff 2) match specific
diff --git a/include/net/flow.h b/include/net/flow.h
index ce136ec2a0b3..7ec259b5d632 100644
--- a/include/net/flow.h
+++ b/include/net/flow.h
@@ -87,7 +87,7 @@ typedef void (*flow_resolve_t)(struct flowi *key, u16 family, u8 dir,
extern void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
flow_resolve_t resolver);
-extern void flow_cache_flush(void *object);
+extern void flow_cache_flush(void);
extern atomic_t flow_cache_genid;
#endif
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index ed453f38f12d..ad78ed5f0535 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -119,12 +119,10 @@
*/
/*
- * sctp_protocol.c
+ * sctp/protocol.c
*/
-extern struct sctp_protocol sctp_proto;
extern struct sock *sctp_get_ctl_sock(void);
-extern int sctp_copy_local_addr_list(struct sctp_protocol *,
- struct sctp_bind_addr *,
+extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
sctp_scope_t, int gfp, int flags);
extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
@@ -275,6 +273,7 @@ extern atomic_t sctp_dbg_objcnt_assoc;
extern atomic_t sctp_dbg_objcnt_transport;
extern atomic_t sctp_dbg_objcnt_chunk;
extern atomic_t sctp_dbg_objcnt_bind_addr;
+extern atomic_t sctp_dbg_objcnt_bind_bucket;
extern atomic_t sctp_dbg_objcnt_addr;
extern atomic_t sctp_dbg_objcnt_ssnmap;
extern atomic_t sctp_dbg_objcnt_datamsg;
@@ -418,6 +417,10 @@ static inline __s32 sctp_jitter(__u32 rto)
static __u32 sctp_rand;
__s32 ret;
+ /* Avoid divide by zero. */
+ if (!rto)
+ rto = 1;
+
sctp_rand += jiffies;
sctp_rand ^= (sctp_rand << 12);
sctp_rand ^= (sctp_rand >> 20);
@@ -448,7 +451,7 @@ static inline int sctp_frag_point(const struct sctp_opt *sp, int pmtu)
* there is room for a param header too.
*/
#define sctp_walk_params(pos, chunk, member)\
-_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
+_sctp_walk_params((pos), (chunk), WORD_ROUND(ntohs((chunk)->chunk_hdr.length)), member)
#define _sctp_walk_params(pos, chunk, end, member)\
for (pos.v = chunk->member;\
@@ -456,6 +459,18 @@ for (pos.v = chunk->member;\
pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \
pos.v += WORD_ROUND(ntohs(pos.p->length)))
+#define sctp_walk_errors(err, chunk_hdr)\
+_sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length))
+
+#define _sctp_walk_errors(err, chunk_hdr, end)\
+for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
+ sizeof(sctp_chunkhdr_t));\
+ (void *)err <= (void *)chunk_hdr + end - sizeof(sctp_errhdr_t) &&\
+ (void *)err <= (void *)chunk_hdr + end - \
+ WORD_ROUND(ntohs(err->length));\
+ err = (sctp_errhdr_t *)((void *)err + \
+ WORD_ROUND(ntohs(err->length))))
+
/* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3)
@@ -491,12 +506,6 @@ void sctp_put_port(struct sock *sk);
/* Static inline functions. */
-/* Return the SCTP protocol structure. */
-static inline struct sctp_protocol *sctp_get_protocol(void)
-{
- return &sctp_proto;
-}
-
/* Convert from an IP version number to an Address Family symbol. */
static inline int ipver2af(__u8 ipver)
{
@@ -524,24 +533,21 @@ static inline int sctp_sanity_check(void)
/* This is the hash function for the SCTP port hash table. */
static inline int sctp_phashfn(__u16 lport)
{
- struct sctp_protocol *sctp_proto = sctp_get_protocol();
- return (lport & (sctp_proto->port_hashsize - 1));
+ return (lport & (sctp_port_hashsize - 1));
}
/* This is the hash function for the endpoint hash table. */
static inline int sctp_ep_hashfn(__u16 lport)
{
- struct sctp_protocol *sctp_proto = sctp_get_protocol();
- return (lport & (sctp_proto->ep_hashsize - 1));
+ return (lport & (sctp_ep_hashsize - 1));
}
/* This is the hash function for the association hash table. */
static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
{
- struct sctp_protocol *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport;
h ^= h>>8;
- return (h & (sctp_proto->assoc_hashsize - 1));
+ return (h & (sctp_assoc_hashsize - 1));
}
/* This is the hash function for the association hash table. This is
@@ -550,10 +556,9 @@ static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
*/
static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
{
- struct sctp_protocol *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport;
h ^= vtag;
- return (h & (sctp_proto->assoc_hashsize-1));
+ return (h & (sctp_assoc_hashsize-1));
}
/* WARNING: Do not change the layout of the members in sctp_sock! */
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index e804c52c82fc..235f4728865a 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -71,7 +71,7 @@ union sctp_addr {
};
/* Forward declarations for data structures. */
-struct sctp_protocol;
+struct sctp_globals;
struct sctp_endpoint;
struct sctp_association;
struct sctp_transport;
@@ -92,28 +92,28 @@ struct sctp_ssnmap;
/* Structures useful for managing bind/connect. */
-typedef struct sctp_bind_bucket {
+struct sctp_bind_bucket {
unsigned short port;
unsigned short fastreuse;
struct sctp_bind_bucket *next;
struct sctp_bind_bucket **pprev;
struct sock *sk;
-} sctp_bind_bucket_t;
+};
-typedef struct sctp_bind_hashbucket {
+struct sctp_bind_hashbucket {
spinlock_t lock;
struct sctp_bind_bucket *chain;
-} sctp_bind_hashbucket_t;
+};
/* Used for hashing all associations. */
-typedef struct sctp_hashbucket {
+struct sctp_hashbucket {
rwlock_t lock;
struct sctp_ep_common *chain;
-} sctp_hashbucket_t __attribute__((__aligned__(8)));
+} __attribute__((__aligned__(8)));
-/* The SCTP protocol structure. */
-struct sctp_protocol {
+/* The SCTP globals structure. */
+extern struct sctp_globals {
/* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
*
* The following protocol parameters are RECOMMENDED:
@@ -167,17 +167,17 @@ struct sctp_protocol {
/* This is the hash of all endpoints. */
int ep_hashsize;
- sctp_hashbucket_t *ep_hashbucket;
+ struct sctp_hashbucket *ep_hashbucket;
/* This is the hash of all associations. */
int assoc_hashsize;
- sctp_hashbucket_t *assoc_hashbucket;
+ struct sctp_hashbucket *assoc_hashbucket;
/* This is the sctp port control hash. */
int port_hashsize;
int port_rover;
spinlock_t port_alloc_lock; /* Protects port_rover. */
- sctp_bind_hashbucket_t *port_hashtable;
+ struct sctp_bind_hashbucket *port_hashtable;
/* This is the global local address list.
* We actively maintain this complete list of interfaces on
@@ -187,8 +187,33 @@ struct sctp_protocol {
*/
struct list_head local_addr_list;
spinlock_t local_addr_lock;
-};
-
+} sctp_globals;
+
+#define sctp_rto_initial (sctp_globals.rto_initial)
+#define sctp_rto_min (sctp_globals.rto_min)
+#define sctp_rto_max (sctp_globals.rto_max)
+#define sctp_rto_alpha (sctp_globals.rto_alpha)
+#define sctp_rto_beta (sctp_globals.rto_beta)
+#define sctp_max_burst (sctp_globals.max_burst)
+#define sctp_valid_cookie_life (sctp_globals.valid_cookie_life)
+#define sctp_cookie_preserve_enable (sctp_globals.cookie_preserve_enable)
+#define sctp_max_retrans_association (sctp_globals.max_retrans_association)
+#define sctp_max_retrans_path (sctp_globals.max_retrans_path)
+#define sctp_max_retrans_init (sctp_globals.max_retrans_init)
+#define sctp_hb_interval (sctp_globals.hb_interval)
+#define sctp_max_instreams (sctp_globals.max_instreams)
+#define sctp_max_outstreams (sctp_globals.max_outstreams)
+#define sctp_address_families (sctp_globals.address_families)
+#define sctp_ep_hashsize (sctp_globals.ep_hashsize)
+#define sctp_ep_hashbucket (sctp_globals.ep_hashbucket)
+#define sctp_assoc_hashsize (sctp_globals.assoc_hashsize)
+#define sctp_assoc_hashbucket (sctp_globals.assoc_hashbucket)
+#define sctp_port_hashsize (sctp_globals.port_hashsize)
+#define sctp_port_rover (sctp_globals.port_rover)
+#define sctp_port_alloc_lock (sctp_globals.port_alloc_lock)
+#define sctp_port_hashtable (sctp_globals.port_hashtable)
+#define sctp_local_addr_list (sctp_globals.local_addr_list)
+#define sctp_local_addr_lock (sctp_globals.local_addr_lock)
/*
* Pointers to address related SCTP functions.
@@ -239,7 +264,9 @@ struct sctp_af {
int (*is_any) (const union sctp_addr *);
int (*available) (const union sctp_addr *);
int (*skb_iif) (const struct sk_buff *sk);
- int (*is_ce) (const struct sk_buff *sk);
+ int (*is_ce) (const struct sk_buff *sk);
+ void (*seq_dump_addr)(struct seq_file *seq,
+ union sctp_addr *addr);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
@@ -289,6 +316,10 @@ struct sctp_opt {
/* Various Socket Options. */
__u16 default_stream;
__u32 default_ppid;
+ __u16 default_flags;
+ __u32 default_context;
+ __u32 default_timetolive;
+
struct sctp_initmsg initmsg;
struct sctp_rtoinfo rtoinfo;
struct sctp_paddrparams paddrparam;
@@ -461,7 +492,7 @@ struct sctp_datamsg {
/* Reference counting. */
atomic_t refcnt;
/* When is this message no longer interesting to the peer? */
- unsigned long expires_at;
+ unsigned long expires_at;
/* Did the messenge fail to send? */
int send_error;
char send_failed;
@@ -1492,13 +1523,12 @@ struct sctp_association {
*/
int counters[SCTP_NUMBER_COUNTERS];
- struct {
- __u16 stream;
- __u16 flags;
- __u32 ppid;
- __u32 context;
- __u32 timetolive;
- } defaults;
+ /* Default send parameters. */
+ __u16 default_stream;
+ __u16 default_flags;
+ __u32 default_ppid;
+ __u32 default_context;
+ __u32 default_timetolive;
/* This tracks outbound ssn for a given stream. */
struct sctp_ssnmap *ssnmap;
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 5494148e0b10..1569a2de6805 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -485,6 +485,11 @@ struct sctp_paddrinfo {
__u32 spinfo_mtu;
};
+/* Peer addresses's state. */
+enum sctp_spinfo_state {
+ SCTP_INACTIVE,
+ SCTP_ACTIVE,
+};
/*
* 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index b166e3ea34a8..f4dd67ca3118 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -334,6 +334,33 @@ static int unregister_vlan_device(const char *vlan_IF_name)
return ret;
}
+static void vlan_setup(struct net_device *new_dev)
+{
+ SET_MODULE_OWNER(new_dev);
+
+ /* new_dev->ifindex = 0; it will be set when added to
+ * the global list.
+ * iflink is set as well.
+ */
+ new_dev->get_stats = vlan_dev_get_stats;
+
+ /* Make this thing known as a VLAN device */
+ new_dev->priv_flags |= IFF_802_1Q_VLAN;
+
+ /* Set us up to have no queue, as the underlying Hardware device
+ * can do all the queueing we could want.
+ */
+ new_dev->tx_queue_len = 0;
+
+ /* set up method calls */
+ new_dev->change_mtu = vlan_dev_change_mtu;
+ new_dev->open = vlan_dev_open;
+ new_dev->stop = vlan_dev_stop;
+ new_dev->set_mac_address = vlan_dev_set_mac_address;
+ new_dev->set_multicast_list = vlan_dev_set_multicast_list;
+ new_dev->destructor = (void (*)(struct net_device *)) kfree;
+}
+
/* Attach a VLAN device to a mac address (ie Ethernet Card).
* Returns the device that was created, or NULL if there was
* an error of some kind.
@@ -344,8 +371,8 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
struct vlan_group *grp;
struct net_device *new_dev;
struct net_device *real_dev; /* the ethernet device */
- int malloc_size = 0;
int r;
+ char name[IFNAMSIZ];
#ifdef VLAN_DEBUG
printk(VLAN_DBG "%s: if_name -:%s:- vid: %i\n",
@@ -403,21 +430,6 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
goto out_unlock;
}
- malloc_size = (sizeof(struct net_device));
- new_dev = (struct net_device *) kmalloc(malloc_size, GFP_KERNEL);
- VLAN_MEM_DBG("net_device malloc, addr: %p size: %i\n",
- new_dev, malloc_size);
-
- if (new_dev == NULL)
- goto out_unlock;
-
- memset(new_dev, 0, malloc_size);
-
- /* Set us up to have no queue, as the underlying Hardware device
- * can do all the queueing we could want.
- */
- new_dev->tx_queue_len = 0;
-
/* Gotta set up the fields for the device. */
#ifdef VLAN_DEBUG
printk(VLAN_DBG "About to allocate name, vlan_name_type: %i\n",
@@ -426,54 +438,44 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
switch (vlan_name_type) {
case VLAN_NAME_TYPE_RAW_PLUS_VID:
/* name will look like: eth1.0005 */
- sprintf(new_dev->name, "%s.%.4i", real_dev->name, VLAN_ID);
+ snprintf(name, IFNAMSIZ, "%s.%.4i", real_dev->name, VLAN_ID);
break;
case VLAN_NAME_TYPE_PLUS_VID_NO_PAD:
/* Put our vlan.VID in the name.
* Name will look like: vlan5
*/
- sprintf(new_dev->name, "vlan%i", VLAN_ID);
+ snprintf(name, IFNAMSIZ, "vlan%i", VLAN_ID);
break;
case VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD:
/* Put our vlan.VID in the name.
* Name will look like: eth0.5
*/
- sprintf(new_dev->name, "%s.%i", real_dev->name, VLAN_ID);
+ snprintf(name, IFNAMSIZ, "%s.%i", real_dev->name, VLAN_ID);
break;
case VLAN_NAME_TYPE_PLUS_VID:
/* Put our vlan.VID in the name.
* Name will look like: vlan0005
*/
default:
- sprintf(new_dev->name, "vlan%.4i", VLAN_ID);
+ snprintf(name, IFNAMSIZ, "vlan%.4i", VLAN_ID);
};
+ new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name,
+ vlan_setup);
+ if (new_dev == NULL)
+ goto out_unlock;
+
#ifdef VLAN_DEBUG
printk(VLAN_DBG "Allocated new name -:%s:-\n", new_dev->name);
#endif
- /* set up method calls */
- new_dev->init = vlan_dev_init;
- new_dev->destructor = vlan_dev_destruct;
- SET_MODULE_OWNER(new_dev);
-
- /* new_dev->ifindex = 0; it will be set when added to
- * the global list.
- * iflink is set as well.
- */
- new_dev->get_stats = vlan_dev_get_stats;
-
/* IFF_BROADCAST|IFF_MULTICAST; ??? */
new_dev->flags = real_dev->flags;
new_dev->flags &= ~IFF_UP;
- /* Make this thing known as a VLAN device */
- new_dev->priv_flags |= IFF_802_1Q_VLAN;
-
/* need 4 bytes for extra VLAN header info,
* hope the underlying device can handle it.
*/
new_dev->mtu = real_dev->mtu;
- new_dev->change_mtu = vlan_dev_change_mtu;
/* TODO: maybe just assign it to be ETHERNET? */
new_dev->type = real_dev->type;
@@ -484,24 +486,14 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
new_dev->hard_header_len += VLAN_HLEN;
}
- new_dev->priv = kmalloc(sizeof(struct vlan_dev_info),
- GFP_KERNEL);
VLAN_MEM_DBG("new_dev->priv malloc, addr: %p size: %i\n",
new_dev->priv,
sizeof(struct vlan_dev_info));
- if (new_dev->priv == NULL)
- goto out_free_newdev;
-
- memset(new_dev->priv, 0, sizeof(struct vlan_dev_info));
-
memcpy(new_dev->broadcast, real_dev->broadcast, real_dev->addr_len);
memcpy(new_dev->dev_addr, real_dev->dev_addr, real_dev->addr_len);
new_dev->addr_len = real_dev->addr_len;
- new_dev->open = vlan_dev_open;
- new_dev->stop = vlan_dev_stop;
-
if (real_dev->features & NETIF_F_HW_VLAN_TX) {
new_dev->hard_header = real_dev->hard_header;
new_dev->hard_start_xmit = vlan_dev_hwaccel_hard_start_xmit;
@@ -512,8 +504,6 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
new_dev->rebuild_header = vlan_dev_rebuild_header;
}
new_dev->hard_header_parse = real_dev->hard_header_parse;
- new_dev->set_mac_address = vlan_dev_set_mac_address;
- new_dev->set_multicast_list = vlan_dev_set_multicast_list;
VLAN_DEV_INFO(new_dev)->vlan_id = VLAN_ID; /* 1 through VLAN_VID_MASK */
VLAN_DEV_INFO(new_dev)->real_dev = real_dev;
@@ -526,7 +516,7 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
#endif
if (register_netdevice(new_dev))
- goto out_free_newdev_priv;
+ goto out_free_newdev;
/* So, got the sucker initialized, now lets place
* it into our local structure.
@@ -572,9 +562,7 @@ static struct net_device *register_vlan_device(const char *eth_IF_name,
out_free_unregister:
unregister_netdev(new_dev);
-
-out_free_newdev_priv:
- kfree(new_dev->priv);
+ goto out_put_dev;
out_free_newdev:
kfree(new_dev);
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index 540b3a90fc09..edf62471f955 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -65,8 +65,6 @@ int vlan_dev_change_mtu(struct net_device *dev, int new_mtu);
int vlan_dev_set_mac_address(struct net_device *dev, void* addr);
int vlan_dev_open(struct net_device* dev);
int vlan_dev_stop(struct net_device* dev);
-int vlan_dev_init(struct net_device* dev);
-void vlan_dev_destruct(struct net_device* dev);
int vlan_dev_set_ingress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
int vlan_dev_set_egress_priority(char* dev_name, __u32 skb_prio, short vlan_prio);
int vlan_dev_set_vlan_flag(char* dev_name, __u32 flag, short flag_val);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 24a4462cc152..25364ceaef50 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -766,28 +766,6 @@ int vlan_dev_stop(struct net_device *dev)
vlan_flush_mc_list(dev);
return 0;
}
-
-int vlan_dev_init(struct net_device *dev)
-{
- /* TODO: figure this out, maybe do nothing?? */
- return 0;
-}
-
-void vlan_dev_destruct(struct net_device *dev)
-{
- if (dev) {
- vlan_flush_mc_list(dev);
- if (dev->priv) {
- if (VLAN_DEV_INFO(dev)->dent)
- BUG();
-
- kfree(dev->priv);
- dev->priv = NULL;
- }
- kfree(dev);
- }
-}
-
/** Taken from Gleb + Lennert's VLAN code, and modified... */
void vlan_dev_set_multicast_list(struct net_device *vlan_dev)
{
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 724b968b05dd..b4e92cb875c6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -110,10 +110,6 @@ static int br_dev_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
return -1;
}
-static void br_dev_destruct(struct net_device *dev)
-{
- kfree(dev->priv);
-}
void br_dev_setup(struct net_device *dev)
{
@@ -124,10 +120,13 @@ void br_dev_setup(struct net_device *dev)
dev->hard_start_xmit = br_dev_xmit;
dev->open = br_dev_open;
dev->set_multicast_list = br_dev_set_multicast_list;
- dev->destructor = br_dev_destruct;
+ dev->destructor = (void (*)(struct net_device *))kfree;
SET_MODULE_OWNER(dev);
dev->stop = br_dev_stop;
dev->accept_fastpath = br_dev_accept_fastpath;
dev->tx_queue_len = 0;
dev->set_mac_address = NULL;
+ dev->priv_flags = IFF_EBRIDGE;
+
+ ether_setup(dev);
}
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 2062884bd2c2..7d72f02a50a8 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -78,17 +78,14 @@ static struct net_bridge *new_nb(const char *name)
struct net_bridge *br;
struct net_device *dev;
- if ((br = kmalloc(sizeof(*br), GFP_KERNEL)) == NULL)
+ dev = alloc_netdev(sizeof(struct net_bridge), name,
+ br_dev_setup);
+
+ if (!dev)
return NULL;
- memset(br, 0, sizeof(*br));
- dev = &br->dev;
-
- strlcpy(dev->name, name, sizeof(dev->name));
- dev->priv = br;
- dev->priv_flags = IFF_EBRIDGE;
- ether_setup(dev);
- br_dev_setup(dev);
+ br = dev->priv;
+ br->dev = dev;
br->lock = SPIN_LOCK_UNLOCKED;
INIT_LIST_HEAD(&br->port_list);
@@ -159,9 +156,9 @@ int br_add_bridge(const char *name)
if ((br = new_nb(name)) == NULL)
return -ENOMEM;
- ret = register_netdev(&br->dev);
+ ret = register_netdev(br->dev);
if (ret)
- kfree(br);
+ kfree(br->dev);
return ret;
}
@@ -219,7 +216,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
br_stp_recalculate_bridge_id(br);
br_fdb_insert(br, p, dev->dev_addr, 1);
- if ((br->dev.flags & IFF_UP) && (dev->flags & IFF_UP))
+ if ((br->dev->flags & IFF_UP) && (dev->flags & IFF_UP))
br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 889680712af7..1e8502adb49c 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -40,7 +40,7 @@ static void br_pass_frame_up(struct net_bridge *br, struct sk_buff *skb)
br->statistics.rx_bytes += skb->len;
indev = skb->dev;
- skb->dev = &br->dev;
+ skb->dev = br->dev;
NF_HOOK(PF_BRIDGE, NF_BR_LOCAL_IN, skb, indev, NULL,
br_pass_frame_up_finish);
@@ -67,7 +67,7 @@ int br_handle_frame_finish(struct sk_buff *skb)
br = p->br;
passedup = 0;
- if (br->dev.flags & IFF_PROMISC) {
+ if (br->dev->flags & IFF_PROMISC) {
struct sk_buff *skb2;
skb2 = skb_clone(skb, GFP_ATOMIC);
@@ -140,7 +140,7 @@ int br_handle_frame(struct sk_buff *skb)
return -1;
}
- if (!memcmp(p->br->dev.dev_addr, dest, ETH_ALEN))
+ if (!memcmp(p->br->dev->dev_addr, dest, ETH_ALEN))
skb->pkt_type = PACKET_HOST;
NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index c21cec286875..607a63581988 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -37,7 +37,7 @@
sizeof(struct bridge_skb_cb)))
#define has_bridge_parent(device) ((device)->br_port != NULL)
-#define bridge_parent(device) (&((device)->br_port->br->dev))
+#define bridge_parent(device) ((device)->br_port->br->dev)
/* We need these fake structures to make netfilter happy --
* lots of places assume that skb->dst != NULL, which isn't
diff --git a/net/bridge/br_notify.c b/net/bridge/br_notify.c
index 142c9290f342..72cc91e87523 100644
--- a/net/bridge/br_notify.c
+++ b/net/bridge/br_notify.c
@@ -52,7 +52,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break;
case NETDEV_DOWN:
- if (br->dev.flags & IFF_UP) {
+ if (br->dev->flags & IFF_UP) {
spin_lock_bh(&br->lock);
br_stp_disable_port(p);
spin_unlock_bh(&br->lock);
@@ -60,7 +60,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
break;
case NETDEV_UP:
- if (!(br->dev.flags & IFF_UP)) {
+ if (!(br->dev->flags & IFF_UP)) {
spin_lock_bh(&br->lock);
br_stp_enable_port(p);
spin_unlock_bh(&br->lock);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 2e9d1a483ee1..ca814cfff2db 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -81,7 +81,7 @@ struct net_bridge
{
spinlock_t lock;
struct list_head port_list;
- struct net_device dev;
+ struct net_device *dev;
struct net_device_stats statistics;
rwlock_t hash_lock;
struct hlist_head hash[BR_HASH_SIZE];
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index a2ba31bc9e7a..26e5ef0e7d92 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -26,7 +26,7 @@ static const char *br_port_state_names[] = {
void br_log_state(const struct net_bridge_port *p)
{
pr_info("%s: port %d(%s) entering %s state\n",
- p->br->dev.name, p->port_no, p->dev->name,
+ p->br->dev->name, p->port_no, p->dev->name,
br_port_state_names[p->state]);
}
@@ -130,7 +130,7 @@ void br_become_root_bridge(struct net_bridge *br)
br_topology_change_detection(br);
del_timer(&br->tcn_timer);
- if (br->dev.flags & IFF_UP) {
+ if (br->dev->flags & IFF_UP) {
br_config_bpdu_generation(br);
mod_timer(&br->hello_timer, jiffies + br->hello_time);
}
@@ -289,10 +289,10 @@ static inline void br_topology_change_acknowledged(struct net_bridge *br)
/* called under bridge lock */
void br_topology_change_detection(struct net_bridge *br)
{
- if (!(br->dev.flags & IFF_UP))
+ if (!(br->dev->flags & IFF_UP))
return;
- pr_info("%s: topology change detected", br->dev.name);
+ pr_info("%s: topology change detected", br->dev->name);
if (br_is_root_bridge(br)) {
printk(", propagating");
br->topology_change = 1;
@@ -446,7 +446,7 @@ void br_received_tcn_bpdu(struct net_bridge_port *p)
{
if (br_is_designated_port(p)) {
pr_info("%s: received tcn bpdu on port %i(%s)\n",
- p->br->dev.name, p->port_no, p->dev->name);
+ p->br->dev->name, p->port_no, p->dev->name);
br_topology_change_detection(p->br);
br_topology_change_acknowledge(p);
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index 1989dbf371ca..b95fa3a880e2 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -145,7 +145,7 @@ void br_stp_handle_bpdu(struct sk_buff *skb)
spin_lock_bh(&br->lock);
if (p->state == BR_STATE_DISABLED
- || !(br->dev.flags & IFF_UP)
+ || !(br->dev->flags & IFF_UP)
|| !br->stp_enabled
|| memcmp(buf, header, 6))
goto out;
diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c
index 43de31e752af..f644f2164a5e 100644
--- a/net/bridge/br_stp_if.c
+++ b/net/bridge/br_stp_if.c
@@ -93,7 +93,7 @@ void br_stp_disable_port(struct net_bridge_port *p)
br = p->br;
printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
- br->dev.name, p->port_no, p->dev->name, "disabled");
+ br->dev->name, p->port_no, p->dev->name, "disabled");
wasroot = br_is_root_bridge(br);
br_become_designated_port(p);
@@ -124,7 +124,7 @@ static void br_stp_change_bridge_id(struct net_bridge *br, unsigned char *addr)
memcpy(oldaddr, br->bridge_id.addr, ETH_ALEN);
memcpy(br->bridge_id.addr, addr, ETH_ALEN);
- memcpy(br->dev.dev_addr, addr, ETH_ALEN);
+ memcpy(br->dev->dev_addr, addr, ETH_ALEN);
list_for_each_entry(p, &br->port_list, list) {
if (!memcmp(p->designated_bridge.addr, oldaddr, ETH_ALEN))
diff --git a/net/bridge/br_stp_timer.c b/net/bridge/br_stp_timer.c
index 72af7397b047..a5a75cede729 100644
--- a/net/bridge/br_stp_timer.c
+++ b/net/bridge/br_stp_timer.c
@@ -38,9 +38,9 @@ static void br_hello_timer_expired(unsigned long arg)
{
struct net_bridge *br = (struct net_bridge *)arg;
- pr_debug("%s: hello timer expired\n", br->dev.name);
+ pr_debug("%s: hello timer expired\n", br->dev->name);
spin_lock_bh(&br->lock);
- if (br->dev.flags & IFF_UP) {
+ if (br->dev->flags & IFF_UP) {
br_config_bpdu_generation(br);
br->hello_timer.expires = jiffies + br->hello_time;
@@ -61,7 +61,7 @@ static void br_message_age_timer_expired(unsigned long arg)
pr_info("%s: neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)\n",
- br->dev.name,
+ br->dev->name,
id->prio[0], id->prio[1],
id->addr[0], id->addr[1], id->addr[2],
id->addr[3], id->addr[4], id->addr[5],
@@ -89,7 +89,7 @@ static void br_forward_delay_timer_expired(unsigned long arg)
struct net_bridge *br = p->br;
pr_debug("%s: %d(%s) forward delay timer\n",
- br->dev.name, p->port_no, p->dev->name);
+ br->dev->name, p->port_no, p->dev->name);
spin_lock_bh(&br->lock);
if (p->state == BR_STATE_LISTENING) {
p->state = BR_STATE_LEARNING;
@@ -108,9 +108,9 @@ static void br_tcn_timer_expired(unsigned long arg)
{
struct net_bridge *br = (struct net_bridge *) arg;
- pr_debug("%s: tcn timer expired\n", br->dev.name);
+ pr_debug("%s: tcn timer expired\n", br->dev->name);
spin_lock_bh(&br->lock);
- if (br->dev.flags & IFF_UP) {
+ if (br->dev->flags & IFF_UP) {
br_transmit_tcn(br);
br->tcn_timer.expires = jiffies + br->bridge_hello_time;
@@ -123,7 +123,7 @@ static void br_topology_change_timer_expired(unsigned long arg)
{
struct net_bridge *br = (struct net_bridge *) arg;
- pr_debug("%s: topo change timer expired\n", br->dev.name);
+ pr_debug("%s: topo change timer expired\n", br->dev->name);
spin_lock_bh(&br->lock);
br->topology_change_detected = 0;
br->topology_change = 0;
@@ -135,7 +135,7 @@ static void br_hold_timer_expired(unsigned long arg)
struct net_bridge_port *p = (struct net_bridge_port *) arg;
pr_debug("%s: %d(%s) hold timer expired\n",
- p->br->dev.name, p->port_no, p->dev->name);
+ p->br->dev->name, p->port_no, p->dev->name);
spin_lock_bh(&p->br->lock);
if (p->config_pending)
diff --git a/net/bridge/netfilter/ebt_redirect.c b/net/bridge/netfilter/ebt_redirect.c
index d482b3dd7932..be4346095c67 100644
--- a/net/bridge/netfilter/ebt_redirect.c
+++ b/net/bridge/netfilter/ebt_redirect.c
@@ -22,7 +22,7 @@ static int ebt_target_redirect(struct sk_buff **pskb, unsigned int hooknr,
if (hooknr != NF_BR_BROUTING)
memcpy((**pskb).mac.ethernet->h_dest,
- in->br_port->br->dev.dev_addr, ETH_ALEN);
+ in->br_port->br->dev->dev_addr, ETH_ALEN);
else {
memcpy((**pskb).mac.ethernet->h_dest,
in->dev_addr, ETH_ALEN);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 7f32804f201a..33b687d60efe 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -135,10 +135,10 @@ static inline int ebt_basic_match(struct ebt_entry *e, struct ethhdr *h,
if (FWINV2(ebt_dev_check(e->out, out), EBT_IOUT))
return 1;
if ((!in || !in->br_port) ? 0 : FWINV2(ebt_dev_check(
- e->logical_in, &in->br_port->br->dev), EBT_ILOGICALIN))
+ e->logical_in, in->br_port->br->dev), EBT_ILOGICALIN))
return 1;
if ((!out || !out->br_port) ? 0 : FWINV2(ebt_dev_check(
- e->logical_out, &out->br_port->br->dev), EBT_ILOGICALOUT))
+ e->logical_out, out->br_port->br->dev), EBT_ILOGICALOUT))
return 1;
if (e->bitmask & EBT_SOURCEMAC) {
diff --git a/net/core/dev.c b/net/core/dev.c
index 9f2ef589af23..c5309ea8d4ad 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3005,14 +3005,15 @@ subsys_initcall(net_dev_init);
#ifdef CONFIG_HOTPLUG
struct net_hotplug_todo {
- struct net_hotplug_todo *next;
+ struct list_head list;
char ifname[IFNAMSIZ];
int is_register;
};
static spinlock_t net_hotplug_list_lock = SPIN_LOCK_UNLOCKED;
-static struct net_hotplug_todo *net_hotplug_list;
+static DECLARE_MUTEX(net_hotplug_run);
+static struct list_head net_hotplug_list = LIST_HEAD_INIT(net_hotplug_list);
-static void net_run_hotplug_one(struct net_hotplug_todo *ent)
+static inline void net_run_hotplug_one(struct net_hotplug_todo *ent)
{
char *argv[3], *envp[5], ifname[12 + IFNAMSIZ], action_str[32];
int i;
@@ -3037,23 +3038,37 @@ static void net_run_hotplug_one(struct net_hotplug_todo *ent)
call_usermodehelper(argv [0], argv, envp, 0);
}
+/* Run all queued hotplug requests.
+ * Requests are run in FIFO order.
+ */
static void net_run_hotplug_todo(void)
{
- struct net_hotplug_todo *list;
+ struct list_head list = LIST_HEAD_INIT(list);
+
+ /* This is racy but okay since any other requests will get
+ * processed when the other guy does rtnl_unlock.
+ */
+ if (list_empty(&net_hotplug_list))
+ return;
+
+ /* Need to guard against multiple cpu's getting out of order. */
+ down(&net_hotplug_run);
+ /* Snapshot list, allow later requests */
spin_lock(&net_hotplug_list_lock);
- list = net_hotplug_list;
- net_hotplug_list = NULL;
+ list_splice_init(&net_hotplug_list, &list);
spin_unlock(&net_hotplug_list_lock);
- while (list != NULL) {
- struct net_hotplug_todo *next = list->next;
+ while (!list_empty(&list)) {
+ struct net_hotplug_todo *ent;
- net_run_hotplug_one(list);
-
- kfree(list);
- list = next;
+ ent = list_entry(list.next, struct net_hotplug_todo, list);
+ list_del(&ent->list);
+ net_run_hotplug_one(ent);
+ kfree(ent);
}
+
+ up(&net_hotplug_run);
}
/* Notify userspace when a netdevice event occurs,
@@ -3065,15 +3080,17 @@ static void net_run_sbin_hotplug(struct net_device *dev, int is_register)
{
struct net_hotplug_todo *ent = kmalloc(sizeof(*ent), GFP_KERNEL);
+ ASSERT_RTNL();
+
if (!ent)
return;
+ INIT_LIST_HEAD(&ent->list);
memcpy(ent->ifname, dev->name, IFNAMSIZ);
ent->is_register = is_register;
spin_lock(&net_hotplug_list_lock);
- ent->next = net_hotplug_list;
- net_hotplug_list = ent;
+ list_add(&ent->list, &net_hotplug_list);
spin_unlock(&net_hotplug_list_lock);
}
#endif
diff --git a/net/core/flow.c b/net/core/flow.c
index f631056949d9..5ef82c3b7ac4 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -15,6 +15,9 @@
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/completion.h>
+#include <linux/percpu.h>
+#include <linux/bitops.h>
+#include <linux/notifier.h>
#include <net/flow.h>
#include <asm/atomic.h>
#include <asm/semaphore.h>
@@ -33,7 +36,10 @@ atomic_t flow_cache_genid = ATOMIC_INIT(0);
static u32 flow_hash_shift;
#define flow_hash_size (1 << flow_hash_shift)
-static struct flow_cache_entry **flow_table;
+static DEFINE_PER_CPU(struct flow_cache_entry **, flow_tables) = { NULL };
+
+#define flow_table(cpu) (per_cpu(flow_tables, cpu))
+
static kmem_cache_t *flow_cachep;
static int flow_lwm, flow_hwm;
@@ -43,30 +49,39 @@ struct flow_percpu_info {
u32 hash_rnd;
int count;
} ____cacheline_aligned;
-static struct flow_percpu_info flow_hash_info[NR_CPUS];
+static DEFINE_PER_CPU(struct flow_percpu_info, flow_hash_info) = { 0 };
-#define flow_hash_rnd_recalc(cpu) (flow_hash_info[cpu].hash_rnd_recalc)
-#define flow_hash_rnd(cpu) (flow_hash_info[cpu].hash_rnd)
-#define flow_count(cpu) (flow_hash_info[cpu].count)
+#define flow_hash_rnd_recalc(cpu) \
+ (per_cpu(flow_hash_info, cpu).hash_rnd_recalc)
+#define flow_hash_rnd(cpu) \
+ (per_cpu(flow_hash_info, cpu).hash_rnd)
+#define flow_count(cpu) \
+ (per_cpu(flow_hash_info, cpu).count)
static struct timer_list flow_hash_rnd_timer;
#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ)
struct flow_flush_info {
- void *object;
atomic_t cpuleft;
+ unsigned long cpumap;
struct completion completion;
};
-static struct tasklet_struct flow_flush_tasklets[NR_CPUS];
-static DECLARE_MUTEX(flow_flush_sem);
+static DEFINE_PER_CPU(struct tasklet_struct, flow_flush_tasklets) = { NULL };
+
+#define flow_flush_tasklet(cpu) (&per_cpu(flow_flush_tasklets, cpu))
+
+static DECLARE_MUTEX(flow_cache_cpu_sem);
+static unsigned long flow_cache_cpu_map;
+static unsigned int flow_cache_cpu_count;
static void flow_cache_new_hashrnd(unsigned long arg)
{
int i;
for (i = 0; i < NR_CPUS; i++)
- flow_hash_rnd_recalc(i) = 1;
+ if (test_bit(i, &flow_cache_cpu_map))
+ flow_hash_rnd_recalc(i) = 1;
flow_hash_rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
add_timer(&flow_hash_rnd_timer);
@@ -80,7 +95,7 @@ static void __flow_cache_shrink(int cpu, int shrink_to)
for (i = 0; i < flow_hash_size; i++) {
int k = 0;
- flp = &flow_table[cpu*flow_hash_size+i];
+ flp = &flow_table(cpu)[i];
while ((fle = *flp) != NULL && k < shrink_to) {
k++;
flp = &fle->next;
@@ -160,11 +175,16 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
local_bh_disable();
cpu = smp_processor_id();
+
+ fle = NULL;
+ if (!test_bit(cpu, &flow_cache_cpu_map))
+ goto nocache;
+
if (flow_hash_rnd_recalc(cpu))
flow_new_hash_rnd(cpu);
hash = flow_hash_code(key, cpu);
- head = &flow_table[(cpu << flow_hash_shift) + hash];
+ head = &flow_table(cpu)[hash];
for (fle = *head; fle; fle = fle->next) {
if (fle->family == family &&
fle->dir == dir &&
@@ -198,6 +218,7 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
}
}
+nocache:
{
void *obj;
atomic_t *obj_ref;
@@ -224,18 +245,20 @@ void *flow_cache_lookup(struct flowi *key, u16 family, u8 dir,
static void flow_cache_flush_tasklet(unsigned long data)
{
struct flow_flush_info *info = (void *)data;
- void *object = info->object;
int i;
int cpu;
cpu = smp_processor_id();
for (i = 0; i < flow_hash_size; i++) {
- struct flow_cache_entry *fle, **flp;
+ struct flow_cache_entry *fle;
- flp = &flow_table[(cpu << flow_hash_shift) + i];
- for (; (fle = *flp) != NULL; flp = &fle->next) {
- if (fle->object != object)
+ fle = flow_table(cpu)[i];
+ for (; fle; fle = fle->next) {
+ unsigned genid = atomic_read(&flow_cache_genid);
+
+ if (!fle->object || fle->genid == genid)
continue;
+
fle->object = NULL;
atomic_dec(fle->object_ref);
}
@@ -245,6 +268,7 @@ static void flow_cache_flush_tasklet(unsigned long data)
complete(&info->completion);
}
+static void flow_cache_flush_per_cpu(void *) __attribute__((__unused__));
static void flow_cache_flush_per_cpu(void *data)
{
struct flow_flush_info *info = data;
@@ -252,24 +276,31 @@ static void flow_cache_flush_per_cpu(void *data)
struct tasklet_struct *tasklet;
cpu = smp_processor_id();
- tasklet = &flow_flush_tasklets[cpu];
- tasklet_init(tasklet, flow_cache_flush_tasklet, (unsigned long)info);
+ if (!test_bit(cpu, &info->cpumap))
+ return;
+
+ tasklet = flow_flush_tasklet(cpu);
+ tasklet->data = (unsigned long)info;
tasklet_schedule(tasklet);
}
-void flow_cache_flush(void *object)
+void flow_cache_flush(void)
{
struct flow_flush_info info;
+ static DECLARE_MUTEX(flow_flush_sem);
+
+ down(&flow_cache_cpu_sem);
+ info.cpumap = flow_cache_cpu_map;
+ atomic_set(&info.cpuleft, flow_cache_cpu_count);
+ up(&flow_cache_cpu_sem);
- info.object = object;
- atomic_set(&info.cpuleft, num_online_cpus());
init_completion(&info.completion);
down(&flow_flush_sem);
- smp_call_function(flow_cache_flush_per_cpu, &info, 1, 0);
local_bh_disable();
- flow_cache_flush_per_cpu(&info);
+ smp_call_function(flow_cache_flush_per_cpu, &info, 1, 0);
+ flow_cache_flush_tasklet((unsigned long)&info);
local_bh_enable();
wait_for_completion(&info.completion);
@@ -277,11 +308,51 @@ void flow_cache_flush(void *object)
up(&flow_flush_sem);
}
-static int __init flow_cache_init(void)
+static void __devinit flow_cache_cpu_online(int cpu)
{
+ struct tasklet_struct *tasklet;
unsigned long order;
- int i;
+ flow_hash_rnd_recalc(cpu) = 1;
+
+ for (order = 0;
+ (PAGE_SIZE << order) <
+ (sizeof(struct flow_cache_entry *)*flow_hash_size);
+ order++)
+ /* NOTHING */;
+
+ flow_table(cpu) = (struct flow_cache_entry **)
+ __get_free_pages(GFP_KERNEL, order);
+
+ memset(flow_table(cpu), 0, PAGE_SIZE << order);
+
+ tasklet = flow_flush_tasklet(cpu);
+ tasklet_init(tasklet, flow_cache_flush_tasklet, 0);
+
+ down(&flow_cache_cpu_sem);
+ set_bit(cpu, &flow_cache_cpu_map);
+ flow_cache_cpu_count++;
+ up(&flow_cache_cpu_sem);
+}
+
+static int __devinit flow_cache_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ unsigned long cpu = (unsigned long)cpu;
+ switch (action) {
+ case CPU_UP_PREPARE:
+ flow_cache_cpu_online(cpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __devinitdata flow_cache_cpu_nb = {
+ .notifier_call = flow_cache_cpu_notify,
+};
+
+static int __init flow_cache_init(void)
+{
flow_cachep = kmem_cache_create("flow_cache",
sizeof(struct flow_cache_entry),
0, SLAB_HWCACHE_ALIGN,
@@ -294,27 +365,13 @@ static int __init flow_cache_init(void)
flow_lwm = 2 * flow_hash_size;
flow_hwm = 4 * flow_hash_size;
- for (i = 0; i < NR_CPUS; i++)
- flow_hash_rnd_recalc(i) = 1;
-
init_timer(&flow_hash_rnd_timer);
flow_hash_rnd_timer.function = flow_cache_new_hashrnd;
flow_hash_rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD;
add_timer(&flow_hash_rnd_timer);
- for (order = 0;
- (PAGE_SIZE << order) <
- (NR_CPUS*sizeof(struct flow_entry *)*flow_hash_size);
- order++)
- /* NOTHING */;
-
- flow_table = (struct flow_cache_entry **)
- __get_free_pages(GFP_ATOMIC, order);
-
- if (!flow_table)
- panic("Failed to allocate flow cache hash table\n");
-
- memset(flow_table, 0, PAGE_SIZE << order);
+ flow_cache_cpu_online(smp_processor_id());
+ register_cpu_notifier(&flow_cache_cpu_nb);
return 0;
}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 257460cb0749..c640ad5b41f9 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1094,6 +1094,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
kfree(p);
return NULL;
}
+ p->sysctl_table = NULL;
write_lock_bh(&tbl->lock);
p->next = tbl->parms.next;
tbl->parms.next = p;
@@ -1113,9 +1114,6 @@ void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
if (*p == parms) {
*p = parms->next;
write_unlock_bh(&tbl->lock);
-#ifdef CONFIG_SYSCTL
- neigh_sysctl_unregister(parms);
-#endif
kfree(parms);
return;
}
@@ -1178,9 +1176,6 @@ int neigh_table_clear(struct neigh_table *tbl)
}
}
write_unlock(&neigh_tbl_lock);
-#ifdef CONFIG_SYSCTL
- neigh_sysctl_unregister(&tbl->parms);
-#endif
return 0;
}
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 209df2e5452c..7bab8839e6b9 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -3,10 +3,6 @@
*
* Copyright (c) 2003 Stephen Hemminber <shemminger@osdl.org>
*
- *
- * TODO:
- * last_tx
- * last_rx
*/
#include <linux/config.h>
@@ -16,33 +12,61 @@
#include <net/sock.h>
#include <linux/rtnetlink.h>
-#define to_net_dev(class) container_of((class), struct net_device, class_dev)
+#define to_class_dev(obj) container_of(obj,struct class_device,kobj)
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+
+/* use same locking rules as GIF* ioctl's */
+static ssize_t netdev_show(const struct class_device *cd, char *buf,
+ ssize_t (*format)(const struct net_device *, char *))
+{
+ struct net_device *net = to_net_dev(cd);
+ ssize_t ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (!net->deadbeaf)
+ ret = (*format)(net, buf);
+ read_unlock(&dev_base_lock);
+
+ return ret;
+}
-/* generate a show function for simple field */
+/* generate a show function for simple field */
#define NETDEVICE_SHOW(field, format_string) \
-static ssize_t show_##field(struct class_device *dev, char *buf) \
+static ssize_t format_##field(const struct net_device *net, char *buf) \
{ \
- return sprintf(buf, format_string, to_net_dev(dev)->field); \
+ return sprintf(buf, format_string, net->field); \
+} \
+static ssize_t show_##field(struct class_device *cd, char *buf) \
+{ \
+ return netdev_show(cd, buf, format_##field); \
}
-/* generate a store function for a field with locking */
-#define NETDEVICE_STORE(field) \
-static ssize_t \
-store_##field(struct class_device *dev, const char *buf, size_t len) \
-{ \
- char *endp; \
- long new = simple_strtol(buf, &endp, 16); \
- \
- if (endp == buf || new < 0) \
- return -EINVAL; \
- \
- if (!capable(CAP_NET_ADMIN)) \
- return -EPERM; \
- \
- rtnl_lock(); \
- to_net_dev(dev)->field = new; \
- rtnl_unlock(); \
- return len; \
+
+/* use same locking and permission rules as SIF* ioctl's */
+static ssize_t netdev_store(struct class_device *dev,
+ const char *buf, size_t len,
+ int (*set)(struct net_device *, unsigned long))
+{
+ struct net_device *net = to_net_dev(dev);
+ char *endp;
+ unsigned long new;
+ int ret = -EINVAL;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ new = simple_strtoul(buf, &endp, 0);
+ if (endp == buf)
+ goto err;
+
+ rtnl_lock();
+ if (!net->deadbeaf) {
+ if ((ret = (*set)(net, new)) == 0)
+ ret = len;
+ }
+ rtnl_unlock();
+ err:
+ return ret;
}
/* generate a read-only network device class attribute */
@@ -56,6 +80,7 @@ NETDEVICE_ATTR(ifindex, "%d\n");
NETDEVICE_ATTR(features, "%#x\n");
NETDEVICE_ATTR(type, "%d\n");
+/* use same locking rules as GIFHWADDR ioctl's */
static ssize_t format_addr(char *buf, const unsigned char *addr, int len)
{
int i;
@@ -72,12 +97,16 @@ static ssize_t format_addr(char *buf, const unsigned char *addr, int len)
static ssize_t show_address(struct class_device *dev, char *buf)
{
struct net_device *net = to_net_dev(dev);
+ if (net->deadbeaf)
+ return -EINVAL;
return format_addr(buf, net->dev_addr, net->addr_len);
}
static ssize_t show_broadcast(struct class_device *dev, char *buf)
{
struct net_device *net = to_net_dev(dev);
+ if (net->deadbeaf)
+ return -EINVAL;
return format_addr(buf, net->broadcast, net->addr_len);
}
@@ -87,54 +116,45 @@ static CLASS_DEVICE_ATTR(broadcast, S_IRUGO, show_broadcast, NULL);
/* read-write attributes */
NETDEVICE_SHOW(mtu, "%d\n");
-static ssize_t store_mtu(struct class_device *dev, const char *buf, size_t len)
+static int change_mtu(struct net_device *net, unsigned long new_mtu)
{
- char *endp;
- int new_mtu;
- int err;
-
- new_mtu = simple_strtoul(buf, &endp, 10);
- if (endp == buf)
- return -EINVAL;
-
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- rtnl_lock();
- err = dev_set_mtu(to_net_dev(dev), new_mtu);
- rtnl_unlock();
+ return dev_set_mtu(net, (int) new_mtu);
+}
- return err == 0 ? len : err;
+static ssize_t store_mtu(struct class_device *dev, const char *buf, size_t len)
+{
+ return netdev_store(dev, buf, len, change_mtu);
}
static CLASS_DEVICE_ATTR(mtu, S_IRUGO | S_IWUSR, show_mtu, store_mtu);
NETDEVICE_SHOW(flags, "%#x\n");
+static int change_flags(struct net_device *net, unsigned long new_flags)
+{
+ return dev_change_flags(net, (unsigned) new_flags);
+}
+
static ssize_t store_flags(struct class_device *dev, const char *buf, size_t len)
{
- unsigned long new_flags;
- char *endp;
- int err = 0;
+ return netdev_store(dev, buf, len, change_flags);
+}
- new_flags = simple_strtoul(buf, &endp, 16);
- if (endp == buf)
- return -EINVAL;
+static CLASS_DEVICE_ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags);
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- rtnl_lock();
- err = dev_change_flags(to_net_dev(dev), new_flags);
- rtnl_unlock();
+NETDEVICE_SHOW(tx_queue_len, "%lu\n");
- return err ? err : len;
+static int change_tx_queue_len(struct net_device *net, unsigned long new_len)
+{
+ net->tx_queue_len = new_len;
+ return 0;
}
-static CLASS_DEVICE_ATTR(flags, S_IRUGO | S_IWUSR, show_flags, store_flags);
+static ssize_t store_tx_queue_len(struct class_device *dev, const char *buf, size_t len)
+{
+ return netdev_store(dev, buf,len, change_tx_queue_len);
+}
-NETDEVICE_SHOW(tx_queue_len, "%lu\n");
-NETDEVICE_STORE(tx_queue_len);
static CLASS_DEVICE_ATTR(tx_queue_len, S_IRUGO | S_IWUSR, show_tx_queue_len,
store_tx_queue_len);
@@ -237,16 +257,17 @@ netstat_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct netstat_fs_entry *entry
= container_of(attr, struct netstat_fs_entry, attr);
- struct class_device *class_dev
- = container_of(kobj->parent, struct class_device, kobj);
struct net_device *dev
- = to_net_dev(class_dev);
- struct net_device_stats *stats
- = dev->get_stats ? dev->get_stats(dev) : NULL;
-
- if (stats && entry->show)
- return entry->show(stats, buf);
- return -EINVAL;
+ = to_net_dev(to_class_dev(kobj->parent));
+ struct net_device_stats *stats;
+ ssize_t ret = -EINVAL;
+
+ read_lock(&dev_base_lock);
+ if (!dev->deadbeaf && entry->show && dev->get_stats &&
+ (stats = (*dev->get_stats)(dev)))
+ ret = entry->show(stats, buf);
+ read_unlock(&dev_base_lock);
+ return ret;
}
static struct sysfs_ops netstat_sysfs_ops = {
@@ -278,17 +299,11 @@ int netdev_register_sysfs(struct net_device *net)
goto out_unreg;
}
-
net->stats_kobj.parent = NULL;
if (net->get_stats) {
struct kobject *k = &net->stats_kobj;
- k->parent = kobject_get(&class_dev->kobj);
- if (!k->parent) {
- ret = -EBUSY;
- goto out_unreg;
- }
-
+ k->parent = &class_dev->kobj;
strlcpy(k->name, "statistics", KOBJ_NAME_LEN);
k->ktype = &netstat_ktype;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 3ccb6c6c2978..d1d3ecfd293d 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -197,7 +197,9 @@ static void inetdev_destroy(struct in_device *in_dev)
/* in_dev_put following below will kill the in_device */
write_unlock_bh(&inetdev_lock);
-
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_unregister(in_dev->arp_parms);
+#endif
neigh_parms_release(&arp_tbl, in_dev->arp_parms);
in_dev_put(in_dev);
}
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 677c3c8f7f2e..8d72a478f244 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -114,20 +114,13 @@
*/
static int ipgre_tunnel_init(struct net_device *dev);
+static void ipgre_tunnel_setup(struct net_device *dev);
/* Fallback tunnel: no source, no destination, no key, no options */
static int ipgre_fb_tunnel_init(struct net_device *dev);
-static struct net_device ipgre_fb_tunnel_dev = {
- .name = "gre0",
- .init = ipgre_fb_tunnel_init
-};
-
-static struct ip_tunnel ipgre_fb_tunnel = {
- .dev = &ipgre_fb_tunnel_dev,
- .parms ={ .name = "gre0" }
-};
+static struct net_device *ipgre_fb_tunnel_dev;
/* Tunnel hash table */
@@ -190,8 +183,9 @@ static struct ip_tunnel * ipgre_tunnel_lookup(u32 remote, u32 local, u32 key)
if (t->parms.i_key == key && (t->dev->flags&IFF_UP))
return t;
}
- if (ipgre_fb_tunnel_dev.flags&IFF_UP)
- return &ipgre_fb_tunnel;
+
+ if (ipgre_fb_tunnel_dev->flags&IFF_UP)
+ return ipgre_fb_tunnel_dev->priv;
return NULL;
}
@@ -246,6 +240,7 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
struct net_device *dev;
unsigned h = HASH(key);
int prio = 0;
+ char name[IFNAMSIZ];
if (local)
prio |= 1;
@@ -262,32 +257,28 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
if (!create)
return NULL;
- dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL);
- if (dev == NULL)
- return NULL;
-
- memset(dev, 0, sizeof(*dev) + sizeof(*t));
- dev->priv = (void*)(dev+1);
- nt = (struct ip_tunnel*)dev->priv;
- nt->dev = dev;
- dev->init = ipgre_tunnel_init;
- memcpy(&nt->parms, parms, sizeof(*parms));
- nt->parms.name[IFNAMSIZ-1] = '\0';
- strcpy(dev->name, nt->parms.name);
- if (dev->name[0] == 0) {
+ if (parms->name[0])
+ strlcpy(name, parms->name, IFNAMSIZ);
+ else {
int i;
for (i=1; i<100; i++) {
- sprintf(dev->name, "gre%d", i);
- if (__dev_get_by_name(dev->name) == NULL)
+ sprintf(name, "gre%d", i);
+ if (__dev_get_by_name(name) == NULL)
break;
}
if (i==100)
goto failed;
- memcpy(nt->parms.name, dev->name, IFNAMSIZ);
}
- SET_MODULE_OWNER(dev);
- if (register_netdevice(dev) < 0)
+
+ dev = alloc_netdev(sizeof(*t), name, ipgre_tunnel_setup);
+ if (register_netdevice(dev) < 0) {
+ kfree(dev);
goto failed;
+ }
+
+ nt = dev->priv;
+ dev->init = ipgre_tunnel_init;
+ nt->parms = *parms;
dev_hold(dev);
ipgre_tunnel_link(nt);
@@ -295,16 +286,9 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct ip_tunnel_parm *parms, int
return nt;
failed:
- kfree(dev);
return NULL;
}
-static void ipgre_tunnel_destructor(struct net_device *dev)
-{
- if (dev != &ipgre_fb_tunnel_dev)
- kfree(dev);
-}
-
static void ipgre_tunnel_uninit(struct net_device *dev)
{
ipgre_tunnel_unlink((struct ip_tunnel*)dev->priv);
@@ -916,7 +900,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGETTUNNEL:
t = NULL;
- if (dev == &ipgre_fb_tunnel_dev) {
+ if (dev == ipgre_fb_tunnel_dev) {
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
err = -EFAULT;
break;
@@ -955,8 +939,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
t = ipgre_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
- if (dev != &ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
- t != &ipgre_fb_tunnel) {
+ if (dev != ipgre_fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
err = -EEXIST;
@@ -1006,7 +989,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if (!capable(CAP_NET_ADMIN))
goto done;
- if (dev == &ipgre_fb_tunnel_dev) {
+ if (dev == ipgre_fb_tunnel_dev) {
err = -EFAULT;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
goto done;
@@ -1014,7 +997,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if ((t = ipgre_tunnel_locate(&p, 0)) == NULL)
goto done;
err = -EPERM;
- if (t == &ipgre_fb_tunnel)
+ if (t == ipgre_fb_tunnel_dev->priv)
goto done;
dev = t->dev;
}
@@ -1140,12 +1123,11 @@ static int ipgre_close(struct net_device *dev)
#endif
-static void ipgre_tunnel_init_gen(struct net_device *dev)
+static void ipgre_tunnel_setup(struct net_device *dev)
{
- struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
-
+ SET_MODULE_OWNER(dev);
dev->uninit = ipgre_tunnel_uninit;
- dev->destructor = ipgre_tunnel_destructor;
+ dev->destructor = (void (*)(struct net_device *))kfree;
dev->hard_start_xmit = ipgre_tunnel_xmit;
dev->get_stats = ipgre_tunnel_get_stats;
dev->do_ioctl = ipgre_tunnel_ioctl;
@@ -1157,8 +1139,6 @@ static void ipgre_tunnel_init_gen(struct net_device *dev)
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
- memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
}
static int ipgre_tunnel_init(struct net_device *dev)
@@ -1173,7 +1153,9 @@ static int ipgre_tunnel_init(struct net_device *dev)
tunnel = (struct ip_tunnel*)dev->priv;
iph = &tunnel->parms.iph;
- ipgre_tunnel_init_gen(dev);
+ tunnel->dev = dev;
+ memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
/* Guess output device to choose reasonable mtu and hard_header_len */
@@ -1231,18 +1213,15 @@ static int ipgre_tunnel_init(struct net_device *dev)
int __init ipgre_fb_tunnel_init(struct net_device *dev)
{
struct ip_tunnel *tunnel = (struct ip_tunnel*)dev->priv;
- struct iphdr *iph;
-
- ipgre_tunnel_init_gen(dev);
+ struct iphdr *iph = &tunnel->parms.iph;
- iph = &ipgre_fb_tunnel.parms.iph;
iph->version = 4;
iph->protocol = IPPROTO_GRE;
iph->ihl = 5;
tunnel->hlen = sizeof(struct iphdr) + 4;
dev_hold(dev);
- tunnels_wc[0] = &ipgre_fb_tunnel;
+ tunnels_wc[0] = tunnel;
return 0;
}
@@ -1259,6 +1238,8 @@ static struct inet_protocol ipgre_protocol = {
int __init ipgre_init(void)
{
+ int err = -EINVAL;
+
printk(KERN_INFO "GRE over IPv4 tunneling driver\n");
if (inet_add_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) {
@@ -1266,10 +1247,23 @@ int __init ipgre_init(void)
return -EAGAIN;
}
- ipgre_fb_tunnel_dev.priv = (void*)&ipgre_fb_tunnel;
- SET_MODULE_OWNER(&ipgre_fb_tunnel_dev);
- register_netdev(&ipgre_fb_tunnel_dev);
- return 0;
+ ipgre_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "gre0",
+ ipgre_tunnel_setup);
+ if (!ipgre_fb_tunnel_dev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ ipgre_fb_tunnel_dev->init = ipgre_fb_tunnel_init;
+
+ if ((err = register_netdev(ipgre_fb_tunnel_dev)))
+ goto fail;
+out:
+ return err;
+fail:
+ inet_del_protocol(&ipgre_protocol, IPPROTO_GRE);
+ kfree(ipgre_fb_tunnel_dev);
+ goto out;
}
void ipgre_fini(void)
@@ -1277,7 +1271,7 @@ void ipgre_fini(void)
if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0)
printk(KERN_INFO "ipgre close: can't remove protocol\n");
- unregister_netdev(&ipgre_fb_tunnel_dev);
+ unregister_netdev(ipgre_fb_tunnel_dev);
}
#ifdef MODULE
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 0571696c7cc6..59c26022efc9 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -122,16 +122,9 @@
static int ipip_fb_tunnel_init(struct net_device *dev);
static int ipip_tunnel_init(struct net_device *dev);
+static void ipip_tunnel_setup(struct net_device *dev);
-static struct net_device ipip_fb_tunnel_dev = {
- .name = "tunl0",
- .init = ipip_fb_tunnel_init,
-};
-
-static struct ip_tunnel ipip_fb_tunnel = {
- .dev = &ipip_fb_tunnel_dev,
- .parms ={ .name = "tunl0", }
-};
+static struct net_device *ipip_fb_tunnel_dev;
static struct ip_tunnel *tunnels_r_l[HASH_SIZE];
static struct ip_tunnel *tunnels_r[HASH_SIZE];
@@ -216,6 +209,7 @@ static struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int c
struct net_device *dev;
unsigned h = 0;
int prio = 0;
+ char name[IFNAMSIZ];
if (remote) {
prio |= 2;
@@ -232,32 +226,33 @@ static struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int c
if (!create)
return NULL;
- dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL);
- if (dev == NULL)
- return NULL;
-
- memset(dev, 0, sizeof(*dev) + sizeof(*t));
- dev->priv = (void*)(dev+1);
- nt = (struct ip_tunnel*)dev->priv;
- nt->dev = dev;
- dev->init = ipip_tunnel_init;
- memcpy(&nt->parms, parms, sizeof(*parms));
- nt->parms.name[IFNAMSIZ-1] = '\0';
- strcpy(dev->name, nt->parms.name);
- if (dev->name[0] == 0) {
+ if (parms->name[0])
+ strlcpy(name, parms->name, IFNAMSIZ);
+ else {
int i;
for (i=1; i<100; i++) {
- sprintf(dev->name, "tunl%d", i);
- if (__dev_get_by_name(dev->name) == NULL)
+ sprintf(name, "tunl%d", i);
+ if (__dev_get_by_name(name) == NULL)
break;
}
if (i==100)
goto failed;
- memcpy(nt->parms.name, dev->name, IFNAMSIZ);
}
+
+ dev = alloc_netdev(sizeof(*t), name, ipip_tunnel_setup);
+ if (dev == NULL)
+ return NULL;
+
+ nt = dev->priv;
SET_MODULE_OWNER(dev);
- if (register_netdevice(dev) < 0)
+ dev->init = ipip_tunnel_init;
+ dev->destructor = (void (*)(struct net_device *))kfree;
+ nt->parms = *parms;
+
+ if (register_netdevice(dev) < 0) {
+ kfree(dev);
goto failed;
+ }
dev_hold(dev);
ipip_tunnel_link(nt);
@@ -265,19 +260,12 @@ static struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int c
return nt;
failed:
- kfree(dev);
return NULL;
}
-static void ipip_tunnel_destructor(struct net_device *dev)
-{
- if (dev != &ipip_fb_tunnel_dev)
- kfree(dev);
-}
-
static void ipip_tunnel_uninit(struct net_device *dev)
{
- if (dev == &ipip_fb_tunnel_dev) {
+ if (dev == ipip_fb_tunnel_dev) {
write_lock_bh(&ipip_lock);
tunnels_wc[0] = NULL;
write_unlock_bh(&ipip_lock);
@@ -682,7 +670,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGETTUNNEL:
t = NULL;
- if (dev == &ipip_fb_tunnel_dev) {
+ if (dev == ipip_fb_tunnel_dev) {
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
err = -EFAULT;
break;
@@ -715,8 +703,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
t = ipip_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
- if (dev != &ipip_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
- t != &ipip_fb_tunnel) {
+ if (dev != ipip_fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
err = -EEXIST;
@@ -757,7 +744,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if (!capable(CAP_NET_ADMIN))
goto done;
- if (dev == &ipip_fb_tunnel_dev) {
+ if (dev == ipip_fb_tunnel_dev) {
err = -EFAULT;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
goto done;
@@ -765,7 +752,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if ((t = ipip_tunnel_locate(&p, 0)) == NULL)
goto done;
err = -EPERM;
- if (t == &ipip_fb_tunnel)
+ if (t->dev == ipip_fb_tunnel_dev)
goto done;
dev = t->dev;
}
@@ -793,12 +780,10 @@ static int ipip_tunnel_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static void ipip_tunnel_init_gen(struct net_device *dev)
+static void ipip_tunnel_setup(struct net_device *dev)
{
- struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
-
+ SET_MODULE_OWNER(dev);
dev->uninit = ipip_tunnel_uninit;
- dev->destructor = ipip_tunnel_destructor;
dev->hard_start_xmit = ipip_tunnel_xmit;
dev->get_stats = ipip_tunnel_get_stats;
dev->do_ioctl = ipip_tunnel_ioctl;
@@ -810,8 +795,6 @@ static void ipip_tunnel_init_gen(struct net_device *dev)
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
- memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
}
static int ipip_tunnel_init(struct net_device *dev)
@@ -822,8 +805,9 @@ static int ipip_tunnel_init(struct net_device *dev)
tunnel = (struct ip_tunnel*)dev->priv;
iph = &tunnel->parms.iph;
-
- ipip_tunnel_init_gen(dev);
+ tunnel->dev = dev;
+ memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
if (iph->daddr) {
struct flowi fl = { .oif = tunnel->parms.link,
@@ -854,17 +838,15 @@ static int ipip_tunnel_init(struct net_device *dev)
static int __init ipip_fb_tunnel_init(struct net_device *dev)
{
- struct iphdr *iph;
+ struct ip_tunnel *tunnel = dev->priv;
+ struct iphdr *iph = &tunnel->parms.iph;
- ipip_tunnel_init_gen(dev);
-
- iph = &ipip_fb_tunnel.parms.iph;
iph->version = 4;
iph->protocol = IPPROTO_IPIP;
iph->ihl = 5;
dev_hold(dev);
- tunnels_wc[0] = &ipip_fb_tunnel;
+ tunnels_wc[0] = tunnel;
return 0;
}
@@ -878,6 +860,8 @@ static char banner[] __initdata =
int __init ipip_init(void)
{
+ int err;
+
printk(banner);
if (xfrm4_tunnel_register(&ipip_handler) < 0) {
@@ -885,10 +869,24 @@ int __init ipip_init(void)
return -EAGAIN;
}
- ipip_fb_tunnel_dev.priv = (void*)&ipip_fb_tunnel;
- SET_MODULE_OWNER(&ipip_fb_tunnel_dev);
- register_netdev(&ipip_fb_tunnel_dev);
- return 0;
+ ipip_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel),
+ "tunl0",
+ ipip_tunnel_setup);
+ if (!ipip_fb_tunnel_dev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ ipip_fb_tunnel_dev->init = ipip_fb_tunnel_init;
+
+ if ((err = register_netdev(ipip_fb_tunnel_dev)))
+ goto fail;
+ out:
+ return err;
+ fail:
+ xfrm4_tunnel_deregister(&ipip_handler);
+ kfree(ipip_fb_tunnel_dev);
+ goto out;
}
static void __exit ipip_fini(void)
@@ -896,7 +894,7 @@ static void __exit ipip_fini(void)
if (xfrm4_tunnel_deregister(&ipip_handler) < 0)
printk(KERN_INFO "ipip close: can't deregister tunnel\n");
- unregister_netdev(&ipip_fb_tunnel_dev);
+ unregister_netdev(ipip_fb_tunnel_dev);
}
#ifdef MODULE
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
index 05e7798d2973..6bc5027ad5a3 100644
--- a/net/ipv4/netfilter/iptable_filter.c
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -107,10 +107,6 @@ ipt_local_out_hook(unsigned int hook,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
- /* FIXME: Push down to extensions --RR */
- if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
- return NF_DROP;
-
/* root is playing with raw sockets. */
if ((*pskb)->len < sizeof(struct iphdr)
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
index 65232b030090..ab4fe4e6ea81 100644
--- a/net/ipv4/netfilter/iptable_mangle.c
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -145,10 +145,6 @@ ipt_local_hook(unsigned int hook,
u_int32_t saddr, daddr;
unsigned long nfmark;
- /* FIXME: Push down to extensions --RR */
- if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
- return NF_DROP;
-
/* root is playing with raw sockets. */
if ((*pskb)->len < sizeof(struct iphdr)
|| (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) {
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 2135ee473a2d..3de2d008ee12 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -172,6 +172,7 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
struct iphdr *iph = skb->nh.iph;
u8 *xprth = skb->nh.raw + iph->ihl*4;
+ memset(fl, 0, sizeof(struct flowi));
if (!(iph->frag_off & htons(IP_MF | IP_OFFSET))) {
switch (iph->protocol) {
case IPPROTO_UDP:
@@ -212,8 +213,6 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl)
fl->fl_ipsec_spi = 0;
break;
};
- } else {
- memset(fl, 0, sizeof(struct flowi));
}
fl->proto = iph->protocol;
fl->fl4_dst = iph->daddr;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d2eb499b3a11..e610a4a6023b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1893,10 +1893,11 @@ static int addrconf_ifdown(struct net_device *dev, int how)
/* Shot the device (if unregistered) */
if (how == 1) {
- neigh_parms_release(&nd_tbl, idev->nd_parms);
#ifdef CONFIG_SYSCTL
addrconf_sysctl_unregister(&idev->cnf);
+ neigh_sysctl_unregister(idev->nd_parms);
#endif
+ neigh_parms_release(&nd_tbl, idev->nd_parms);
in6_dev_put(idev);
}
return 0;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 1eccc9aa0b1e..243aa2f6a5ca 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1210,7 +1210,7 @@ static struct sk_buff *mld_newpack(struct net_device *dev, int size)
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
- skb = sock_alloc_send_skb(sk, size + dev->hard_header_len+15, 0, &err);
+ skb = sock_alloc_send_skb(sk, size + dev->hard_header_len+15, 1, &err);
if (skb == 0)
return 0;
@@ -1545,7 +1545,7 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
payload_len = len + sizeof(ra);
full_len = sizeof(struct ipv6hdr) + payload_len;
- skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 0, &err);
+ skb = sock_alloc_send_skb(sk, dev->hard_header_len + full_len + 15, 1, &err);
if (skb == NULL)
return;
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index c75510fa39a9..aab59050a6c5 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -1487,6 +1487,9 @@ int __init ndisc_init(struct net_proto_family *ops)
void ndisc_cleanup(void)
{
+#ifdef CONFIG_SYSCTL
+ neigh_sysctl_unregister(&nd_tbl.parms);
+#endif
neigh_table_clear(&nd_tbl);
sock_release(ndisc_socket);
ndisc_socket = NULL; /* For safety. */
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 74e6aaafd3db..92e6ff6c41b6 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -596,10 +596,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
BUG_TRAP(FRAG6_CB(head)->offset == 0);
/* Unfragmented part is taken from the first segment. */
- payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len;
- nhoff = head->h.raw - head->nh.raw;
-
- if (payload_len > 65535 + 8)
+ payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr);
+ if (payload_len > 65535)
goto out_oversize;
/* Head of list must not be cloned. */
@@ -633,9 +631,10 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff **skb_in,
* header in order to calculate ICV correctly. */
nhoff = fq->nhoffset;
head->nh.raw[nhoff] = head->h.raw[0];
- memmove(head->head+8, head->head, (head->data-head->head)-8);
- head->mac.raw += 8;
- head->nh.raw += 8;
+ memmove(head->head + sizeof(struct frag_hdr), head->head,
+ (head->data - head->head) - sizeof(struct frag_hdr));
+ head->mac.raw += sizeof(struct frag_hdr);
+ head->nh.raw += sizeof(struct frag_hdr);
skb_shinfo(head)->frag_list = head->next;
head->h.raw = head->data;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 997ea3512f27..4bbfa57a29cb 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -61,16 +61,9 @@
static int ipip6_fb_tunnel_init(struct net_device *dev);
static int ipip6_tunnel_init(struct net_device *dev);
+static void ipip6_tunnel_setup(struct net_device *dev);
-static struct net_device ipip6_fb_tunnel_dev = {
- .name = "sit0",
- .init = ipip6_fb_tunnel_init
-};
-
-static struct ip_tunnel ipip6_fb_tunnel = {
- .dev = &ipip6_fb_tunnel_dev,
- .parms = { .name = "sit0" }
-};
+static struct net_device *ipip6_fb_tunnel_dev;
static struct ip_tunnel *tunnels_r_l[HASH_SIZE];
static struct ip_tunnel *tunnels_r[HASH_SIZE];
@@ -154,6 +147,7 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int
struct net_device *dev;
unsigned h = 0;
int prio = 0;
+ char name[IFNAMSIZ];
if (remote) {
prio |= 2;
@@ -168,54 +162,47 @@ static struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int
return t;
}
if (!create)
- return NULL;
-
- dev = kmalloc(sizeof(*dev) + sizeof(*t), GFP_KERNEL);
- if (dev == NULL)
- return NULL;
+ goto failed;
- memset(dev, 0, sizeof(*dev) + sizeof(*t));
- dev->priv = (void*)(dev+1);
- nt = (struct ip_tunnel*)dev->priv;
- nt->dev = dev;
- dev->init = ipip6_tunnel_init;
- memcpy(&nt->parms, parms, sizeof(*parms));
- nt->parms.name[IFNAMSIZ-1] = '\0';
- strcpy(dev->name, nt->parms.name);
- if (dev->name[0] == 0) {
+ if (parms->name[0])
+ strlcpy(name, parms->name, IFNAMSIZ);
+ else {
int i;
for (i=1; i<100; i++) {
- sprintf(dev->name, "sit%d", i);
- if (__dev_get_by_name(dev->name) == NULL)
+ sprintf(name, "sit%d", i);
+ if (__dev_get_by_name(name) == NULL)
break;
}
if (i==100)
goto failed;
- memcpy(nt->parms.name, dev->name, IFNAMSIZ);
}
- SET_MODULE_OWNER(dev);
- if (register_netdevice(dev) < 0)
+
+ dev = alloc_netdev(sizeof(*t), name, ipip6_tunnel_setup);
+ if (dev == NULL)
+ return NULL;
+
+ nt = dev->priv;
+ dev->init = ipip6_tunnel_init;
+ nt->parms = *parms;
+
+ if (register_netdevice(dev) < 0) {
+ kfree(dev);
goto failed;
+ }
dev_hold(dev);
+
ipip6_tunnel_link(nt);
/* Do not decrement MOD_USE_COUNT here. */
return nt;
failed:
- kfree(dev);
return NULL;
}
-static void ipip6_tunnel_destructor(struct net_device *dev)
-{
- if (dev != &ipip6_fb_tunnel_dev)
- kfree(dev);
-}
-
static void ipip6_tunnel_uninit(struct net_device *dev)
{
- if (dev == &ipip6_fb_tunnel_dev) {
+ if (dev == ipip6_fb_tunnel_dev) {
write_lock_bh(&ipip6_lock);
tunnels_wc[0] = NULL;
write_unlock_bh(&ipip6_lock);
@@ -621,7 +608,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCGETTUNNEL:
t = NULL;
- if (dev == &ipip6_fb_tunnel_dev) {
+ if (dev == ipip6_fb_tunnel_dev) {
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) {
err = -EFAULT;
break;
@@ -654,8 +641,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
t = ipip6_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
- if (dev != &ipip6_fb_tunnel_dev && cmd == SIOCCHGTUNNEL &&
- t != &ipip6_fb_tunnel) {
+ if (dev != ipip6_fb_tunnel_dev && cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
err = -EEXIST;
@@ -695,7 +681,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if (!capable(CAP_NET_ADMIN))
goto done;
- if (dev == &ipip6_fb_tunnel_dev) {
+ if (dev == ipip6_fb_tunnel_dev) {
err = -EFAULT;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
goto done;
@@ -703,7 +689,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
if ((t = ipip6_tunnel_locate(&p, 0)) == NULL)
goto done;
err = -EPERM;
- if (t == &ipip6_fb_tunnel)
+ if (t == ipip6_fb_tunnel_dev->priv)
goto done;
dev = t->dev;
}
@@ -731,12 +717,11 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
return 0;
}
-static void ipip6_tunnel_init_gen(struct net_device *dev)
+static void ipip6_tunnel_setup(struct net_device *dev)
{
- struct ip_tunnel *t = (struct ip_tunnel*)dev->priv;
-
- dev->destructor = ipip6_tunnel_destructor;
+ SET_MODULE_OWNER(dev);
dev->uninit = ipip6_tunnel_uninit;
+ dev->destructor = (void (*)(struct net_device *))kfree;
dev->hard_start_xmit = ipip6_tunnel_xmit;
dev->get_stats = ipip6_tunnel_get_stats;
dev->do_ioctl = ipip6_tunnel_ioctl;
@@ -748,8 +733,6 @@ static void ipip6_tunnel_init_gen(struct net_device *dev)
dev->flags = IFF_NOARP;
dev->iflink = 0;
dev->addr_len = 4;
- memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
}
static int ipip6_tunnel_init(struct net_device *dev)
@@ -760,8 +743,9 @@ static int ipip6_tunnel_init(struct net_device *dev)
tunnel = (struct ip_tunnel*)dev->priv;
iph = &tunnel->parms.iph;
-
- ipip6_tunnel_init_gen(dev);
+ tunnel->dev = dev;
+ memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
if (iph->daddr) {
struct flowi fl = { .nl_u = { .ip4_u =
@@ -793,18 +777,16 @@ static int ipip6_tunnel_init(struct net_device *dev)
int __init ipip6_fb_tunnel_init(struct net_device *dev)
{
- struct iphdr *iph;
+ struct ip_tunnel *tunnel = dev->priv;
+ struct iphdr *iph = &tunnel->parms.iph;
- ipip6_tunnel_init_gen(dev);
-
- iph = &ipip6_fb_tunnel.parms.iph;
iph->version = 4;
iph->protocol = IPPROTO_IPV6;
iph->ihl = 5;
iph->ttl = 64;
dev_hold(dev);
- tunnels_wc[0] = &ipip6_fb_tunnel;
+ tunnels_wc[0] = tunnel;
return 0;
}
@@ -817,12 +799,14 @@ static struct inet_protocol sit_protocol = {
void sit_cleanup(void)
{
inet_del_protocol(&sit_protocol, IPPROTO_IPV6);
- unregister_netdev(&ipip6_fb_tunnel_dev);
+ unregister_netdev(ipip6_fb_tunnel_dev);
}
#endif
int __init sit_init(void)
{
+ int err;
+
printk(KERN_INFO "IPv6 over IPv4 tunneling driver\n");
if (inet_add_protocol(&sit_protocol, IPPROTO_IPV6) < 0) {
@@ -830,9 +814,22 @@ int __init sit_init(void)
return -EAGAIN;
}
- ipip6_fb_tunnel_dev.priv = (void*)&ipip6_fb_tunnel;
- strcpy(ipip6_fb_tunnel_dev.name, ipip6_fb_tunnel.parms.name);
- SET_MODULE_OWNER(&ipip6_fb_tunnel_dev);
- register_netdev(&ipip6_fb_tunnel_dev);
- return 0;
+ ipip6_fb_tunnel_dev = alloc_netdev(sizeof(struct ip_tunnel), "sit0",
+ ipip6_tunnel_setup);
+ if (!ipip6_fb_tunnel_dev) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ ipip6_fb_tunnel_dev->init = ipip6_fb_tunnel_init;
+
+ if ((err = register_netdev(ipip6_fb_tunnel_dev)))
+ goto fail;
+
+ out:
+ return err;
+ fail:
+ inet_del_protocol(&sit_protocol, IPPROTO_IPV6);
+ kfree(ipip6_fb_tunnel_dev);
+ goto out;
}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index bca19a128614..ead47d99314d 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -584,7 +584,7 @@ static struct sock *udp_v6_mcast_next(struct sock *sk,
for (; s; s = s->sk_next) {
struct inet_opt *inet = inet_sk(s);
- if (inet->num == num && sk->sk_family == PF_INET6) {
+ if (inet->num == num && s->sk_family == PF_INET6) {
struct ipv6_pinfo *np = inet6_sk(s);
if (inet->dport) {
if (inet->dport != rmt_port)
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index b9b57b2f928b..a13091a74149 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -177,6 +177,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl)
struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
u8 nexthdr = skb->nh.ipv6h->nexthdr;
+ memset(fl, 0, sizeof(struct flowi));
ipv6_addr_copy(&fl->fl6_dst, &hdr->daddr);
ipv6_addr_copy(&fl->fl6_src, &hdr->saddr);
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index b7b4cf9f3381..775d37489930 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -183,7 +183,7 @@ static int llc_seq_core_show(struct seq_file *seq, void *v)
timer_pending(&llc->pf_cycle_timer.timer),
timer_pending(&llc->rej_sent_timer.timer),
timer_pending(&llc->busy_state_timer.timer),
- !!sk->sk_backlog.tail, sock_owned_by_user(sk));
+ !!sk->sk_backlog.tail, !!sock_owned_by_user(sk));
out:
return 0;
}
diff --git a/net/netsyms.c b/net/netsyms.c
index b81dc1bf3fc5..5ad7714b21e8 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -190,6 +190,7 @@ EXPORT_SYMBOL(neigh_app_ns);
#endif
#ifdef CONFIG_SYSCTL
EXPORT_SYMBOL(neigh_sysctl_register);
+EXPORT_SYMBOL(neigh_sysctl_unregister);
#endif
EXPORT_SYMBOL(pneigh_lookup);
EXPORT_SYMBOL(pneigh_enqueue);
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index ac69c28813dc..169ebd0e30c7 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -96,7 +96,6 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
int gfp)
{
struct sctp_opt *sp;
- struct sctp_protocol *proto = sctp_get_protocol();
int i;
/* Retrieve the SCTP per socket area. */
@@ -129,26 +128,26 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
asoc->state_timestamp = jiffies;
/* Set things that have constant value. */
- asoc->cookie_life.tv_sec = sctp_proto.valid_cookie_life / HZ;
- asoc->cookie_life.tv_usec = (sctp_proto.valid_cookie_life % HZ) *
+ asoc->cookie_life.tv_sec = sctp_valid_cookie_life / HZ;
+ asoc->cookie_life.tv_usec = (sctp_valid_cookie_life % HZ) *
1000000L / HZ;
asoc->pmtu = 0;
asoc->frag_point = 0;
/* Initialize the default association max_retrans and RTO values. */
- asoc->max_retrans = proto->max_retrans_association;
- asoc->rto_initial = proto->rto_initial;
- asoc->rto_max = proto->rto_max;
- asoc->rto_min = proto->rto_min;
+ asoc->max_retrans = sctp_max_retrans_association;
+ asoc->rto_initial = sctp_rto_initial;
+ asoc->rto_max = sctp_rto_max;
+ asoc->rto_min = sctp_rto_min;
- asoc->overall_error_threshold = 0;
+ asoc->overall_error_threshold = asoc->max_retrans;
asoc->overall_error_count = 0;
/* Initialize the maximum mumber of new data packets that can be sent
* in a burst.
*/
- asoc->max_burst = proto->max_burst;
+ asoc->max_burst = sctp_max_burst;
/* Copy things from the endpoint. */
for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) {
@@ -277,6 +276,12 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc,
asoc->autoclose = sp->autoclose;
+ asoc->default_stream = sp->default_stream;
+ asoc->default_ppid = sp->default_ppid;
+ asoc->default_flags = sp->default_flags;
+ asoc->default_context = sp->default_context;
+ asoc->default_timetolive = sp->default_timetolive;
+
return asoc;
fail_init:
@@ -478,16 +483,8 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
peer->partial_bytes_acked = 0;
peer->flight_size = 0;
-
peer->error_threshold = peer->max_retrans;
- /* Update the overall error threshold value of the association
- * taking the new peer's error threshold into account.
- */
- asoc->overall_error_threshold =
- min(asoc->overall_error_threshold + peer->error_threshold,
- asoc->max_retrans);
-
/* By default, enable heartbeat for peer address. */
peer->hb_allowed = 1;
@@ -550,12 +547,12 @@ void sctp_assoc_control_transport(struct sctp_association *asoc,
/* Record the transition on the transport. */
switch (command) {
case SCTP_TRANSPORT_UP:
- transport->active = 1;
+ transport->active = SCTP_ACTIVE;
spc_state = ADDRESS_AVAILABLE;
break;
case SCTP_TRANSPORT_DOWN:
- transport->active = 0;
+ transport->active = SCTP_INACTIVE;
spc_state = ADDRESS_UNREACHABLE;
break;
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 1b7bc9a7fe05..4f0b41028879 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -329,12 +329,10 @@ static int sctp_copy_one_addr(struct sctp_bind_addr *dest,
union sctp_addr *addr,
sctp_scope_t scope, int gfp, int flags)
{
- struct sctp_protocol *proto = sctp_get_protocol();
int error = 0;
if (sctp_is_any(addr)) {
- error = sctp_copy_local_addr_list(proto, dest, scope,
- gfp, flags);
+ error = sctp_copy_local_addr_list(dest, scope, gfp, flags);
} else if (sctp_in_scope(addr, scope)) {
/* Now that the address is in scope, check to see if
* the address type is supported by local sock as
diff --git a/net/sctp/input.c b/net/sctp/input.c
index f5483d72c288..8c4cc2c233a6 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -503,9 +503,10 @@ int sctp_rcv_ootb(struct sk_buff *skb)
goto discard;
if (SCTP_CID_ERROR == ch->type) {
- err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t));
- if (SCTP_ERROR_STALE_COOKIE == err->cause)
- goto discard;
+ sctp_walk_errors(err, ch) {
+ if (SCTP_ERROR_STALE_COOKIE == err->cause)
+ goto discard;
+ }
}
ch = (sctp_chunkhdr_t *) ch_end;
@@ -522,12 +523,12 @@ void __sctp_hash_endpoint(struct sctp_endpoint *ep)
{
struct sctp_ep_common **epp;
struct sctp_ep_common *epb;
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
epb = &ep->base;
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
- head = &sctp_proto.ep_hashbucket[epb->hashent];
+ head = &sctp_ep_hashbucket[epb->hashent];
sctp_write_lock(&head->lock);
epp = &head->chain;
@@ -550,14 +551,14 @@ void sctp_hash_endpoint(struct sctp_endpoint *ep)
/* Remove endpoint from the hash table. */
void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
{
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
epb = &ep->base;
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
- head = &sctp_proto.ep_hashbucket[epb->hashent];
+ head = &sctp_ep_hashbucket[epb->hashent];
sctp_write_lock(&head->lock);
@@ -582,13 +583,13 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep)
/* Look up an endpoint. */
struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
{
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
struct sctp_endpoint *ep;
int hash;
hash = sctp_ep_hashfn(laddr->v4.sin_port);
- head = &sctp_proto.ep_hashbucket[hash];
+ head = &sctp_ep_hashbucket[hash];
read_lock(&head->lock);
for (epb = head->chain; epb; epb = epb->next) {
ep = sctp_ep(epb);
@@ -619,14 +620,14 @@ void __sctp_hash_established(struct sctp_association *asoc)
{
struct sctp_ep_common **epp;
struct sctp_ep_common *epb;
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
epb = &asoc->base;
/* Calculate which chain this entry will belong to. */
epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port);
- head = &sctp_proto.assoc_hashbucket[epb->hashent];
+ head = &sctp_assoc_hashbucket[epb->hashent];
sctp_write_lock(&head->lock);
epp = &head->chain;
@@ -649,7 +650,7 @@ void sctp_unhash_established(struct sctp_association *asoc)
/* Remove association from the hash table. */
void __sctp_unhash_established(struct sctp_association *asoc)
{
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
epb = &asoc->base;
@@ -657,7 +658,7 @@ void __sctp_unhash_established(struct sctp_association *asoc)
epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port,
asoc->peer.port);
- head = &sctp_proto.assoc_hashbucket[epb->hashent];
+ head = &sctp_assoc_hashbucket[epb->hashent];
sctp_write_lock(&head->lock);
@@ -677,7 +678,7 @@ struct sctp_association *__sctp_lookup_association(
const union sctp_addr *peer,
struct sctp_transport **pt)
{
- sctp_hashbucket_t *head;
+ struct sctp_hashbucket *head;
struct sctp_ep_common *epb;
struct sctp_association *asoc;
struct sctp_transport *transport;
@@ -687,7 +688,7 @@ struct sctp_association *__sctp_lookup_association(
* have wildcards anyways.
*/
hash = sctp_assoc_hashfn(local->v4.sin_port, peer->v4.sin_port);
- head = &sctp_proto.assoc_hashbucket[hash];
+ head = &sctp_assoc_hashbucket[hash];
read_lock(&head->lock);
for (epb = head->chain; epb; epb = epb->next) {
asoc = sctp_assoc(epb);
@@ -766,6 +767,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
sctp_chunkhdr_t *ch;
union sctp_params params;
sctp_init_chunk_t *init;
+ struct sctp_transport *transport;
ch = (sctp_chunkhdr_t *) skb->data;
@@ -805,7 +807,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
continue;
sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source), 0);
- asoc = __sctp_lookup_association(laddr, paddr, transportp);
+ asoc = __sctp_lookup_association(laddr, paddr, &transport);
if (asoc)
return asoc;
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 7fe938ffc498..3a3e998749a4 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -61,6 +61,7 @@
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/random.h>
+#include <linux/seq_file.h>
#include <net/protocol.h>
#include <net/tcp.h>
@@ -578,6 +579,13 @@ static int sctp_v6_is_ce(const struct sk_buff *skb)
return *((__u32 *)(skb->nh.ipv6h)) & htonl(1<<20);
}
+/* Dump the v6 addr to the seq file. */
+static void sctp_v6_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
+{
+ seq_printf(seq, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
+ NIP6(addr->v6.sin6_addr));
+}
+
/* Initialize a PF_INET6 socket msg_name. */
static void sctp_inet6_msgname(char *msgname, int *addr_len)
{
@@ -840,6 +848,7 @@ static struct sctp_af sctp_ipv6_specific = {
.available = sctp_v6_available,
.skb_iif = sctp_v6_skb_iif,
.is_ce = sctp_v6_is_ce,
+ .seq_dump_addr = sctp_v6_seq_dump_addr,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6,
diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c
index 7e29bd6959a5..48dd58b68878 100644
--- a/net/sctp/objcnt.c
+++ b/net/sctp/objcnt.c
@@ -53,6 +53,7 @@ SCTP_DBG_OBJCNT(ep);
SCTP_DBG_OBJCNT(transport);
SCTP_DBG_OBJCNT(assoc);
SCTP_DBG_OBJCNT(bind_addr);
+SCTP_DBG_OBJCNT(bind_bucket);
SCTP_DBG_OBJCNT(chunk);
SCTP_DBG_OBJCNT(addr);
SCTP_DBG_OBJCNT(ssnmap);
@@ -68,6 +69,7 @@ sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
SCTP_DBG_OBJCNT_ENTRY(transport),
SCTP_DBG_OBJCNT_ENTRY(chunk),
SCTP_DBG_OBJCNT_ENTRY(bind_addr),
+ SCTP_DBG_OBJCNT_ENTRY(bind_bucket),
SCTP_DBG_OBJCNT_ENTRY(addr),
SCTP_DBG_OBJCNT_ENTRY(ssnmap),
SCTP_DBG_OBJCNT_ENTRY(datamsg),
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index fef9b27a98a5..9e1671d317d4 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -128,3 +128,162 @@ void sctp_snmp_proc_exit(void)
{
remove_proc_entry("snmp", proc_net_sctp);
}
+
+/* Dump local addresses of an association/endpoint. */
+static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_common *epb)
+{
+ struct list_head *pos;
+ struct sockaddr_storage_list *laddr;
+ union sctp_addr *addr;
+ struct sctp_af *af;
+
+ list_for_each(pos, &epb->bind_addr.address_list) {
+ laddr = list_entry(pos, struct sockaddr_storage_list, list);
+ addr = (union sctp_addr *)&laddr->a;
+ af = sctp_get_af_specific(addr->sa.sa_family);
+ af->seq_dump_addr(seq, addr);
+ }
+}
+
+/* Dump remote addresses of an association. */
+static void sctp_seq_dump_remote_addrs(struct seq_file *seq, struct sctp_association *assoc)
+{
+ struct list_head *pos;
+ struct sctp_transport *transport;
+ union sctp_addr *addr;
+ struct sctp_af *af;
+
+ list_for_each(pos, &assoc->peer.transport_addr_list) {
+ transport = list_entry(pos, struct sctp_transport, transports);
+ addr = (union sctp_addr *)&transport->ipaddr;
+ af = sctp_get_af_specific(addr->sa.sa_family);
+ af->seq_dump_addr(seq, addr);
+ }
+}
+
+/* Display sctp endpoints (/proc/net/sctp/eps). */
+static int sctp_eps_seq_show(struct seq_file *seq, void *v)
+{
+ struct sctp_hashbucket *head;
+ struct sctp_ep_common *epb;
+ struct sctp_endpoint *ep;
+ struct sock *sk;
+ int hash;
+
+ seq_printf(seq, " ENDPT SOCK STY SST HBKT LPORT LADDRS\n");
+ for (hash = 0; hash < sctp_ep_hashsize; hash++) {
+ head = &sctp_ep_hashbucket[hash];
+ read_lock(&head->lock);
+ for (epb = head->chain; epb; epb = epb->next) {
+ ep = sctp_ep(epb);
+ sk = epb->sk;
+ seq_printf(seq, "%8p %8p %-3d %-3d %-4d %-5d ", ep, sk,
+ sctp_sk(sk)->type, sk->sk_state, hash,
+ epb->bind_addr.port);
+ sctp_seq_dump_local_addrs(seq, epb);
+ seq_printf(seq, "\n");
+ }
+ read_unlock(&head->lock);
+ }
+
+ return 0;
+}
+
+/* Initialize the seq file operations for 'eps' object. */
+static int sctp_eps_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sctp_eps_seq_show, NULL);
+}
+
+static struct file_operations sctp_eps_seq_fops = {
+ .open = sctp_eps_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* Set up the proc fs entry for 'eps' object. */
+int __init sctp_eps_proc_init(void)
+{
+ struct proc_dir_entry *p;
+
+ p = create_proc_entry("eps", S_IRUGO, proc_net_sctp);
+ if (!p)
+ return -ENOMEM;
+
+ p->proc_fops = &sctp_eps_seq_fops;
+
+ return 0;
+}
+
+/* Cleanup the proc fs entry for 'eps' object. */
+void sctp_eps_proc_exit(void)
+{
+ remove_proc_entry("eps", proc_net_sctp);
+}
+
+/* Display sctp associations (/proc/net/sctp/assocs). */
+static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
+{
+ struct sctp_hashbucket *head;
+ struct sctp_ep_common *epb;
+ struct sctp_association *assoc;
+ struct sock *sk;
+ int hash;
+
+ seq_printf(seq, " ASSOC SOCK STY SST ST HBKT LPORT RPORT "
+ "LADDRS <-> RADDRS\n");
+ for (hash = 0; hash < sctp_assoc_hashsize; hash++) {
+ head = &sctp_assoc_hashbucket[hash];
+ read_lock(&head->lock);
+ for (epb = head->chain; epb; epb = epb->next) {
+ assoc = sctp_assoc(epb);
+ sk = epb->sk;
+ seq_printf(seq,
+ "%8p %8p %-3d %-3d %-2d %-4d %-5d %-5d ",
+ assoc, sk, sctp_sk(sk)->type, sk->sk_state,
+ assoc->state, hash, epb->bind_addr.port,
+ assoc->peer.port);
+ sctp_seq_dump_local_addrs(seq, epb);
+ seq_printf(seq, "<-> ");
+ sctp_seq_dump_remote_addrs(seq, assoc);
+ seq_printf(seq, "\n");
+ }
+ read_unlock(&head->lock);
+ }
+
+ return 0;
+}
+
+/* Initialize the seq file operations for 'assocs' object. */
+static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sctp_assocs_seq_show, NULL);
+}
+
+static struct file_operations sctp_assocs_seq_fops = {
+ .open = sctp_assocs_seq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/* Set up the proc fs entry for 'assocs' object. */
+int __init sctp_assocs_proc_init(void)
+{
+ struct proc_dir_entry *p;
+
+ p = create_proc_entry("assocs", S_IRUGO, proc_net_sctp);
+ if (!p)
+ return -ENOMEM;
+
+ p->proc_fops = &sctp_assocs_seq_fops;
+
+ return 0;
+}
+
+/* Cleanup the proc fs entry for 'assocs' object. */
+void sctp_assocs_proc_exit(void)
+{
+ remove_proc_entry("assocs", proc_net_sctp);
+}
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 185142cd0bdd..32df2a5d5774 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1,7 +1,7 @@
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
- * Copyright (c) 2001-2002 International Business Machines, Corp.
+ * Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
@@ -50,6 +50,7 @@
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
+#include <linux/seq_file.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/ipv6.h>
@@ -59,7 +60,7 @@
#include <net/inet_ecn.h>
/* Global data structures. */
-struct sctp_protocol sctp_proto;
+struct sctp_globals sctp_globals;
struct proc_dir_entry *proc_net_sctp;
DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics);
@@ -74,10 +75,17 @@ static struct sctp_pf *sctp_pf_inet_specific;
static struct sctp_af *sctp_af_v4_specific;
static struct sctp_af *sctp_af_v6_specific;
+kmem_cache_t *sctp_chunk_cachep;
+kmem_cache_t *sctp_bucket_cachep;
+
extern struct net_proto_family inet_family_ops;
extern int sctp_snmp_proc_init(void);
extern int sctp_snmp_proc_exit(void);
+extern int sctp_eps_proc_init(void);
+extern int sctp_eps_proc_exit(void);
+extern int sctp_assocs_proc_init(void);
+extern int sctp_assocs_proc_exit(void);
/* Return the address of the control sock. */
struct sock *sctp_get_ctl_sock(void)
@@ -88,8 +96,6 @@ struct sock *sctp_get_ctl_sock(void)
/* Set up the proc fs entry for the SCTP protocol. */
__init int sctp_proc_init(void)
{
- int rc = 0;
-
if (!proc_net_sctp) {
struct proc_dir_entry *ent;
ent = proc_mkdir("net/sctp", 0);
@@ -97,20 +103,31 @@ __init int sctp_proc_init(void)
ent->owner = THIS_MODULE;
proc_net_sctp = ent;
} else
- rc = -ENOMEM;
+ goto out_nomem;
}
if (sctp_snmp_proc_init())
- rc = -ENOMEM;
+ goto out_nomem;
+ if (sctp_eps_proc_init())
+ goto out_nomem;
+ if (sctp_assocs_proc_init())
+ goto out_nomem;
+
+ return 0;
- return rc;
+out_nomem:
+ return -ENOMEM;
}
-/* Clean up the proc fs entry for the SCTP protocol. */
+/* Clean up the proc fs entry for the SCTP protocol.
+ * Note: Do not make this __exit as it is used in the init error
+ * path.
+ */
void sctp_proc_exit(void)
{
-
sctp_snmp_proc_exit();
+ sctp_eps_proc_exit();
+ sctp_assocs_proc_exit();
if (proc_net_sctp) {
proc_net_sctp = NULL;
@@ -153,7 +170,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
/* Extract our IP addresses from the system and stash them in the
* protocol structure.
*/
-static void __sctp_get_local_addr_list(struct sctp_protocol *proto)
+static void __sctp_get_local_addr_list(void)
{
struct net_device *dev;
struct list_head *pos;
@@ -161,30 +178,30 @@ static void __sctp_get_local_addr_list(struct sctp_protocol *proto)
read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
- list_for_each(pos, &proto->address_families) {
+ __list_for_each(pos, &sctp_address_families) {
af = list_entry(pos, struct sctp_af, list);
- af->copy_addrlist(&proto->local_addr_list, dev);
+ af->copy_addrlist(&sctp_local_addr_list, dev);
}
}
read_unlock(&dev_base_lock);
}
-static void sctp_get_local_addr_list(struct sctp_protocol *proto)
+static void sctp_get_local_addr_list(void)
{
unsigned long flags;
- sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
- __sctp_get_local_addr_list(&sctp_proto);
- sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ __sctp_get_local_addr_list();
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
}
/* Free the existing local addresses. */
-static void __sctp_free_local_addr_list(struct sctp_protocol *proto)
+static void __sctp_free_local_addr_list(void)
{
struct sockaddr_storage_list *addr;
struct list_head *pos, *temp;
- list_for_each_safe(pos, temp, &proto->local_addr_list) {
+ list_for_each_safe(pos, temp, &sctp_local_addr_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
list_del(pos);
kfree(addr);
@@ -192,18 +209,17 @@ static void __sctp_free_local_addr_list(struct sctp_protocol *proto)
}
/* Free the existing local addresses. */
-static void sctp_free_local_addr_list(struct sctp_protocol *proto)
+static void sctp_free_local_addr_list(void)
{
unsigned long flags;
- sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
- __sctp_free_local_addr_list(proto);
- sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ __sctp_free_local_addr_list();
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
}
/* Copy the local addresses which are valid for 'scope' into 'bp'. */
-int sctp_copy_local_addr_list(struct sctp_protocol *proto,
- struct sctp_bind_addr *bp, sctp_scope_t scope,
+int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
int gfp, int copy_flags)
{
struct sockaddr_storage_list *addr;
@@ -211,8 +227,8 @@ int sctp_copy_local_addr_list(struct sctp_protocol *proto,
struct list_head *pos;
unsigned long flags;
- sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
- list_for_each(pos, &proto->local_addr_list) {
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ list_for_each(pos, &sctp_local_addr_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_in_scope(&addr->a, scope)) {
/* Now that the address is in scope, check to see if
@@ -233,7 +249,7 @@ int sctp_copy_local_addr_list(struct sctp_protocol *proto,
}
end_copy:
- sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
return error;
}
@@ -564,6 +580,12 @@ out:
return newsk;
}
+/* Dump the v4 addr to the seq file. */
+static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
+{
+ seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
+}
+
/* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list.
*/
@@ -572,10 +594,10 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
{
unsigned long flags;
- sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
- __sctp_free_local_addr_list(&sctp_proto);
- __sctp_get_local_addr_list(&sctp_proto);
- sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
+ sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
+ __sctp_free_local_addr_list();
+ __sctp_get_local_addr_list();
+ sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);
return NOTIFY_DONE;
}
@@ -626,7 +648,7 @@ int sctp_register_af(struct sctp_af *af)
}
INIT_LIST_HEAD(&af->list);
- list_add_tail(&af->list, &sctp_proto.address_families);
+ list_add_tail(&af->list, &sctp_address_families);
return 1;
}
@@ -839,6 +861,7 @@ struct sctp_af sctp_ipv4_specific = {
.scope = sctp_v4_scope,
.skb_iif = sctp_v4_skb_iif,
.is_ce = sctp_v4_is_ce,
+ .seq_dump_addr = sctp_v4_seq_dump_addr,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET,
@@ -914,6 +937,22 @@ __init int sctp_init(void)
inet_register_protosw(&sctp_seqpacket_protosw);
inet_register_protosw(&sctp_stream_protosw);
+ /* Allocate a cache pools. */
+ sctp_bucket_cachep = kmem_cache_create("sctp_bind_bucket",
+ sizeof(struct sctp_bind_bucket),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+
+ if (!sctp_bucket_cachep)
+ goto err_bucket_cachep;
+
+ sctp_chunk_cachep = kmem_cache_create("sctp_chunk",
+ sizeof(struct sctp_chunk),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (!sctp_chunk_cachep)
+ goto err_chunk_cachep;
+
/* Allocate and initialise sctp mibs. */
status = init_sctp_mibs();
if (status)
@@ -932,91 +971,91 @@ __init int sctp_init(void)
*/
/* The following protocol parameters are RECOMMENDED: */
/* RTO.Initial - 3 seconds */
- sctp_proto.rto_initial = SCTP_RTO_INITIAL;
+ sctp_rto_initial = SCTP_RTO_INITIAL;
/* RTO.Min - 1 second */
- sctp_proto.rto_min = SCTP_RTO_MIN;
+ sctp_rto_min = SCTP_RTO_MIN;
/* RTO.Max - 60 seconds */
- sctp_proto.rto_max = SCTP_RTO_MAX;
+ sctp_rto_max = SCTP_RTO_MAX;
/* RTO.Alpha - 1/8 */
- sctp_proto.rto_alpha = SCTP_RTO_ALPHA;
+ sctp_rto_alpha = SCTP_RTO_ALPHA;
/* RTO.Beta - 1/4 */
- sctp_proto.rto_beta = SCTP_RTO_BETA;
+ sctp_rto_beta = SCTP_RTO_BETA;
/* Valid.Cookie.Life - 60 seconds */
- sctp_proto.valid_cookie_life = 60 * HZ;
+ sctp_valid_cookie_life = 60 * HZ;
/* Whether Cookie Preservative is enabled(1) or not(0) */
- sctp_proto.cookie_preserve_enable = 1;
+ sctp_cookie_preserve_enable = 1;
/* Max.Burst - 4 */
- sctp_proto.max_burst = SCTP_MAX_BURST;
+ sctp_max_burst = SCTP_MAX_BURST;
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
*/
- sctp_proto.max_retrans_association = 10;
- sctp_proto.max_retrans_path = 5;
- sctp_proto.max_retrans_init = 8;
+ sctp_max_retrans_association = 10;
+ sctp_max_retrans_path = 5;
+ sctp_max_retrans_init = 8;
/* HB.interval - 30 seconds */
- sctp_proto.hb_interval = 30 * HZ;
+ sctp_hb_interval = 30 * HZ;
/* Implementation specific variables. */
/* Initialize default stream count setup information. */
- sctp_proto.max_instreams = SCTP_DEFAULT_INSTREAMS;
- sctp_proto.max_outstreams = SCTP_DEFAULT_OUTSTREAMS;
+ sctp_max_instreams = SCTP_DEFAULT_INSTREAMS;
+ sctp_max_outstreams = SCTP_DEFAULT_OUTSTREAMS;
/* Allocate and initialize the association hash table. */
- sctp_proto.assoc_hashsize = 4096;
- sctp_proto.assoc_hashbucket = (sctp_hashbucket_t *)
- kmalloc(4096 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
- if (!sctp_proto.assoc_hashbucket) {
+ sctp_assoc_hashsize = 4096;
+ sctp_assoc_hashbucket = (struct sctp_hashbucket *)
+ kmalloc(4096 * sizeof(struct sctp_hashbucket), GFP_KERNEL);
+ if (!sctp_assoc_hashbucket) {
printk(KERN_ERR "SCTP: Failed association hash alloc.\n");
status = -ENOMEM;
goto err_ahash_alloc;
}
- for (i = 0; i < sctp_proto.assoc_hashsize; i++) {
- sctp_proto.assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED;
- sctp_proto.assoc_hashbucket[i].chain = NULL;
+ for (i = 0; i < sctp_assoc_hashsize; i++) {
+ sctp_assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED;
+ sctp_assoc_hashbucket[i].chain = NULL;
}
/* Allocate and initialize the endpoint hash table. */
- sctp_proto.ep_hashsize = 64;
- sctp_proto.ep_hashbucket = (sctp_hashbucket_t *)
- kmalloc(64 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
- if (!sctp_proto.ep_hashbucket) {
+ sctp_ep_hashsize = 64;
+ sctp_ep_hashbucket = (struct sctp_hashbucket *)
+ kmalloc(64 * sizeof(struct sctp_hashbucket), GFP_KERNEL);
+ if (!sctp_ep_hashbucket) {
printk(KERN_ERR "SCTP: Failed endpoint_hash alloc.\n");
status = -ENOMEM;
goto err_ehash_alloc;
}
- for (i = 0; i < sctp_proto.ep_hashsize; i++) {
- sctp_proto.ep_hashbucket[i].lock = RW_LOCK_UNLOCKED;
- sctp_proto.ep_hashbucket[i].chain = NULL;
+ for (i = 0; i < sctp_ep_hashsize; i++) {
+ sctp_ep_hashbucket[i].lock = RW_LOCK_UNLOCKED;
+ sctp_ep_hashbucket[i].chain = NULL;
}
/* Allocate and initialize the SCTP port hash table. */
- sctp_proto.port_hashsize = 4096;
- sctp_proto.port_hashtable = (sctp_bind_hashbucket_t *)
- kmalloc(4096 * sizeof(sctp_bind_hashbucket_t), GFP_KERNEL);
- if (!sctp_proto.port_hashtable) {
+ sctp_port_hashsize = 4096;
+ sctp_port_hashtable = (struct sctp_bind_hashbucket *)
+ kmalloc(4096 * sizeof(struct sctp_bind_hashbucket),GFP_KERNEL);
+ if (!sctp_port_hashtable) {
printk(KERN_ERR "SCTP: Failed bind hash alloc.");
status = -ENOMEM;
goto err_bhash_alloc;
}
- sctp_proto.port_alloc_lock = SPIN_LOCK_UNLOCKED;
- sctp_proto.port_rover = sysctl_local_port_range[0] - 1;
- for (i = 0; i < sctp_proto.port_hashsize; i++) {
- sctp_proto.port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
- sctp_proto.port_hashtable[i].chain = NULL;
+ sctp_port_alloc_lock = SPIN_LOCK_UNLOCKED;
+ sctp_port_rover = sysctl_local_port_range[0] - 1;
+ for (i = 0; i < sctp_port_hashsize; i++) {
+ sctp_port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
+ sctp_port_hashtable[i].chain = NULL;
}
sctp_sysctl_register();
- INIT_LIST_HEAD(&sctp_proto.address_families);
+ INIT_LIST_HEAD(&sctp_address_families);
sctp_register_af(&sctp_ipv4_specific);
status = sctp_v6_init();
@@ -1031,13 +1070,13 @@ __init int sctp_init(void)
}
/* Initialize the local address list. */
- INIT_LIST_HEAD(&sctp_proto.local_addr_list);
- sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED;
+ INIT_LIST_HEAD(&sctp_local_addr_list);
+ sctp_local_addr_lock = SPIN_LOCK_UNLOCKED;
/* Register notifier for inet address additions/deletions. */
register_inetaddr_notifier(&sctp_inetaddr_notifier);
- sctp_get_local_addr_list(&sctp_proto);
+ sctp_get_local_addr_list();
__unsafe(THIS_MODULE);
return 0;
@@ -1047,16 +1086,20 @@ err_ctl_sock_init:
err_v6_init:
sctp_sysctl_unregister();
list_del(&sctp_ipv4_specific.list);
- kfree(sctp_proto.port_hashtable);
+ kfree(sctp_port_hashtable);
err_bhash_alloc:
- kfree(sctp_proto.ep_hashbucket);
+ kfree(sctp_ep_hashbucket);
err_ehash_alloc:
- kfree(sctp_proto.assoc_hashbucket);
+ kfree(sctp_assoc_hashbucket);
err_ahash_alloc:
sctp_dbg_objcnt_exit();
sctp_proc_exit();
cleanup_sctp_mibs();
err_init_mibs:
+ kmem_cache_destroy(sctp_chunk_cachep);
+err_chunk_cachep:
+ kmem_cache_destroy(sctp_bucket_cachep);
+err_bucket_cachep:
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
inet_unregister_protosw(&sctp_seqpacket_protosw);
inet_unregister_protosw(&sctp_stream_protosw);
@@ -1074,7 +1117,7 @@ __exit void sctp_exit(void)
unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
/* Free the local address list. */
- sctp_free_local_addr_list(&sctp_proto);
+ sctp_free_local_addr_list();
/* Free the control endpoint. */
sock_release(sctp_ctl_socket);
@@ -1083,9 +1126,12 @@ __exit void sctp_exit(void)
sctp_sysctl_unregister();
list_del(&sctp_ipv4_specific.list);
- kfree(sctp_proto.assoc_hashbucket);
- kfree(sctp_proto.ep_hashbucket);
- kfree(sctp_proto.port_hashtable);
+ kfree(sctp_assoc_hashbucket);
+ kfree(sctp_ep_hashbucket);
+ kfree(sctp_port_hashtable);
+
+ kmem_cache_destroy(sctp_chunk_cachep);
+ kmem_cache_destroy(sctp_bucket_cachep);
sctp_dbg_objcnt_exit();
sctp_proc_exit();
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 520260340f4b..bdc1fc117e83 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -68,6 +68,8 @@
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
+extern kmem_cache_t *sctp_chunk_cachep;
+
/* What was the inbound interface for this chunk? */
int sctp_chunk_iif(const struct sctp_chunk *chunk)
{
@@ -874,7 +876,7 @@ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc,
const void *payload, const size_t paylen)
{
struct sctp_chunk *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
- 0, paylen);
+ 0, paylen);
if (!retval)
goto nodata;
@@ -976,7 +978,9 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
const struct sctp_association *asoc,
struct sock *sk)
{
- struct sctp_chunk *retval = t_new(struct sctp_chunk, GFP_ATOMIC);
+ struct sctp_chunk *retval;
+
+ retval = kmem_cache_alloc(sctp_chunk_cachep, SLAB_ATOMIC);
if (!retval)
goto nodata;
@@ -1048,7 +1052,7 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk)
* arguments, reserving enough space for a 'paylen' byte payload.
*/
struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
- __u8 type, __u8 flags, int paylen)
+ __u8 type, __u8 flags, int paylen)
{
struct sctp_chunk *retval;
sctp_chunkhdr_t *chunk_hdr;
@@ -1075,13 +1079,12 @@ struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc,
}
retval->chunk_hdr = chunk_hdr;
- retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(sctp_chunkhdr_t);
+ retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(struct sctp_chunkhdr);
/* Set the skb to the belonging sock for accounting. */
skb->sk = sk;
return retval;
-
nodata:
return NULL;
}
@@ -1090,12 +1093,11 @@ nodata:
/* Release the memory occupied by a chunk. */
static void sctp_chunk_destroy(struct sctp_chunk *chunk)
{
-
/* Free the chunk skb data and the SCTP_chunk stub itself. */
dev_kfree_skb(chunk->skb);
- kfree(chunk);
SCTP_DBG_OBJCNT_DEC(chunk);
+ kmem_cache_free(sctp_chunk_cachep, chunk);
}
/* Possibly, free the chunk. */
@@ -1728,8 +1730,12 @@ int sctp_verify_init(const struct sctp_association *asoc,
sctp_walk_params(param, peer_init, init_hdr.params) {
- if (!sctp_verify_param(asoc, param, cid, chunk, errp))
- return 0;
+ if (!sctp_verify_param(asoc, param, cid, chunk, errp)) {
+ if (SCTP_PARAM_HOST_NAME_ADDRESS == param.p->type)
+ return 0;
+ else
+ return 1;
+ }
} /* for (loop through all parameters) */
@@ -1907,7 +1913,7 @@ int sctp_process_param(struct sctp_association *asoc, union sctp_params param,
break;
case SCTP_PARAM_COOKIE_PRESERVATIVE:
- if (!sctp_proto.cookie_preserve_enable)
+ if (!sctp_cookie_preserve_enable)
break;
stale = ntohl(param.life->lifespan_increment);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index dc8d2983c2bf..cd3782134a2c 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -415,7 +415,8 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
struct sctp_ulpevent *event;
event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC,
- 0, 0, 0, GFP_ATOMIC);
+ (__u16)error, 0, 0,
+ GFP_ATOMIC);
if (event)
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 52f4ee0c8d90..905d6b6aa6bc 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -738,7 +738,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const struct sctp_endpoint *ep,
{
struct sctp_transport *transport = (struct sctp_transport *) arg;
- if (asoc->overall_error_count >= asoc->overall_error_threshold) {
+ if (asoc->overall_error_count > asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED,
SCTP_U32(SCTP_ERROR_NO_ERROR));
@@ -1238,7 +1238,6 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* parameter type.
*/
sctp_addto_chunk(repl, len, unk_param);
- sctp_chunk_free(err_chunk);
}
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
@@ -1788,24 +1787,17 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep,
struct sctp_chunk *chunk = arg;
sctp_errhdr_t *err;
- err = (sctp_errhdr_t *)(chunk->skb->data);
-
- /* If we have gotten too many failures, give up. */
- if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] >
- asoc->max_init_attempts) {
- /* INIT_FAILED will issue an ulpevent. */
- sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED,
- SCTP_U32(err->cause));
- return SCTP_DISPOSITION_DELETE_TCB;
- }
-
/* Process the error here */
- switch (err->cause) {
- case SCTP_ERROR_STALE_COOKIE:
- return sctp_sf_do_5_2_6_stale(ep, asoc, type, arg, commands);
- default:
- return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
+ /* FUTURE FIXME: When PR-SCTP related and other optional
+ * parms are emitted, this will have to change to handle multiple
+ * errors.
+ */
+ sctp_walk_errors(err, chunk->chunk_hdr) {
+ if (SCTP_ERROR_STALE_COOKIE == err->cause)
+ return sctp_sf_do_5_2_6_stale(ep, asoc, type,
+ arg, commands);
}
+ return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
}
/*
@@ -2067,6 +2059,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
struct sctp_chunk *chunk = arg;
sctp_shutdownhdr_t *sdh;
sctp_disposition_t disposition;
+ struct sctp_ulpevent *ev;
/* Convert the elaborate header. */
sdh = (sctp_shutdownhdr_t *)chunk->skb->data;
@@ -2097,12 +2090,28 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep,
arg, commands);
}
+ if (SCTP_DISPOSITION_NOMEM == disposition)
+ goto out;
+
/* - verify, by checking the Cumulative TSN Ack field of the
* chunk, that all its outstanding DATA chunks have been
* received by the SHUTDOWN sender.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_CTSN,
SCTP_U32(chunk->subh.shutdown_hdr->cum_tsn_ack));
+
+ /* API 5.3.1.5 SCTP_SHUTDOWN_EVENT
+ * When a peer sends a SHUTDOWN, SCTP delivers this notification to
+ * inform the application that it should cease sending data.
+ */
+ ev = sctp_ulpevent_make_shutdown_event(asoc, 0, GFP_ATOMIC);
+ if (!ev) {
+ disposition = SCTP_DISPOSITION_NOMEM;
+ goto out;
+ }
+ sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+
+out:
return disposition;
}
@@ -2334,7 +2343,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
if (af && af->is_ce(chunk->skb) && asoc->peer.ecn_capable) {
/* Do real work as sideffect. */
- sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
+ sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
SCTP_U32(tsn));
}
}
@@ -2375,7 +2384,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep,
* PMTU. In cases, such as loopback, this might be a rather
* large spill over.
*/
- if (!asoc->rwnd || asoc->rwnd_over ||
+ if (!asoc->rwnd || asoc->rwnd_over ||
(datalen > asoc->rwnd + asoc->frag_point)) {
/* If this is the next TSN, consider reneging to make
@@ -2594,7 +2603,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
if (af && af->is_ce(chunk->skb) && asoc->peer.ecn_capable) {
/* Do real work as sideffect. */
- sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
+ sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE,
SCTP_U32(tsn));
}
}
@@ -2740,6 +2749,9 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep,
/* Pull the SACK chunk from the data buffer */
sackh = sctp_sm_pull_sack(chunk);
+ /* Was this a bogus SACK? */
+ if (!sackh)
+ return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
chunk->subh.sack_hdr = sackh;
ctsn = ntohl(sackh->cum_tsn_ack);
@@ -4406,22 +4418,27 @@ sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep,
********************************************************************/
/* Pull the SACK chunk based on the SACK header. */
-sctp_sackhdr_t *sctp_sm_pull_sack(struct sctp_chunk *chunk)
+struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk)
{
- sctp_sackhdr_t *sack;
+ struct sctp_sackhdr *sack;
+ unsigned int len;
__u16 num_blocks;
__u16 num_dup_tsns;
- /* FIXME: Protect ourselves from reading too far into
+ /* Protect ourselves from reading too far into
* the skb from a bogus sender.
*/
- sack = (sctp_sackhdr_t *) chunk->skb->data;
- skb_pull(chunk->skb, sizeof(sctp_sackhdr_t));
+ sack = (struct sctp_sackhdr *) chunk->skb->data;
num_blocks = ntohs(sack->num_gap_ack_blocks);
num_dup_tsns = ntohs(sack->num_dup_tsns);
+ len = sizeof(struct sctp_sackhdr);
+ len = (num_blocks + num_dup_tsns) * sizeof(__u32);
+ if (len > chunk->skb->len)
+ return NULL;
+
+ skb_pull(chunk->skb, len);
- skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32));
return sack;
}
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index fd9fa6932ee5..6eea179c5c52 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -436,51 +436,6 @@ sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NU
TYPE_SCTP_SHUTDOWN_COMPLETE,
}; /* state_fn_t chunk_event_table[][] */
-static sctp_sm_table_entry_t
-chunk_event_table_asconf[SCTP_STATE_NUM_STATES] = {
- /* SCTP_STATE_EMPTY */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_CLOSED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_COOKIE_WAIT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_COOKIE_ECHOED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_ESTABLISHED */
- {.fn = sctp_sf_discard_chunk,
- .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf)"},
- /* SCTP_STATE_SHUTDOWN_PENDING */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_SENT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_RECEIVED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_ACK_SENT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
-}; /* chunk asconf */
-
-static sctp_sm_table_entry_t
-chunk_event_table_asconf_ack[SCTP_STATE_NUM_STATES] = {
- /* SCTP_STATE_EMPTY */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_CLOSED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_COOKIE_WAIT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_COOKIE_ECHOED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_ESTABLISHED */
- {.fn = sctp_sf_discard_chunk,
- .name = "sctp_sf_discard_chunk (will be sctp_addip_do_asconf_ack)"},
- /* SCTP_STATE_SHUTDOWN_PENDING */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_SENT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_RECEIVED */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
- /* SCTP_STATE_SHUTDOWN_ACK_SENT */
- {.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"},
-}; /* chunk asconf_ack */
static sctp_sm_table_entry_t
chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
@@ -783,7 +738,7 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
- {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
+ {.fn = sctp_sf_t5_timer_expire, .name = "sctp_sf_t5_timer_expire"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_t5_timer_expire, .name = "sctp_sf_t5_timer_expire"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
@@ -877,13 +832,5 @@ sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t stat
return &chunk_event_table[cid][state];
}
- switch (cid) {
- case SCTP_CID_ASCONF:
- return &chunk_event_table_asconf[state];
-
- case SCTP_CID_ASCONF_ACK:
- return &chunk_event_table_asconf_ack[state];
- default:
- return &chunk_event_table_unknown[state];
- }
+ return &chunk_event_table_unknown[state];
}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 7d0d2bb912c2..f5f0f022994a 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -99,6 +99,8 @@ static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
+extern kmem_cache_t *sctp_bucket_cachep;
+
/* Look up the association by its id. If this is not a UDP-style
* socket, the ID field is always ignored.
*/
@@ -106,10 +108,16 @@ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
{
struct sctp_association *asoc = NULL;
- /* If this is not a UDP-style socket, assoc id should be
- * ignored.
- */
+ /* If this is not a UDP-style socket, assoc id should be ignored. */
if (!sctp_style(sk, UDP)) {
+ /* Return NULL if the socket state is not ESTABLISHED. It
+ * could be a TCP-style listening socket or a socket which
+ * hasn't yet called connect() to establish an association.
+ */
+ if (!sctp_sstate(sk, ESTABLISHED))
+ return NULL;
+
+ /* Get the first and the only association from the list. */
if (!list_empty(&sctp_sk(sk)->ep->asocs))
asoc = list_entry(sctp_sk(sk)->ep->asocs.next,
struct sctp_association, asocs);
@@ -132,6 +140,30 @@ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id)
return asoc;
}
+/* Look up the transport from an address and an assoc id. If both address and
+ * id are specified, the associations matching the address and the id should be
+ * the same.
+ */
+struct sctp_transport *sctp_addr_id2transport(struct sock *sk,
+ struct sockaddr_storage *addr,
+ sctp_assoc_t id)
+{
+ struct sctp_association *addr_asoc = NULL, *id_asoc = NULL;
+ struct sctp_transport *transport;
+
+ addr_asoc = sctp_endpoint_lookup_assoc(sctp_sk(sk)->ep,
+ (union sctp_addr *)addr,
+ &transport);
+ if (!addr_asoc)
+ return NULL;
+
+ id_asoc = sctp_id2assoc(sk, id);
+ if (id_asoc && (id_asoc != addr_asoc))
+ return NULL;
+
+ return transport;
+}
+
/* API 3.1.2 bind() - UDP Style Syntax
* The syntax of bind() is,
*
@@ -710,7 +742,7 @@ SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
struct sctp_association *asoc;
struct list_head *pos, *temp;
- printk("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout);
+ SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p, timeout:%ld)\n", sk, timeout);
sctp_lock_sock(sk);
sk->sk_shutdown = SHUTDOWN_MASK;
@@ -1061,11 +1093,11 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* If the user didn't specify SNDRCVINFO, make up one with
* some defaults.
*/
- default_sinfo.sinfo_stream = asoc->defaults.stream;
- default_sinfo.sinfo_flags = asoc->defaults.flags;
- default_sinfo.sinfo_ppid = asoc->defaults.ppid;
- default_sinfo.sinfo_context = asoc->defaults.context;
- default_sinfo.sinfo_timetolive = asoc->defaults.timetolive;
+ default_sinfo.sinfo_stream = asoc->default_stream;
+ default_sinfo.sinfo_flags = asoc->default_flags;
+ default_sinfo.sinfo_ppid = asoc->default_ppid;
+ default_sinfo.sinfo_context = asoc->default_context;
+ default_sinfo.sinfo_timetolive = asoc->default_timetolive;
default_sinfo.sinfo_assoc_id = sctp_assoc2id(asoc);
sinfo = &default_sinfo;
}
@@ -1333,6 +1365,13 @@ out:
return err;
}
+/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
+ *
+ * This option is a on/off flag. If enabled no SCTP message
+ * fragmentation will be performed. Instead if a message being sent
+ * exceeds the current PMTU size, the message will NOT be sent and
+ * instead a error will be indicated to the user.
+ */
static int sctp_setsockopt_disable_fragments(struct sock *sk,
char *optval, int optlen)
{
@@ -1359,6 +1398,17 @@ static int sctp_setsockopt_events(struct sock *sk, char *optval,
return 0;
}
+/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
+ *
+ * This socket option is applicable to the UDP-style socket only. When
+ * set it will cause associations that are idle for more than the
+ * specified number of seconds to automatically close. An association
+ * being idle is defined an association that has NOT sent or received
+ * user data. The special value of '0' indicates that no automatic
+ * close of any associations should be performed. The option expects an
+ * integer defining the number of seconds of idle time before an
+ * association is closed.
+ */
static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
int optlen)
{
@@ -1376,12 +1426,41 @@ static int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
return 0;
}
+/* 7.1.13 Peer Address Parameters (SCTP_SET_PEER_ADDR_PARAMS)
+ *
+ * Applications can enable or disable heartbeats for any peer address of
+ * an association, modify an address's heartbeat interval, force a
+ * heartbeat to be sent immediately, and adjust the address's maximum
+ * number of retransmissions sent before an address is considered
+ * unreachable. The following structure is used to access and modify an
+ * address's parameters:
+ *
+ * struct sctp_paddrparams {
+ * sctp_assoc_t spp_assoc_id;
+ * struct sockaddr_storage spp_address;
+ * uint32_t spp_hbinterval;
+ * uint16_t spp_pathmaxrxt;
+ * };
+ *
+ * spp_assoc_id - (UDP style socket) This is filled in the application,
+ * and identifies the association for this query.
+ * spp_address - This specifies which address is of interest.
+ * spp_hbinterval - This contains the value of the heartbeat interval,
+ * in milliseconds. A value of 0, when modifying the
+ * parameter, specifies that the heartbeat on this
+ * address should be disabled. A value of UINT32_MAX
+ * (4294967295), when modifying the parameter,
+ * specifies that a heartbeat should be sent
+ * immediately to the peer address, and the current
+ * interval should remain unchanged.
+ * spp_pathmaxrxt - This contains the maximum number of
+ * retransmissions before this address shall be
+ * considered unreachable.
+ */
static int sctp_setsockopt_peer_addr_params(struct sock *sk,
char *optval, int optlen)
{
struct sctp_paddrparams params;
- struct sctp_association *asoc;
- union sctp_addr *addr;
struct sctp_transport *trans;
int error;
@@ -1390,15 +1469,10 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
- asoc = sctp_id2assoc(sk, params.spp_assoc_id);
- if (!asoc)
- return -EINVAL;
-
- addr = (union sctp_addr *) &(params.spp_address);
-
- trans = sctp_assoc_lookup_paddr(asoc, addr);
+ trans = sctp_addr_id2transport(sk, &params.spp_address,
+ params.spp_assoc_id);
if (!trans)
- return -ENOENT;
+ return -EINVAL;
/* Applications can enable or disable heartbeats for any peer address
* of an association, modify an address's heartbeat interval, force a
@@ -1412,7 +1486,7 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
* and the current interval should remain unchanged.
*/
if (0xffffffff == params.spp_hbinterval) {
- error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans);
+ error = sctp_primitive_REQUESTHEARTBEAT (trans->asoc, trans);
if (error)
return error;
} else {
@@ -1435,6 +1509,17 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
return 0;
}
+/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
+ *
+ * Applications can specify protocol parameters for the default association
+ * initialization. The option name argument to setsockopt() and getsockopt()
+ * is SCTP_INITMSG.
+ *
+ * Setting initialization parameters is effective only on an unconnected
+ * socket (for UDP-style sockets only future associations are effected
+ * by the change). With TCP-style sockets, this option is inherited by
+ * sockets derived from a listener socket.
+ */
static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen)
{
if (optlen != sizeof(struct sctp_initmsg))
@@ -1445,7 +1530,7 @@ static int sctp_setsockopt_initmsg(struct sock *sk, char *optval, int optlen)
}
/*
- * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
+ * 7.1.14 Set default send parameters (SET_DEFAULT_SEND_PARAM)
*
* Applications that wish to use the sendto() system call may wish to
* specify a default set of parameters that would normally be supplied
@@ -1463,6 +1548,7 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
+ struct sctp_opt *sp = sctp_sk(sk);
if (optlen != sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
@@ -1470,14 +1556,23 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
return -EFAULT;
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
- if (!asoc)
+ if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
- asoc->defaults.stream = info.sinfo_stream;
- asoc->defaults.flags = info.sinfo_flags;
- asoc->defaults.ppid = info.sinfo_ppid;
- asoc->defaults.context = info.sinfo_context;
- asoc->defaults.timetolive = info.sinfo_timetolive;
+ if (asoc) {
+ asoc->default_stream = info.sinfo_stream;
+ asoc->default_flags = info.sinfo_flags;
+ asoc->default_ppid = info.sinfo_ppid;
+ asoc->default_context = info.sinfo_context;
+ asoc->default_timetolive = info.sinfo_timetolive;
+ } else {
+ sp->default_stream = info.sinfo_stream;
+ sp->default_flags = info.sinfo_flags;
+ sp->default_ppid = info.sinfo_ppid;
+ sp->default_context = info.sinfo_context;
+ sp->default_timetolive = info.sinfo_timetolive;
+ }
+
return 0;
}
@@ -1490,8 +1585,6 @@ static int sctp_setsockopt_default_send_param(struct sock *sk,
static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
{
struct sctp_setpeerprim prim;
- struct sctp_association *asoc;
- union sctp_addr *addr;
struct sctp_transport *trans;
if (optlen != sizeof(struct sctp_setpeerprim))
@@ -1500,18 +1593,11 @@ static int sctp_setsockopt_peer_prim(struct sock *sk, char *optval, int optlen)
if (copy_from_user(&prim, optval, sizeof(struct sctp_setpeerprim)))
return -EFAULT;
- asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
- if (!asoc)
- return -EINVAL;
-
- /* Find the requested address. */
- addr = (union sctp_addr *) &(prim.sspp_addr);
-
- trans = sctp_assoc_lookup_paddr(asoc, addr);
+ trans = sctp_addr_id2transport(sk, &prim.sspp_addr, prim.sspp_assoc_id);
if (!trans)
- return -ENOENT;
+ return -EINVAL;
- sctp_assoc_set_primary(asoc, trans);
+ sctp_assoc_set_primary(trans->asoc, trans);
return 0;
}
@@ -1906,13 +1992,10 @@ SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
SCTP_STATIC int sctp_init_sock(struct sock *sk)
{
struct sctp_endpoint *ep;
- struct sctp_protocol *proto;
struct sctp_opt *sp;
SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
- proto = sctp_get_protocol();
-
sp = sctp_sk(sk);
/* Initialize the SCTP per socket area. */
@@ -1933,23 +2016,26 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
*/
sp->default_stream = 0;
sp->default_ppid = 0;
+ sp->default_flags = 0;
+ sp->default_context = 0;
+ sp->default_timetolive = 0;
/* Initialize default setup parameters. These parameters
* can be modified with the SCTP_INITMSG socket option or
* overridden by the SCTP_INIT CMSG.
*/
- sp->initmsg.sinit_num_ostreams = proto->max_outstreams;
- sp->initmsg.sinit_max_instreams = proto->max_instreams;
- sp->initmsg.sinit_max_attempts = proto->max_retrans_init;
- sp->initmsg.sinit_max_init_timeo = proto->rto_max / HZ;
+ sp->initmsg.sinit_num_ostreams = sctp_max_outstreams;
+ sp->initmsg.sinit_max_instreams = sctp_max_instreams;
+ sp->initmsg.sinit_max_attempts = sctp_max_retrans_init;
+ sp->initmsg.sinit_max_init_timeo = sctp_rto_max / HZ;
/* Initialize default RTO related parameters. These parameters can
* be modified for with the SCTP_RTOINFO socket option.
* FIXME: These are not used yet.
*/
- sp->rtoinfo.srto_initial = proto->rto_initial;
- sp->rtoinfo.srto_max = proto->rto_max;
- sp->rtoinfo.srto_min = proto->rto_min;
+ sp->rtoinfo.srto_initial = sctp_rto_initial;
+ sp->rtoinfo.srto_max = sctp_rto_max;
+ sp->rtoinfo.srto_min = sctp_rto_min;
/* Initialize default event subscriptions.
* the struct sock is initialized to zero, so only
@@ -1964,8 +2050,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
/* Default Peer Address Parameters. These defaults can
* be modified via SCTP_SET_PEER_ADDR_PARAMS
*/
- sp->paddrparam.spp_hbinterval = proto->hb_interval / HZ;
- sp->paddrparam.spp_pathmaxrxt = proto->max_retrans_path;
+ sp->paddrparam.spp_hbinterval = sctp_hb_interval / HZ;
+ sp->paddrparam.spp_pathmaxrxt = sctp_max_retrans_path;
/* If enabled no SCTP message fragmentation will be performed.
* Configure through SCTP_DISABLE_FRAGMENTS socket option.
@@ -2131,6 +2217,64 @@ out:
return (retval);
}
+
+/* 7.2.2 Peer Address Information (SCTP_GET_PEER_ADDR_INFO)
+ *
+ * Applications can retrieve information about a specific peer address
+ * of an association, including its reachability state, congestion
+ * window, and retransmission timer values. This information is
+ * read-only.
+ */
+static int sctp_getsockopt_peer_addr_info(struct sock *sk, int len,
+ char *optval, int *optlen)
+{
+ struct sctp_paddrinfo pinfo;
+ struct sctp_transport *transport;
+ int retval = 0;
+
+ if (len != sizeof(pinfo)) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ if (copy_from_user(&pinfo, optval, sizeof(pinfo))) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ transport = sctp_addr_id2transport(sk, &pinfo.spinfo_address,
+ pinfo.spinfo_assoc_id);
+ if (!transport)
+ return -EINVAL;
+
+ pinfo.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
+ pinfo.spinfo_state = transport->active;
+ pinfo.spinfo_cwnd = transport->cwnd;
+ pinfo.spinfo_srtt = transport->srtt;
+ pinfo.spinfo_rto = transport->rto;
+ pinfo.spinfo_mtu = transport->pmtu;
+
+ if (put_user(len, optlen)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ if (copy_to_user(optval, &pinfo, len)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+out:
+ return (retval);
+}
+
+/* 7.1.12 Enable/Disable message fragmentation (SCTP_DISABLE_FRAGMENTS)
+ *
+ * This option is a on/off flag. If enabled no SCTP message
+ * fragmentation will be performed. Instead if a message being sent
+ * exceeds the current PMTU size, the message will NOT be sent and
+ * instead a error will be indicated to the user.
+ */
static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
char *optval, int *optlen)
{
@@ -2148,6 +2292,11 @@ static int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
return 0;
}
+/* 7.1.15 Set notification and ancillary events (SCTP_SET_EVENTS)
+ *
+ * This socket option is used to specify various notifications and
+ * ancillary data the user wishes to receive.
+ */
static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen)
{
if (len != sizeof(struct sctp_event_subscribe))
@@ -2157,6 +2306,17 @@ static int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, in
return 0;
}
+/* 7.1.8 Automatic Close of associations (SCTP_AUTOCLOSE)
+ *
+ * This socket option is applicable to the UDP-style socket only. When
+ * set it will cause associations that are idle for more than the
+ * specified number of seconds to automatically close. An association
+ * being idle is defined an association that has NOT sent or received
+ * user data. The special value of '0' indicates that no automatic
+ * close of any associations should be performed. The option expects an
+ * integer defining the number of seconds of idle time before an
+ * association is closed.
+ */
static int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
{
/* Applicable to UDP-style socket only */
@@ -2240,12 +2400,41 @@ out:
return retval;
}
+/* 7.1.13 Peer Address Parameters (SCTP_SET_PEER_ADDR_PARAMS)
+ *
+ * Applications can enable or disable heartbeats for any peer address of
+ * an association, modify an address's heartbeat interval, force a
+ * heartbeat to be sent immediately, and adjust the address's maximum
+ * number of retransmissions sent before an address is considered
+ * unreachable. The following structure is used to access and modify an
+ * address's parameters:
+ *
+ * struct sctp_paddrparams {
+ * sctp_assoc_t spp_assoc_id;
+ * struct sockaddr_storage spp_address;
+ * uint32_t spp_hbinterval;
+ * uint16_t spp_pathmaxrxt;
+ * };
+ *
+ * spp_assoc_id - (UDP style socket) This is filled in the application,
+ * and identifies the association for this query.
+ * spp_address - This specifies which address is of interest.
+ * spp_hbinterval - This contains the value of the heartbeat interval,
+ * in milliseconds. A value of 0, when modifying the
+ * parameter, specifies that the heartbeat on this
+ * address should be disabled. A value of UINT32_MAX
+ * (4294967295), when modifying the parameter,
+ * specifies that a heartbeat should be sent
+ * immediately to the peer address, and the current
+ * interval should remain unchanged.
+ * spp_pathmaxrxt - This contains the maximum number of
+ * retransmissions before this address shall be
+ * considered unreachable.
+ */
static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
char *optval, int *optlen)
{
struct sctp_paddrparams params;
- struct sctp_association *asoc;
- union sctp_addr *addr;
struct sctp_transport *trans;
if (len != sizeof(struct sctp_paddrparams))
@@ -2253,15 +2442,10 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
if (copy_from_user(&params, optval, *optlen))
return -EFAULT;
- asoc = sctp_id2assoc(sk, params.spp_assoc_id);
- if (!asoc)
- return -EINVAL;
-
- addr = (union sctp_addr *) &(params.spp_address);
-
- trans = sctp_assoc_lookup_paddr(asoc, addr);
+ trans = sctp_addr_id2transport(sk, &params.spp_address,
+ params.spp_assoc_id);
if (!trans)
- return -ENOENT;
+ return -EINVAL;
/* The value of the heartbeat interval, in milliseconds. A value of 0,
* when modifying the parameter, specifies that the heartbeat on this
@@ -2286,6 +2470,17 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
return 0;
}
+/* 7.1.3 Initialization Parameters (SCTP_INITMSG)
+ *
+ * Applications can specify protocol parameters for the default association
+ * initialization. The option name argument to setsockopt() and getsockopt()
+ * is SCTP_INITMSG.
+ *
+ * Setting initialization parameters is effective only on an unconnected
+ * socket (for UDP-style sockets only future associations are effected
+ * by the change). With TCP-style sockets, this option is inherited by
+ * sockets derived from a listener socket.
+ */
static int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval, int *optlen)
{
if (len != sizeof(struct sctp_initmsg))
@@ -2491,7 +2686,7 @@ static int sctp_getsockopt_peer_prim(struct sock *sk, int len,
/*
*
- * 7.1.15 Set default send parameters (SET_DEFAULT_SEND_PARAM)
+ * 7.1.14 Set default send parameters (SET_DEFAULT_SEND_PARAM)
*
* Applications that wish to use the sendto() system call may wish to
* specify a default set of parameters that would normally be supplied
@@ -2511,6 +2706,7 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
{
struct sctp_sndrcvinfo info;
struct sctp_association *asoc;
+ struct sctp_opt *sp = sctp_sk(sk);
if (len != sizeof(struct sctp_sndrcvinfo))
return -EINVAL;
@@ -2518,14 +2714,22 @@ static int sctp_getsockopt_default_send_param(struct sock *sk,
return -EFAULT;
asoc = sctp_id2assoc(sk, info.sinfo_assoc_id);
- if (!asoc)
+ if (!asoc && info.sinfo_assoc_id && sctp_style(sk, UDP))
return -EINVAL;
- info.sinfo_stream = asoc->defaults.stream;
- info.sinfo_flags = asoc->defaults.flags;
- info.sinfo_ppid = asoc->defaults.ppid;
- info.sinfo_context = asoc->defaults.context;
- info.sinfo_timetolive = asoc->defaults.timetolive;
+ if (asoc) {
+ info.sinfo_stream = asoc->default_stream;
+ info.sinfo_flags = asoc->default_flags;
+ info.sinfo_ppid = asoc->default_ppid;
+ info.sinfo_context = asoc->default_context;
+ info.sinfo_timetolive = asoc->default_timetolive;
+ } else {
+ info.sinfo_stream = sp->default_stream;
+ info.sinfo_flags = sp->default_flags;
+ info.sinfo_ppid = sp->default_ppid;
+ info.sinfo_context = sp->default_context;
+ info.sinfo_timetolive = sp->default_timetolive;
+ }
if (copy_to_user(optval, &info, sizeof(struct sctp_sndrcvinfo)))
return -EFAULT;
@@ -2698,6 +2902,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_MAXSEG:
retval = sctp_getsockopt_maxseg(sk, len, optval, optlen);
break;
+ case SCTP_GET_PEER_ADDR_INFO:
+ retval = sctp_getsockopt_peer_addr_info(sk, len, optval,
+ optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
@@ -2721,7 +2929,7 @@ static void sctp_unhash(struct sock *sk)
*
* The port hash table (contained in the 'global' SCTP protocol storage
* returned by struct sctp_protocol *sctp_get_protocol()). The hash
- * table is an array of 4096 lists (sctp_bind_hashbucket_t). Each
+ * table is an array of 4096 lists (sctp_bind_hashbucket). Each
* list (the list number is the port number hashed out, so as you
* would expect from a hash function, all the ports in a given list have
* such a number that hashes out to the same list number; you were
@@ -2729,13 +2937,13 @@ static void sctp_unhash(struct sock *sk)
* link to the socket (struct sock) that uses it, the port number and
* a fastreuse flag (FIXME: NPI ipg).
*/
-static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
- unsigned short snum);
+static struct sctp_bind_bucket *sctp_bucket_create(
+ struct sctp_bind_hashbucket *head, unsigned short snum);
+
static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{
- sctp_bind_hashbucket_t *head; /* hash list */
- sctp_bind_bucket_t *pp; /* hash list port iterator */
- struct sctp_protocol *sctp = sctp_get_protocol();
+ struct sctp_bind_hashbucket *head; /* hash list */
+ struct sctp_bind_bucket *pp; /* hash list port iterator */
unsigned short snum;
int ret;
@@ -2750,8 +2958,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
if (snum == 0) {
/* Search for an available port.
*
- * 'sctp->port_rover' was the last port assigned, so
- * we start to search from 'sctp->port_rover +
+ * 'sctp_port_rover' was the last port assigned, so
+ * we start to search from 'sctp_port_rover +
* 1'. What we do is first check if port 'rover' is
* already in the hash table; if not, we use that; if
* it is, we try next.
@@ -2762,14 +2970,14 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
int rover;
int index;
- sctp_spin_lock(&sctp->port_alloc_lock);
- rover = sctp->port_rover;
+ sctp_spin_lock(&sctp_port_alloc_lock);
+ rover = sctp_port_rover;
do {
rover++;
if ((rover < low) || (rover > high))
rover = low;
index = sctp_phashfn(rover);
- head = &sctp->port_hashtable[index];
+ head = &sctp_port_hashtable[index];
sctp_spin_lock(&head->lock);
for (pp = head->chain; pp; pp = pp->next)
if (pp->port == rover)
@@ -2778,8 +2986,8 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
next:
sctp_spin_unlock(&head->lock);
} while (--remaining > 0);
- sctp->port_rover = rover;
- sctp_spin_unlock(&sctp->port_alloc_lock);
+ sctp_port_rover = rover;
+ sctp_spin_unlock(&sctp_port_alloc_lock);
/* Exhausted local port range during search? */
ret = 1;
@@ -2799,7 +3007,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
* to the port number (snum) - we detect that with the
* port iterator, pp being NULL.
*/
- head = &sctp->port_hashtable[sctp_phashfn(snum)];
+ head = &sctp_port_hashtable[sctp_phashfn(snum)];
sctp_spin_lock(&head->lock);
for (pp = head->chain; pp; pp = pp->next) {
if (pp->port == snum)
@@ -3105,12 +3313,13 @@ unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
* 2nd Level Abstractions
********************************************************************/
-static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsigned short snum)
+static struct sctp_bind_bucket *sctp_bucket_create(
+ struct sctp_bind_hashbucket *head, unsigned short snum)
{
- sctp_bind_bucket_t *pp;
+ struct sctp_bind_bucket *pp;
- SCTP_DEBUG_PRINTK( "sctp_bucket_create() begins, snum=%d\n", snum);
- pp = kmalloc(sizeof(sctp_bind_bucket_t), GFP_ATOMIC);
+ pp = kmem_cache_alloc(sctp_bucket_cachep, SLAB_ATOMIC);
+ SCTP_DBG_OBJCNT_INC(bind_bucket);
if (pp) {
pp->port = snum;
pp->fastreuse = 0;
@@ -3120,31 +3329,36 @@ static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsi
head->chain = pp;
pp->pprev = &head->chain;
}
- SCTP_DEBUG_PRINTK("sctp_bucket_create() ends, pp=%p\n", pp);
return pp;
}
+/* Caller must hold hashbucket lock for this tb with local BH disabled */
+static void sctp_bucket_destroy(struct sctp_bind_bucket *pp)
+{
+ if (!pp->sk) {
+ if (pp->next)
+ pp->next->pprev = pp->pprev;
+ *(pp->pprev) = pp->next;
+ kmem_cache_free(sctp_bucket_cachep, pp);
+ SCTP_DBG_OBJCNT_DEC(bind_bucket);
+ }
+}
+
/* FIXME: Comments! */
static __inline__ void __sctp_put_port(struct sock *sk)
{
- struct sctp_protocol *sctp_proto = sctp_get_protocol();
- sctp_bind_hashbucket_t *head =
- &sctp_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
- sctp_bind_bucket_t *pp;
+ struct sctp_bind_hashbucket *head =
+ &sctp_port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
+ struct sctp_bind_bucket *pp;
sctp_spin_lock(&head->lock);
- pp = (sctp_bind_bucket_t *)sk->sk_prev;
+ pp = (struct sctp_bind_bucket *)sk->sk_prev;
if (sk->sk_bind_next)
sk->sk_bind_next->sk_bind_pprev = sk->sk_bind_pprev;
*(sk->sk_bind_pprev) = sk->sk_bind_next;
sk->sk_prev = NULL;
inet_sk(sk)->num = 0;
- if (pp->sk) {
- if (pp->next)
- pp->next->pprev = pp->pprev;
- *(pp->pprev) = pp->next;
- kfree(pp);
- }
+ sctp_bucket_destroy(pp);
sctp_spin_unlock(&head->lock);
}
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 1e54322277e6..fb44048abea2 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -42,13 +42,11 @@
#include <net/sctp/structs.h>
#include <linux/sysctl.h>
-extern struct sctp_protocol sctp_proto;
-
static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_INITIAL,
.procname = "rto_initial",
- .data = &sctp_proto.rto_initial,
+ .data = &sctp_rto_initial,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -57,7 +55,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_MIN,
.procname = "rto_min",
- .data = &sctp_proto.rto_min,
+ .data = &sctp_rto_min,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -66,7 +64,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_MAX,
.procname = "rto_max",
- .data = &sctp_proto.rto_max,
+ .data = &sctp_rto_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -75,7 +73,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_VALID_COOKIE_LIFE,
.procname = "valid_cookie_life",
- .data = &sctp_proto.valid_cookie_life,
+ .data = &sctp_valid_cookie_life,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -84,7 +82,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_MAX_BURST,
.procname = "max_burst",
- .data = &sctp_proto.max_burst,
+ .data = &sctp_max_burst,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
@@ -92,7 +90,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_ASSOCIATION_MAX_RETRANS,
.procname = "association_max_retrans",
- .data = &sctp_proto.max_retrans_association,
+ .data = &sctp_max_retrans_association,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
@@ -100,7 +98,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_PATH_MAX_RETRANS,
.procname = "path_max_retrans",
- .data = &sctp_proto.max_retrans_path,
+ .data = &sctp_max_retrans_path,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
@@ -108,7 +106,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_MAX_INIT_RETRANSMITS,
.procname = "max_init_retransmits",
- .data = &sctp_proto.max_retrans_init,
+ .data = &sctp_max_retrans_init,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
@@ -116,7 +114,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_HB_INTERVAL,
.procname = "hb_interval",
- .data = &sctp_proto.hb_interval,
+ .data = &sctp_hb_interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -125,7 +123,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_PRESERVE_ENABLE,
.procname = "cookie_preserve_enable",
- .data = &sctp_proto.cookie_preserve_enable,
+ .data = &sctp_cookie_preserve_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
@@ -134,7 +132,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_ALPHA,
.procname = "rto_alpha_exp_divisor",
- .data = &sctp_proto.rto_alpha,
+ .data = &sctp_rto_alpha,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
@@ -142,7 +140,7 @@ static ctl_table sctp_table[] = {
{
.ctl_name = NET_SCTP_RTO_BETA,
.procname = "rto_beta_exp_divisor",
- .data = &sctp_proto.rto_beta,
+ .data = &sctp_rto_beta,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 2fa0743eaaab..9728fc5b7bf2 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -82,8 +82,6 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
const union sctp_addr *addr,
int gfp)
{
- struct sctp_protocol *proto = sctp_get_protocol();
-
/* Copy in the address. */
peer->ipaddr = *addr;
peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
@@ -99,7 +97,7 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
* parameter 'RTO.Initial'.
*/
peer->rtt = 0;
- peer->rto = proto->rto_initial;
+ peer->rto = sctp_rto_initial;
peer->rttvar = 0;
peer->srtt = 0;
peer->rto_pending = 0;
@@ -108,11 +106,11 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
peer->last_time_used = jiffies;
peer->last_time_ecne_reduced = jiffies;
- peer->active = 1;
+ peer->active = SCTP_ACTIVE;
peer->hb_allowed = 0;
/* Initialize the default path max_retrans. */
- peer->max_retrans = proto->max_retrans_path;
+ peer->max_retrans = sctp_max_retrans_path;
peer->error_threshold = 0;
peer->error_count = 0;
@@ -272,8 +270,6 @@ void sctp_transport_put(struct sctp_transport *transport)
/* Update transport's RTO based on the newly calculated RTT. */
void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
{
- struct sctp_protocol *proto = sctp_get_protocol();
-
/* Check for valid transport. */
SCTP_ASSERT(tp, "NULL transport", return);
@@ -292,10 +288,10 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
* For example, assuming the default value of RTO.Alpha of
* 1/8, rto_alpha would be expressed as 3.
*/
- tp->rttvar = tp->rttvar - (tp->rttvar >> proto->rto_beta)
- + ((abs(tp->srtt - rtt)) >> proto->rto_beta);
- tp->srtt = tp->srtt - (tp->srtt >> proto->rto_alpha)
- + (rtt >> proto->rto_alpha);
+ tp->rttvar = tp->rttvar - (tp->rttvar >> sctp_rto_beta)
+ + ((abs(tp->srtt - rtt)) >> sctp_rto_beta);
+ tp->srtt = tp->srtt - (tp->srtt >> sctp_rto_alpha)
+ + (rtt >> sctp_rto_alpha);
} else {
/* 6.3.1 C2) When the first RTT measurement R is made, set
* SRTT <- R, RTTVAR <- R/2.
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index d0d639246727..5a0fb48be991 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -224,7 +224,7 @@ static void xfrm_policy_gc_kill(struct xfrm_policy *policy)
atomic_dec(&policy->refcnt);
if (atomic_read(&policy->refcnt) > 1)
- flow_cache_flush(policy);
+ flow_cache_flush();
xfrm_pol_put(policy);
}