Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net...
authorDavid S. Miller <davem@davemloft.net>
Sun, 27 Jan 2013 06:26:27 +0000 (01:26 -0500)
committerDavid S. Miller <davem@davemloft.net>
Sun, 27 Jan 2013 06:26:27 +0000 (01:26 -0500)
Signed-off-by: David S. Miller <davem@davemloft.net>
50 files changed:
Documentation/networking/nf_conntrack-sysctl.txt [new file with mode: 0644]
arch/mips/include/uapi/asm/socket.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
include/linux/netfilter/nf_conntrack_sip.h
include/net/netfilter/nf_conntrack_acct.h
include/net/netfilter/nf_conntrack_core.h
include/net/netfilter/nf_conntrack_ecache.h
include/net/netfilter/nf_conntrack_expect.h
include/net/netfilter/nf_conntrack_extend.h
include/net/netfilter/nf_conntrack_helper.h
include/net/netfilter/nf_conntrack_l3proto.h
include/net/netfilter/nf_conntrack_l4proto.h
include/net/netfilter/nf_conntrack_labels.h [new file with mode: 0644]
include/net/netfilter/nf_conntrack_timeout.h
include/net/netfilter/nf_conntrack_timestamp.h
include/net/netns/conntrack.h
include/uapi/linux/netfilter/Kbuild
include/uapi/linux/netfilter/nf_conntrack_common.h
include/uapi/linux/netfilter/nfnetlink_conntrack.h
include/uapi/linux/netfilter/xt_bpf.h [new file with mode: 0644]
include/uapi/linux/netfilter/xt_connlabel.h [new file with mode: 0644]
net/ipv4/inet_connection_sock.c
net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
net/netfilter/Kconfig
net/netfilter/Makefile
net/netfilter/nf_conntrack_acct.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_ecache.c
net/netfilter/nf_conntrack_expect.c
net/netfilter/nf_conntrack_helper.c
net/netfilter/nf_conntrack_labels.c [new file with mode: 0644]
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nf_conntrack_proto.c
net/netfilter/nf_conntrack_proto_dccp.c
net/netfilter/nf_conntrack_proto_gre.c
net/netfilter/nf_conntrack_proto_sctp.c
net/netfilter/nf_conntrack_proto_udplite.c
net/netfilter/nf_conntrack_sip.c
net/netfilter/nf_conntrack_snmp.c
net/netfilter/nf_conntrack_standalone.c
net/netfilter/nf_conntrack_timeout.c
net/netfilter/nf_conntrack_timestamp.c
net/netfilter/nf_nat_sip.c
net/netfilter/xt_bpf.c [new file with mode: 0644]
net/netfilter/xt_connlabel.c [new file with mode: 0644]

diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt
new file mode 100644 (file)
index 0000000..70da508
--- /dev/null
@@ -0,0 +1,176 @@
+/proc/sys/net/netfilter/nf_conntrack_* Variables:
+
+nf_conntrack_acct - BOOLEAN
+       0 - disabled (default)
+       not 0 - enabled
+
+       Enable connection tracking flow accounting. 64-bit byte and packet
+       counters per flow are added.
+
+nf_conntrack_buckets - INTEGER (read-only)
+       Size of hash table. If not specified as parameter during module
+       loading, the default size is calculated by dividing total memory
+       by 16384 to determine the number of buckets but the hash table will
+       never have fewer than 32 or more than 16384 buckets.
+
+nf_conntrack_checksum - BOOLEAN
+       0 - disabled
+       not 0 - enabled (default)
+
+       Verify checksum of incoming packets. Packets with bad checksums are
+       in INVALID state. If this is enabled, such packets will not be
+       considered for connection tracking.
+
+nf_conntrack_count - INTEGER (read-only)
+       Number of currently allocated flow entries.
+
+nf_conntrack_events - BOOLEAN
+       0 - disabled
+       not 0 - enabled (default)
+
+       If this option is enabled, the connection tracking code will
+       provide userspace with connection tracking events via ctnetlink.
+
+nf_conntrack_events_retry_timeout - INTEGER (seconds)
+       default 15
+
+       This option is only relevant when "reliable connection tracking
+       events" are used.  Normally, ctnetlink is "lossy", that is,
+       events are normally dropped when userspace listeners can't keep up.
+
+       Userspace can request "reliable event mode".  When this mode is
+       active, the conntrack will only be destroyed after the event was
+       delivered.  If event delivery fails, the kernel periodically
+       re-tries to send the event to userspace.
+
+       This is the maximum interval the kernel should use when re-trying
+       to deliver the destroy event.
+
+       A higher number means there will be fewer delivery retries and it
+       will take longer for a backlog to be processed.
+
+nf_conntrack_expect_max - INTEGER
+       Maximum size of expectation table.  Default value is
+       nf_conntrack_buckets / 256. Minimum is 1.
+
+nf_conntrack_frag6_high_thresh - INTEGER
+       default 262144
+
+       Maximum memory used to reassemble IPv6 fragments.  When
+       nf_conntrack_frag6_high_thresh bytes of memory is allocated for this
+       purpose, the fragment handler will toss packets until
+       nf_conntrack_frag6_low_thresh is reached.
+
+nf_conntrack_frag6_low_thresh - INTEGER
+       default 196608
+
+       See nf_conntrack_frag6_low_thresh
+
+nf_conntrack_frag6_timeout - INTEGER (seconds)
+       default 60
+
+       Time to keep an IPv6 fragment in memory.
+
+nf_conntrack_generic_timeout - INTEGER (seconds)
+       default 600
+
+       Default for generic timeout.  This refers to layer 4 unknown/unsupported
+       protocols.
+
+nf_conntrack_helper - BOOLEAN
+       0 - disabled
+       not 0 - enabled (default)
+
+       Enable automatic conntrack helper assignment.
+
+nf_conntrack_icmp_timeout - INTEGER (seconds)
+       default 30
+
+       Default for ICMP timeout.
+
+nf_conntrack_icmpv6_timeout - INTEGER (seconds)
+       default 30
+
+       Default for ICMP6 timeout.
+
+nf_conntrack_log_invalid - INTEGER
+       0   - disable (default)
+       1   - log ICMP packets
+       6   - log TCP packets
+       17  - log UDP packets
+       33  - log DCCP packets
+       41  - log ICMPv6 packets
+       136 - log UDPLITE packets
+       255 - log packets of any protocol
+
+       Log invalid packets of a type specified by value.
+
+nf_conntrack_max - INTEGER
+       Size of connection tracking table.  Default value is
+       nf_conntrack_buckets value * 4.
+
+nf_conntrack_tcp_be_liberal - BOOLEAN
+       0 - disabled (default)
+       not 0 - enabled
+
+       Be conservative in what you do, be liberal in what you accept from others.
+       If it's non-zero, we mark only out of window RST segments as INVALID.
+
+nf_conntrack_tcp_loose - BOOLEAN
+       0 - disabled
+       not 0 - enabled (default)
+
+       If it is set to zero, we disable picking up already established
+       connections.
+
+nf_conntrack_tcp_max_retrans - INTEGER
+       default 3
+
+       Maximum number of packets that can be retransmitted without
+       received an (acceptable) ACK from the destination. If this number
+       is reached, a shorter timer will be started.
+
+nf_conntrack_tcp_timeout_close - INTEGER (seconds)
+       default 10
+
+nf_conntrack_tcp_timeout_close_wait - INTEGER (seconds)
+       default 60
+
+nf_conntrack_tcp_timeout_established - INTEGER (seconds)
+       default 432000 (5 days)
+
+nf_conntrack_tcp_timeout_fin_wait - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_last_ack - INTEGER (seconds)
+       default 30
+
+nf_conntrack_tcp_timeout_max_retrans - INTEGER (seconds)
+       default 300
+
+nf_conntrack_tcp_timeout_syn_recv - INTEGER (seconds)
+       default 60
+
+nf_conntrack_tcp_timeout_syn_sent - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_time_wait - INTEGER (seconds)
+       default 120
+
+nf_conntrack_tcp_timeout_unacknowledged - INTEGER (seconds)
+       default 300
+
+nf_conntrack_timestamp - BOOLEAN
+       0 - disabled (default)
+       not 0 - enabled
+
+       Enable connection tracking flow timestamping.
+
+nf_conntrack_udp_timeout - INTEGER (seconds)
+       default 30
+
+nf_conntrack_udp_timeout_stream2 - INTEGER (seconds)
+       default 180
+
+       This extended timeout will be used in case there is an UDP stream
+       detected.
index 7e2723637b356d08169ae18c5b82eb1403801a43..3e68bfbda6bc2f8e574a614a23d6713053182233 100644 (file)
@@ -29,7 +29,6 @@
                                   socket to transmit pending data.  */
 #define SO_OOBINLINE 0x0100    /* Receive out-of-band data in-band.  */
 #define SO_REUSEPORT 0x0200    /* Allow local address and port reuse.  */
-#endif
 
 #define SO_TYPE                0x1008  /* Compatible name for SO_STYLE.  */
 #define SO_STYLE       SO_TYPE /* Synonym */
index 893cbe8dd8e2e4e4a776b8538b5d512fa9d4cbd8..f71aef58f84d9ec8e03d6acb35a55f2f2abc9268 100644 (file)
@@ -37,9 +37,9 @@
 #include "qlcnic_83xx_hw.h"
 
 #define _QLCNIC_LINUX_MAJOR 5
-#define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 31
-#define QLCNIC_LINUX_VERSIONID  "5.1.31"
+#define _QLCNIC_LINUX_MINOR 1
+#define _QLCNIC_LINUX_SUBVERSION 32
+#define QLCNIC_LINUX_VERSIONID  "5.1.32"
 #define QLCNIC_DRV_IDC_VER  0x01
 #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\
                 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
@@ -436,6 +436,7 @@ struct qlcnic_hardware_context {
        u16 act_pci_func;
 
        u32 capabilities;
+       u32 capabilities2;
        u32 temp;
        u32 int_vec_bit;
        u32 fw_hal_version;
@@ -745,6 +746,11 @@ struct qlcnic_mac_list_s {
        uint8_t mac_addr[ETH_ALEN+2];
 };
 
+/* MAC Learn */
+#define NO_MAC_LEARN           0
+#define DRV_MAC_LEARN          1
+#define FDB_MAC_LEARN          2
+
 #define QLCNIC_HOST_REQUEST    0x13
 #define QLCNIC_REQUEST         0x14
 
@@ -798,6 +804,8 @@ struct qlcnic_mac_list_s {
 #define QLCNIC_FW_CAPABILITY_MORE_CAPS         BIT_31
 
 #define QLCNIC_FW_CAPABILITY_2_LRO_MAX_TCP_SEG BIT_2
+#define QLCNIC_FW_CAP2_HW_LRO_IPV6             BIT_3
+#define QLCNIC_FW_CAPABILITY_2_OCBB            BIT_5
 
 /* module types */
 #define LINKEVENT_MODULE_NOT_PRESENT                   1
@@ -978,7 +986,8 @@ struct qlcnic_adapter {
        u8 mac_addr[ETH_ALEN];
 
        u64 dev_rst_time;
-       u8 mac_learn;
+       bool drv_mac_learn;
+       bool fdb_mac_learn;
        unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)];
        u8 flash_mfg_id;
        struct qlcnic_npar_info *npars;
@@ -1418,9 +1427,12 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,
                struct qlcnic_host_rds_ring *rds_ring, u8 ring_id);
 int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);
 void qlcnic_set_multi(struct net_device *netdev);
+int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *);
+int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *);
 void qlcnic_free_mac_list(struct qlcnic_adapter *adapter);
 
 int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *);
 int qlcnic_change_mtu(struct net_device *netdev, int new_mtu);
 netdev_features_t qlcnic_fix_features(struct net_device *netdev,
        netdev_features_t features);
index ee68fe35a27e55bdaea6b883c56b15702a229fce..7372964d3a765729c64ec54231d023b7e27aa50f 100644 (file)
@@ -160,6 +160,37 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,
        return cmd->rsp.arg[0];
 }
 
+int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *adapter)
+{
+       struct qlcnic_cmd_args cmd;
+       u32 arg1, arg2, arg3;
+       char drv_string[12];
+       int err = 0;
+
+       memset(drv_string, 0, sizeof(drv_string));
+       snprintf(drv_string, sizeof(drv_string), "%d"".""%d"".""%d",
+                _QLCNIC_LINUX_MAJOR, _QLCNIC_LINUX_MINOR,
+                _QLCNIC_LINUX_SUBVERSION);
+
+       qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_DRV_VER);
+       memcpy(&arg1, drv_string, sizeof(u32));
+       memcpy(&arg2, drv_string + 4, sizeof(u32));
+       memcpy(&arg3, drv_string + 8, sizeof(u32));
+
+       cmd.req.arg[1] = arg1;
+       cmd.req.arg[2] = arg2;
+       cmd.req.arg[3] = arg3;
+
+       err = qlcnic_issue_cmd(adapter, &cmd);
+       if (err) {
+               dev_info(&adapter->pdev->dev,
+                        "Failed to set driver version in firmware\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
 int
 qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu)
 {
index 6f5b5eb2c44ab605efcd11950c14a85d483fc00b..6c6ecfc152b8b27fb6283a1b47e22152b9ea72b9 100644 (file)
@@ -446,7 +446,29 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
        return qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
 }
 
-static int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
+int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr)
+{
+       struct list_head *head;
+       struct qlcnic_mac_list_s *cur;
+       int err = -EINVAL;
+
+       /* Delete MAC from the existing list */
+       list_for_each(head, &adapter->mac_list) {
+               cur = list_entry(head, struct qlcnic_mac_list_s, list);
+               if (memcmp(addr, cur->mac_addr, ETH_ALEN) == 0) {
+                       err = qlcnic_sre_macaddr_change(adapter, cur->mac_addr,
+                                                       0, QLCNIC_MAC_DEL);
+                       if (err)
+                               return err;
+                       list_del(&cur->list);
+                       kfree(cur);
+                       return err;
+               }
+       }
+       return err;
+}
+
+int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)
 {
        struct list_head *head;
        struct qlcnic_mac_list_s *cur;
@@ -510,11 +532,11 @@ void qlcnic_set_multi(struct net_device *netdev)
        }
 
 send_fw_cmd:
