diff options
43 files changed, 1312 insertions, 726 deletions
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 0f4203b499a..6055b9c0ac0 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -307,7 +307,9 @@ static void hci_uart_tty_close(struct tty_struct *tty) if (hu) { struct hci_dev *hdev = hu->hdev; - hci_uart_close(hdev); + + if (hdev) + hci_uart_close(hdev); if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) { hu->proto->close(hu); @@ -473,12 +475,18 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, tty->low_latency = 1; } else return -EBUSY; + break; case HCIUARTGETPROTO: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return hu->proto->id; return -EUNATCH; + case HCIUARTGETDEVICE: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return hu->hdev->id; + return -EUNATCH; + default: err = n_tty_ioctl(tty, file, cmd, arg); break; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index b250e6789de..1097ce72393 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -28,8 +28,9 @@ #endif /* Ioctls */ -#define HCIUARTSETPROTO _IOW('U', 200, int) -#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 4 diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index 7c8ccc09b60..829da9a1d11 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -141,6 +141,20 @@ config ACT200L_DONGLE To activate support for ACTiSYS IR-200L dongle you will have to start irattach like this: "irattach -d act200l". +config KINGSUN_DONGLE + tristate "KingSun/DonShine DS-620 IrDA-USB dongle" + depends on IRDA && USB && EXPERIMENTAL + help + Say Y or M here if you want to build support for the KingSun/DonShine + DS-620 IrDA-USB bridge device driver. + + This USB bridge does not conform to the IrDA-USB device class + specification, and therefore needs its own specific driver. This + dongle supports SIR speed only (9600 bps). + + To compile it as a module, choose M here: the module will be called + kingsun-sir. + comment "Old SIR device drivers" config IRPORT_SIR diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index 5be09f1b9ee..233a2f92373 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o obj-$(CONFIG_TOIM3232_DONGLE) += toim3232-sir.o +obj-$(CONFIG_KINGSUN_DONGLE) += kingsun-sir.o # The SIR helper module sir-dev-objs := sir_dev.o sir_dongle.o diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c new file mode 100644 index 00000000000..217429122e7 --- /dev/null +++ b/drivers/net/irda/kingsun-sir.c @@ -0,0 +1,657 @@ +/***************************************************************************** +* +* Filename: kingsun-sir.c +* Version: 0.1.1 +* Description: Irda KingSun/DonShine USB Dongle +* Status: Experimental +* Author: Alex Villac�s Lasso <a_villacis@palosanto.com> +* +* Based on stir4200 and mcs7780 drivers, with (strange?) differences +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +* +*****************************************************************************/ + +/* + * This is my current (2007-04-25) understanding of how this dongle is supposed + * to work. This is based on reverse-engineering and examination of the packet + * data sent and received by the WinXP driver using USBSnoopy. Feel free to + * update here as more of this dongle is known: + * + * General: Unlike the other USB IrDA dongles, this particular dongle exposes, + * not two bulk (in and out) endpoints, but two *interrupt* ones. This dongle, + * like the bulk based ones (stir4200.c and mcs7780.c), requires polling in + * order to receive data. + * Transmission: Just like stir4200, this dongle uses a raw stream of data, + * which needs to be wrapped and escaped in a similar way as in stir4200.c. + * Reception: Poll-based, as in stir4200. Each read returns the contents of a + * 8-byte buffer, of which the first byte (LSB) indicates the number of bytes + * (1-7) of valid data contained within the remaining 7 bytes. For example, if + * the buffer had the following contents: + * 06 ff ff ff c0 01 04 aa + * This means that (06) there are 6 bytes of valid data. The byte 0xaa at the + * end is garbage (left over from a previous reception) and is discarded. + * If a read returns an "impossible" value as the length of valid data (such as + * 0x36) in the first byte, then the buffer is uninitialized (as is the case of + * first plug-in) and its contents should be discarded. There is currently no + * evidence that the top 5 bits of the 1st byte of the buffer can have values + * other than 0 once reception begins. + * Once valid bytes are collected, the assembled stream is a sequence of + * wrapped IrDA frames that is unwrapped and unescaped as in stir4200.c. + * BIG FAT WARNING: the dongle does *not* reset the RX buffer in any way after + * a successful read from the host, which means that in absence of further + * reception, repeated reads from the dongle will return the exact same + * contents repeatedly. Attempts to be smart and cache a previous read seem + * to result in corrupted packets, so this driver depends on the unwrap logic + * to sort out any repeated reads. + * Speed change: no commands observed so far to change speed, assumed fixed + * 9600bps (SIR). + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/crc32.h> + +#include <asm/unaligned.h> +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include <net/irda/irda.h> +#include <net/irda/wrapper.h> +#include <net/irda/crc.h> + +/* + * According to lsusb, 0x07c0 is assigned to + * "Code Mercenaries Hard- und Software GmbH" + */ +#define KING_VENDOR_ID 0x07c0 +#define KING_PRODUCT_ID 0x4200 + +/* These are the currently known USB ids */ +static struct usb_device_id dongles[] = { + /* KingSun Co,Ltd IrDA/USB Bridge */ + { USB_DEVICE(KING_VENDOR_ID, KING_PRODUCT_ID) }, + { } +}; + +MODULE_DEVICE_TABLE(usb, dongles); + +#define KINGSUN_MTT 0x07 + +#define KINGSUN_FIFO_SIZE 4096 +#define KINGSUN_EP_IN 0 +#define KINGSUN_EP_OUT 1 + +struct kingsun_cb { + struct usb_device *usbdev; /* init: probe_irda */ + struct net_device *netdev; /* network layer */ + struct irlap_cb *irlap; /* The link layer we are binded to */ + struct net_device_stats stats; /* network statistics */ + struct qos_info qos; + + __u8 *in_buf; /* receive buffer */ + __u8 *out_buf; /* transmit buffer */ + __u8 max_rx; /* max. atomic read from dongle + (usually 8), also size of in_buf */ + __u8 max_tx; /* max. atomic write to dongle + (usually 8) */ + + iobuff_t rx_buff; /* receive unwrap state machine */ + struct timeval rx_time; + spinlock_t lock; + int receiving; + + __u8 ep_in; + __u8 ep_out; + + struct urb *tx_urb; + struct urb *rx_urb; +}; + +/* Callback transmission routine */ +static void kingsun_send_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + struct net_device *netdev = kingsun->netdev; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + err("kingsun_send_irq: Network not running!"); + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_send_irq: urb asynchronously failed - %d", + urb->status); + } + netif_wake_queue(netdev); +} + +/* + * Called from net/core when new frame is available. + */ +static int kingsun_hard_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct kingsun_cb *kingsun; + int wraplen; + int ret = 0; + + if (skb == NULL || netdev == NULL) + return -EINVAL; + + netif_stop_queue(netdev); + + /* the IRDA wrapping routines don't deal with non linear skb */ + SKB_LINEAR_ASSERT(skb); + + kingsun = netdev_priv(netdev); + + spin_lock(&kingsun->lock); + + /* Append data to the end of whatever data remains to be transmitted */ + wraplen = async_wrap_skb(skb, + kingsun->out_buf, + KINGSUN_FIFO_SIZE); + + /* Calculate how much data can be transmitted in this urb */ + usb_fill_int_urb(kingsun->tx_urb, kingsun->usbdev, + usb_sndintpipe(kingsun->usbdev, kingsun->ep_out), + kingsun->out_buf, wraplen, kingsun_send_irq, + kingsun, 1); + + if ((ret = usb_submit_urb(kingsun->tx_urb, GFP_ATOMIC))) { + err("kingsun_hard_xmit: failed tx_urb submit: %d", ret); + switch (ret) { + case -ENODEV: + case -EPIPE: + break; + default: + kingsun->stats.tx_errors++; + netif_start_queue(netdev); + } + } else { + kingsun->stats.tx_packets++; + kingsun->stats.tx_bytes += skb->len; + } + + dev_kfree_skb(skb); + spin_unlock(&kingsun->lock); + + return ret; +} + +/* Receive callback function */ +static void kingsun_rcv_irq(struct urb *urb) +{ + struct kingsun_cb *kingsun = urb->context; + int ret; + + /* in process of stopping, just drop data */ + if (!netif_running(kingsun->netdev)) { + kingsun->receiving = 0; + return; + } + + /* unlink, shutdown, unplug, other nasties */ + if (urb->status != 0) { + err("kingsun_rcv_irq: urb asynchronously failed - %d", + urb->status); + kingsun->receiving = 0; + return; + } + + if (urb->actual_length == kingsun->max_rx) { + __u8 *bytes = urb->transfer_buffer; + int i; + + /* The very first byte in the buffer indicates the length of + valid data in the read. This byte must be in the range + 1..kingsun->max_rx -1 . Values outside this range indicate + an uninitialized Rx buffer when the dongle has just been + plugged in. */ + if (bytes[0] >= 1 && bytes[0] < kingsun->max_rx) { + for (i = 1; i <= bytes[0]; i++) { + async_unwrap_char(kingsun->netdev, + &kingsun->stats, + &kingsun->rx_buff, bytes[i]); + } + kingsun->netdev->last_rx = jiffies; + do_gettimeofday(&kingsun->rx_time); + kingsun->receiving = + (kingsun->rx_buff.state != OUTSIDE_FRAME) + ? 1 : 0; + } + } else if (urb->actual_length > 0) { + err("%s(): Unexpected response length, expected %d got %d", + __FUNCTION__, kingsun->max_rx, urb->actual_length); + } + /* This urb has already been filled in kingsun_net_open */ + ret = usb_submit_urb(urb, GFP_ATOMIC); +} + +/* + * Function kingsun_net_open (dev) + * + * Network device is taken up. Usually this is done by "ifconfig irda0 up" + */ +static int kingsun_net_open(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + int err = -ENOMEM; + char hwname[16]; + + /* At this point, urbs are NULL, and skb is NULL (see kingsun_probe) */ + kingsun->receiving = 0; + + /* Initialize for SIR to copy data directly into skb. */ + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.truesize = IRDA_SKB_MAX_MTU; + kingsun->rx_buff.skb = dev_alloc_skb(IRDA_SKB_MAX_MTU); + if (!kingsun->rx_buff.skb) + goto free_mem; + + skb_reserve(kingsun->rx_buff.skb, 1); + kingsun->rx_buff.head = kingsun->rx_buff.skb->data; + do_gettimeofday(&kingsun->rx_time); + + kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->rx_urb) + goto free_mem; + + kingsun->tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!kingsun->tx_urb) + goto free_mem; + + /* + * Now that everything should be initialized properly, + * Open new IrLAP layer instance to take care of us... + */ + sprintf(hwname, "usb#%d", kingsun->usbdev->devnum); + kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname); + if (!kingsun->irlap) { + err("kingsun-sir: irlap_open failed"); + goto free_mem; + } + + /* Start first reception */ + usb_fill_int_urb(kingsun->rx_urb, kingsun->usbdev, + usb_rcvintpipe(kingsun->usbdev, kingsun->ep_in), + kingsun->in_buf, kingsun->max_rx, + kingsun_rcv_irq, kingsun, 1); + kingsun->rx_urb->status = 0; + err = usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + if (err) { + err("kingsun-sir: first urb-submit failed: %d", err); + goto close_irlap; + } + + netif_start_queue(netdev); + + /* Situation at this point: + - all work buffers allocated + - urbs allocated and ready to fill + - max rx packet known (in max_rx) + - unwrap state machine initialized, in state outside of any frame + - receive request in progress + - IrLAP layer started, about to hand over packets to send + */ + + return 0; + + close_irlap: + irlap_close(kingsun->irlap); + free_mem: + if (kingsun->tx_urb) { + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb) { + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + if (kingsun->rx_buff.skb) { + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + } + return err; +} + +/* + * Function kingsun_net_close (kingsun) + * + * Network device is taken down. Usually this is done by + * "ifconfig irda0 down" + */ +static int kingsun_net_close(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + + /* Stop transmit processing */ + netif_stop_queue(netdev); + + /* Mop up receive && transmit urb's */ + usb_kill_urb(kingsun->tx_urb); + usb_kill_urb(kingsun->rx_urb); + + usb_free_urb(kingsun->tx_urb); + usb_free_urb(kingsun->rx_urb); + + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + + kfree_skb(kingsun->rx_buff.skb); + kingsun->rx_buff.skb = NULL; + kingsun->rx_buff.head = NULL; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->receiving = 0; + + /* Stop and remove instance of IrLAP */ + if (kingsun->irlap) + irlap_close(kingsun->irlap); + + kingsun->irlap = NULL; + + return 0; +} + +/* + * IOCTLs : Extra out-of-band network commands... + */ +static int kingsun_net_ioctl(struct net_device *netdev, struct ifreq *rq, + int cmd) +{ + struct if_irda_req *irq = (struct if_irda_req *) rq; + struct kingsun_cb *kingsun = netdev_priv(netdev); + int ret = 0; + + switch (cmd) { + case SIOCSBANDWIDTH: /* Set bandwidth */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the device is still there */ + if (netif_device_present(kingsun->netdev)) + /* No observed commands for speed change */ + ret = -EOPNOTSUPP; + break; + + case SIOCSMEDIABUSY: /* Set media busy */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + /* Check if the IrDA stack is still there */ + if (netif_running(kingsun->netdev)) + irda_device_set_media_busy(kingsun->netdev, TRUE); + break; + + case SIOCGRECEIVING: + /* Only approximately true */ + irq->ifr_receiving = kingsun->receiving; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +/* + * Get device stats (for /proc/net/dev and ifconfig) + */ +static struct net_device_stats * +kingsun_net_get_stats(struct net_device *netdev) +{ + struct kingsun_cb *kingsun = netdev_priv(netdev); + return &kingsun->stats; +} + +/* + * This routine is called by the USB subsystem for each new device + * in the system. We need to check if the device is ours, and in + * this case start handling it. + */ +static int kingsun_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + + struct usb_device *dev = interface_to_usbdev(intf); + struct kingsun_cb *kingsun = NULL; + struct net_device *net = NULL; + int ret = -ENOMEM; + int pipe, maxp_in, maxp_out; + __u8 ep_in; + __u8 ep_out; + + /* Check that there really are two interrupt endpoints. + Check based on the one in drivers/usb/input/usbmouse.c + */ + interface = intf->cur_altsetting; + if (interface->desc.bNumEndpoints != 2) { + err("kingsun-sir: expected 2 endpoints, found %d", + interface->desc.bNumEndpoints); + return -ENODEV; + } + endpoint = &interface->endpoint[KINGSUN_EP_IN].desc; + if (!usb_endpoint_is_int_in(endpoint)) { + err("kingsun-sir: endpoint 0 is not interrupt IN"); + return -ENODEV; + } + + ep_in = endpoint->bEndpointAddress; + pipe = usb_rcvintpipe(dev, ep_in); + maxp_in = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + if (maxp_in > 255 || maxp_in <= 1) { + err("%s: endpoint 0 has max packet size %d not in range", + __FILE__, maxp_in); + return -ENODEV; + } + + endpoint = &interface->endpoint[KINGSUN_EP_OUT].desc; + if (!usb_endpoint_is_int_out(endpoint)) { + err("kingsun-sir: endpoint 1 is not interrupt OUT"); + return -ENODEV; + } + + ep_out = endpoint->bEndpointAddress; + pipe = usb_sndintpipe(dev, ep_out); + maxp_out = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + /* Allocate network device container. */ + net = alloc_irdadev(sizeof(*kingsun)); + if(!net) + goto err_out1; + + SET_MODULE_OWNER(net); + SET_NETDEV_DEV(net, &intf->dev); + kingsun = netdev_priv(net); + kingsun->irlap = NULL; + kingsun->tx_urb = NULL; + kingsun->rx_urb = NULL; + kingsun->ep_in = ep_in; + kingsun->ep_out = ep_out; + kingsun->in_buf = NULL; + kingsun->out_buf = NULL; + kingsun->max_rx = (__u8)maxp_in; + kingsun->max_tx = (__u8)maxp_out; + kingsun->netdev = net; + kingsun->usbdev = dev; + kingsun->rx_buff.in_frame = FALSE; + kingsun->rx_buff.state = OUTSIDE_FRAME; + kingsun->rx_buff.skb = NULL; + kingsun->receiving = 0; + spin_lock_init(&kingsun->lock); + + /* Allocate input buffer */ + kingsun->in_buf = (__u8 *)kmalloc(kingsun->max_rx, GFP_KERNEL); + if (!kingsun->in_buf) + goto free_mem; + + /* Allocate output buffer */ + kingsun->out_buf = (__u8 *)kmalloc(KINGSUN_FIFO_SIZE, GFP_KERNEL); + if (!kingsun->out_buf) + goto free_mem; + + printk(KERN_INFO "KingSun/DonShine IRDA/USB found at address %d, " + "Vendor: %x, Product: %x\n", + dev->devnum, le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); + + /* Initialize QoS for this device */ + irda_init_max_qos_capabilies(&kingsun->qos); + + /* That's the Rx capability. */ + kingsun->qos.baud_rate.bits &= IR_9600; + kingsun->qos.min_turn_time.bits &= KINGSUN_MTT; + irda_qos_bits_to_value(&kingsun->qos); + + /* Override the network functions we need to use */ + net->hard_start_xmit = kingsun_hard_xmit; + net->open = kingsun_net_open; + net->stop = kingsun_net_close; + net->get_stats = kingsun_net_get_stats; + net->do_ioctl = kingsun_net_ioctl; + + ret = register_netdev(net); + if (ret != 0) + goto free_mem; + + info("IrDA: Registered KingSun/DonShine device %s", net->name); + + usb_set_intfdata(intf, kingsun); + + /* Situation at this point: + - all work buffers allocated + - urbs not allocated, set to NULL + - max rx packet known (in max_rx) + - unwrap state machine (partially) initialized, but skb == NULL + */ + + return 0; + +free_mem: + if (kingsun->out_buf) kfree(kingsun->out_buf); + if (kingsun->in_buf) kfree(kingsun->in_buf); + free_netdev(net); +err_out1: + return ret; +} + +/* + * The current device is removed, the USB layer tell us to shut it down... + */ +static void kingsun_disconnect(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (!kingsun) + return; + + unregister_netdev(kingsun->netdev); + + /* Mop up receive && transmit urb's */ + if (kingsun->tx_urb != NULL) { + usb_kill_urb(kingsun->tx_urb); + usb_free_urb(kingsun->tx_urb); + kingsun->tx_urb = NULL; + } + if (kingsun->rx_urb != NULL) { + usb_kill_urb(kingsun->rx_urb); + usb_free_urb(kingsun->rx_urb); + kingsun->rx_urb = NULL; + } + + kfree(kingsun->out_buf); + kfree(kingsun->in_buf); + free_netdev(kingsun->netdev); + + usb_set_intfdata(intf, NULL); +} + +#ifdef CONFIG_PM +/* USB suspend, so power off the transmitter/receiver */ +static int kingsun_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + netif_device_detach(kingsun->netdev); + if (kingsun->tx_urb != NULL) usb_kill_urb(kingsun->tx_urb); + if (kingsun->rx_urb != NULL) usb_kill_urb(kingsun->rx_urb); + return 0; +} + +/* Coming out of suspend, so reset hardware */ +static int kingsun_resume(struct usb_interface *intf) +{ + struct kingsun_cb *kingsun = usb_get_intfdata(intf); + + if (kingsun->rx_urb != NULL) + usb_submit_urb(kingsun->rx_urb, GFP_KERNEL); + netif_device_attach(kingsun->netdev); + + return 0; +} +#endif + +/* + * USB device callbacks + */ +static struct usb_driver irda_driver = { + .name = "kingsun-sir", + .probe = kingsun_probe, + .disconnect = kingsun_disconnect, + .id_table = dongles, +#ifdef CONFIG_PM + .suspend = kingsun_suspend, + .resume = kingsun_resume, +#endif +}; + +/* + * Module insertion + */ +static int __init kingsun_init(void) +{ + return usb_register(&irda_driver); +} +module_init(kingsun_init); + +/* + * Module removal + */ +static void __exit kingsun_cleanup(void) +{ + /* Deregister the driver and remove all pending instances */ + usb_deregister(&irda_driver); +} +module_exit(kingsun_cleanup); + +MODULE_AUTHOR("Alex Villac�s Lasso <a_villacis@palosanto.com>"); +MODULE_DESCRIPTION("IrDA-USB Dongle Driver for KingSun/DonShine"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 30446222b39..f671cd2f133 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -467,6 +467,8 @@ struct net_device /* device index hash chain */ struct hlist_node index_hlist; + struct net_device *link_watch_next; + /* register/unregister state machine */ enum { NETREG_UNINITIALIZED=0, NETREG_REGISTERED, /* completed register_netdevice */ diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index 022edfa97ed..7e733a6ba4f 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -54,6 +54,14 @@ struct xt_entry_target unsigned char data[0]; }; +#define XT_TARGET_INIT(__name, __size) \ +{ \ + .target.u.user = { \ + .target_size = XT_ALIGN(__size), \ + .name = __name, \ + }, \ +} + struct xt_standard_target { struct xt_entry_target target; diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index 24c8786d12e..584cd1b18f1 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -238,6 +238,47 @@ static __inline__ struct arpt_entry_target *arpt_get_target(struct arpt_entry *e */ #ifdef __KERNEL__ +/* Standard entry. */ +struct arpt_standard +{ + struct arpt_entry entry; + struct arpt_standard_target target; +}; + +struct arpt_error_target +{ + struct arpt_entry_target target; + char errorname[ARPT_FUNCTION_MAXNAMELEN]; +}; + +struct arpt_error +{ + struct arpt_entry entry; + struct arpt_error_target target; +}; + +#define ARPT_ENTRY_INIT(__size) \ +{ \ + .target_offset = sizeof(struct arpt_entry), \ + .next_offset = (__size), \ +} + +#define ARPT_STANDARD_INIT(__verdict) \ +{ \ + .entry = ARPT_ENTRY_INIT(sizeof(struct arpt_standard)), \ + .target = XT_TARGET_INIT(ARPT_STANDARD_TARGET, \ + sizeof(struct arpt_standard_target)), \ + .target.verdict = -(__verdict) - 1, \ +} + +#define ARPT_ERROR_INIT \ +{ \ + .entry = ARPT_ENTRY_INIT(sizeof(struct arpt_error)), \ + .target = XT_TARGET_INIT(ARPT_ERROR_TARGET, \ + sizeof(struct arpt_error_target)), \ + .target.errorname = "ERROR", \ +} + #define arpt_register_target(tgt) \ ({ (tgt)->family = NF_ARP; \ xt_register_target(tgt); }) diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index 9527296595c..2f46dd728ee 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -295,6 +295,28 @@ struct ipt_error struct ipt_error_target target; }; +#define IPT_ENTRY_INIT(__size) \ +{ \ + .target_offset = sizeof(struct ipt_entry), \ + .next_offset = (__size), \ +} + +#define IPT_STANDARD_INIT(__verdict) \ +{ \ + .entry = IPT_ENTRY_INIT(sizeof(struct ipt_standard)), \ + .target = XT_TARGET_INIT(IPT_STANDARD_TARGET, \ + sizeof(struct xt_standard_target)), \ + .target.verdict = -(__verdict) - 1, \ +} + +#define IPT_ERROR_INIT \ +{ \ + .entry = IPT_ENTRY_INIT(sizeof(struct ipt_error)), \ + .target = XT_TARGET_INIT(IPT_ERROR_TARGET, \ + sizeof(struct ipt_error_target)), \ + .target.errorname = "ERROR", \ +} + extern unsigned int ipt_do_table(struct sk_buff **pskb, unsigned int hook, const struct net_device *in, diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 61aa10412fc..4686f8342cb 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -123,6 +123,28 @@ struct ip6t_error struct ip6t_error_target target; }; +#define IP6T_ENTRY_INIT(__size) \ +{ \ + .target_offset = sizeof(struct ip6t_entry), \ + .next_offset = (__size), \ +} + +#define IP6T_STANDARD_INIT(__verdict) \ +{ \ + .entry = IP6T_ENTRY_INIT(sizeof(struct ip6t_standard)), \ + .target = XT_TARGET_INIT(IP6T_STANDARD_TARGET, \ + sizeof(struct ip6t_standard_target)), \ + .target.verdict = -(__verdict) - 1, \ +} + +#define IP6T_ERROR_INIT \ +{ \ + .entry = IP6T_ENTRY_INIT(sizeof(struct ip6t_error)), \ + .target = XT_TARGET_INIT(IP6T_ERROR_TARGET, \ + sizeof(struct ip6t_error_target)), \ + .target.errorname = "ERROR", \ +} + /* * New IP firewall options for [gs]etsockopt at the RAW IP level. * Unlike BSD Linux inherits IP options so you don't have to use diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 1c6b8bd09b9..4732432f8eb 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -183,13 +183,6 @@ extern void nf_conntrack_hash_insert(struct nf_conn *ct); extern void nf_conntrack_flush(void); -extern struct nf_conntrack_helper * -nf_ct_helper_find_get( const struct nf_conntrack_tuple *tuple); -extern void nf_ct_helper_put(struct nf_conntrack_helper *helper); - -extern struct nf_conntrack_helper * -__nf_conntrack_helper_find_byname(const char *name); - extern int nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, const struct nf_conntrack_tuple *orig); diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index f32f714e5d9..96a58d8e1d3 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -56,9 +56,6 @@ struct nf_conntrack_l3proto */ int (*new)(struct nf_conn *conntrack, const struct sk_buff *skb); - /* Called when a conntrack entry is destroyed */ - void (*destroy)(struct nf_conn *conntrack); - /* * Called before tracking. * *dataoff: offset of protocol header (TCP, UDP,...) in *pskb diff --git a/include/net/netfilter/nf_nat_rule.h b/include/net/netfilter/nf_nat_rule.h index e76565459ad..f9743187d57 100644 --- a/include/net/netfilter/nf_nat_rule.h +++ b/include/net/netfilter/nf_nat_rule.h @@ -10,16 +10,11 @@ extern int nf_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, - struct nf_conn *ct, - struct nf_nat_info *info); + struct nf_conn *ct); extern unsigned int -alloc_null_binding(struct nf_conn *ct, - struct nf_nat_info *info, - unsigned int hooknum); +alloc_null_binding(struct nf_conn *ct, unsigned int hooknum); extern unsigned int -alloc_null_binding_confirmed(struct nf_conn *ct, - struct nf_nat_info *info, - unsigned int hooknum); +alloc_null_binding_confirmed(struct nf_conn *ct, unsigned int hooknum); #endif /* _NF_NAT_RULE_H */ diff --git a/include/net/udp.h b/include/net/udp.h index 98755ebaf16..496f89d45c8 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -119,9 +119,16 @@ static inline void udp_lib_close(struct sock *sk, long timeout) } +struct udp_get_port_ops { + int (*saddr_cmp)(const struct sock *sk1, const struct sock *sk2); + int (*saddr_any)(const struct sock *sk); + unsigned int (*hash_port_and_rcv_saddr)(__u16 port, + const struct sock *sk); +}; + /* net/ipv4/udp.c */ extern int udp_get_port(struct sock *sk, unsigned short snum, - int (*saddr_cmp)(const struct sock *, const struct sock *)); + const struct udp_get_port_ops *ops); extern void udp_err(struct sk_buff *, u32); extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, diff --git a/include/net/udplite.h b/include/net/udplite.h index 635b0eafca9..50b4b424d1c 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -120,5 +120,5 @@ static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) extern void udplite4_register(void); extern int udplite_get_port(struct sock *sk, unsigned short snum, - int (*scmp)(const struct sock *, const struct sock *)); + const struct udp_get_port_ops *ops); #endif /* _UDPLITE_H */ diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index d342e89b8bd..0ea40ab4dba 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -174,7 +174,7 @@ static inline int hidp_queue_event(struct hidp_session *session, struct input_de static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct hid_device *hid = dev->private; + struct hid_device *hid = input_get_drvdata(dev); struct hidp_session *session = hid->driver_data; return hidp_queue_event(session, dev, type, code, value); @@ -182,7 +182,7 @@ static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigne static int hidp_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { - struct hidp_session *session = dev->private; + struct hidp_session *session = input_get_drvdata(dev); return hidp_queue_event(session, dev, type, code, value); } @@ -630,7 +630,7 @@ static inline void hidp_setup_input(struct hidp_session *session, struct hidp_co struct input_dev *input = session->input; int i; - input->private = session; + input_set_drvdata(input, session); input->name = "Bluetooth HID Boot Protocol Device"; @@ -663,7 +663,7 @@ static inline void hidp_setup_input(struct hidp_session *session, struct hidp_co input->relbit[0] |= BIT(REL_WHEEL); } - input->cdev.dev = hidp_get_device(session); + input->dev.parent = hidp_get_device(session); input->event = hidp_input_event; @@ -864,7 +864,7 @@ failed: if (session->hid) hid_free_device(session->hid); - kfree(session->input); + input_free_device(session->input); kfree(session); return err; } diff --git a/net/core/link_watch.c b/net/core/link_watch.c index e3c26a9ccad..a5e372b9ec4 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -19,7 +19,6 @@ #include <linux/rtnetlink.h> #include <linux/jiffies.h> #include <linux/spinlock.h> -#include <linux/list.h> #include <linux/slab.h> #include <linux/workqueue.h> #include <linux/bitops.h> @@ -27,8 +26,7 @@ enum lw_bits { - LW_RUNNING = 0, - LW_SE_USED + LW_URGENT = 0, }; static unsigned long linkwatch_flags; @@ -37,17 +35,9 @@ static unsigned long linkwatch_nextevent; static void linkwatch_event(struct work_struct *dummy); static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event); -static LIST_HEAD(lweventlist); +static struct net_device *lweventlist; static DEFINE_SPINLOCK(lweventlist_lock); -struct lw_event { - struct list_head list; - struct net_device *dev; -}; - -/* Avoid kmalloc() for most systems */ -static struct lw_event singleevent; - static unsigned char default_operstate(const struct net_device *dev) { if (!netif_carrier_ok(dev)) @@ -87,25 +77,102 @@ static void rfc2863_policy(struct net_device *dev) } -/* Must be called with the rtnl semaphore held */ -void linkwatch_run_queue(void) +static int linkwatch_urgent_event(struct net_device *dev) { - struct list_head head, *n, *next; + return netif_running(dev) && netif_carrier_ok(dev) && + dev->qdisc != dev->qdisc_sleeping; +} + + +static void linkwatch_add_event(struct net_device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&lweventlist_lock, flags); + dev->link_watch_next = lweventlist; + lweventlist = dev; + spin_unlock_irqrestore(&lweventlist_lock, flags); +} + + +static void linkwatch_schedule_work(int urgent) +{ + unsigned long delay = linkwatch_nextevent - jiffies; + + if (test_bit(LW_URGENT, &linkwatch_flags)) + return; + + /* Minimise down-time: drop delay for up event. */ + if (urgent) { + if (test_and_set_bit(LW_URGENT, &linkwatch_flags)) + return; + delay = 0; + } + + /* If we wrap around we'll delay it by at most HZ. */ + if (delay > HZ) + delay = 0; + + /* + * This is true if we've scheduled it immeditately or if we don't + * need an immediate execution and it's already pending. + */ + if (schedule_delayed_work(&linkwatch_work, delay) == !delay) + return; + + /* Don't bother if there is nothing urgent. */ + if (!test_bit(LW_URGENT, &linkwatch_flags)) + return; + + /* It's already running which is good enough. */ + if (!cancel_delayed_work(&linkwatch_work)) + return; + + /* Otherwise we reschedule it again for immediate exection. */ + schedule_delayed_work(&linkwatch_work, 0); +} + + +static void __linkwatch_run_queue(int urgent_only) +{ + struct net_device *next; + + /* + * Limit the number of linkwatch events to one + * per second so that a runaway driver does not + * cause a storm of messages on the netlink + * socket. This limit does not apply to up events + * while the device qdisc is down. + */ + if (!urgent_only) + linkwatch_nextevent = jiffies + HZ; + /* Limit wrap-around effect on delay. */ + else if (time_after(linkwatch_nextevent, jiffies + HZ)) + linkwatch_nextevent = jiffies; + + clear_bit(LW_URGENT, &linkwatch_flags); spin_lock_irq(&lweventlist_lock); - list_replace_init(&lweventlist, &head); + next = lweventlist; + lweventlist = NULL; spin_unlock_irq(&lweventlist_lock); - list_for_each_safe(n, next, &head) { - struct lw_event *event = list_entry(n, struct lw_event, list); - struct net_device *dev = event->dev; + while (next) { + struct net_device *dev = next; - if (event == &singleevent) { - clear_bit(LW_SE_USED, &linkwatch_flags); - } else { - kfree(event); + next = dev->link_watch_next; + + if (urgent_only && !linkwatch_urgent_event(dev)) { + linkwatch_add_event(dev); + continue; } + /* + * Make sure the above read is complete since it can be + * rewritten as soon as we clear the bit below. + */ + smp_mb__before_clear_bit(); + /* We are about to handle this device, * so new events can be accepted */ @@ -124,58 +191,39 @@ void linkwatch_run_queue(void) dev_put(dev); } + + if (lweventlist) + linkwatch_schedule_work(0); } -static void linkwatch_event(struct work_struct *dummy) +/* Must be called with the rtnl semaphore held */ +void linkwatch_run_queue(void) { - /* Limit the number of linkwatch events to one - * per second so that a runaway driver does not - * cause a storm of messages on the netlink - * socket - */ - linkwatch_nextevent = jiffies + HZ; - clear_bit(LW_RUNNING, &linkwatch_flags); + __linkwatch_run_queue(0); +} + +static void linkwatch_event(struct work_struct *dummy) +{ rtnl_lock(); - linkwatch_run_queue(); + __linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies)); rtnl_unlock(); } void linkwatch_fire_event(struct net_device *dev) { - if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { - unsigned long flags; - struct lw_event *event; - - if (test_and_set_bit(LW_SE_USED, &linkwatch_flags)) { - event = kmalloc(sizeof(struct lw_event), GFP_ATOMIC); - - if (unlikely(event == NULL)) { - clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); - return; - } - } else { - event = &singleevent; - } + int urgent = linkwatch_urgent_event(dev); + if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { dev_hold(dev); - event->dev = dev; - - spin_lock_irqsave(&lweventlist_lock, flags); - list_add_tail(&event->list, &lweventlist); - spin_unlock_irqrestore(&lweventlist_lock, flags); - if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) { - unsigned long delay = linkwatch_nextevent - jiffies; + linkwatch_add_event(dev); + } else if (!urgent) + return; - /* If we wrap around we'll delay it by at most HZ. */ - if (delay > HZ) - delay = 0; - schedule_delayed_work(&linkwatch_work, delay); - } - } + linkwatch_schedule_work(urgent); } EXPORT_SYMBOL(linkwatch_fire_event); diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index 7edea2a1696..75c02306253 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -15,128 +15,34 @@ MODULE_DESCRIPTION("arptables filter table"); #define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | \ (1 << NF_ARP_FORWARD)) -/* Standard entry. */ -struct arpt_standard -{ - struct arpt_entry entry; - struct arpt_standard_target target; -}; - -struct arpt_error_target -{ - struct arpt_entry_target target; - char errorname[ARPT_FUNCTION_MAXNAMELEN]; -}; - -struct arpt_error -{ - struct arpt_entry entry; - struct arpt_error_target target; -}; - static struct { struct arpt_replace repl; struct arpt_standard entries[3]; struct arpt_error term; -} initial_table __initdata -= { { "filter", FILTER_VALID_HOOKS, 4, - sizeof(struct arpt_standard) * 3 + sizeof(struct arpt_error), - { [NF_ARP_IN] = 0, - [NF_ARP_OUT] = sizeof(struct arpt_standard), - [NF_ARP_FORWARD] = 2 * sizeof(struct arpt_standard), }, - { [NF_ARP_IN] = 0, - [NF_ARP_OUT] = sizeof(struct arpt_standard), - [NF_ARP_FORWARD] = 2 * sizeof(struct arpt_standard), }, - 0, NULL, { } }, - { - /* ARP_IN */ - { - { - { - { 0 }, { 0 }, { 0 }, { 0 }, - 0, 0, - { { 0, }, { 0, } }, - { { 0, }, { 0, } }, - 0, 0, - 0, 0, - 0, 0, - "", "", { 0 }, { 0 }, - 0, 0 - }, - sizeof(struct arpt_entry), - sizeof(struct arpt_standard), - 0, - { 0, 0 }, { } }, - { { { { ARPT_ALIGN(sizeof(struct arpt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } - }, - /* ARP_OUT */ - { - { - { - { 0 }, { 0 }, { 0 }, { 0 }, - 0, 0, - { { 0, }, { 0, } }, - { { 0, }, { 0, } }, - 0, 0, - 0, 0, - 0, 0, - "", "", { 0 }, { 0 }, - 0, 0 - }, - sizeof(struct arpt_entry), - sizeof(struct arpt_standard), - 0, - { 0, 0 }, { } }, - { { { { ARPT_ALIGN(sizeof(struct arpt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } - }, - /* ARP_FORWARD */ - { - { - { - { 0 }, { 0 }, { 0 }, { 0 }, - 0, 0, - { { 0, }, { 0, } }, - { { 0, }, { 0, } }, - 0, 0, - 0, 0, - 0, 0, - "", "", { 0 }, { 0 }, - 0, 0 - }, - sizeof(struct arpt_entry), - sizeof(struct arpt_standard), - 0, - { 0, 0 }, { } }, - { { { { ARPT_ALIGN(sizeof(struct arpt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } - } - }, - /* ERROR */ - { - { - { - { 0 }, { 0 }, { 0 }, { 0 }, - 0, 0, - { { 0, }, { 0, } }, - { { 0, }, { 0, } }, - 0, 0, - 0, 0, - 0, 0, - "", "", { 0 }, { 0 }, - 0, 0 - }, - sizeof(struct arpt_entry), - sizeof(struct arpt_error), - 0, - { 0, 0 }, { } }, - { { { { ARPT_ALIGN(sizeof(struct arpt_error_target)), ARPT_ERROR_TARGET } }, - { } }, - "ERROR" - } - } +} initial_table __initdata = { + .repl = { + .name = "filter", + .valid_hooks = FILTER_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct arpt_standard) * 3 + sizeof(struct arpt_error), + .hook_entry = { + [NF_ARP_IN] = 0, + [NF_ARP_OUT] = sizeof(struct arpt_standard), + [NF_ARP_FORWARD] = 2 * sizeof(struct arpt_standard), + }, + .underflow = { + [NF_ARP_IN] = 0, + [NF_ARP_OUT] = sizeof(struct arpt_standard), + [NF_ARP_FORWARD] = 2 * sizeof(struct arpt_standard), + }, + }, + .entries = { + ARPT_STANDARD_INIT(NF_ACCEPT), /* ARP_IN */ + ARPT_STANDARD_INIT(NF_ACCEPT), /* ARP_OUT */ + ARPT_STANDARD_INIT(NF_ACCEPT), /* ARP_FORWARD */ + }, + .term = ARPT_ERROR_INIT, }; static struct arpt_table packet_filter = { diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 42728909eba..4f51c1d7d2d 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -26,53 +26,29 @@ static struct struct ipt_replace repl; struct ipt_standard entries[3]; struct ipt_error term; -} initial_table __initdata -= { { "filter", FILTER_VALID_HOOKS, 4, - sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), - { [NF_IP_LOCAL_IN] = 0, - [NF_IP_FORWARD] = sizeof(struct ipt_standard), - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, - { [NF_IP_LOCAL_IN] = 0, - [NF_IP_FORWARD] = sizeof(struct ipt_standard), - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, - 0, NULL, { } }, - { - /* LOCAL_IN */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* FORWARD */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_OUT */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } } - }, - /* ERROR */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, - { } }, - "ERROR" - } - } +} initial_table __initdata = { + .repl = { + .name = "filter", + .valid_hooks = FILTER_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), + .hook_entry = { + [NF_IP_LOCAL_IN] = 0, + [NF_IP_FORWARD] = sizeof(struct ipt_standard), + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + .underflow = { + [NF_IP_LOCAL_IN] = 0, + [NF_IP_FORWARD] = sizeof(struct ipt_standard), + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2, + }, + }, + .entries = { + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IPT_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IPT_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_filter = { @@ -105,7 +81,8 @@ ipt_local_out_hook(unsigned int hook, if ((*pskb)->len < sizeof(struct iphdr) || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) - printk("ipt_hook: happy cracking.\n"); + printk("iptable_filter: ignoring short SOCK_RAW " + "packet.\n"); return NF_ACCEPT; } diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 9278802f274..902446f7cbc 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -33,73 +33,35 @@ static struct struct ipt_replace repl; struct ipt_standard entries[5]; struct ipt_error term; -} initial_table __initdata -= { { "mangle", MANGLE_VALID_HOOKS, 6, - sizeof(struct ipt_standard) * 5 + sizeof(struct ipt_error), - { [NF_IP_PRE_ROUTING] = 0, - [NF_IP_LOCAL_IN] = sizeof(struct ipt_standard), - [NF_IP_FORWARD] = sizeof(struct ipt_standard) * 2, - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 3, - [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard) * 4 }, - { [NF_IP_PRE_ROUTING] = 0, - [NF_IP_LOCAL_IN] = sizeof(struct ipt_standard), - [NF_IP_FORWARD] = sizeof(struct ipt_standard) * 2, - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 3, - [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard) * 4 }, - 0, NULL, { } }, - { - /* PRE_ROUTING */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_IN */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* FORWARD */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_OUT */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* POST_ROUTING */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_standard), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - }, - /* ERROR */ - { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ipt_entry), - sizeof(struct ipt_error), - 0, { 0, 0 }, { } }, - { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, - { } }, - "ERROR" - } - } +} initial_table __initdata = { + .repl = { + .name = "mangle", + .valid_hooks = MANGLE_VALID_HOOKS, + .num_entries = 6, + .size = sizeof(struct ipt_standard) * 5 + sizeof(struct ipt_error), + .hook_entry = { + [NF_IP_PRE_ROUTING] = 0, + [NF_IP_LOCAL_IN] = sizeof(struct ipt_standard), + [NF_IP_FORWARD] = sizeof(struct ipt_standard) * 2, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 3, + [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard) * 4, + }, + .underflow = { + [NF_IP_PRE_ROUTING] = 0, + [NF_IP_LOCAL_IN] = sizeof(struct ipt_standard), + [NF_IP_FORWARD] = sizeof(struct ipt_standard) * 2, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 3, + [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard) * 4, + }, + }, + .entries = { + IPT_STANDARD_INIT(NF_ACCEPT), /* PRE_ROUTING */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IPT_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + IPT_STANDARD_INIT(NF_ACCEPT), /* POST_ROUTING */ + }, + .term = IPT_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_mangler = { @@ -138,7 +100,8 @@ ipt_local_hook(unsigned int hook, if ((*pskb)->len < sizeof(struct iphdr) || ip_hdrlen(*pskb) < sizeof(struct iphdr)) { if (net_ratelimit()) - printk("ipt_hook: happy cracking.\n"); + printk("iptable_mangle: ignoring short SOCK_RAW " + "packet.\n"); return NF_ACCEPT; } diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 18c3d4c9ff5..d6e50339568 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -5,6 +5,7 @@ */ #include <linux/module.h> #include <linux/netfilter_ipv4/ip_tables.h> +#include <net/ip.h> #define RAW_VALID_HOOKS ((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT)) @@ -21,62 +22,18 @@ static struct .size = sizeof(struct ipt_standard) * 2 + sizeof(struct ipt_error), .hook_entry = { [NF_IP_PRE_ROUTING] = 0, - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) }, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) + }, .underflow = { [NF_IP_PRE_ROUTING] = 0, - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) }, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) + }, }, .entries = { - /* PRE_ROUTING */ - { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_standard), - }, - .target = { - .target = { - .u = { - .target_size = IPT_ALIGN(sizeof(struct ipt_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, - - /* LOCAL_OUT */ - { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_standard), - }, - .target = { - .target = { - .u = { - .target_size = IPT_ALIGN(sizeof(struct ipt_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, + IPT_STANDARD_INIT(NF_ACCEPT), /* PRE_ROUTING */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ }, - /* ERROR */ - .term = { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_error), - }, - .target = { - .target = { - .u = { - .user = { - .target_size = IPT_ALIGN(sizeof(struct ipt_error_target)), - .name = IPT_ERROR_TARGET, - }, - }, - }, - .errorname = "ERROR", - }, - } + .term = IPT_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_raw = { @@ -98,6 +55,24 @@ ipt_hook(unsigned int hook, return ipt_do_table(pskb, hook, in, out, &packet_raw); } +static unsigned int +ipt_local_hook(unsigned int hook, + struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + /* root is playing with raw sockets. */ + if ((*pskb)->len < sizeof(struct iphdr) || + ip_hdrlen(*pskb) < sizeof(struct iphdr)) { + if (net_ratelimit()) + printk("iptable_raw: ignoring short SOCK_RAW" + "packet.\n"); + return NF_ACCEPT; + } + return ipt_do_table(pskb, hook, in, out, &packet_raw); +} + /* 'raw' is the very first table. */ static struct nf_hook_ops ipt_ops[] = { { @@ -108,7 +83,7 @@ static struct nf_hook_ops ipt_ops[] = { .owner = THIS_MODULE, }, { - .hook = ipt_hook, + .hook = ipt_local_hook, .pf = PF_INET, .hooknum = NF_IP_LOCAL_OUT, .priority = NF_IP_PRI_RAW, diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c index 2534f718ab9..6740736c5e7 100644 --- a/net/ipv4/netfilter/nf_nat_rule.c +++ b/net/ipv4/netfilter/nf_nat_rule.c @@ -46,77 +46,20 @@ static struct .hook_entry = { [NF_IP_PRE_ROUTING] = 0, [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard), - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 + }, .underflow = { [NF_IP_PRE_ROUTING] = 0, [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard), - [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, + [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 + }, }, .entries = { - /* PRE_ROUTING */ - { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_standard), - }, - .target = { - .target = { - .u = { - .target_size = IPT_ALIGN(sizeof(struct ipt_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, - /* POST_ROUTING */ - { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_standard), - }, - .target = { - .target = { - .u = { - .target_size = IPT_ALIGN(sizeof(struct ipt_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, - /* LOCAL_OUT */ - { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_standard), - }, - .target = { - .target = { - .u = { - .target_size = IPT_ALIGN(sizeof(struct ipt_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, + IPT_STANDARD_INIT(NF_ACCEPT), /* PRE_ROUTING */ + IPT_STANDARD_INIT(NF_ACCEPT), /* POST_ROUTING */ + IPT_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ }, - /* ERROR */ - .term = { - .entry = { - .target_offset = sizeof(struct ipt_entry), - .next_offset = sizeof(struct ipt_error), - }, - .target = { - .target = { - .u = { - .user = { - .target_size = IPT_ALIGN(sizeof(struct ipt_error_target)), - .name = IPT_ERROR_TARGET, - }, - }, - }, - .errorname = "ERROR", - }, - } + .term = IPT_ERROR_INIT, /* ERROR */ }; static struct xt_table nat_table = { @@ -230,9 +173,7 @@ static int ipt_dnat_checkentry(const char *tablename, } inline unsigned int -alloc_null_binding(struct nf_conn *ct, - struct nf_nat_info *info, - unsigned int hooknum) +alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) { /* Force range to this IP; let proto decide mapping for per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). @@ -251,9 +192,7 @@ alloc_null_binding(struct nf_conn *ct, } unsigned int -alloc_null_binding_confirmed(struct nf_conn *ct, - struct nf_nat_info *info, - unsigned int hooknum) +alloc_null_binding_confirmed(struct nf_conn *ct, unsigned int hooknum) { __be32 ip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC @@ -275,8 +214,7 @@ int nf_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, - struct nf_conn *ct, - struct nf_nat_info *info) + struct nf_conn *ct) { int ret; @@ -285,7 +223,7 @@ int nf_nat_rule_find(struct sk_buff **pskb, if (ret == NF_ACCEPT) { if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) /* NUL mapping */ - ret = alloc_null_binding(ct, info, hooknum); + ret = alloc_null_binding(ct, hooknum); } return ret; } diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c index 64bbed2ba78..55dac36dbc8 100644 --- a/net/ipv4/netfilter/nf_nat_standalone.c +++ b/net/ipv4/netfilter/nf_nat_standalone.c @@ -80,7 +80,6 @@ nf_nat_fn(unsigned int hooknum, struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_conn_nat *nat; - struct nf_nat_info *info; /* maniptype == SRC for postrouting. */ enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); @@ -129,7 +128,6 @@ nf_nat_fn(unsigned int hooknum, } /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ case IP_CT_NEW: - info = &nat->info; /* Seen it before? This can happen for loopback, retrans, or local packets.. */ @@ -138,14 +136,13 @@ nf_nat_fn(unsigned int hooknum, if (unlikely(nf_ct_is_confirmed(ct))) /* NAT module was loaded late */ - ret = alloc_null_binding_confirmed(ct, info, - hooknum); + ret = alloc_null_binding_confirmed(ct, hooknum); else if (hooknum == NF_IP_LOCAL_IN) /* LOCAL_IN hook doesn't have a chain! */ - ret = alloc_null_binding(ct, info, hooknum); + ret = alloc_null_binding(ct, hooknum); else ret = nf_nat_rule_find(pskb, hooknum, in, out, - ct, info); + ct); if (ret != NF_ACCEPT) { return ret; @@ -160,10 +157,8 @@ nf_nat_fn(unsigned int hooknum, /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); - info = &nat->info; } - NF_CT_ASSERT(info); return nf_nat_packet(ct, ctinfo, hooknum, pskb); } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 66026df1cc7..4c7e95fa090 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -118,15 +118,15 @@ static int udp_port_rover; * Note about this hash function : * Typical use is probably daddr = 0, only dport is going to vary hash */ -static inline unsigned int hash_port_and_addr(__u16 port, __be32 addr) +static inline unsigned int udp_hash_port(__u16 port) { - addr ^= addr >> 16; - addr ^= addr >> 8; - return port ^ addr; + return port; } static inline int __udp_lib_port_inuse(unsigned int hash, int port, - __be32 daddr, struct hlist_head udptable[]) + const struct sock *this_sk, + struct hlist_head udptable[], + const struct udp_get_port_ops *ops) { struct sock *sk; struct hlist_node *node; @@ -138,7 +138,10 @@ static inline int __udp_lib_port_inuse(unsigned int hash, int port, inet = inet_sk(sk); if (inet->num != port) continue; - if (inet->rcv_saddr == daddr) + if (this_sk) { + if (ops->saddr_cmp(sk, this_sk)) + return 1; + } else if (ops->saddr_any(sk)) return 1; } return 0; @@ -151,12 +154,11 @@ static inline int __udp_lib_port_inuse(unsigned int hash, int port, * @snum: port number to look up * @udptable: hash list table, must be of UDP_HTABLE_SIZE * @port_rover: pointer to record of last unallocated port - * @saddr_comp: AF-dependent comparison of bound local IP addresses + * @ops: AF-dependent address operations */ int __udp_lib_get_port(struct sock *sk, unsigned short snum, struct hlist_head udptable[], int *port_rover, - int (*saddr_comp)(const struct sock *sk1, - const struct sock *sk2 ) ) + const struct udp_get_port_ops *ops) { struct hlist_node *node; struct hlist_head *head; @@ -176,8 +178,7 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum, for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) { int size; - hash = hash_port_and_addr(result, - inet_sk(sk)->rcv_saddr); + hash = ops->hash_port_and_rcv_saddr(result, sk); head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; if (hlist_empty(head)) { if (result > sysctl_local_port_range[1]) @@ -203,17 +204,16 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum, result = sysctl_local_port_range[0] + ((result - sysctl_local_port_range[0]) & (UDP_HTABLE_SIZE - 1)); - hash = hash_port_and_addr(result, 0); + hash = udp_hash_port(result); if (__udp_lib_port_inuse(hash, result, - 0, udptable)) + NULL, udptable, ops)) continue; - if (!inet_sk(sk)->rcv_saddr) + if (ops->saddr_any(sk)) break; - hash = hash_port_and_addr(result, - inet_sk(sk)->rcv_saddr); + hash = ops->hash_port_and_rcv_saddr(result, sk); if (! __udp_lib_port_inuse(hash, result, - inet_sk(sk)->rcv_saddr, udptable)) + sk, udptable, ops)) break; } if (i >= (1 << 16) / UDP_HTABLE_SIZE) @@ -221,7 +221,7 @@ int __udp_lib_get_port(struct sock *sk, unsigned short snum, gotit: *port_rover = snum = result; } else { - hash = hash_port_and_addr(snum, 0); + hash = udp_hash_port(snum); head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; sk_for_each(sk2, node, head) @@ -231,12 +231,11 @@ gotit: (!sk2->sk_reuse || !sk->sk_reuse) && (!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && - (*saddr_comp)(sk, sk2)) + ops->saddr_cmp(sk, sk2)) goto fail; - if (inet_sk(sk)->rcv_saddr) { - hash = hash_port_and_addr(snum, - inet_sk(sk)->rcv_saddr); + if (!ops->saddr_any(sk)) { + hash = ops->hash_port_and_rcv_saddr(snum, sk); head = &udptable[hash & (UDP_HTABLE_SIZE - 1)]; sk_for_each(sk2, node, head) @@ -248,7 +247,7 @@ gotit: !sk->sk_bound_dev_if || sk2->sk_bound_dev_if == sk->sk_bound_dev_if) && - (*saddr_comp)(sk, sk2)) + ops->saddr_cmp(sk, sk2)) goto fail; } } @@ -266,12 +265,12 @@ fail: } int udp_get_port(struct sock *sk, unsigned short snum, - int (*scmp)(const struct sock *, const struct sock *)) + const struct udp_get_port_ops *ops) { - return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, scmp); + return __udp_lib_get_port(sk, snum, udp_hash, &udp_port_rover, ops); } -int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) +static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) { struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2); @@ -280,9 +279,33 @@ int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2) inet1->rcv_saddr == inet2->rcv_saddr )); } +static int ipv4_rcv_saddr_any(const struct sock *sk) +{ + return !inet_sk(sk)->rcv_saddr; +} + +static inline unsigned int ipv4_hash_port_and_addr(__u16 port, __be32 addr) +{ + addr ^= addr >> 16; + addr ^= addr >> 8; + return port ^ addr; +} + +static unsigned int ipv4_hash_port_and_rcv_saddr(__u16 port, + const struct sock *sk) +{ + return ipv4_hash_port_and_addr(port, inet_sk(sk)->rcv_saddr); +} + +const struct udp_get_port_ops udp_ipv4_ops = { + .saddr_cmp = ipv4_rcv_saddr_equal, + .saddr_any = ipv4_rcv_saddr_any, + .hash_port_and_rcv_saddr = ipv4_hash_port_and_rcv_saddr, +}; + static inline int udp_v4_get_port(struct sock *sk, unsigned short snum) { - return udp_get_port(sk, snum, ipv4_rcv_saddr_equal); + return udp_get_port(sk, snum, &udp_ipv4_ops); } /* UDP is nearly always wildcards out the wazoo, it makes no sense to try @@ -297,8 +320,8 @@ static struct sock *__udp4_lib_lookup(__be32 saddr, __be16 sport, unsigned int hash, hashwild; int score, best = -1, hport = ntohs(dport); - hash = hash_port_and_addr(hport, daddr); - hashwild = hash_port_and_addr(hport, 0); + hash = ipv4_hash_port_and_addr(hport, daddr); + hashwild = udp_hash_port(hport); read_lock(&udp_hash_lock); @@ -1198,8 +1221,8 @@ static int __udp4_lib_mcast_deliver(struct sk_buff *skb, struct sock *sk, *skw, *sknext; int dif; int hport = ntohs(uh->dest); - unsigned int hash = hash_port_and_addr(hport, daddr); - unsigned int hashwild = hash_port_and_addr(hport, 0); + unsigned int hash = ipv4_hash_port_and_addr(hport, daddr); + unsigned int hashwild = udp_hash_port(hport); dif = skb->dev->ifindex; diff --git a/net/ipv4/udp_impl.h b/net/ipv4/udp_impl.h index 820a477cfaa..06d94195e64 100644 --- a/net/ipv4/udp_impl.h +++ b/net/ipv4/udp_impl.h @@ -5,14 +5,14 @@ #include <net/protocol.h> #include <net/inet_common.h> +extern const struct udp_get_port_ops udp_ipv4_ops; + extern int __udp4_lib_rcv(struct sk_buff *, struct hlist_head [], int ); extern void __udp4_lib_err(struct sk_buff *, u32, struct hlist_head []); extern int __udp_lib_get_port(struct sock *sk, unsigned short snum, struct hlist_head udptable[], int *port_rover, - int (*)(const struct sock*,const struct sock*)); -extern int ipv4_rcv_saddr_equal(const struct sock *, const struct sock *); - + const struct udp_get_port_ops *ops); extern int udp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int optlen); diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index f34fd686a8f..3653b32dce2 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -19,14 +19,15 @@ struct hlist_head udplite_hash[UDP_HTABLE_SIZE]; static int udplite_port_rover; int udplite_get_port(struct sock *sk, unsigned short p, - int (*c)(const struct sock *, const struct sock *)) + const struct udp_get_port_ops *ops) { - return __udp_lib_get_port(sk, p, udplite_hash, &udplite_port_rover, c); + return __udp_lib_get_port(sk, p, udplite_hash, + &udplite_port_rover, ops); } static int udplite_v4_get_port(struct sock *sk, unsigned short snum) { - return udplite_get_port(sk, snum, ipv4_rcv_saddr_equal); + return udplite_get_port(sk, snum, &udp_ipv4_ops); } static int udplite_rcv(struct sk_buff *skb) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index d02685c6bc6..c7ea248fae2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -4204,6 +4204,10 @@ int __init addrconf_init(void) return err; ip6_null_entry.rt6i_idev = in6_dev_get(&loopback_dev); +#ifdef CONFIG_IPV6_MULTIPLE_TABLES + ip6_prohibit_entry.rt6i_idev = in6_dev_get(&loopback_dev); + ip6_blk_hole_entry.rt6i_idev = in6_dev_get(&loopback_dev); +#endif register_netdevice_notifier(&ipv6_dev_notf); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 6d8e4ac7bda..14be0b9b77a 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -660,6 +660,14 @@ EXPORT_SYMBOL_GPL(ipv6_invert_rthdr); Hop-by-hop options. **********************************/ +/* + * Note: we cannot rely on skb->dst before we assign it in ip6_route_input(). + */ +static inline struct inet6_dev *ipv6_skb_idev(struct sk_buff *skb) +{ + return skb->dst ? ip6_dst_idev(skb->dst) : __in6_dev_get(skb->dev); +} + /* Router Alert as of RFC 2711 */ static int ipv6_hop_ra(struct sk_buff **skbp, int optoff) @@ -688,25 +696,25 @@ static int ipv6_hop_jumbo(struct sk_buff **skbp, int optoff) if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", nh[optoff+1]); - IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), + IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); goto drop; } pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); if (pkt_len <= IPV6_MAXPLEN) { - IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); + IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); return 0; } if (ipv6_hdr(skb)->payload_len) { - IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INHDRERRORS); + IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); return 0; } if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { - IP6_INC_STATS_BH(ip6_dst_idev(skb->dst), IPSTATS_MIB_INTRUNCATEDPKTS); + IP6_INC_STATS_BH(ipv6_skb_idev(skb), IPSTATS_MIB_INTRUNCATEDPKTS); goto drop; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index f508171bab7..4704b5fc308 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -463,10 +463,17 @@ int ip6_forward(struct sk_buff *skb) */ if (xrlim_allow(dst, 1*HZ)) ndisc_send_redirect(skb, n, target); - } else if (ipv6_addr_type(&hdr->saddr)&(IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK - |IPV6_ADDR_LINKLOCAL)) { + } else { + int addrtype = ipv6_addr_type(&hdr->saddr); + /* This check is security critical. */ - goto error; + if (addrtype & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LOOPBACK)) + goto error; + if (addrtype & IPV6_ADDR_LINKLOCAL) { + icmpv6_send(skb, ICMPV6_DEST_UNREACH, + ICMPV6_NOT_NEIGHBOUR, 0, skb->dev); + goto error; + } } if (skb->len > dst_mtu(dst)) { diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 76f0cf66f95..7e32e2aaf7f 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -24,53 +24,29 @@ static struct struct ip6t_replace repl; struct ip6t_standard entries[3]; struct ip6t_error term; -} initial_table __initdata -= { { "filter", FILTER_VALID_HOOKS, 4, - sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), - { [NF_IP6_LOCAL_IN] = 0, - [NF_IP6_FORWARD] = sizeof(struct ip6t_standard), - [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2 }, - { [NF_IP6_LOCAL_IN] = 0, - [NF_IP6_FORWARD] = sizeof(struct ip6t_standard), - [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2 }, - 0, NULL, { } }, - { - /* LOCAL_IN */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* FORWARD */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_OUT */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } } - }, - /* ERROR */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_error), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, - { } }, - "ERROR" - } - } +} initial_table __initdata = { + .repl = { + .name = "filter", + .valid_hooks = FILTER_VALID_HOOKS, + .num_entries = 4, + .size = sizeof(struct ip6t_standard) * 3 + sizeof(struct ip6t_error), + .hook_entry = { + [NF_IP6_LOCAL_IN] = 0, + [NF_IP6_FORWARD] = sizeof(struct ip6t_standard), + [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2 + }, + .underflow = { + [NF_IP6_LOCAL_IN] = 0, + [NF_IP6_FORWARD] = sizeof(struct ip6t_standard), + [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 2 + }, + }, + .entries = { + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + }, + .term = IP6T_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_filter = { diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index a9f10e32c16..f2d26495f41 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -32,73 +32,35 @@ static struct struct ip6t_replace repl; struct ip6t_standard entries[5]; struct ip6t_error term; -} initial_table __initdata -= { { "mangle", MANGLE_VALID_HOOKS, 6, - sizeof(struct ip6t_standard) * 5 + sizeof(struct ip6t_error), - { [NF_IP6_PRE_ROUTING] = 0, - [NF_IP6_LOCAL_IN] = sizeof(struct ip6t_standard), - [NF_IP6_FORWARD] = sizeof(struct ip6t_standard) * 2, - [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 3, - [NF_IP6_POST_ROUTING] = sizeof(struct ip6t_standard) * 4}, - { [NF_IP6_PRE_ROUTING] = 0, - [NF_IP6_LOCAL_IN] = sizeof(struct ip6t_standard), - [NF_IP6_FORWARD] = sizeof(struct ip6t_standard) * 2, - [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 3, - [NF_IP6_POST_ROUTING] = sizeof(struct ip6t_standard) * 4}, - 0, NULL, { } }, - { - /* PRE_ROUTING */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_IN */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* FORWARD */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* LOCAL_OUT */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } }, - /* POST_ROUTING */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_standard), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } }, - -NF_ACCEPT - 1 } } - }, - /* ERROR */ - { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 }, - 0, - sizeof(struct ip6t_entry), - sizeof(struct ip6t_error), - 0, { 0, 0 }, { } }, - { { { { IP6T_ALIGN(sizeof(struct ip6t_error_target)), IP6T_ERROR_TARGET } }, - { } }, - "ERROR" - } - } +} initial_table __initdata = { + .repl = { + .name = "mangle", + .valid_hooks = MANGLE_VALID_HOOKS, + .num_entries = 6, + .size = sizeof(struct ip6t_standard) * 5 + sizeof(struct ip6t_error), + .hook_entry = { + [NF_IP6_PRE_ROUTING] = 0, + [NF_IP6_LOCAL_IN] = sizeof(struct ip6t_standard), + [NF_IP6_FORWARD] = sizeof(struct ip6t_standard) * 2, + [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 3, + [NF_IP6_POST_ROUTING] = sizeof(struct ip6t_standard) * 4, + }, + .underflow = { + [NF_IP6_PRE_ROUTING] = 0, + [NF_IP6_LOCAL_IN] = sizeof(struct ip6t_standard), + [NF_IP6_FORWARD] = sizeof(struct ip6t_standard) * 2, + [NF_IP6_LOCAL_OUT] = sizeof(struct ip6t_standard) * 3, + [NF_IP6_POST_ROUTING] = sizeof(struct ip6t_standard) * 4, + }, + }, + .entries = { + IP6T_STANDARD_INIT(NF_ACCEPT), /* PRE_ROUTING */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_IN */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* FORWARD */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* POST_ROUTING */ + }, + .term = IP6T_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_mangler = { diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index a3eb5b8ce18..0acda45d455 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -35,56 +35,10 @@ static struct }, }, .entries = { - /* PRE_ROUTING */ - { - .entry = { - .target_offset = sizeof(struct ip6t_entry), - .next_offset = sizeof(struct ip6t_standard), - }, - .target = { - .target = { - .u = { - .target_size = IP6T_ALIGN(sizeof(struct ip6t_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, - - /* LOCAL_OUT */ - { - .entry = { - .target_offset = sizeof(struct ip6t_entry), - .next_offset = sizeof(struct ip6t_standard), - }, - .target = { - .target = { - .u = { - .target_size = IP6T_ALIGN(sizeof(struct ip6t_standard_target)), - }, - }, - .verdict = -NF_ACCEPT - 1, - }, - }, + IP6T_STANDARD_INIT(NF_ACCEPT), /* PRE_ROUTING */ + IP6T_STANDARD_INIT(NF_ACCEPT), /* LOCAL_OUT */ }, - /* ERROR */ - .term = { - .entry = { - .target_offset = sizeof(struct ip6t_entry), - .next_offset = sizeof(struct ip6t_error), - }, - .target = { - .target = { - .u = { - .user = { - .target_size = IP6T_ALIGN(sizeof(struct ip6t_error_target)), - .name = IP6T_ERROR_TARGET, - }, - }, - }, - .errorname = "ERROR", - }, - } + .term = IP6T_ERROR_INIT, /* ERROR */ }; static struct xt_table packet_raw = { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index b083c09e3d2..a7ae59c954d 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -52,9 +52,28 @@ DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6) __read_mostly; +static int ipv6_rcv_saddr_any(const struct sock *sk) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + + return ipv6_addr_any(&np->rcv_saddr); +} + +static unsigned int ipv6_hash_port_and_rcv_saddr(__u16 port, + const struct sock *sk) +{ + return port; +} + +const struct udp_get_port_ops udp_ipv6_ops = { + .saddr_cmp = ipv6_rcv_saddr_equal, + .saddr_any = ipv6_rcv_saddr_any, + .hash_port_and_rcv_saddr = ipv6_hash_port_and_rcv_saddr, +}; + static inline int udp_v6_get_port(struct sock *sk, unsigned short snum) { - return udp_get_port(sk, snum, ipv6_rcv_saddr_equal); + return udp_get_port(sk, snum, &udp_ipv6_ops); } static struct sock *__udp6_lib_lookup(struct in6_addr *saddr, __be16 sport, diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h index 6e252f318f7..36b0c11a28a 100644 --- a/net/ipv6/udp_impl.h +++ b/net/ipv6/udp_impl.h @@ -6,6 +6,8 @@ #include <net/addrconf.h> #include <net/inet_common.h> +extern const struct udp_get_port_ops udp_ipv6_ops; + extern int __udp6_lib_rcv(struct sk_buff **, struct hlist_head [], int ); extern void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, int , int , int , __be32 , struct hlist_head []); diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index f54016a5500..c40a51362f8 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -37,7 +37,7 @@ static struct inet6_protocol udplitev6_protocol = { static int udplite_v6_get_port(struct sock *sk, unsigned short snum) { - return udplite_get_port(sk, snum, ipv6_rcv_saddr_equal); + return udplite_get_port(sk, snum, &udp_ipv6_ops); } struct proto udplitev6_prot = { diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c index 822917debef..3e07e9d6fa4 100644 --- a/net/mac80211/ieee80211_sta.c +++ b/net/mac80211/ieee80211_sta.c @@ -17,6 +17,7 @@ * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, * SSID) */ +#include <linux/delay.h> #include <linux/if_ether.h> #include <linux/skbuff.h> #include <linux/netdevice.h> @@ -27,7 +28,6 @@ #include <linux/rtnetlink.h> #include <net/iw_handler.h> #include <asm/types.h> -#include <asm/delay.h> #include <net/mac80211.h> #include "ieee80211_i.h" diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index e132c8ae878..e8b5c2d7db6 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -299,7 +299,6 @@ destroy_conntrack(struct nf_conntrack *nfct) { struct nf_conn *ct = (struct nf_conn *)nfct; struct nf_conn_help *help = nfct_help(ct); - struct nf_conntrack_l3proto *l3proto; struct nf_conntrack_l4proto *l4proto; typeof(nf_conntrack_destroyed) destroyed; @@ -317,10 +316,6 @@ destroy_conntrack(struct nf_conntrack *nfct) * destroy_conntrack() MUST NOT be called with a write lock * to nf_conntrack_lock!!! -HW */ rcu_read_lock(); - l3proto = __nf_ct_l3proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num); - if (l3proto && l3proto->destroy) - l3proto->destroy(ct); - l4proto = __nf_ct_l4proto_find(ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.l3num, ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.protonum); if (l4proto && l4proto->destroy) @@ -893,8 +888,13 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, NF_CT_DUMP_TUPLE(newreply); ct->tuplehash[IP_CT_DIR_REPLY].tuple = *newreply; - if (!ct->master && help && help->expecting == 0) - help->helper = __nf_ct_helper_find(newreply); + if (!ct->master && help && help->expecting == 0) { + struct nf_conntrack_helper *helper; + helper = __nf_ct_helper_find(newreply); + if (helper) + memset(&help->help, 0, sizeof(help->help)); + help->helper = helper; + } write_unlock_bh(&nf_conntrack_lock); } EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index aa1a97ee514..d6d39e24132 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -830,11 +830,6 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[]) char *helpname; int err; - if (!help) { - /* FIXME: we need to reallocate and rehash */ - return -EBUSY; - } - /* don't change helper of sibling connections */ if (ct->master) return -EINVAL; @@ -843,25 +838,34 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nfattr *cda[]) if (err < 0) return err; - helper = __nf_conntrack_helper_find_byname(helpname); - if (!helper) { - if (!strcmp(helpname, "")) - helper = NULL; - else - return -EINVAL; - } - - if (help->helper) { - if (!helper) { + if (!strcmp(helpname, "")) { + if (help && help->helper) { /* we had a helper before ... */ nf_ct_remove_expectations(ct); help->helper = NULL; - } else { - /* need to zero data of old helper */ - memset(&help->help, 0, sizeof(help->help)); } + + return 0; } + if (!help) { + /* FIXME: we need to reallocate and rehash */ + return -EBUSY; + } + + helper = __nf_conntrack_helper_find_byname(helpname); + if (helper == NULL) + return -EINVAL; + + if (help->helper == helper) + return 0; + + if (help->helper) + /* we had a helper before ... */ + nf_ct_remove_expectations(ct); + + /* need to zero data of old helper */ + memset(&help->help, 0, sizeof(help->help)); help->helper = helper; return 0; diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index f4ea8fe07a5..189ded5f378 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -134,12 +134,66 @@ static void destroy(const struct xt_match *match, void *matchinfo) nf_ct_l3proto_module_put(match->family); } +#ifdef CONFIG_COMPAT +struct compat_xt_conntrack_info +{ + compat_uint_t statemask; + compat_uint_t statusmask; + struct ip_conntrack_old_tuple tuple[IP_CT_DIR_MAX]; + struct in_addr sipmsk[IP_CT_DIR_MAX]; + struct in_addr dipmsk[IP_CT_DIR_MAX]; + compat_ulong_t expires_min; + compat_ulong_t expires_max; + u_int8_t flags; + u_int8_t invflags; +}; + +static void compat_from_user(void *dst, void *src) +{ + struct compat_xt_conntrack_info *cm = src; + struct xt_conntrack_info m = { + .statemask = cm->statemask, + .statusmask = cm->statusmask, + .expires_min = cm->expires_min, + .expires_max = cm->expires_max, + .flags = cm->flags, + .invflags = cm->invflags, + }; + memcpy(m.tuple, cm->tuple, sizeof(m.tuple)); + memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk)); + memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk)); + memcpy(dst, &m, sizeof(m)); +} + +static int compat_to_user(void __user *dst, void *src) +{ + struct xt_conntrack_info *m = src; + struct compat_xt_conntrack_info cm = { + .statemask = m->statemask, + .statusmask = m->statusmask, + .expires_min = m->expires_min, + .expires_max = m->expires_max, + .flags = m->flags, + .invflags = m->invflags, + }; + memcpy(cm.tuple, m->tuple, sizeof(cm.tuple)); + memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk)); + memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk)); + return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0; +} +#endif + static struct xt_match conntrack_match = { .name = "conntrack", .match = match, .checkentry = checkentry, .destroy = destroy, .matchsize = sizeof(struct xt_conntrack_info), +#ifdef CONFIG_COMPAT + .compatsize = sizeof(struct compat_xt_conntrack_info), + .compat_from_user = compat_from_user, + .compat_to_user = compat_to_user, +#endif .family = AF_INET, .me = THIS_MODULE, }; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 3385ee59254..f28bb2dc58d 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -71,12 +71,9 @@ void qdisc_unlock_tree(struct net_device *dev) /* Kick device. - Note, that this procedure can be called by a watchdog timer, so that - we do not check dev->tbusy flag here. - Returns: 0 - queue is empty. - >0 - queue is not empty, but throttled. - <0 - queue is not empty. Device is throttled, if dev->tbusy != 0. + Returns: 0 - queue is empty or throttled. + >0 - queue is not empty. NOTE: Called under dev->queue_lock with locally disabled BH. */ @@ -115,7 +112,7 @@ static inline int qdisc_restart(struct net_device *dev) kfree_skb(skb); if (net_ratelimit()) printk(KERN_DEBUG "Dead loop on netdevice %s, fix it urgently!\n", dev->name); - return -1; + goto out; } __get_cpu_var(netdev_rx_stat).cpu_collision++; goto requeue; @@ -135,10 +132,12 @@ static inline int qdisc_restart(struct net_device *dev) netif_tx_unlock(dev); } spin_lock(&dev->queue_lock); - return -1; + q = dev->qdisc; + goto out; } if (ret == NETDEV_TX_LOCKED && nolock) { spin_lock(&dev->queue_lock); + q = dev->qdisc; goto collision; } } @@ -163,26 +162,28 @@ static inline int qdisc_restart(struct net_device *dev) */ requeue: - if (skb->next) + if (unlikely(q == &noop_qdisc)) + kfree_skb(skb); + else if (skb->next) dev->gso_skb = skb; else q->ops->requeue(skb, q); netif_schedule(dev); - return 1; + return 0; } + +out: BUG_ON((int) q->q.qlen < 0); return q->q.qlen; } void __qdisc_run(struct net_device *dev) { - if (unlikely(dev->qdisc == &noop_qdisc)) - goto out; - - while (qdisc_restart(dev) < 0 && !netif_queue_stopped(dev)) - /* NOTHING */; + do { + if (!qdisc_restart(dev)) + break; + } while (!netif_queue_stopped(dev)); -out: clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state); } @@ -544,6 +545,7 @@ void dev_activate(struct net_device *dev) void dev_deactivate(struct net_device *dev) { struct Qdisc *qdisc; + struct sk_buff *skb; spin_lock_bh(&dev->queue_lock); qdisc = dev->qdisc; @@ -551,8 +553,12 @@ void dev_deactivate(struct net_device *dev) qdisc_reset(qdisc); + skb = dev->gso_skb; + dev->gso_skb = NULL; spin_unlock_bh(&dev->queue_lock); + kfree_skb(skb); + dev_watchdog_down(dev); /* Wait for outstanding dev_queue_xmit calls. */ @@ -561,11 +567,6 @@ void dev_deactivate(struct net_device *dev) /* Wait for outstanding qdisc_run calls. */ while (test_bit(__LINK_STATE_QDISC_RUNNING, &dev->state)) yield(); - - if (dev->gso_skb) { - kfree_skb(dev->gso_skb); - dev->gso_skb = NULL; - } } void dev_init_scheduler(struct net_device *dev) diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index d24914db786..f05ad9a30b4 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -94,14 +94,13 @@ teql_enqueue(struct sk_buff *skb, struct Qdisc* sch) struct net_device *dev = sch->dev; struct teql_sched_data *q = qdisc_priv(sch); - __skb_queue_tail(&q->q, skb); - if (q->q.qlen <= dev->tx_queue_len) { + if (q->q.qlen < dev->tx_queue_len) { + __skb_queue_tail(&q->q, skb); sch->bstats.bytes += skb->len; sch->bstats.packets++; return 0; } - __skb_unlink(skb, &q->q); kfree_skb(skb); sch->qstats.drops++; return NET_XMIT_DROP; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 83a76ba9d7b..4dcdabf5647 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4164,6 +4164,7 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, rwlock_t *addr_lock; int err = 0; void *addrs; + void *buf; int bytes_copied = 0; if (len != sizeof(struct sctp_getaddrs_old)) @@ -4217,13 +4218,14 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, } } + buf = addrs; list_for_each(pos, &bp->address_list) { addr = list_entry(pos, struct sctp_sockaddr_entry, list); memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - memcpy(addrs, &temp, addrlen); - to += addrlen; + memcpy(buf, &temp, addrlen); + buf += addrlen; bytes_copied += addrlen; cnt ++; if (cnt >= getaddrs.addr_num) break; @@ -4266,6 +4268,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, size_t space_left; int bytes_copied = 0; void *addrs; + void *buf; if (len <= sizeof(struct sctp_getaddrs)) return -EINVAL; @@ -4316,6 +4319,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, } } + buf = addrs; list_for_each(pos, &bp->address_list) { addr = list_entry(pos, struct sctp_sockaddr_entry, list); memcpy(&temp, &addr->a, sizeof(temp)); @@ -4325,8 +4329,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, err = -ENOMEM; /*fixme: right error?*/ goto error; } - memcpy(addrs, &temp, addrlen); - to += addrlen; + memcpy(buf, &temp, addrlen); + buf += addrlen; bytes_copied += addrlen; cnt ++; space_left -= addrlen; @@ -5227,7 +5231,12 @@ int sctp_inet_listen(struct socket *sock, int backlog) /* Allocate HMAC for generating cookie. */ if (sctp_hmac_alg) { tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC); - if (!tfm) { + if (IS_ERR(tfm)) { + if (net_ratelimit()) { + printk(KERN_INFO + "SCTP: failed to load transform for %s: %ld\n", + sctp_hmac_alg, PTR_ERR(tfm)); + } err = -ENOSYS; goto out; } diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 661ea2dd78b..bfecb353ab3 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -141,11 +141,6 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( * an ABORT, so we need to include it in the sac_info. */ if (chunk) { - /* sctp_inqu_pop() has allready pulled off the chunk - * header. We need to put it back temporarily - */ - skb_push(chunk->skb, sizeof(sctp_chunkhdr_t)); - /* Copy the chunk data to a new skb and reserve enough * head room to use as notification. */ @@ -155,9 +150,6 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( if (!skb) goto fail; - /* put back the chunk header now that we have a copy */ - skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); - /* Embed the event fields inside the cloned skb. */ event = sctp_skb2event(skb); sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize); @@ -168,7 +160,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_assoc_change( /* Trim the buffer to the right length. */ skb_trim(skb, sizeof(struct sctp_assoc_change) + - ntohs(chunk->chunk_hdr->length)); + ntohs(chunk->chunk_hdr->length) - + sizeof(sctp_chunkhdr_t)); } else { event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change), MSG_NOTIFICATION, gfp); |