-       if (mode == VPORT_MISS_MODE_ACCEPT_ALL) {
+       if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) {
                qlcnic_alloc_lb_filters_mem(adapter);
-               adapter->mac_learn = 1;
+               adapter->drv_mac_learn = true;
        } else {
-               adapter->mac_learn = 0;
+               adapter->drv_mac_learn = false;
        }
 
        qlcnic_nic_set_promisc(adapter, mode);
@@ -687,6 +709,11 @@ void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)
                        "Could not send interrupt coalescing parameters\n");
 }
 
+#define QLCNIC_ENABLE_IPV4_LRO         1
+#define QLCNIC_ENABLE_IPV6_LRO         2
+#define QLCNIC_NO_DEST_IPV4_CHECK      (1 << 8)
+#define QLCNIC_NO_DEST_IPV6_CHECK      (2 << 8)
+
 int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
 {
        struct qlcnic_nic_req req;
@@ -703,7 +730,15 @@ int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)
        word = QLCNIC_H2C_OPCODE_CONFIG_HW_LRO | ((u64)adapter->portnum << 16);
        req.req_hdr = cpu_to_le64(word);
 
-       req.words[0] = cpu_to_le64(enable);
+       word = 0;
+       if (enable) {
+               word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK;
+               if (adapter->ahw->capabilities2 & QLCNIC_FW_CAP2_HW_LRO_IPV6)
+                       word |= QLCNIC_ENABLE_IPV6_LRO |
+                               QLCNIC_NO_DEST_IPV6_CHECK;
+       }
+
+       req.words[0] = cpu_to_le64(word);
 
        rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
        if (rv != 0)
@@ -743,7 +778,10 @@ int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)
 }
 
 
-#define RSS_HASHTYPE_IP_TCP    0x3
+#define QLCNIC_RSS_HASHTYPE_IP_TCP     0x3
+#define QLCNIC_ENABLE_TYPE_C_RSS       BIT_10
+#define QLCNIC_RSS_FEATURE_FLAG        (1ULL << 63)
+#define QLCNIC_RSS_IND_TABLE_MASK      0x7ULL
 
 int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int enable)
 {
@@ -770,13 +808,19 @@ int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int enable)
         *      7-6: hash_type_ipv6
         *        8: enable
         *        9: use indirection table
-        *    47-10: reserved
-        *    63-48: indirection table mask
+        *       10: type-c rss
+        *       11: udp rss
+        *    47-12: reserved
+        *    62-48: indirection table mask
+        *       63: feature flag
         */
-       word =  ((u64)(RSS_HASHTYPE_IP_TCP & 0x3) << 4) |
-               ((u64)(RSS_HASHTYPE_IP_TCP & 0x3) << 6) |
+       word =  ((u64)(QLCNIC_RSS_HASHTYPE_IP_TCP & 0x3) << 4) |
+               ((u64)(QLCNIC_RSS_HASHTYPE_IP_TCP & 0x3) << 6) |
                ((u64)(enable & 0x1) << 8) |
-               ((0x7ULL) << 48);
+               ((u64)QLCNIC_RSS_IND_TABLE_MASK << 48) |
+               (u64)QLCNIC_ENABLE_TYPE_C_RSS |
+               (u64)QLCNIC_RSS_FEATURE_FLAG;
+
        req.words[0] = cpu_to_le64(word);
        for (i = 0; i < 5; i++)
                req.words[i+1] = cpu_to_le64(key[i]);
@@ -1358,7 +1402,7 @@ int qlcnic_82xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
        word = QLCNIC_H2C_OPCODE_CONFIG_LED | ((u64)adapter->portnum << 16);
        req.req_hdr = cpu_to_le64(word);
 
-       req.words[0] = cpu_to_le64((u64)rate << 32);
+       req.words[0] = cpu_to_le64(((u64)rate << 32) | adapter->portnum);
        req.words[1] = cpu_to_le64(state);
 
        rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);
index 383ecd20d9b504158e1ac179e00d8fffc85a3d17..fdf34836ef41a224a7b3141cec5c1c217bf83003 100644 (file)
@@ -521,7 +521,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
        if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb)))
                goto unwind_buff;
 
-       if (adapter->mac_learn)
+       if (adapter->drv_mac_learn)
                qlcnic_send_filter(adapter, first_desc, skb);
 
        adapter->stats.txbytes += skb->len;
@@ -973,6 +973,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
        struct sk_buff *skb;
        struct qlcnic_host_rds_ring *rds_ring;
        struct iphdr *iph;
+       struct ipv6hdr *ipv6h;
        struct tcphdr *th;
        bool push, timestamp;
        int index, l2_hdr_offset, l4_hdr_offset;
@@ -1016,12 +1017,21 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,
        }
 
        skb->protocol = eth_type_trans(skb, netdev);
-       iph = (struct iphdr *)skb->data;
-       th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
-       length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
-       iph->tot_len = htons(length);
-       iph->check = 0;
-       iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+       if (htons(skb->protocol) == ETH_P_IPV6) {
+               ipv6h = (struct ipv6hdr *)skb->data;
+               th = (struct tcphdr *)(skb->data + sizeof(struct ipv6hdr));
+               length = (th->doff << 2) + lro_length;
+               ipv6h->payload_len = htons(length);
+       } else {
+               iph = (struct iphdr *)skb->data;
+               th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
+               length = (iph->ihl << 2) + (th->doff << 2) + lro_length;
+               iph->tot_len = htons(length);
+               iph->check = 0;
+               iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+       }
+
        th->psh = push;
        th->seq = htonl(seq_number);
        length = skb->len;
index fb7ac8ecd45a6302c58949ca8a813579654df954..e6b363a7664f907c86d723ac44f0b8fbc76589b1 100644 (file)
@@ -32,7 +32,8 @@ static const char qlcnic_driver_string[] = "QLogic 1/10 GbE "
 
 static int qlcnic_mac_learn;
 module_param(qlcnic_mac_learn, int, 0444);
-MODULE_PARM_DESC(qlcnic_mac_learn, "Mac Filter (0=disabled, 1=enabled)");
+MODULE_PARM_DESC(qlcnic_mac_learn,
+                "Mac Filter (0=learning is disabled, 1=Driver learning is enabled, 2=FDB learning is enabled)");
 
 int qlcnic_use_msi = 1;
 MODULE_PARM_DESC(use_msi, "MSI interrupt (0=disabled, 1=enabled");
@@ -246,6 +247,77 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)
        return 0;
 }
 
+static int qlcnic_fdb_del(struct ndmsg *ndm, struct net_device *netdev,
+                       const unsigned char *addr)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err = -EOPNOTSUPP;
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return err;
+       }
+
+       if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {
+               if (is_unicast_ether_addr(addr))
+                       err = qlcnic_nic_del_mac(adapter, addr);
+               else if (is_multicast_ether_addr(addr))
+                       err = dev_mc_del(netdev, addr);
+               else
+                       err =  -EINVAL;
+       }
+       return err;
+}
+
+static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+                       struct net_device *netdev,
+                       const unsigned char *addr, u16 flags)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       int err = 0;
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {
+               pr_info("%s: FDB e-switch is not enabled\n", __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (ether_addr_equal(addr, adapter->mac_addr))
+               return err;
+
+       if (is_unicast_ether_addr(addr))
+               err = qlcnic_nic_add_mac(adapter, addr);
+       else if (is_multicast_ether_addr(addr))
+               err = dev_mc_add_excl(netdev, addr);
+       else
+               err = -EINVAL;
+
+       return err;
+}
+
+static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,
+                       struct net_device *netdev, int idx)
+{
+       struct qlcnic_adapter *adapter = netdev_priv(netdev);
+
+       if (!adapter->fdb_mac_learn) {
+               pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n",
+                       __func__);
+               return -EOPNOTSUPP;
+       }
+
+       if (adapter->flags & QLCNIC_ESWITCH_ENABLED)
+               idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx);
+
+       return idx;
+}
+
 static void qlcnic_82xx_cancel_idc_work(struct qlcnic_adapter *adapter)
 {
        while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state))
@@ -268,6 +340,9 @@ static const struct net_device_ops qlcnic_netdev_ops = {
        .ndo_tx_timeout    = qlcnic_tx_timeout,
        .ndo_vlan_rx_add_vid    = qlcnic_vlan_rx_add,
        .ndo_vlan_rx_kill_vid   = qlcnic_vlan_rx_del,
+       .ndo_fdb_add            = qlcnic_fdb_add,
+       .ndo_fdb_del            = qlcnic_fdb_del,
+       .ndo_fdb_dump           = qlcnic_fdb_dump,
 #ifdef CONFIG_NET_POLL_CONTROLLER
        .ndo_poll_controller = qlcnic_poll_controller,
 #endif
@@ -395,8 +470,9 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)
        return err;
 }
 
-static void qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
+static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
 {
+       int err = 0;
        u32 offset, mask_reg;
        const struct qlcnic_legacy_intr_set *legacy_intrp;
        struct qlcnic_hardware_context *ahw = adapter->ahw;
@@ -409,8 +485,10 @@ static void qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
                                                            offset);
                dev_info(&pdev->dev, "using msi interrupts\n");
                adapter->msix_entries[0].vector = pdev->irq;
-               return;
+               return err;
        }
+       if (qlcnic_use_msi || qlcnic_use_msi_x)
+               return -EOPNOTSUPP;
 
        legacy_intrp = &legacy_intr[adapter->ahw->pci_func];
        adapter->ahw->int_vec_bit = legacy_intrp->int_vec_bit;
@@ -422,11 +500,12 @@ static void qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter)
        adapter->crb_int_state_reg = qlcnic_get_ioaddr(ahw, ISR_INT_STATE_REG);
        dev_info(&pdev->dev, "using legacy interrupts\n");
        adapter->msix_entries[0].vector = pdev->irq;
+       return err;
 }
 
 int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr)
 {
-       int num_msix, err;
+       int num_msix, err = 0;
 
        if (!num_intr)
                num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS;
@@ -441,8 +520,11 @@ int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr)
        if (err == -ENOMEM || !err)
                return err;
 
-       qlcnic_enable_msi_legacy(adapter);
-       return 0;
+       err = qlcnic_enable_msi_legacy(adapter);
+       if (!err)
+               return err;
+
+       return -EIO;
 }
 
 void qlcnic_teardown_intr(struct qlcnic_adapter *adapter)
@@ -781,6 +863,12 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter)
        adapter->ahw->max_tx_ques = nic_info.max_tx_ques;
        adapter->ahw->max_rx_ques = nic_info.max_rx_ques;
        adapter->ahw->capabilities = nic_info.capabilities;
+
+       if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
+               u32 temp;
+               temp = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
+               adapter->ahw->capabilities2 = temp;
+       }
        adapter->ahw->max_mac_filters = nic_info.max_mac_filters;
        adapter->ahw->max_mtu = nic_info.max_mtu;
 
@@ -1724,6 +1812,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct qlcnic_adapter *adapter = NULL;
        struct qlcnic_hardware_context *ahw;
        int err, pci_using_dac = -1;
+       u32 capab2;
        char board_name[QLCNIC_MAX_BOARD_NAME_LEN];
 
        err = pci_enable_device(pdev);
@@ -1788,7 +1877,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        adapter->dev_rst_time = jiffies;
        adapter->ahw->revision_id = pdev->revision;
-       adapter->mac_learn = qlcnic_mac_learn;
+       if (qlcnic_mac_learn == FDB_MAC_LEARN)
+               adapter->fdb_mac_learn = true;
+       else if (qlcnic_mac_learn == DRV_MAC_LEARN)
+               adapter->drv_mac_learn = true;
        adapter->max_drv_tx_rings = 1;
 
        rwlock_init(&adapter->ahw->crb_lock);
@@ -1836,8 +1928,10 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                        board_name, adapter->ahw->revision_id);
        }
        err = qlcnic_setup_intr(adapter, 0);
-       if (err)
+       if (err) {
+               dev_err(&pdev->dev, "Failed to setup interrupt\n");
                goto err_out_disable_msi;
+       }
 
        if (qlcnic_83xx_check(adapter)) {
                err = qlcnic_83xx_setup_mbx_intr(adapter);
@@ -1849,6 +1943,14 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (err)
                goto err_out_disable_mbx_intr;
 
+       if (qlcnic_82xx_check(adapter)) {
+               if (ahw->capabilities & QLCNIC_FW_CAPABILITY_MORE_CAPS) {
+                       capab2 = QLCRD32(adapter, CRB_FW_CAPABILITIES_2);
+                       if (capab2 & QLCNIC_FW_CAPABILITY_2_OCBB)
+                               qlcnic_fw_cmd_set_drv_version(adapter);
+               }
+       }
+
        pci_set_drvdata(pdev, adapter);
 
        if (qlcnic_82xx_check(adapter))
@@ -1869,7 +1971,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (qlcnic_get_act_pci_func(adapter))
                goto err_out_disable_mbx_intr;
 
-       if (adapter->mac_learn)
+       if (adapter->drv_mac_learn)
                qlcnic_alloc_lb_filters_mem(adapter);
 
        qlcnic_add_sysfs(adapter);
@@ -2118,7 +2220,7 @@ void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter)
        }
 
        head = kcalloc(adapter->fhash.fbucket_size,
-                      sizeof(struct hlist_head), GFP_KERNEL);
+                      sizeof(struct hlist_head), GFP_ATOMIC);
 
        if (!head)
                return;
@@ -2961,6 +3063,12 @@ static int qlcnic_attach_func(struct pci_dev *pdev)
        adapter->msix_entries = NULL;
        err = qlcnic_setup_intr(adapter, 0);
 
+       if (err) {
+               kfree(adapter->msix_entries);
+               netdev_err(netdev, "failed to setup interrupt\n");
+               return err;
+       }
+
        if (qlcnic_83xx_check(adapter)) {
                err = qlcnic_83xx_setup_mbx_intr(adapter);
                if (err) {
@@ -3116,9 +3224,11 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len)
        qlcnic_detach(adapter);
        qlcnic_teardown_intr(adapter);
        err = qlcnic_setup_intr(adapter, data);
-       if (err)
-               dev_err(&adapter->pdev->dev,
-                       "failed setting max_rss; rss disabled\n");
+       if (err) {
+               kfree(adapter->msix_entries);
+               netdev_err(netdev, "failed to setup interrupt\n");
+               return err;
+       }
 
        if (qlcnic_83xx_check(adapter)) {
                err = qlcnic_83xx_setup_mbx_intr(adapter);
index 387bdd02945d13555d79e0becd9cb0bc1fb5737f..ba7f571a2b1cba1f2381ee50c7030946543f31ac 100644 (file)
@@ -4,12 +4,15 @@
 
 #include <net/netfilter/nf_conntrack_expect.h>
 
+#include <linux/types.h>
+
 #define SIP_PORT       5060
 #define SIP_TIMEOUT    3600
 
 struct nf_ct_sip_master {
        unsigned int    register_cseq;
        unsigned int    invite_cseq;
+       __be16          forced_dport;
 };
 
 enum sip_expectation_classes {
index 463ae8e166965908d2fc0a60e92296123b846f2e..2bdb7a15fe06b102a5fc88c2cc3da5662ff53a95 100644 (file)
@@ -57,7 +57,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable)
        net->ct.sysctl_acct = enable;
 }
 
-extern int nf_conntrack_acct_init(struct net *net);
-extern void nf_conntrack_acct_fini(struct net *net);
+extern int nf_conntrack_acct_pernet_init(struct net *net);
+extern void nf_conntrack_acct_pernet_fini(struct net *net);
 
+extern int nf_conntrack_acct_init(void);
+extern void nf_conntrack_acct_fini(void);
 #endif /* _NF_CONNTRACK_ACCT_H */
index e98aeb3da033a38e29f745d5f043a33d3888bcc3..930275fa2ea68f082e5974586abc39c5138706c4 100644 (file)
@@ -25,12 +25,19 @@ extern unsigned int nf_conntrack_in(struct net *net,
                                    unsigned int hooknum,
                                    struct sk_buff *skb);
 
-extern int nf_conntrack_init(struct net *net);
-extern void nf_conntrack_cleanup(struct net *net);
+extern int nf_conntrack_init_net(struct net *net);
+extern void nf_conntrack_cleanup_net(struct net *net);
 
-extern int nf_conntrack_proto_init(struct net *net);
-extern void nf_conntrack_proto_fini(struct net *net);
+extern int nf_conntrack_proto_pernet_init(struct net *net);
+extern void nf_conntrack_proto_pernet_fini(struct net *net);
 
+extern int nf_conntrack_proto_init(void);
+extern void nf_conntrack_proto_fini(void);
+
+extern int nf_conntrack_init_start(void);
+extern void nf_conntrack_cleanup_start(void);
+
+extern void nf_conntrack_init_end(void);
 extern void nf_conntrack_cleanup_end(void);
 
 extern bool
index 5654d292efd4f0883f6051610f6144552f20cb61..092dc651689f81d85a6ae686c5d761aee006918d 100644 (file)
@@ -207,9 +207,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event,
        nf_ct_expect_event_report(event, exp, 0, 0);
 }
 
-extern int nf_conntrack_ecache_init(struct net *net);
-extern void nf_conntrack_ecache_fini(struct net *net);
+extern int nf_conntrack_ecache_pernet_init(struct net *net);
+extern void nf_conntrack_ecache_pernet_fini(struct net *net);
 
+extern int nf_conntrack_ecache_init(void);
+extern void nf_conntrack_ecache_fini(void);
 #else /* CONFIG_NF_CONNTRACK_EVENTS */
 
 static inline void nf_conntrack_event_cache(enum ip_conntrack_events event,
@@ -232,12 +234,21 @@ static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events e,
                                             u32 portid,
                                             int report) {}
 
-static inline int nf_conntrack_ecache_init(struct net *net)
+static inline int nf_conntrack_ecache_pernet_init(struct net *net)
 {
        return 0;
 }
 
-static inline void nf_conntrack_ecache_fini(struct net *net)
+static inline void nf_conntrack_ecache_pernet_fini(struct net *net)
+{
+}
+
+static inline int nf_conntrack_ecache_init(void)
+{
+       return 0;
+}
+
+static inline void nf_conntrack_ecache_fini(void)
 {
 }
 #endif /* CONFIG_NF_CONNTRACK_EVENTS */
index cc13f377a705c36c62987060d3cbe5e63eac8147..cbbae7621e229806bab08c8d622c599ea4db9364 100644 (file)
@@ -69,8 +69,11 @@ struct nf_conntrack_expect_policy {
 
 #define NF_CT_EXPECT_CLASS_DEFAULT     0
 
-int nf_conntrack_expect_init(struct net *net);
-void nf_conntrack_expect_fini(struct net *net);
+int nf_conntrack_expect_pernet_init(struct net *net);
+void nf_conntrack_expect_pernet_fini(struct net *net);
+
+int nf_conntrack_expect_init(void);
+void nf_conntrack_expect_fini(void);
 
 struct nf_conntrack_expect *
 __nf_ct_expect_find(struct net *net, u16 zone,
index 8b4d1fc29096b1d543241360d7832a32dcfb1904..977bc8a46444d12b960fd3646969a2e96ce064dd 100644 (file)
@@ -22,6 +22,9 @@ enum nf_ct_ext_id {
 #endif
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
        NF_CT_EXT_TIMEOUT,
+#endif
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+       NF_CT_EXT_LABELS,
 #endif
        NF_CT_EXT_NUM,
 };
@@ -33,6 +36,7 @@ enum nf_ct_ext_id {
 #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
 #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp
 #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
+#define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
 
 /* Extensions: optional stuff which isn't permanently in struct. */
 struct nf_ct_ext {
index 9aad956d1008e2be1c0b6e55b7588098321c642d..ce27edf57570e68db084630fa5cb86c1ba4bc880 100644 (file)
@@ -82,8 +82,11 @@ static inline void *nfct_help_data(const struct nf_conn *ct)
        return (void *)help->data;
 }
 
-extern int nf_conntrack_helper_init(struct net *net);
-extern void nf_conntrack_helper_fini(struct net *net);
+extern int nf_conntrack_helper_pernet_init(struct net *net);
+extern void nf_conntrack_helper_pernet_fini(struct net *net);
+
+extern int nf_conntrack_helper_init(void);
+extern void nf_conntrack_helper_fini(void);
 
 extern int nf_conntrack_broadcast_help(struct sk_buff *skb,
                                       unsigned int protoff,
index 6f7c13f4ac0329366ed32b9c5a27dc13253daff5..3bb89eac3fa130a477b925d87c441189d27836e1 100644 (file)
@@ -76,11 +76,16 @@ struct nf_conntrack_l3proto {
 
 extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX];
 
-/* Protocol registration. */
-extern int nf_conntrack_l3proto_register(struct net *net,
+/* Protocol pernet registration. */
+extern int nf_ct_l3proto_pernet_register(struct net *net,
                                         struct nf_conntrack_l3proto *proto);
-extern void nf_conntrack_l3proto_unregister(struct net *net,
+extern void nf_ct_l3proto_pernet_unregister(struct net *net,
                                            struct nf_conntrack_l3proto *proto);
+
+/* Protocol global registration. */
+extern int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto);
+extern void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto);
+
 extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto);
 extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p);
 
index c3be4aef6bf7d37055e521ca6f5dcf4468e8ae49..914d8d9007981bd9b4cf7db67dc79ec78c6896d0 100644 (file)
@@ -121,12 +121,16 @@ extern struct nf_conntrack_l4proto *
 nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto);
 extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p);
 
-/* Protocol registration. */
-extern int nf_conntrack_l4proto_register(struct net *net,
+/* Protocol pernet registration. */
+extern int nf_ct_l4proto_pernet_register(struct net *net,
                                         struct nf_conntrack_l4proto *proto);
-extern void nf_conntrack_l4proto_unregister(struct net *net,
+extern void nf_ct_l4proto_pernet_unregister(struct net *net,
                                            struct nf_conntrack_l4proto *proto);
 
+/* Protocol global registration. */
+extern int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto);
+extern void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto);
+
 static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn)
 {
 #if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
diff --git a/include/net/netfilter/nf_conntrack_labels.h b/include/net/netfilter/nf_conntrack_labels.h
new file mode 100644 (file)
index 0000000..c985695
--- /dev/null
@@ -0,0 +1,58 @@
+#include <linux/types.h>
+#include <net/net_namespace.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tuple_common.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_extend.h>
+
+#include <uapi/linux/netfilter/xt_connlabel.h>
+
+struct nf_conn_labels {
+       u8 words;
+       unsigned long bits[];
+};
+
+static inline struct nf_conn_labels *nf_ct_labels_find(const struct nf_conn *ct)
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+       return nf_ct_ext_find(ct, NF_CT_EXT_LABELS);
+#else
+       return NULL;
+#endif
+}
+
+static inline struct nf_conn_labels *nf_ct_labels_ext_add(struct nf_conn *ct)
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+       struct nf_conn_labels *cl_ext;
+       struct net *net = nf_ct_net(ct);
+       u8 words;
+
+       words = ACCESS_ONCE(net->ct.label_words);
+       if (words == 0 || WARN_ON_ONCE(words > 8))
+               return NULL;
+
+       cl_ext = nf_ct_ext_add_length(ct, NF_CT_EXT_LABELS,
+                                     words * sizeof(long), GFP_ATOMIC);
+       if (cl_ext != NULL)
+               cl_ext->words = words;
+
+       return cl_ext;
+#else
+       return NULL;
+#endif
+}
+
+bool nf_connlabel_match(const struct nf_conn *ct, u16 bit);
+int nf_connlabel_set(struct nf_conn *ct, u16 bit);
+
+int nf_connlabels_replace(struct nf_conn *ct,
+                         const u32 *data, const u32 *mask, unsigned int words);
+
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+int nf_conntrack_labels_init(void);
+void nf_conntrack_labels_fini(void);
+#else
+static inline int nf_conntrack_labels_init(void) { return 0; }
+static inline void nf_conntrack_labels_fini(void) {}
+#endif
index e41e472d08f2123a7a0917e598196ec4ebf8e5d3..d23aceb16d9443a865bc0e789a60089b0d9c40bc 100644 (file)
@@ -76,15 +76,15 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct,
 }
 
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
-extern int nf_conntrack_timeout_init(struct net *net);
-extern void nf_conntrack_timeout_fini(struct net *net);
+extern int nf_conntrack_timeout_init(void);
+extern void nf_conntrack_timeout_fini(void);
 #else
-static inline int nf_conntrack_timeout_init(struct net *net)
+static inline int nf_conntrack_timeout_init(void)
 {
         return 0;
 }
 
-static inline void nf_conntrack_timeout_fini(struct net *net)
+static inline void nf_conntrack_timeout_fini(void)
 {
         return;
 }
index fc9c82b1f06b1b484bbcdbe8ac0e52e615d2b42d..b00461413efd4d49ec3e11ba4a3e0786594a32f2 100644 (file)
@@ -48,15 +48,28 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable)
 }
 
 #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-extern int nf_conntrack_tstamp_init(struct net *net);
-extern void nf_conntrack_tstamp_fini(struct net *net);
+extern int nf_conntrack_tstamp_pernet_init(struct net *net);
+extern void nf_conntrack_tstamp_pernet_fini(struct net *net);
+
+extern int nf_conntrack_tstamp_init(void);
+extern void nf_conntrack_tstamp_fini(void);
 #else
-static inline int nf_conntrack_tstamp_init(struct net *net)
+static inline int nf_conntrack_tstamp_pernet_init(struct net *net)
+{
+       return 0;
+}
+
+static inline void nf_conntrack_tstamp_pernet_fini(struct net *net)
+{
+       return;
+}
+
+static inline int nf_conntrack_tstamp_init(void)
 {
        return 0;
 }
 
-static inline void nf_conntrack_tstamp_fini(struct net *net)
+static inline void nf_conntrack_tstamp_fini(void)
 {
        return;
 }
index 923cb20051edfbb93a3fb84c8c78b7dae0993c82..c9c0c538b68bb478c924e93d9b5408c933100459 100644 (file)
@@ -84,6 +84,10 @@ struct netns_ct {
        int                     sysctl_auto_assign_helper;
        bool                    auto_assign_helper_warned;
        struct nf_ip_net        nf_ct_proto;
+#if defined(CONFIG_NF_CONNTRACK_LABELS)
+       unsigned int            labels_used;
+       u8                      label_words;
+#endif
 #ifdef CONFIG_NF_NAT_NEEDED
        struct hlist_head       *nat_bysource;
        unsigned int            nat_htable_size;
index 08f555fef13fee2946951983ae7cf2e210aa05cc..41115776d76f74996a0815e044533fc5e5ee8f78 100644 (file)
@@ -35,9 +35,11 @@ header-y += xt_TCPOPTSTRIP.h
 header-y += xt_TEE.h
 header-y += xt_TPROXY.h
 header-y += xt_addrtype.h
+header-y += xt_bpf.h
 header-y += xt_cluster.h
 header-y += xt_comment.h
 header-y += xt_connbytes.h
+header-y += xt_connlabel.h
 header-y += xt_connlimit.h
 header-y += xt_connmark.h
 header-y += xt_conntrack.h
index 1644cdd8be9109abc11a338451f1a9655e32cc1a..d69483fb382537e39c99251f5d3f580bf8115b62 100644 (file)
@@ -101,6 +101,7 @@ enum ip_conntrack_events {
        IPCT_MARK,              /* new mark has been set */
        IPCT_NATSEQADJ,         /* NAT is doing sequence adjustment */
        IPCT_SECMARK,           /* new security mark has been set */
+       IPCT_LABEL,             /* new connlabel has been set */
 };
 
 enum ip_conntrack_expect_events {
index 86e930cf3dfba3f472046e8cded054a1159a83d8..08fabc6c93f3ae6d7fb7ae1f8e443b288a93d952 100644 (file)
@@ -49,6 +49,8 @@ enum ctattr_type {
        CTA_SECCTX,
        CTA_TIMESTAMP,
        CTA_MARK_MASK,
+       CTA_LABELS,
+       CTA_LABELS_MASK,
        __CTA_MAX
 };
 #define CTA_MAX (__CTA_MAX - 1)
diff --git a/include/uapi/linux/netfilter/xt_bpf.h b/include/uapi/linux/netfilter/xt_bpf.h
new file mode 100644 (file)
index 0000000..5dda450
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _XT_BPF_H
+#define _XT_BPF_H
+
+#include <linux/filter.h>
+#include <linux/types.h>
+
+#define XT_BPF_MAX_NUM_INSTR   64
+
+struct xt_bpf_info {
+       __u16 bpf_program_num_elem;
+       struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
+
+       /* only used in the kernel */
+       struct sk_filter *filter __attribute__((aligned(8)));
+};
+
+#endif /*_XT_BPF_H */
diff --git a/include/uapi/linux/netfilter/xt_connlabel.h b/include/uapi/linux/netfilter/xt_connlabel.h
new file mode 100644 (file)
index 0000000..c4bc9ee
--- /dev/null
@@ -0,0 +1,12 @@
+#include <linux/types.h>
+
+#define XT_CONNLABEL_MAXBIT 127
+enum xt_connlabel_mtopts {
+       XT_CONNLABEL_OP_INVERT = 1 << 0,
+       XT_CONNLABEL_OP_SET    = 1 << 1,
+};
+
+struct xt_connlabel_mtinfo {
+       __u16 bit;
+       __u16 options;
+};
index 8bb623d357adf9c1ec2b88010338f5be1b3182ae..11cb4979a465af4c037ea60b6852870c2e7f725e 100644 (file)
@@ -204,7 +204,8 @@ tb_found:
                        ret = 1;
                        if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true)) {
                                if (((sk->sk_reuse && sk->sk_state != TCP_LISTEN) ||
-                                    (sk->sk_reuseport && uid_eq(tb->fastuid, uid))) &&
+                                    (tb->fastreuseport > 0 &&
+                                     sk->sk_reuseport && uid_eq(tb->fastuid, uid))) &&
                                    smallest_size != -1 && --attempts >= 0) {
                                        spin_unlock(&head->lock);
                                        goto again;
@@ -227,19 +228,15 @@ tb_not_found:
                if (sk->sk_reuseport) {
                        tb->fastreuseport = 1;
                        tb->fastuid = uid;
-               } else {
+               } else
                        tb->fastreuseport = 0;
-                       tb->fastuid = 0;
-               }
        } else {
                if (tb->fastreuse &&
                    (!sk->sk_reuse || sk->sk_state == TCP_LISTEN))
                        tb->fastreuse = 0;
                if (tb->fastreuseport &&
-                   (!sk->sk_reuseport || !uid_eq(tb->fastuid, uid))) {
+                   (!sk->sk_reuseport || !uid_eq(tb->fastuid, uid)))
                        tb->fastreuseport = 0;
-                       tb->fastuid = 0;
-               }
        }
 success:
        if (!inet_csk(sk)->icsk_bind_hash)
index fcdd0c2406e6d85d888633222e6697bd2352db86..48990ada0e1ecb698e0ff0439533c5afce591519 100644 (file)
@@ -420,54 +420,43 @@ static int ipv4_net_init(struct net *net)
 {
        int ret = 0;
 
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_tcp4);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_tcp4 :protocol register failed\n");
+               pr_err("nf_conntrack_tcp4: pernet registration failed\n");
                goto out_tcp;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_udp4);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_udp4 :protocol register failed\n");
+               pr_err("nf_conntrack_udp4: pernet registration failed\n");
                goto out_udp;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_icmp);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmp);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_icmp4 :protocol register failed\n");
+               pr_err("nf_conntrack_icmp4: pernet registration failed\n");
                goto out_icmp;
        }
-       ret = nf_conntrack_l3proto_register(net,
-                                           &nf_conntrack_l3proto_ipv4);
+       ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l3proto_ipv4 :protocol register failed\n");
+               pr_err("nf_conntrack_ipv4: pernet registration failed\n");
                goto out_ipv4;
        }
        return 0;
 out_ipv4:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_icmp);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
 out_icmp:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_udp4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
 out_udp:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_tcp4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
 out_tcp:
        return ret;
 }
 
 static void ipv4_net_exit(struct net *net)
 {
-       nf_conntrack_l3proto_unregister(net,
-                                       &nf_conntrack_l3proto_ipv4);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_icmp);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_udp4);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_tcp4);
+       nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmp);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp4);
 }
 
 static struct pernet_operations ipv4_net_ops = {
@@ -500,16 +489,49 @@ static int __init nf_conntrack_l3proto_ipv4_init(void)
                pr_err("nf_conntrack_ipv4: can't register hooks.\n");
                goto cleanup_pernet;
        }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp4);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv4: can't register tcp4 proto.\n");
+               goto cleanup_hooks;
+       }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp4);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv4: can't register udp4 proto.\n");
+               goto cleanup_tcp4;
+       }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmp);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv4: can't register icmpv4 proto.\n");
+               goto cleanup_udp4;
+       }
+
+       ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv4);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv4: can't register ipv4 proto.\n");
+               goto cleanup_icmpv4;
+       }
+
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
        ret = nf_conntrack_ipv4_compat_init();
        if (ret < 0)
-               goto cleanup_hooks;
+               goto cleanup_proto;
 #endif
        return ret;
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
+ cleanup_proto:
+       nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
+#endif
+ cleanup_icmpv4:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
+ cleanup_udp4:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
+ cleanup_tcp4:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
  cleanup_hooks:
        nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
-#endif
  cleanup_pernet:
        unregister_pernet_subsys(&ipv4_net_ops);
  cleanup_sockopt:
@@ -523,6 +545,10 @@ static void __exit nf_conntrack_l3proto_ipv4_fini(void)
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
        nf_conntrack_ipv4_compat_fini();
 #endif
+       nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv4);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmp);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp4);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp4);
        nf_unregister_hooks(ipv4_conntrack_ops, ARRAY_SIZE(ipv4_conntrack_ops));
        unregister_pernet_subsys(&ipv4_net_ops);
        nf_unregister_sockopt(&so_getorigdst);
index 137e245860ab41a9ec923a8655f538c8cc8d4e52..8a45bb20bedb49674fbb024a3dfb90ea5c54298c 100644 (file)
@@ -421,54 +421,43 @@ static int ipv6_net_init(struct net *net)
 {
        int ret = 0;
 
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_tcp6);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_tcp6);
        if (ret < 0) {
-               printk(KERN_ERR "nf_conntrack_l4proto_tcp6: protocol register failed\n");
+               pr_err("nf_conntrack_tcp6: pernet registration failed\n");
                goto out;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_udp6);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udp6);
        if (ret < 0) {
-               printk(KERN_ERR "nf_conntrack_l4proto_udp6: protocol register failed\n");
+               pr_err("nf_conntrack_udp6: pernet registration failed\n");
                goto cleanup_tcp6;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_icmpv6);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_icmpv6);
        if (ret < 0) {
-               printk(KERN_ERR "nf_conntrack_l4proto_icmp6: protocol register failed\n");
+               pr_err("nf_conntrack_icmp6: pernet registration failed\n");
                goto cleanup_udp6;
        }
-       ret = nf_conntrack_l3proto_register(net,
-                                           &nf_conntrack_l3proto_ipv6);
+       ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6);
        if (ret < 0) {
-               printk(KERN_ERR "nf_conntrack_l3proto_ipv6: protocol register failed\n");
+               pr_err("nf_conntrack_ipv6: pernet registration failed.\n");
                goto cleanup_icmpv6;
        }
        return 0;
  cleanup_icmpv6:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_icmpv6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
  cleanup_udp6:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_udp6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
  cleanup_tcp6:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_tcp6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
  out:
        return ret;
 }
 
 static void ipv6_net_exit(struct net *net)
 {
-       nf_conntrack_l3proto_unregister(net,
-                                       &nf_conntrack_l3proto_ipv6);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_icmpv6);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_udp6);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_tcp6);
+       nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_icmpv6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udp6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_tcp6);
 }
 
 static struct pernet_operations ipv6_net_ops = {
@@ -491,19 +480,52 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
 
        ret = register_pernet_subsys(&ipv6_net_ops);
        if (ret < 0)
-               goto cleanup_pernet;
+               goto cleanup_sockopt;
+
        ret = nf_register_hooks(ipv6_conntrack_ops,
                                ARRAY_SIZE(ipv6_conntrack_ops));
        if (ret < 0) {
                pr_err("nf_conntrack_ipv6: can't register pre-routing defrag "
                       "hook.\n");
-               goto cleanup_ipv6;
+               goto cleanup_pernet;
+       }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_tcp6);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv6: can't register tcp6 proto.\n");
+               goto cleanup_hooks;
+       }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udp6);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv6: can't register udp6 proto.\n");
+               goto cleanup_tcp6;
+       }
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_icmpv6);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv6: can't register icmpv6 proto.\n");
+               goto cleanup_udp6;
+       }
+
+       ret = nf_ct_l3proto_register(&nf_conntrack_l3proto_ipv6);
+       if (ret < 0) {
+               pr_err("nf_conntrack_ipv6: can't register ipv6 proto.\n");
+               goto cleanup_icmpv6;
        }
        return ret;
 
- cleanup_ipv6:
-       unregister_pernet_subsys(&ipv6_net_ops);
+ cleanup_icmpv6:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
+ cleanup_udp6:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
+ cleanup_tcp6:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
+ cleanup_hooks:
+       nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
  cleanup_pernet:
+       unregister_pernet_subsys(&ipv6_net_ops);
+ cleanup_sockopt:
        nf_unregister_sockopt(&so_getorigdst6);
        return ret;
 }
@@ -511,6 +533,10 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
 static void __exit nf_conntrack_l3proto_ipv6_fini(void)
 {
        synchronize_net();
+       nf_ct_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_tcp6);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udp6);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_icmpv6);
        nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
        unregister_pernet_subsys(&ipv6_net_ops);
        nf_unregister_sockopt(&so_getorigdst6);
index 49e96df5fbc4b38e842540f65c5b23309440c597..eb2c8ebf6d9930ef650ec8e9a12178e89cfb2199 100644 (file)
@@ -124,6 +124,12 @@ config NF_CONNTRACK_TIMESTAMP
 
          If unsure, say `N'.
 
+config NF_CONNTRACK_LABELS
+       bool
+       help
+         This option enables support for assigning user-defined flag bits
+         to connection tracking entries.  It selected by the connlabel match.
+
 config NF_CT_PROTO_DCCP
        tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)'
        depends on EXPERIMENTAL
@@ -805,6 +811,15 @@ config NETFILTER_XT_MATCH_ADDRTYPE
          If you want to compile it as a module, say M here and read
          <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_BPF
+       tristate '"bpf" match support'
+       depends on NETFILTER_ADVANCED
+       help
+         BPF matching applies a linux socket filter to each packet and
+         accepts those for which the filter returns non-zero.
+
+         To compile it as a module, choose M here.  If unsure, say N.
+
 config NETFILTER_XT_MATCH_CLUSTER
        tristate '"cluster" match support'
        depends on NF_CONNTRACK
@@ -842,6 +857,18 @@ config NETFILTER_XT_MATCH_CONNBYTES
          If you want to compile it as a module, say M here and read
          <file:Documentation/kbuild/modules.txt>.  If unsure, say `N'.
 
+config NETFILTER_XT_MATCH_CONNLABEL
+       tristate '"connlabel" match support'
+       select NF_CONNTRACK_LABELS
+       depends on NETFILTER_ADVANCED
+       ---help---
+         This match allows you to test and assign userspace-defined labels names
+         to a connection.  The kernel only stores bit values - mapping
+         names to bits is done by userspace.
+
+         Unlike connmark, more than 32 flag bits may be assigned to a
+         connection simultaneously.
+
 config NETFILTER_XT_MATCH_CONNLIMIT
        tristate '"connlimit" match support"'
        depends on NF_CONNTRACK
index 32596978df1d9bb0b92f1b0b1b81c4adf7204c94..a1abf87d43bfbd902f82cc9f8aae156d936cd89a 100644 (file)
@@ -4,6 +4,7 @@ nf_conntrack-y  := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_exp
 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMEOUT) += nf_conntrack_timeout.o
 nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
 nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
+nf_conntrack-$(CONFIG_NF_CONNTRACK_LABELS) += nf_conntrack_labels.o
 
 obj-$(CONFIG_NETFILTER) = netfilter.o
 
@@ -98,9 +99,11 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_IDLETIMER) += xt_IDLETIMER.o
 
 # matches
 obj-$(CONFIG_NETFILTER_XT_MATCH_ADDRTYPE) += xt_addrtype.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_BPF) += xt_bpf.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CLUSTER) += xt_cluster.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_COMMENT) += xt_comment.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNBYTES) += xt_connbytes.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLABEL) += xt_connlabel.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNLIMIT) += xt_connlimit.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CONNTRACK) += xt_conntrack.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_CPU) += xt_cpu.o
index 7df424e2d10cf6146e7c3b38eaf0bf86a2bb0544..2d3030ab5b619c1a354ec328acf2e85bfd6fd250 100644 (file)
@@ -106,36 +106,26 @@ static void nf_conntrack_acct_fini_sysctl(struct net *net)
 }
 #endif
 
-int nf_conntrack_acct_init(struct net *net)
+int nf_conntrack_acct_pernet_init(struct net *net)
 {
-       int ret;
-
        net->ct.sysctl_acct = nf_ct_acct;
+       return nf_conntrack_acct_init_sysctl(net);
+}
 
-       if (net_eq(net, &init_net)) {
-               ret = nf_ct_extend_register(&acct_extend);
-               if (ret < 0) {
-                       printk(KERN_ERR "nf_conntrack_acct: Unable to register extension\n");
-                       goto out_extend_register;
-               }
-       }
+void nf_conntrack_acct_pernet_fini(struct net *net)
+{
+       nf_conntrack_acct_fini_sysctl(net);
+}
 
-       ret = nf_conntrack_acct_init_sysctl(net);
+int nf_conntrack_acct_init(void)
+{
+       int ret = nf_ct_extend_register(&acct_extend);
        if (ret < 0)
-               goto out_sysctl;
-
-       return 0;
-
-out_sysctl:
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&acct_extend);
-out_extend_register:
+               pr_err("nf_conntrack_acct: Unable to register extension\n");
        return ret;
 }
 
-void nf_conntrack_acct_fini(struct net *net)
+void nf_conntrack_acct_fini(void)
 {
-       nf_conntrack_acct_fini_sysctl(net);
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&acct_extend);
+       nf_ct_extend_unregister(&acct_extend);
 }
index e4a0c4fb3a7cef64d1f15c9173d5a3e62ad616b4..c8e001a9c45b15a8fe4038845f2111c6470c67b7 100644 (file)
@@ -45,6 +45,7 @@
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <net/netfilter/nf_conntrack_timestamp.h>
 #include <net/netfilter/nf_conntrack_timeout.h>
+#include <net/netfilter/nf_conntrack_labels.h>
 #include <net/netfilter/nf_nat.h>
 #include <net/netfilter/nf_nat_core.h>
 
@@ -763,6 +764,7 @@ void nf_conntrack_free(struct nf_conn *ct)
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_free);
 
+
 /* Allocate a new conntrack: we return -ENOMEM if classification
    failed due to stress.  Otherwise it really is unclassifiable. */
 static struct nf_conntrack_tuple_hash *
@@ -809,6 +811,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
 
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
        nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
+       nf_ct_labels_ext_add(ct);
 
        ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
        nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
@@ -1331,18 +1334,42 @@ static int untrack_refs(void)
        return cnt;
 }
 
-static void nf_conntrack_cleanup_init_net(void)
+void nf_conntrack_cleanup_start(void)
+{
+       RCU_INIT_POINTER(ip_ct_attach, NULL);
+}
+
+void nf_conntrack_cleanup_end(void)
 {
+       RCU_INIT_POINTER(nf_ct_destroy, NULL);
        while (untrack_refs() > 0)
                schedule();
 
 #ifdef CONFIG_NF_CONNTRACK_ZONES
        nf_ct_extend_unregister(&nf_ct_zone_extend);
 #endif
+       nf_conntrack_proto_fini();
+       nf_conntrack_labels_fini();
+       nf_conntrack_helper_fini();
+       nf_conntrack_timeout_fini();
+       nf_conntrack_ecache_fini();
+       nf_conntrack_tstamp_fini();
+       nf_conntrack_acct_fini();
+       nf_conntrack_expect_fini();
 }
 
-static void nf_conntrack_cleanup_net(struct net *net)
+/*
+ * Mishearing the voices in his head, our hero wonders how he's
+ * supposed to kill the mall.
+ */
+void nf_conntrack_cleanup_net(struct net *net)
 {
+       /*
+        * This makes sure all current packets have passed through
+        *  netfilter framework.  Roll on, two-stage module
+        *  delete...
+        */
+       synchronize_net();
  i_see_dead_people:
        nf_ct_iterate_cleanup(net, kill_all, NULL);
        nf_ct_release_dying_list(net);
@@ -1352,38 +1379,17 @@ static void nf_conntrack_cleanup_net(struct net *net)
        }
 
        nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
-       nf_conntrack_helper_fini(net);
-       nf_conntrack_timeout_fini(net);
-       nf_conntrack_ecache_fini(net);
-       nf_conntrack_tstamp_fini(net);
-       nf_conntrack_acct_fini(net);
-       nf_conntrack_expect_fini(net);
+       nf_conntrack_proto_pernet_fini(net);
+       nf_conntrack_helper_pernet_fini(net);
+       nf_conntrack_ecache_pernet_fini(net);
+       nf_conntrack_tstamp_pernet_fini(net);
+       nf_conntrack_acct_pernet_fini(net);
+       nf_conntrack_expect_pernet_fini(net);
        kmem_cache_destroy(net->ct.nf_conntrack_cachep);
        kfree(net->ct.slabname);
        free_percpu(net->ct.stat);
 }
 
-/* Mishearing the voices in his head, our hero wonders how he's
-   supposed to kill the mall. */
-void nf_conntrack_cleanup(struct net *net)
-{
-       if (net_eq(net, &init_net))
-               RCU_INIT_POINTER(ip_ct_attach, NULL);
-
-       /* This makes sure all current packets have passed through
-          netfilter framework.  Roll on, two-stage module
-          delete... */
-       synchronize_net();
-       nf_conntrack_proto_fini(net);
-       nf_conntrack_cleanup_net(net);
-}
-
-void nf_conntrack_cleanup_end(void)
-{
-       RCU_INIT_POINTER(nf_ct_destroy, NULL);
-       nf_conntrack_cleanup_init_net();
-}
-
 void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
 {
        struct hlist_nulls_head *hash;
@@ -1474,7 +1480,7 @@ void nf_ct_untracked_status_or(unsigned long bits)
 }
 EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
 
-static int nf_conntrack_init_init_net(void)
+int nf_conntrack_init_start(void)
 {
        int max_factor = 8;
        int ret, cpu;
@@ -1501,11 +1507,44 @@ static int nf_conntrack_init_init_net(void)
        printk(KERN_INFO "nf_conntrack version %s (%u buckets, %d max)\n",
               NF_CONNTRACK_VERSION, nf_conntrack_htable_size,
               nf_conntrack_max);
+
+       ret = nf_conntrack_expect_init();
+       if (ret < 0)
+               goto err_expect;
+
+       ret = nf_conntrack_acct_init();
+       if (ret < 0)
+               goto err_acct;
+
+       ret = nf_conntrack_tstamp_init();
+       if (ret < 0)
+               goto err_tstamp;
+
+       ret = nf_conntrack_ecache_init();
+       if (ret < 0)
+               goto err_ecache;
+
+       ret = nf_conntrack_timeout_init();
+       if (ret < 0)
+               goto err_timeout;
+
+       ret = nf_conntrack_helper_init();
+       if (ret < 0)
+               goto err_helper;
+
+       ret = nf_conntrack_labels_init();
+       if (ret < 0)
+               goto err_labels;
+
 #ifdef CONFIG_NF_CONNTRACK_ZONES
        ret = nf_ct_extend_register(&nf_ct_zone_extend);
        if (ret < 0)
                goto err_extend;
 #endif
+       ret = nf_conntrack_proto_init();
+       if (ret < 0)
+               goto err_proto;
+
        /* Set up fake conntrack: to never be deleted, not in any hashes */
        for_each_possible_cpu(cpu) {
                struct nf_conn *ct = &per_cpu(nf_conntrack_untracked, cpu);
@@ -1516,12 +1555,38 @@ static int nf_conntrack_init_init_net(void)
        nf_ct_untracked_status_or(IPS_CONFIRMED | IPS_UNTRACKED);
        return 0;
 
+err_proto:
 #ifdef CONFIG_NF_CONNTRACK_ZONES
+       nf_ct_extend_unregister(&nf_ct_zone_extend);
 err_extend:
 #endif
+       nf_conntrack_labels_fini();
+err_labels:
+       nf_conntrack_helper_fini();
+err_helper:
+       nf_conntrack_timeout_fini();
+err_timeout:
+       nf_conntrack_ecache_fini();
+err_ecache:
+       nf_conntrack_tstamp_fini();
+err_tstamp:
+       nf_conntrack_acct_fini();
+err_acct:
+       nf_conntrack_expect_fini();
+err_expect:
        return ret;
 }
 
+void nf_conntrack_init_end(void)
+{
+       /* For use by REJECT target */
+       RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
+       RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
+
+       /* Howto get NAT offsets */
+       RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
+}
+
 /*
  * We need to use special "null" values, not used in hash table
  */
@@ -1529,7 +1594,7 @@ err_extend:
 #define DYING_NULLS_VAL                ((1<<30)+1)
 #define TEMPLATE_NULLS_VAL     ((1<<30)+2)
 
-static int nf_conntrack_init_net(struct net *net)
+int nf_conntrack_init_net(struct net *net)
 {
        int ret;
 
@@ -1565,35 +1630,36 @@ static int nf_conntrack_init_net(struct net *net)
                printk(KERN_ERR "Unable to create nf_conntrack_hash\n");
                goto err_hash;
        }
-       ret = nf_conntrack_expect_init(net);
+       ret = nf_conntrack_expect_pernet_init(net);
        if (ret < 0)
                goto err_expect;
-       ret = nf_conntrack_acct_init(net);
+       ret = nf_conntrack_acct_pernet_init(net);
        if (ret < 0)
                goto err_acct;
-       ret = nf_conntrack_tstamp_init(net);
+       ret = nf_conntrack_tstamp_pernet_init(net);
        if (ret < 0)
                goto err_tstamp;
-       ret = nf_conntrack_ecache_init(net);
+       ret = nf_conntrack_ecache_pernet_init(net);
        if (ret < 0)
                goto err_ecache;
-       ret = nf_conntrack_timeout_init(net);
-       if (ret < 0)
-               goto err_timeout;
-       ret = nf_conntrack_helper_init(net);
+       ret = nf_conntrack_helper_pernet_init(net);
        if (ret < 0)
                goto err_helper;
+       ret = nf_conntrack_proto_pernet_init(net);
+       if (ret < 0)
+               goto err_proto;
        return 0;
+
+err_proto:
+       nf_conntrack_helper_pernet_fini(net);
 err_helper:
-       nf_conntrack_timeout_fini(net);
-err_timeout:
-       nf_conntrack_ecache_fini(net);
+       nf_conntrack_ecache_pernet_fini(net);
 err_ecache:
-       nf_conntrack_tstamp_fini(net);
+       nf_conntrack_tstamp_pernet_fini(net);
 err_tstamp:
-       nf_conntrack_acct_fini(net);
+       nf_conntrack_acct_pernet_fini(net);
 err_acct:
-       nf_conntrack_expect_fini(net);
+       nf_conntrack_expect_pernet_fini(net);
 err_expect:
        nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
 err_hash:
@@ -1610,38 +1676,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct,
                        enum ip_conntrack_dir dir,
                        u32 seq);
 EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
-
-int nf_conntrack_init(struct net *net)
-{
-       int ret;
-
-       if (net_eq(net, &init_net)) {
-               ret = nf_conntrack_init_init_net();
-               if (ret < 0)
-                       goto out_init_net;
-       }
-       ret = nf_conntrack_proto_init(net);
-       if (ret < 0)
-               goto out_proto;
-       ret = nf_conntrack_init_net(net);
-       if (ret < 0)
-               goto out_net;
-
-       if (net_eq(net, &init_net)) {
-               /* For use by REJECT target */
-               RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
-               RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
-
-               /* Howto get NAT offsets */
-               RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
-       }
-       return 0;
-
-out_net:
-       nf_conntrack_proto_fini(net);
-out_proto:
-       if (net_eq(net, &init_net))
-               nf_conntrack_cleanup_init_net();
-out_init_net:
-       return ret;
-}
index faa978f1714b831ff81d81e9ec4732eb3f167fb7..b5d2eb8bf0d58f98f2d9ea3a4e467e8b2861a7cf 100644 (file)
@@ -233,38 +233,27 @@ static void nf_conntrack_event_fini_sysctl(struct net *net)
 }
 #endif /* CONFIG_SYSCTL */
 
-int nf_conntrack_ecache_init(struct net *net)
+int nf_conntrack_ecache_pernet_init(struct net *net)
 {
-       int ret;
-
        net->ct.sysctl_events = nf_ct_events;
        net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
+       return nf_conntrack_event_init_sysctl(net);
+}
 
-       if (net_eq(net, &init_net)) {
-               ret = nf_ct_extend_register(&event_extend);
-               if (ret < 0) {
-                       printk(KERN_ERR "nf_ct_event: Unable to register "
-                                       "event extension.\n");
-                       goto out_extend_register;
-               }
-       }
+void nf_conntrack_ecache_pernet_fini(struct net *net)
+{
+       nf_conntrack_event_fini_sysctl(net);
+}
 
-       ret = nf_conntrack_event_init_sysctl(net);
+int nf_conntrack_ecache_init(void)
+{
+       int ret = nf_ct_extend_register(&event_extend);
        if (ret < 0)
-               goto out_sysctl;
-
-       return 0;
-
-out_sysctl:
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&event_extend);
-out_extend_register:
+               pr_err("nf_ct_event: Unable to register event extension.\n");
        return ret;
 }
 
-void nf_conntrack_ecache_fini(struct net *net)
+void nf_conntrack_ecache_fini(void)
 {
-       nf_conntrack_event_fini_sysctl(net);
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&event_extend);
+       nf_ct_extend_unregister(&event_extend);
 }
index 527651a53a45ded66c97162f4e072ca55a7d1efa..bdd341899ed3f933a3b124e8d6a04e8c4100796a 100644 (file)
@@ -587,53 +587,50 @@ static void exp_proc_remove(struct net *net)
 
 module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
 
-int nf_conntrack_expect_init(struct net *net)
+int nf_conntrack_expect_pernet_init(struct net *net)
 {
        int err = -ENOMEM;
 
-       if (net_eq(net, &init_net)) {
-               if (!nf_ct_expect_hsize) {
-                       nf_ct_expect_hsize = net->ct.htable_size / 256;
-                       if (!nf_ct_expect_hsize)
-                               nf_ct_expect_hsize = 1;
-               }
-               nf_ct_expect_max = nf_ct_expect_hsize * 4;
-       }
-
        net->ct.expect_count = 0;
        net->ct.expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, 0);
        if (net->ct.expect_hash == NULL)
                goto err1;
 
-       if (net_eq(net, &init_net)) {
-               nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
-                                       sizeof(struct nf_conntrack_expect),
-                                       0, 0, NULL);
-               if (!nf_ct_expect_cachep)
-                       goto err2;
-       }
-
        err = exp_proc_init(net);
        if (err < 0)
-               goto err3;
+               goto err2;
 
        return 0;
-
-err3:
-       if (net_eq(net, &init_net))
-               kmem_cache_destroy(nf_ct_expect_cachep);
 err2:
        nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
 err1:
        return err;
 }
 
-void nf_conntrack_expect_fini(struct net *net)
+void nf_conntrack_expect_pernet_fini(struct net *net)
 {
        exp_proc_remove(net);
-       if (net_eq(net, &init_net)) {
-               rcu_barrier(); /* Wait for call_rcu() before destroy */
-               kmem_cache_destroy(nf_ct_expect_cachep);
-       }
        nf_ct_free_hashtable(net->ct.expect_hash, nf_ct_expect_hsize);
 }
+
+int nf_conntrack_expect_init(void)
+{
+       if (!nf_ct_expect_hsize) {
+               nf_ct_expect_hsize = nf_conntrack_htable_size / 256;
+               if (!nf_ct_expect_hsize)
+                       nf_ct_expect_hsize = 1;
+       }
+       nf_ct_expect_max = nf_ct_expect_hsize * 4;
+       nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect",
+                               sizeof(struct nf_conntrack_expect),
+                               0, 0, NULL);
+       if (!nf_ct_expect_cachep)
+               return -ENOMEM;
+       return 0;
+}
+
+void nf_conntrack_expect_fini(void)
+{
+       rcu_barrier(); /* Wait for call_rcu() before destroy */
+       kmem_cache_destroy(nf_ct_expect_cachep);
+}
index 884f2b39319a258ffbaa4360736fd7f83b867195..2f380f73c4c09cc54b70fde2686baabb90507f87 100644 (file)
@@ -423,44 +423,41 @@ static struct nf_ct_ext_type helper_extend __read_mostly = {
        .id     = NF_CT_EXT_HELPER,
 };
 
-int nf_conntrack_helper_init(struct net *net)
+int nf_conntrack_helper_pernet_init(struct net *net)
 {
-       int err;
-
        net->ct.auto_assign_helper_warned = false;
        net->ct.sysctl_auto_assign_helper = nf_ct_auto_assign_helper;
+       return nf_conntrack_helper_init_sysctl(net);
+}
 
-       if (net_eq(net, &init_net)) {
-               nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
-               nf_ct_helper_hash =
-                       nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
-               if (!nf_ct_helper_hash)
-                       return -ENOMEM;
+void nf_conntrack_helper_pernet_fini(struct net *net)
+{
+       nf_conntrack_helper_fini_sysctl(net);
+}
 
-               err = nf_ct_extend_register(&helper_extend);
-               if (err < 0)
-                       goto err1;
+int nf_conntrack_helper_init(void)
+{
+       int ret;
+       nf_ct_helper_hsize = 1; /* gets rounded up to use one page */
+       nf_ct_helper_hash =
+               nf_ct_alloc_hashtable(&nf_ct_helper_hsize, 0);
+       if (!nf_ct_helper_hash)
+               return -ENOMEM;
+
+       ret = nf_ct_extend_register(&helper_extend);
+       if (ret < 0) {
+               pr_err("nf_ct_helper: Unable to register helper extension.\n");
+               goto out_extend;
        }
 
-       err = nf_conntrack_helper_init_sysctl(net);
-       if (err < 0)
-               goto out_sysctl;
-
        return 0;
-
-out_sysctl:
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&helper_extend);
-err1:
+out_extend:
        nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
-       return err;
+       return ret;
 }
 
-void nf_conntrack_helper_fini(struct net *net)
+void nf_conntrack_helper_fini(void)
 {
-       nf_conntrack_helper_fini_sysctl(net);
-       if (net_eq(net, &init_net)) {
-               nf_ct_extend_unregister(&helper_extend);
-               nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
-       }
+       nf_ct_extend_unregister(&helper_extend);
+       nf_ct_free_hashtable(nf_ct_helper_hash, nf_ct_helper_hsize);
 }
diff --git a/net/netfilter/nf_conntrack_labels.c b/net/netfilter/nf_conntrack_labels.c
new file mode 100644 (file)
index 0000000..8fe2e99
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * test/set flag bits stored in conntrack extension area.
+ *
+ * (C) 2013 Astaro GmbH & Co KG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/ctype.h>
+#include <linux/export.h>
+#include <linux/jhash.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include <net/netfilter/nf_conntrack_ecache.h>
+#include <net/netfilter/nf_conntrack_labels.h>
+
+static unsigned int label_bits(const struct nf_conn_labels *l)
+{
+       unsigned int longs = l->words;
+       return longs * BITS_PER_LONG;
+}
+
+bool nf_connlabel_match(const struct nf_conn *ct, u16 bit)
+{
+       struct nf_conn_labels *labels = nf_ct_labels_find(ct);
+
+       if (!labels)
+               return false;
+
+       return bit < label_bits(labels) && test_bit(bit, labels->bits);
+}
+EXPORT_SYMBOL_GPL(nf_connlabel_match);
+
+int nf_connlabel_set(struct nf_conn *ct, u16 bit)
+{
+       struct nf_conn_labels *labels = nf_ct_labels_find(ct);
+
+       if (!labels || bit >= label_bits(labels))
+               return -ENOSPC;
+
+       if (test_bit(bit, labels->bits))
+               return 0;
+
+       if (test_and_set_bit(bit, labels->bits))
+               nf_conntrack_event_cache(IPCT_LABEL, ct);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nf_connlabel_set);
+
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
+static void replace_u32(u32 *address, u32 mask, u32 new)
+{
+       u32 old, tmp;
+
+       do {
+               old = *address;
+               tmp = (old & mask) ^ new;
+       } while (cmpxchg(address, old, tmp) != old);
+}
+
+int nf_connlabels_replace(struct nf_conn *ct,
+                         const u32 *data,
+                         const u32 *mask, unsigned int words32)
+{
+       struct nf_conn_labels *labels;
+       unsigned int size, i;
+       u32 *dst;
+
+       labels = nf_ct_labels_find(ct);
+       if (!labels)
+               return -ENOSPC;
+
+       size = labels->words * sizeof(long);
+       if (size < (words32 * sizeof(u32)))
+               words32 = size / sizeof(u32);
+
+       dst = (u32 *) labels->bits;
+       if (words32) {
+               for (i = 0; i < words32; i++)
+                       replace_u32(&dst[i], mask ? ~mask[i] : 0, data[i]);
+       }
+
+       size /= sizeof(u32);
+       for (i = words32; i < size; i++) /* pad */
+               replace_u32(&dst[i], 0, 0);
+
+       nf_conntrack_event_cache(IPCT_LABEL, ct);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nf_connlabels_replace);
+#endif
+
+static struct nf_ct_ext_type labels_extend __read_mostly = {
+       .len    = sizeof(struct nf_conn_labels),
+       .align  = __alignof__(struct nf_conn_labels),
+       .id     = NF_CT_EXT_LABELS,
+};
+
+int nf_conntrack_labels_init(void)
+{
+       return nf_ct_extend_register(&labels_extend);
+}
+
+void nf_conntrack_labels_fini(void)
+{
+       nf_ct_extend_unregister(&labels_extend);
+}
index 627b0e50b2389120e86ed107a3af01d690e07a29..2334cc5d2b16ec3b95284458a9126655a9aea91b 100644 (file)
@@ -43,6 +43,7 @@
 #include <net/netfilter/nf_conntrack_acct.h>
 #include <net/netfilter/nf_conntrack_zones.h>
 #include <net/netfilter/nf_conntrack_timestamp.h>
+#include <net/netfilter/nf_conntrack_labels.h>
 #ifdef CONFIG_NF_NAT_NEEDED
 #include <net/netfilter/nf_nat_core.h>
 #include <net/netfilter/nf_nat_l4proto.h>
@@ -323,6 +324,40 @@ nla_put_failure:
 #define ctnetlink_dump_secctx(a, b) (0)
 #endif
 
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+static int ctnetlink_label_size(const struct nf_conn *ct)
+{
+       struct nf_conn_labels *labels = nf_ct_labels_find(ct);
+
+       if (!labels)
+               return 0;
+       return nla_total_size(labels->words * sizeof(long));
+}
+
+static int
+ctnetlink_dump_labels(struct sk_buff *skb, const struct nf_conn *ct)
+{
+       struct nf_conn_labels *labels = nf_ct_labels_find(ct);
+       unsigned int len, i;
+
+       if (!labels)
+               return 0;
+
+       len = labels->words * sizeof(long);
+       i = 0;
+       do {
+               if (labels->bits[i] != 0)
+                       return nla_put(skb, CTA_LABELS, len, labels->bits);
+               i++;
+       } while (i < labels->words);
+
+       return 0;
+}
+#else
+#define ctnetlink_dump_labels(a, b) (0)
+#define ctnetlink_label_size(a)        (0)
+#endif
+
 #define master_tuple(ct) &(ct->master->tuplehash[IP_CT_DIR_ORIGINAL].tuple)
 
 static inline int
@@ -463,6 +498,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
            ctnetlink_dump_helpinfo(skb, ct) < 0 ||
            ctnetlink_dump_mark(skb, ct) < 0 ||
            ctnetlink_dump_secctx(skb, ct) < 0 ||
+           ctnetlink_dump_labels(skb, ct) < 0 ||
            ctnetlink_dump_id(skb, ct) < 0 ||
            ctnetlink_dump_use(skb, ct) < 0 ||
            ctnetlink_dump_master(skb, ct) < 0 ||
@@ -561,6 +597,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
               + nla_total_size(sizeof(u_int32_t)) /* CTA_MARK */
 #endif
               + ctnetlink_proto_size(ct)
+              + ctnetlink_label_size(ct)
               ;
 }
 
@@ -662,6 +699,9 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
                    && ctnetlink_dump_secctx(skb, ct) < 0)
                        goto nla_put_failure;
 #endif
+               if (events & (1 << IPCT_LABEL) &&
+                    ctnetlink_dump_labels(skb, ct) < 0)
+                       goto nla_put_failure;
 
                if (events & (1 << IPCT_RELATED) &&
                    ctnetlink_dump_master(skb, ct) < 0)
@@ -921,6 +961,7 @@ ctnetlink_parse_help(const struct nlattr *attr, char **helper_name,
        return 0;
 }
 
+#define __CTA_LABELS_MAX_LENGTH ((XT_CONNLABEL_MAXBIT + 1) / BITS_PER_BYTE)
 static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
        [CTA_TUPLE_ORIG]        = { .type = NLA_NESTED },
        [CTA_TUPLE_REPLY]       = { .type = NLA_NESTED },
@@ -937,6 +978,10 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
        [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED },
        [CTA_ZONE]              = { .type = NLA_U16 },
        [CTA_MARK_MASK]         = { .type = NLA_U32 },
+       [CTA_LABELS]            = { .type = NLA_BINARY,
+                                   .len = __CTA_LABELS_MAX_LENGTH },
+       [CTA_LABELS_MASK]       = { .type = NLA_BINARY,
+                                   .len = __CTA_LABELS_MAX_LENGTH },
 };
 
 static int
@@ -1464,6 +1509,31 @@ ctnetlink_change_nat_seq_adj(struct nf_conn *ct,
 }
 #endif
 
+static int
+ctnetlink_attach_labels(struct nf_conn *ct, const struct nlattr * const cda[])
+{
+#ifdef CONFIG_NF_CONNTRACK_LABELS
+       size_t len = nla_len(cda[CTA_LABELS]);
+       const void *mask = cda[CTA_LABELS_MASK];
+
+       if (len & (sizeof(u32)-1)) /* must be multiple of u32 */
+               return -EINVAL;
+
+       if (mask) {
+               if (nla_len(cda[CTA_LABELS_MASK]) == 0 ||
+                   nla_len(cda[CTA_LABELS_MASK]) != len)
+                       return -EINVAL;
+               mask = nla_data(cda[CTA_LABELS_MASK]);
+       }
+
+       len /= sizeof(u32);
+
+       return nf_connlabels_replace(ct, nla_data(cda[CTA_LABELS]), mask, len);
+#else
+       return -EOPNOTSUPP;
+#endif
+}
+
 static int
 ctnetlink_change_conntrack(struct nf_conn *ct,
                           const struct nlattr * const cda[])
@@ -1510,6 +1580,11 @@ ctnetlink_change_conntrack(struct nf_conn *ct,
                        return err;
        }
 #endif
+       if (cda[CTA_LABELS]) {
+               err = ctnetlink_attach_labels(ct, cda);
+               if (err < 0)
+                       return err;
+       }
 
        return 0;
 }
@@ -1598,6 +1673,8 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
        nf_ct_acct_ext_add(ct, GFP_ATOMIC);
        nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
        nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
+       nf_ct_labels_ext_add(ct);
+
        /* we must add conntrack extensions before confirmation. */
        ct->status |= IPS_CONFIRMED;
 
@@ -1716,6 +1793,10 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb,
                        else
                                events = IPCT_NEW;
 
+                       if (cda[CTA_LABELS] &&
+                           ctnetlink_attach_labels(ct, cda) == 0)
+                               events |= (1 << IPCT_LABEL);
+
                        nf_conntrack_eventmask_report((1 << IPCT_REPLY) |
                                                      (1 << IPCT_ASSURED) |
                                                      (1 << IPCT_HELPER) |
@@ -1983,6 +2064,8 @@ ctnetlink_nfqueue_build(struct sk_buff *skb, struct nf_conn *ct)
        if (ct->mark && ctnetlink_dump_mark(skb, ct) < 0)
                goto nla_put_failure;
 #endif
+       if (ctnetlink_dump_labels(skb, ct) < 0)
+               goto nla_put_failure;
        rcu_read_unlock();
        return 0;
 
@@ -2011,6 +2094,11 @@ ctnetlink_nfqueue_parse_ct(const struct nlattr *cda[], struct nf_conn *ct)
                if (err < 0)
                        return err;
        }
+       if (cda[CTA_LABELS]) {
+               err = ctnetlink_attach_labels(ct, cda);
+               if (err < 0)
+                       return err;
+       }
 #if defined(CONFIG_NF_CONNTRACK_MARK)
        if (cda[CTA_MARK])
                ct->mark = ntohl(nla_get_be32(cda[CTA_MARK]));
index 51e928db48c846f469da93ed70ed072807f3359f..58ab4050830cbadc5e1b817ea600f60bacff29d0 100644 (file)
@@ -212,8 +212,7 @@ static void nf_ct_l3proto_unregister_sysctl(struct net *net,
 #endif
 }
 
-static int
-nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto)
+int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto)
 {
        int ret = 0;
        struct nf_conntrack_l3proto *old;
@@ -242,8 +241,9 @@ out_unlock:
        return ret;
 
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_register);
 
-int nf_conntrack_l3proto_register(struct net *net,
+int nf_ct_l3proto_pernet_register(struct net *net,
                                  struct nf_conntrack_l3proto *proto)
 {
        int ret = 0;
@@ -254,22 +254,11 @@ int nf_conntrack_l3proto_register(struct net *net,
                        return ret;
        }
 
-       ret = nf_ct_l3proto_register_sysctl(net, proto);
-       if (ret < 0)
-               return ret;
-
-       if (net == &init_net) {
-               ret = nf_conntrack_l3proto_register_net(proto);
-               if (ret < 0)
-                       nf_ct_l3proto_unregister_sysctl(net, proto);
-       }
-
-       return ret;
+       return nf_ct_l3proto_register_sysctl(net, proto);
 }
-EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register);
 
-static void
-nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
+void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto)
 {
        BUG_ON(proto->l3proto >= AF_MAX);
 
@@ -283,19 +272,17 @@ nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
 
        synchronize_rcu();
 }
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister);
 
-void nf_conntrack_l3proto_unregister(struct net *net,
+void nf_ct_l3proto_pernet_unregister(struct net *net,
                                     struct nf_conntrack_l3proto *proto)
 {
-       if (net == &init_net)
-               nf_conntrack_l3proto_unregister_net(proto);
-
        nf_ct_l3proto_unregister_sysctl(net, proto);
 
        /* Remove all contrack entries for this protocol */
        nf_ct_iterate_cleanup(net, kill_l3proto, proto);
 }
-EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
+EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister);
 
 static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
                                              struct nf_conntrack_l4proto *l4proto)
@@ -376,8 +363,7 @@ void nf_ct_l4proto_unregister_sysctl(struct net *net,
 
 /* FIXME: Allow NULL functions and sub in pointers to generic for
    them. --RR */
-static int
-nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
+int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto)
 {
        int ret = 0;
 
@@ -431,8 +417,9 @@ out_unlock:
        mutex_unlock(&nf_ct_proto_mutex);
        return ret;
 }
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_register);
 
-int nf_conntrack_l4proto_register(struct net *net,
+int nf_ct_l4proto_pernet_register(struct net *net,
                                  struct nf_conntrack_l4proto *l4proto)
 {
        int ret = 0;
@@ -452,22 +439,13 @@ int nf_conntrack_l4proto_register(struct net *net,
        if (ret < 0)
                goto out;
 
-       if (net == &init_net) {
-               ret = nf_conntrack_l4proto_register_net(l4proto);
-               if (ret < 0) {
-                       nf_ct_l4proto_unregister_sysctl(net, pn, l4proto);
-                       goto out;
-               }
-       }
-
        pn->users++;
 out:
        return ret;
 }
-EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register);
 
-static void
-nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
+void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
 {
        BUG_ON(l4proto->l3proto >= PF_MAX);
 
@@ -482,15 +460,13 @@ nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
 
        synchronize_rcu();
 }
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister);
 
-void nf_conntrack_l4proto_unregister(struct net *net,
+void nf_ct_l4proto_pernet_unregister(struct net *net,
                                     struct nf_conntrack_l4proto *l4proto)
 {
        struct nf_proto_net *pn = NULL;
 
-       if (net == &init_net)
-               nf_conntrack_l4proto_unregister_net(l4proto);
-
        pn = nf_ct_l4proto_net(net, l4proto);
        if (pn == NULL)
                return;
@@ -501,11 +477,10 @@ void nf_conntrack_l4proto_unregister(struct net *net,
        /* Remove all contrack entries for this protocol */
        nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
 }
-EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
+EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister);
 
-int nf_conntrack_proto_init(struct net *net)
+int nf_conntrack_proto_pernet_init(struct net *net)
 {
-       unsigned int i;
        int err;
        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
                                        &nf_conntrack_l4proto_generic);
@@ -520,19 +495,12 @@ int nf_conntrack_proto_init(struct net *net)
        if (err < 0)
                return err;
 
-       if (net == &init_net) {
-               for (i = 0; i < AF_MAX; i++)
-                       rcu_assign_pointer(nf_ct_l3protos[i],
-                                          &nf_conntrack_l3proto_generic);
-       }
-
        pn->users++;
        return 0;
 }
 
-void nf_conntrack_proto_fini(struct net *net)
+void nf_conntrack_proto_pernet_fini(struct net *net)
 {
-       unsigned int i;
        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
                                        &nf_conntrack_l4proto_generic);
 
@@ -540,9 +508,21 @@ void nf_conntrack_proto_fini(struct net *net)
        nf_ct_l4proto_unregister_sysctl(net,
                                        pn,
                                        &nf_conntrack_l4proto_generic);
-       if (net == &init_net) {
-               /* free l3proto protocol tables */
-               for (i = 0; i < PF_MAX; i++)
-                       kfree(nf_ct_protos[i]);
-       }
+}
+
+int nf_conntrack_proto_init(void)
+{
+       unsigned int i;
+       for (i = 0; i < AF_MAX; i++)
+               rcu_assign_pointer(nf_ct_l3protos[i],
+                                  &nf_conntrack_l3proto_generic);
+       return 0;
+}
+
+void nf_conntrack_proto_fini(void)
+{
+       unsigned int i;
+       /* free l3proto protocol tables */
+       for (i = 0; i < PF_MAX; i++)
+               kfree(nf_ct_protos[i]);
 }
index a8ae287bc7afe00ec89ed8032f3cbd7ea1c54b7a..432f95780003f2e36a4d0fb08aaa10b900e052ff 100644 (file)
@@ -935,32 +935,27 @@ static struct nf_conntrack_l4proto dccp_proto6 __read_mostly = {
 static __net_init int dccp_net_init(struct net *net)
 {
        int ret = 0;
-       ret = nf_conntrack_l4proto_register(net,
-                                           &dccp_proto4);
+       ret = nf_ct_l4proto_pernet_register(net, &dccp_proto4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_dccp4 :protocol register failed.\n");
+               pr_err("nf_conntrack_dccp4: pernet registration failed.\n");
                goto out;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &dccp_proto6);
+       ret = nf_ct_l4proto_pernet_register(net, &dccp_proto6);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_dccp6 :protocol register failed.\n");
+               pr_err("nf_conntrack_dccp6: pernet registration failed.\n");
                goto cleanup_dccp4;
        }
        return 0;
 cleanup_dccp4:
-       nf_conntrack_l4proto_unregister(net,
-                                       &dccp_proto4);
+       nf_ct_l4proto_pernet_unregister(net, &dccp_proto4);
 out:
        return ret;
 }
 
 static __net_exit void dccp_net_exit(struct net *net)
 {
-       nf_conntrack_l4proto_unregister(net,
-                                       &dccp_proto6);
-       nf_conntrack_l4proto_unregister(net,
-                                       &dccp_proto4);
+       nf_ct_l4proto_pernet_unregister(net, &dccp_proto6);
+       nf_ct_l4proto_pernet_unregister(net, &dccp_proto4);
 }
 
 static struct pernet_operations dccp_net_ops = {
@@ -972,11 +967,33 @@ static struct pernet_operations dccp_net_ops = {
 
 static int __init nf_conntrack_proto_dccp_init(void)
 {
-       return register_pernet_subsys(&dccp_net_ops);
+       int ret;
+
+       ret = nf_ct_l4proto_register(&dccp_proto4);
+       if (ret < 0)
+               goto out_dccp4;
+
+       ret = nf_ct_l4proto_register(&dccp_proto6);
+       if (ret < 0)
+               goto out_dccp6;
+
+       ret = register_pernet_subsys(&dccp_net_ops);
+       if (ret < 0)
+               goto out_pernet;
+
+       return 0;
+out_pernet:
+       nf_ct_l4proto_unregister(&dccp_proto6);
+out_dccp6:
+       nf_ct_l4proto_unregister(&dccp_proto4);
+out_dccp4:
+       return ret;
 }
 
 static void __exit nf_conntrack_proto_dccp_fini(void)
 {
+       nf_ct_l4proto_unregister(&dccp_proto6);
+       nf_ct_l4proto_unregister(&dccp_proto4);
        unregister_pernet_subsys(&dccp_net_ops);
 }
 
index b09b7af7f6f803414b962039e37d41df9fbb8a29..bd7d01d9c7e77d0e8a3a1e50116f854bb4925e14 100644 (file)
@@ -397,15 +397,15 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = {
 static int proto_gre_net_init(struct net *net)
 {
        int ret = 0;
-       ret = nf_conntrack_l4proto_register(net, &nf_conntrack_l4proto_gre4);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_gre4);
        if (ret < 0)
-               pr_err("nf_conntrack_l4proto_gre4 :protocol register failed.\n");
+               pr_err("nf_conntrack_gre4: pernet registration failed.\n");
        return ret;
 }
 
 static void proto_gre_net_exit(struct net *net)
 {
-       nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_gre4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_gre4);
        nf_ct_gre_keymap_flush(net);
 }
 
@@ -418,11 +418,26 @@ static struct pernet_operations proto_gre_net_ops = {
 
 static int __init nf_ct_proto_gre_init(void)
 {
-       return register_pernet_subsys(&proto_gre_net_ops);
+       int ret;
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_gre4);
+       if (ret < 0)
+               goto out_gre4;
+
+       ret = register_pernet_subsys(&proto_gre_net_ops);
+       if (ret < 0)
+               goto out_pernet;
+
+       return 0;
+out_pernet:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4);
+out_gre4:
+       return ret;
 }
 
 static void __exit nf_ct_proto_gre_fini(void)
 {
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_gre4);
        unregister_pernet_subsys(&proto_gre_net_ops);
 }
 
index c746d61f83edb562f17b64ba63566bc0f12d8b2b..480f616d59361e0fff4abf182dc9f35974606187 100644 (file)
@@ -853,33 +853,28 @@ static int sctp_net_init(struct net *net)
 {
        int ret = 0;
 
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_sctp4);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_sctp4 :protocol register failed.\n");
+               pr_err("nf_conntrack_sctp4: pernet registration failed.\n");
                goto out;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_sctp6);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_sctp6);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_sctp6 :protocol register failed.\n");
+               pr_err("nf_conntrack_sctp6: pernet registration failed.\n");
                goto cleanup_sctp4;
        }
        return 0;
 
 cleanup_sctp4:
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_sctp4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4);
 out:
        return ret;
 }
 
 static void sctp_net_exit(struct net *net)
 {
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_sctp6);
-       nf_conntrack_l4proto_unregister(net,
-                                       &nf_conntrack_l4proto_sctp4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_sctp4);
 }
 
 static struct pernet_operations sctp_net_ops = {
@@ -891,11 +886,33 @@ static struct pernet_operations sctp_net_ops = {
 
 static int __init nf_conntrack_proto_sctp_init(void)
 {
-       return register_pernet_subsys(&sctp_net_ops);
+       int ret;
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp4);
+       if (ret < 0)
+               goto out_sctp4;
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_sctp6);
+       if (ret < 0)
+               goto out_sctp6;
+
+       ret = register_pernet_subsys(&sctp_net_ops);
+       if (ret < 0)
+               goto out_pernet;
+
+       return 0;
+out_pernet:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6);
+out_sctp6:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
+out_sctp4:
+       return ret;
 }
 
 static void __exit nf_conntrack_proto_sctp_fini(void)
 {
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp6);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_sctp4);
        unregister_pernet_subsys(&sctp_net_ops);
 }
 
index 4b66df2092869a16420573aa2ed64495746a067f..157489581c313b02456b18d0e3e886746a60671f 100644 (file)
@@ -336,30 +336,28 @@ static int udplite_net_init(struct net *net)
 {
        int ret = 0;
 
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_udplite4);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite4);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n");
+               pr_err("nf_conntrack_udplite4: pernet registration failed.\n");
                goto out;
        }
-       ret = nf_conntrack_l4proto_register(net,
-                                           &nf_conntrack_l4proto_udplite6);
+       ret = nf_ct_l4proto_pernet_register(net, &nf_conntrack_l4proto_udplite6);
        if (ret < 0) {
-               pr_err("nf_conntrack_l4proto_udplite4 :protocol register failed.\n");
+               pr_err("nf_conntrack_udplite6: pernet registration failed.\n");
                goto cleanup_udplite4;
        }
        return 0;
 
 cleanup_udplite4:
-       nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4);
 out:
        return ret;
 }
 
 static void udplite_net_exit(struct net *net)
 {
-       nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite6);
-       nf_conntrack_l4proto_unregister(net, &nf_conntrack_l4proto_udplite4);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite6);
+       nf_ct_l4proto_pernet_unregister(net, &nf_conntrack_l4proto_udplite4);
 }
 
 static struct pernet_operations udplite_net_ops = {
@@ -371,11 +369,33 @@ static struct pernet_operations udplite_net_ops = {
 
 static int __init nf_conntrack_proto_udplite_init(void)
 {
-       return register_pernet_subsys(&udplite_net_ops);
+       int ret;
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite4);
+       if (ret < 0)
+               goto out_udplite4;
+
+       ret = nf_ct_l4proto_register(&nf_conntrack_l4proto_udplite6);
+       if (ret < 0)
+               goto out_udplite6;
+
+       ret = register_pernet_subsys(&udplite_net_ops);
+       if (ret < 0)
+               goto out_pernet;
+
+       return 0;
+out_pernet:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6);
+out_udplite6:
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
+out_udplite4:
+       return ret;
 }
 
 static void __exit nf_conntrack_proto_udplite_exit(void)
 {
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite6);
+       nf_ct_l4proto_unregister(&nf_conntrack_l4proto_udplite4);
        unregister_pernet_subsys(&udplite_net_ops);
 }
 
index df8f4f284481042800b3da96ab41bf3589ef512e..72a67bbe3518e954eb0435858d82690c4af7510d 100644 (file)
@@ -1440,8 +1440,25 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
 {
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
+       enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
        unsigned int matchoff, matchlen;
        unsigned int cseq, i;
+       union nf_inet_addr addr;
+       __be16 port;
+
+       /* Many Cisco IP phones use a high source port for SIP requests, but
+        * listen for the response on port 5060.  If we are the local
+        * router for one of these phones, save the port number from the
+        * Via: header so that nf_nat_sip can redirect the responses to
+        * the correct port.
+        */
+       if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
+                                   SIP_HDR_VIA_UDP, NULL, &matchoff,
+                                   &matchlen, &addr, &port) > 0 &&
+           port != ct->tuplehash[dir].tuple.src.u.udp.port &&
+           nf_inet_addr_cmp(&addr, &ct->tuplehash[dir].tuple.src.u3))
+               ct_sip_info->forced_dport = port;
 
        for (i = 0; i < ARRAY_SIZE(sip_handlers); i++) {
                const struct sip_handler *handler;
index 6e545e26289e5e82aca4a5de2744cce06cac54b9..87b95a2c270cd367fd0e910243221f60308c3263 100644 (file)
@@ -16,6 +16,7 @@
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_helper.h>
 #include <net/netfilter/nf_conntrack_expect.h>
+#include <linux/netfilter/nf_conntrack_snmp.h>
 
 #define SNMP_PORT      161
 
index e7185c68481659445b571ab5e7866728dd3947ec..7936bf7f90bab46a160b31f4ae82e84aee88efc5 100644 (file)
@@ -472,13 +472,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
 {
        struct ctl_table *table;
 
-       if (net_eq(net, &init_net)) {
-               nf_ct_netfilter_header =
-                      register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
-               if (!nf_ct_netfilter_header)
-                       goto out;
-       }
-
        table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table),
                        GFP_KERNEL);
        if (!table)
@@ -502,10 +495,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
 out_unregister_netfilter:
        kfree(table);
 out_kmemdup:
-       if (net_eq(net, &init_net))
-               unregister_net_sysctl_table(nf_ct_netfilter_header);
-out:
-       printk(KERN_ERR "nf_conntrack: can't register to sysctl.\n");
        return -ENOMEM;
 }
 
@@ -513,8 +502,6 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
 {
        struct ctl_table *table;
 
-       if (net_eq(net, &init_net))
-               unregister_net_sysctl_table(nf_ct_netfilter_header);
        table = net->ct.sysctl_header->ctl_table_arg;
        unregister_net_sysctl_table(net->ct.sysctl_header);
        kfree(table);
@@ -530,51 +517,85 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
 }
 #endif /* CONFIG_SYSCTL */
 
-static int nf_conntrack_net_init(struct net *net)
+static int nf_conntrack_pernet_init(struct net *net)
 {
        int ret;
 
-       ret = nf_conntrack_init(net);
+       ret = nf_conntrack_init_net(net);
        if (ret < 0)
                goto out_init;
+
        ret = nf_conntrack_standalone_init_proc(net);
        if (ret < 0)
                goto out_proc;
+
        net->ct.sysctl_checksum = 1;
        net->ct.sysctl_log_invalid = 0;
        ret = nf_conntrack_standalone_init_sysctl(net);
        if (ret < 0)
                goto out_sysctl;
+
        return 0;
 
 out_sysctl:
        nf_conntrack_standalone_fini_proc(net);
 out_proc:
-       nf_conntrack_cleanup(net);
+       nf_conntrack_cleanup_net(net);
 out_init:
        return ret;
 }
 
-static void nf_conntrack_net_exit(struct net *net)
+static void nf_conntrack_pernet_exit(struct net *net)
 {
        nf_conntrack_standalone_fini_sysctl(net);
        nf_conntrack_standalone_fini_proc(net);
-       nf_conntrack_cleanup(net);
+       nf_conntrack_cleanup_net(net);
 }
 
 static struct pernet_operations nf_conntrack_net_ops = {
-       .init = nf_conntrack_net_init,
-       .exit = nf_conntrack_net_exit,
+       .init = nf_conntrack_pernet_init,
+       .exit = nf_conntrack_pernet_exit,
 };
 
 static int __init nf_conntrack_standalone_init(void)
 {
-       return register_pernet_subsys(&nf_conntrack_net_ops);
+       int ret = nf_conntrack_init_start();
+       if (ret < 0)
+               goto out_start;
+
+#ifdef CONFIG_SYSCTL
+       nf_ct_netfilter_header =
+               register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
+       if (!nf_ct_netfilter_header) {
+               pr_err("nf_conntrack: can't register to sysctl.\n");
+               goto out_sysctl;
+       }
+#endif
+
+       ret = register_pernet_subsys(&nf_conntrack_net_ops);
+       if (ret < 0)
+               goto out_pernet;
+
+       nf_conntrack_init_end();
+       return 0;
+
+out_pernet:
+#ifdef CONFIG_SYSCTL
+       unregister_net_sysctl_table(nf_ct_netfilter_header);
+out_sysctl:
+#endif
+       nf_conntrack_cleanup_end();
+out_start:
+       return ret;
 }
 
 static void __exit nf_conntrack_standalone_fini(void)
 {
+       nf_conntrack_cleanup_start();
        unregister_pernet_subsys(&nf_conntrack_net_ops);
+#ifdef CONFIG_SYSCTL
+       unregister_net_sysctl_table(nf_ct_netfilter_header);
+#endif
        nf_conntrack_cleanup_end();
 }
 
index a878ce5b252c84e5508f432ace3e9ffe99f836d4..93da609d9d299375bdc704264fd021a317bd4e6b 100644 (file)
@@ -37,24 +37,15 @@ static struct nf_ct_ext_type timeout_extend __read_mostly = {
        .id     = NF_CT_EXT_TIMEOUT,
 };
 
-int nf_conntrack_timeout_init(struct net *net)
+int nf_conntrack_timeout_init(void)
 {
-       int ret = 0;
-
-       if (net_eq(net, &init_net)) {
-               ret = nf_ct_extend_register(&timeout_extend);
-               if (ret < 0) {
-                       printk(KERN_ERR "nf_ct_timeout: Unable to register "
-                                       "timeout extension.\n");
-                       return ret;
-               }
-       }
-
-       return 0;
+       int ret = nf_ct_extend_register(&timeout_extend);
+       if (ret < 0)
+               pr_err("nf_ct_timeout: Unable to register timeout extension.\n");
+       return ret;
 }
 
-void nf_conntrack_timeout_fini(struct net *net)
+void nf_conntrack_timeout_fini(void)
 {
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&timeout_extend);
+       nf_ct_extend_unregister(&timeout_extend);
 }
index 7ea8026f07c9c843698bb54c68eb236840956bf9..902fb0a6b38ad9baac15c3fc3f17a3e695510db8 100644 (file)
@@ -88,37 +88,28 @@ static void nf_conntrack_tstamp_fini_sysctl(struct net *net)
 }
 #endif
 
-int nf_conntrack_tstamp_init(struct net *net)
+int nf_conntrack_tstamp_pernet_init(struct net *net)
 {
-       int ret;
-
        net->ct.sysctl_tstamp = nf_ct_tstamp;
+       return nf_conntrack_tstamp_init_sysctl(net);
+}
 
-       if (net_eq(net, &init_net)) {
-               ret = nf_ct_extend_register(&tstamp_extend);
-               if (ret < 0) {
-                       printk(KERN_ERR "nf_ct_tstamp: Unable to register "
-                                       "extension\n");
-                       goto out_extend_register;
-               }
-       }
+void nf_conntrack_tstamp_pernet_fini(struct net *net)
+{
+       nf_conntrack_tstamp_fini_sysctl(net);
+       nf_ct_extend_unregister(&tstamp_extend);
+}
 
-       ret = nf_conntrack_tstamp_init_sysctl(net);
+int nf_conntrack_tstamp_init(void)
+{
+       int ret;
+       ret = nf_ct_extend_register(&tstamp_extend);
        if (ret < 0)
-               goto out_sysctl;
-
-       return 0;
-
-out_sysctl:
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&tstamp_extend);
-out_extend_register:
+               pr_err("nf_ct_tstamp: Unable to register extension\n");
        return ret;
 }
 
-void nf_conntrack_tstamp_fini(struct net *net)
+void nf_conntrack_tstamp_fini(void)
 {
-       nf_conntrack_tstamp_fini_sysctl(net);
-       if (net_eq(net, &init_net))
-               nf_ct_extend_unregister(&tstamp_extend);
+       nf_ct_extend_unregister(&tstamp_extend);
 }
index 16303c752213119fa9f2c19adddc73bf191d9d68..5951146e7688962f1281bff1a0f54c68bb0dde7f 100644 (file)
@@ -95,6 +95,7 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
        unsigned int buflen;
        union nf_inet_addr newaddr;
@@ -107,7 +108,8 @@ static int map_addr(struct sk_buff *skb, unsigned int protoff,
        } else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) &&
                   ct->tuplehash[dir].tuple.dst.u.udp.port == port) {
                newaddr = ct->tuplehash[!dir].tuple.src.u3;
-               newport = ct->tuplehash[!dir].tuple.src.u.udp.port;
+               newport = ct_sip_info->forced_dport ? :
+                         ct->tuplehash[!dir].tuple.src.u.udp.port;
        } else
                return 1;
 
@@ -144,6 +146,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        unsigned int coff, matchoff, matchlen;
        enum sip_header_types hdr;
        union nf_inet_addr addr;
@@ -258,6 +261,21 @@ next:
            !map_sip_addr(skb, protoff, dataoff, dptr, datalen, SIP_HDR_TO))
                return NF_DROP;
 
+       /* Mangle destination port for Cisco phones, then fix up checksums */
+       if (dir == IP_CT_DIR_REPLY && ct_sip_info->forced_dport) {
+               struct udphdr *uh;
+
+               if (!skb_make_writable(skb, skb->len))
+                       return NF_DROP;
+
+               uh = (void *)skb->data + protoff;
+               uh->dest = ct_sip_info->forced_dport;
+
+               if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo, protoff,
+                                             0, 0, NULL, 0))
+                       return NF_DROP;
+       }
+
        return NF_ACCEPT;
 }
 
@@ -311,8 +329,10 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
        enum ip_conntrack_info ctinfo;
        struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
        enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
+       struct nf_ct_sip_master *ct_sip_info = nfct_help_data(ct);
        union nf_inet_addr newaddr;
        u_int16_t port;
+       __be16 srcport;
        char buffer[INET6_ADDRSTRLEN + sizeof("[]:nnnnn")];
        unsigned int buflen;
 
@@ -326,8 +346,9 @@ static unsigned int nf_nat_sip_expect(struct sk_buff *skb, unsigned int protoff,
        /* If the signalling port matches the connection's source port in the
         * original direction, try to use the destination port in the opposite
         * direction. */
-       if (exp->tuple.dst.u.udp.port ==
-           ct->tuplehash[dir].tuple.src.u.udp.port)
+       srcport = ct_sip_info->forced_dport ? :
+                 ct->tuplehash[dir].tuple.src.u.udp.port;
+       if (exp->tuple.dst.u.udp.port == srcport)
                port = ntohs(ct->tuplehash[!dir].tuple.dst.u.udp.port);
        else
                port = ntohs(exp->tuple.dst.u.udp.port);
diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c
new file mode 100644 (file)
index 0000000..12d4da8
--- /dev/null
@@ -0,0 +1,73 @@
+/* Xtables module to match packets using a BPF filter.
+ * Copyright 2013 Google Inc.
+ * Written by Willem de Bruijn <willemb@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/filter.h>
+
+#include <linux/netfilter/xt_bpf.h>
+#include <linux/netfilter/x_tables.h>
+
+MODULE_AUTHOR("Willem de Bruijn <willemb@google.com>");
+MODULE_DESCRIPTION("Xtables: BPF filter match");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_bpf");
+MODULE_ALIAS("ip6t_bpf");
+
+static int bpf_mt_check(const struct xt_mtchk_param *par)
+{
+       struct xt_bpf_info *info = par->matchinfo;
+       struct sock_fprog program;
+
+       program.len = info->bpf_program_num_elem;
+       program.filter = (struct sock_filter __user *) info->bpf_program;
+       if (sk_unattached_filter_create(&info->filter, &program)) {
+               pr_info("bpf: check failed: parse error\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static bool bpf_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_bpf_info *info = par->matchinfo;
+
+       return SK_RUN_FILTER(info->filter, skb);
+}
+
+static void bpf_mt_destroy(const struct xt_mtdtor_param *par)
+{
+       const struct xt_bpf_info *info = par->matchinfo;
+       sk_unattached_filter_destroy(info->filter);
+}
+
+static struct xt_match bpf_mt_reg __read_mostly = {
+       .name           = "bpf",
+       .revision       = 0,
+       .family         = NFPROTO_UNSPEC,
+       .checkentry     = bpf_mt_check,
+       .match          = bpf_mt,
+       .destroy        = bpf_mt_destroy,
+       .matchsize      = sizeof(struct xt_bpf_info),
+       .me             = THIS_MODULE,
+};
+
+static int __init bpf_mt_init(void)
+{
+       return xt_register_match(&bpf_mt_reg);
+}
+
+static void __exit bpf_mt_exit(void)
+{
+       xt_unregister_match(&bpf_mt_reg);
+}
+
+module_init(bpf_mt_init);
+module_exit(bpf_mt_exit);
diff --git a/net/netfilter/xt_connlabel.c b/net/netfilter/xt_connlabel.c
new file mode 100644 (file)
index 0000000..9f8719d
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * (C) 2013 Astaro GmbH & Co KG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_labels.h>
+#include <linux/netfilter/x_tables.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+MODULE_DESCRIPTION("Xtables: add/match connection trackling labels");
+MODULE_ALIAS("ipt_connlabel");
+MODULE_ALIAS("ip6t_connlabel");
+
+static bool
+connlabel_mt(const struct sk_buff *skb, struct xt_action_param *par)
+{
+       const struct xt_connlabel_mtinfo *info = par->matchinfo;
+       enum ip_conntrack_info ctinfo;
+       struct nf_conn *ct;
+       bool invert = info->options & XT_CONNLABEL_OP_INVERT;
+
+       ct = nf_ct_get(skb, &ctinfo);
+       if (ct == NULL || nf_ct_is_untracked(ct))
+               return invert;
+
+       if (info->options & XT_CONNLABEL_OP_SET)
+               return (nf_connlabel_set(ct, info->bit) == 0) ^ invert;
+
+       return nf_connlabel_match(ct, info->bit) ^ invert;
+}
+
+static int connlabel_mt_check(const struct xt_mtchk_param *par)
+{
+       const int options = XT_CONNLABEL_OP_INVERT |
+                           XT_CONNLABEL_OP_SET;
+       struct xt_connlabel_mtinfo *info = par->matchinfo;
+       int ret;
+       size_t words;
+
+       if (info->bit > XT_CONNLABEL_MAXBIT)
+               return -ERANGE;
+
+       if (info->options & ~options) {
+               pr_err("Unknown options in mask %x\n", info->options);
+               return -EINVAL;
+       }
+
+       ret = nf_ct_l3proto_try_module_get(par->family);
+       if (ret < 0) {
+               pr_info("cannot load conntrack support for proto=%u\n",
+                                                       par->family);
+               return ret;
+       }
+
+       par->net->ct.labels_used++;
+       words = BITS_TO_LONGS(info->bit+1);
+       if (words > par->net->ct.label_words)
+               par->net->ct.label_words = words;
+
+       return ret;
+}
+
+static void connlabel_mt_destroy(const struct xt_mtdtor_param *par)
+{
+       par->net->ct.labels_used--;
+       if (par->net->ct.labels_used == 0)
+               par->net->ct.label_words = 0;
+       nf_ct_l3proto_module_put(par->family);
+}
+
+static struct xt_match connlabels_mt_reg __read_mostly = {
+       .name           = "connlabel",
+       .family         = NFPROTO_UNSPEC,
+       .checkentry     = connlabel_mt_check,
+       .match          = connlabel_mt,
+       .matchsize      = sizeof(struct xt_connlabel_mtinfo),
+       .destroy        = connlabel_mt_destroy,
+       .me             = THIS_MODULE,
+};
+
+static int __init connlabel_mt_init(void)
+{
+       return xt_register_match(&connlabels_mt_reg);
+}
+
+static void __exit connlabel_mt_exit(void)
+{
+       xt_unregister_match(&connlabels_mt_reg);
+}
+
+module_init(connlabel_mt_init);
+module_exit(connlabel_mt_exit);