aboutsummaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2013-01-29 10:48:30 +0100
committerJiri Kosina <jkosina@suse.cz>2013-01-29 10:48:30 +0100
commit617677295b53a40d0e54aac4cbbc216ffbc755dd (patch)
tree51b9e87213243ed5efff252c8e8d8fec4eebc588 /net
parent5c8d1b68e01a144813e38795fe6dbe7ebb506131 (diff)
parent6abb7c25775b7fb2225ad0508236d63ca710e65f (diff)
Merge branch 'master' into for-next
Conflicts: drivers/devfreq/exynos4_bus.c Sync with Linus' tree to be able to apply patches that are against newer code (mvneta).
Diffstat (limited to 'net')
-rw-r--r--net/8021q/vlan.c15
-rw-r--r--net/8021q/vlan_dev.c12
-rw-r--r--net/9p/trans_virtio.c3
-rw-r--r--net/atm/atm_sysfs.c40
-rw-r--r--net/atm/br2684.c91
-rw-r--r--net/atm/common.c12
-rw-r--r--net/atm/pppoatm.c68
-rw-r--r--net/batman-adv/Kconfig11
-rw-r--r--net/batman-adv/Makefile1
-rw-r--r--net/batman-adv/bat_iv_ogm.c53
-rw-r--r--net/batman-adv/bitarray.c23
-rw-r--r--net/batman-adv/bridge_loop_avoidance.c131
-rw-r--r--net/batman-adv/bridge_loop_avoidance.h6
-rw-r--r--net/batman-adv/debugfs.c60
-rw-r--r--net/batman-adv/distributed-arp-table.c1083
-rw-r--r--net/batman-adv/distributed-arp-table.h167
-rw-r--r--net/batman-adv/gateway_client.c19
-rw-r--r--net/batman-adv/hard-interface.c51
-rw-r--r--net/batman-adv/hash.h22
-rw-r--r--net/batman-adv/icmp_socket.c16
-rw-r--r--net/batman-adv/main.c89
-rw-r--r--net/batman-adv/main.h36
-rw-r--r--net/batman-adv/originator.c22
-rw-r--r--net/batman-adv/packet.h86
-rw-r--r--net/batman-adv/routing.c315
-rw-r--r--net/batman-adv/send.c43
-rw-r--r--net/batman-adv/send.h3
-rw-r--r--net/batman-adv/soft-interface.c106
-rw-r--r--net/batman-adv/sysfs.c58
-rw-r--r--net/batman-adv/translation-table.c541
-rw-r--r--net/batman-adv/translation-table.h8
-rw-r--r--net/batman-adv/types.h102
-rw-r--r--net/batman-adv/unicast.c143
-rw-r--r--net/batman-adv/unicast.h36
-rw-r--r--net/batman-adv/vis.c44
-rw-r--r--net/bluetooth/Kconfig2
-rw-r--r--net/bluetooth/Makefile2
-rw-r--r--net/bluetooth/a2mp.c459
-rw-r--r--net/bluetooth/af_bluetooth.c10
-rw-r--r--net/bluetooth/amp.c471
-rw-r--r--net/bluetooth/bnep/core.c3
-rw-r--r--net/bluetooth/bnep/netdev.c1
-rw-r--r--net/bluetooth/cmtp/capi.c2
-rw-r--r--net/bluetooth/cmtp/core.c2
-rw-r--r--net/bluetooth/cmtp/sock.c2
-rw-r--r--net/bluetooth/hci_conn.c76
-rw-r--r--net/bluetooth/hci_core.c245
-rw-r--r--net/bluetooth/hci_event.c564
-rw-r--r--net/bluetooth/hci_sysfs.c10
-rw-r--r--net/bluetooth/hidp/core.c19
-rw-r--r--net/bluetooth/l2cap_core.c1588
-rw-r--r--net/bluetooth/l2cap_sock.c94
-rw-r--r--net/bluetooth/lib.c14
-rw-r--r--net/bluetooth/mgmt.c117
-rw-r--r--net/bluetooth/rfcomm/core.c19
-rw-r--r--net/bluetooth/rfcomm/sock.c13
-rw-r--r--net/bluetooth/rfcomm/tty.c6
-rw-r--r--net/bluetooth/sco.c100
-rw-r--r--net/bluetooth/smp.c4
-rw-r--r--net/bridge/Makefile2
-rw-r--r--net/bridge/br_device.c4
-rw-r--r--net/bridge/br_if.c8
-rw-r--r--net/bridge/br_input.c17
-rw-r--r--net/bridge/br_ioctl.c25
-rw-r--r--net/bridge/br_mdb.c493
-rw-r--r--net/bridge/br_multicast.c88
-rw-r--r--net/bridge/br_netlink.c247
-rw-r--r--net/bridge/br_private.h55
-rw-r--r--net/bridge/br_stp.c22
-rw-r--r--net/bridge/br_stp_bpdu.c7
-rw-r--r--net/bridge/br_sysfs_br.c22
-rw-r--r--net/bridge/br_sysfs_if.c47
-rw-r--r--net/caif/caif_usb.c18
-rw-r--r--net/caif/cfctrl.c3
-rw-r--r--net/can/bcm.c3
-rw-r--r--net/can/gw.c6
-rw-r--r--net/ceph/ceph_common.c3
-rw-r--r--net/ceph/messenger.c136
-rw-r--r--net/ceph/osd_client.c93
-rw-r--r--net/ceph/osdmap.c47
-rw-r--r--net/core/dev.c252
-rw-r--r--net/core/dev_addr_lists.c3
-rw-r--r--net/core/ethtool.c2
-rw-r--r--net/core/filter.c139
-rw-r--r--net/core/flow.c4
-rw-r--r--net/core/neighbour.c20
-rw-r--r--net/core/net-sysfs.c41
-rw-r--r--net/core/net_namespace.c55
-rw-r--r--net/core/netpoll.c6
-rw-r--r--net/core/netprio_cgroup.c262
-rw-r--r--net/core/pktgen.c47
-rw-r--r--net/core/request_sock.c2
-rw-r--r--net/core/rtnetlink.c233
-rw-r--r--net/core/scm.c11
-rw-r--r--net/core/skbuff.c84
-rw-r--r--net/core/sock.c84
-rw-r--r--net/core/sysctl_net_core.c5
-rw-r--r--net/dcb/dcbnl.c8
-rw-r--r--net/dccp/ipv4.c4
-rw-r--r--net/dccp/ipv6.c3
-rw-r--r--net/dccp/minisocks.c3
-rw-r--r--net/decnet/dn_dev.c6
-rw-r--r--net/decnet/dn_fib.c6
-rw-r--r--net/dns_resolver/dns_key.c15
-rw-r--r--net/dsa/Kconfig18
-rw-r--r--net/ieee802154/6lowpan.c3
-rw-r--r--net/ipv4/af_inet.c93
-rw-r--r--net/ipv4/ah4.c18
-rw-r--r--net/ipv4/arp.c12
-rw-r--r--net/ipv4/datagram.c25
-rw-r--r--net/ipv4/devinet.c200
-rw-r--r--net/ipv4/esp4.c12
-rw-r--r--net/ipv4/fib_frontend.c2
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/icmp.c3
-rw-r--r--net/ipv4/inet_connection_sock.c41
-rw-r--r--net/ipv4/inet_diag.c164
-rw-r--r--net/ipv4/inet_hashtables.c36
-rw-r--r--net/ipv4/ip_fragment.c23
-rw-r--r--net/ipv4/ip_gre.c51
-rw-r--r--net/ipv4/ip_options.c6
-rw-r--r--net/ipv4/ip_output.c4
-rw-r--r--net/ipv4/ip_sockglue.c42
-rw-r--r--net/ipv4/ip_vti.c31
-rw-r--r--net/ipv4/ipcomp.c7
-rw-r--r--net/ipv4/ipconfig.c14
-rw-r--r--net/ipv4/ipip.c271
-rw-r--r--net/ipv4/ipmr.c141
-rw-r--r--net/ipv4/netfilter/arp_tables.c8
-rw-r--r--net/ipv4/netfilter/ip_tables.c8
-rw-r--r--net/ipv4/netfilter/ipt_CLUSTERIP.c9
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c1
-rw-r--r--net/ipv4/netfilter/iptable_nat.c19
-rw-r--r--net/ipv4/ping.c1
-rw-r--r--net/ipv4/protocol.c21
-rw-r--r--net/ipv4/raw.c1
-rw-r--r--net/ipv4/route.c90
-rw-r--r--net/ipv4/syncookies.c2
-rw-r--r--net/ipv4/sysctl_net_ipv4.c3
-rw-r--r--net/ipv4/tcp.c59
-rw-r--r--net/ipv4/tcp_cong.c3
-rw-r--r--net/ipv4/tcp_illinois.c8
-rw-r--r--net/ipv4/tcp_input.c83
-rw-r--r--net/ipv4/tcp_ipv4.c53
-rw-r--r--net/ipv4/tcp_metrics.c14
-rw-r--r--net/ipv4/tcp_minisocks.c8
-rw-r--r--net/ipv4/tcp_output.c24
-rw-r--r--net/ipv4/tcp_timer.c8
-rw-r--r--net/ipv4/udp.c1
-rw-r--r--net/ipv4/xfrm4_policy.c13
-rw-r--r--net/ipv6/Makefile5
-rw-r--r--net/ipv6/addrconf.c229
-rw-r--r--net/ipv6/af_inet6.c245
-rw-r--r--net/ipv6/ah6.c21
-rw-r--r--net/ipv6/anycast.c7
-rw-r--r--net/ipv6/datagram.c8
-rw-r--r--net/ipv6/esp6.c5
-rw-r--r--net/ipv6/exthdrs.c70
-rw-r--r--net/ipv6/exthdrs_core.c168
-rw-r--r--net/ipv6/exthdrs_offload.c41
-rw-r--r--net/ipv6/fib6_rules.c2
-rw-r--r--net/ipv6/icmp.c14
-rw-r--r--net/ipv6/inet6_connection_sock.c3
-rw-r--r--net/ipv6/inet6_hashtables.c27
-rw-r--r--net/ipv6/ip6_fib.c57
-rw-r--r--net/ipv6/ip6_flowlabel.c3
-rw-r--r--net/ipv6/ip6_gre.c40
-rw-r--r--net/ipv6/ip6_offload.c282
-rw-r--r--net/ipv6/ip6_offload.h18
-rw-r--r--net/ipv6/ip6_output.c76
-rw-r--r--net/ipv6/ip6_tunnel.c288
-rw-r--r--net/ipv6/ip6mr.c160
-rw-r--r--net/ipv6/ipv6_sockglue.c10
-rw-r--r--net/ipv6/mcast.c7
-rw-r--r--net/ipv6/ndisc.c82
-rw-r--r--net/ipv6/netfilter/ip6_tables.c117
-rw-r--r--net/ipv6/netfilter/ip6t_NPT.c33
-rw-r--r--net/ipv6/netfilter/ip6t_REJECT.c1
-rw-r--r--net/ipv6/netfilter/ip6t_rpfilter.c2
-rw-r--r--net/ipv6/netfilter/ip6table_nat.c19
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c74
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c4
-rw-r--r--net/ipv6/netfilter/nf_conntrack_reasm.c9
-rw-r--r--net/ipv6/netfilter/nf_defrag_ipv6_hooks.c6
-rw-r--r--net/ipv6/netfilter/nf_nat_proto_icmpv6.c2
-rw-r--r--net/ipv6/output_core.c76
-rw-r--r--net/ipv6/protocol.c25
-rw-r--r--net/ipv6/raw.c6
-rw-r--r--net/ipv6/reassembly.c4
-rw-r--r--net/ipv6/route.c210
-rw-r--r--net/ipv6/sit.c459
-rw-r--r--net/ipv6/syncookies.c2
-rw-r--r--net/ipv6/tcp_ipv6.c127
-rw-r--r--net/ipv6/tcpv6_offload.c95
-rw-r--r--net/ipv6/udp.c94
-rw-r--r--net/ipv6/udp_offload.c120
-rw-r--r--net/ipv6/xfrm6_policy.c21
-rw-r--r--net/ipv6/xfrm6_state.c4
-rw-r--r--net/irda/ircomm/ircomm_tty.c1
-rw-r--r--net/irda/irttp.c1
-rw-r--r--net/iucv/iucv.c2
-rw-r--r--net/key/af_key.c2
-rw-r--r--net/l2tp/l2tp_eth.c1
-rw-r--r--net/l2tp/l2tp_netlink.c2
-rw-r--r--net/llc/af_llc.c2
-rw-r--r--net/mac80211/Kconfig2
-rw-r--r--net/mac80211/Makefile1
-rw-r--r--net/mac80211/aes_cmac.c18
-rw-r--r--net/mac80211/agg-rx.c2
-rw-r--r--net/mac80211/agg-tx.c14
-rw-r--r--net/mac80211/cfg.c516
-rw-r--r--net/mac80211/chan.c439
-rw-r--r--net/mac80211/debugfs.h6
-rw-r--r--net/mac80211/debugfs_key.c23
-rw-r--r--net/mac80211/debugfs_netdev.c78
-rw-r--r--net/mac80211/debugfs_sta.c59
-rw-r--r--net/mac80211/driver-ops.h140
-rw-r--r--net/mac80211/ht.c4
-rw-r--r--net/mac80211/ibss.c190
-rw-r--r--net/mac80211/ieee80211_i.h238
-rw-r--r--net/mac80211/iface.c191
-rw-r--r--net/mac80211/key.c15
-rw-r--r--net/mac80211/key.h11
-rw-r--r--net/mac80211/main.c156
-rw-r--r--net/mac80211/mesh.c90
-rw-r--r--net/mac80211/mesh.h20
-rw-r--r--net/mac80211/mesh_hwmp.c5
-rw-r--r--net/mac80211/mesh_plink.c64
-rw-r--r--net/mac80211/mesh_sync.c105
-rw-r--r--net/mac80211/mlme.c810
-rw-r--r--net/mac80211/offchannel.c43
-rw-r--r--net/mac80211/pm.c46
-rw-r--r--net/mac80211/rate.c5
-rw-r--r--net/mac80211/rate.h12
-rw-r--r--net/mac80211/rc80211_minstrel.c9
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c8
-rw-r--r--net/mac80211/rx.c324
-rw-r--r--net/mac80211/scan.c122
-rw-r--r--net/mac80211/sta_info.c126
-rw-r--r--net/mac80211/sta_info.h34
-rw-r--r--net/mac80211/status.c178
-rw-r--r--net/mac80211/trace.h246
-rw-r--r--net/mac80211/tx.c303
-rw-r--r--net/mac80211/util.c407
-rw-r--r--net/mac80211/vht.c35
-rw-r--r--net/mac80211/wme.c40
-rw-r--r--net/mac80211/wpa.c5
-rw-r--r--net/mac802154/ieee802154_dev.c4
-rw-r--r--net/mac802154/tx.c7
-rw-r--r--net/mac802154/wpan.c6
-rw-r--r--net/netfilter/Kconfig7
-rw-r--r--net/netfilter/core.c2
-rw-r--r--net/netfilter/ipset/ip_set_core.c245
-rw-r--r--net/netfilter/ipset/ip_set_hash_ip.c4
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipport.c7
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportip.c7
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c7
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c2
-rw-r--r--net/netfilter/ipvs/Kconfig7
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c15
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c404
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c8
-rw-r--r--net/netfilter/ipvs/ip_vs_dh.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_lblcr.c6
-rw-r--r--net/netfilter/ipvs/ip_vs_nfct.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_pe_sip.c18
-rw-r--r--net/netfilter/ipvs/ip_vs_proto.c6
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_ah_esp.c9
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_sctp.c42
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_tcp.c40
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_udp.c41
-rw-r--r--net/netfilter/ipvs/ip_vs_sched.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_sh.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c81
-rw-r--r--net/netfilter/nf_conntrack_acct.c4
-rw-r--r--net/netfilter/nf_conntrack_core.c42
-rw-r--r--net/netfilter/nf_conntrack_ecache.c4
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c3
-rw-r--r--net/netfilter/nf_conntrack_helper.c4
-rw-r--r--net/netfilter/nf_conntrack_netlink.c120
-rw-r--r--net/netfilter/nf_conntrack_proto_dccp.c8
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c2
-rw-r--r--net/netfilter/nf_conntrack_standalone.c5
-rw-r--r--net/netfilter/nf_conntrack_timestamp.c4
-rw-r--r--net/netfilter/nf_queue.c152
-rw-r--r--net/netfilter/nfnetlink.c2
-rw-r--r--net/netfilter/nfnetlink_cttimeout.c3
-rw-r--r--net/netfilter/nfnetlink_log.c16
-rw-r--r--net/netfilter/nfnetlink_queue_core.c14
-rw-r--r--net/netfilter/x_tables.c28
-rw-r--r--net/netfilter/xt_CT.c62
-rw-r--r--net/netfilter/xt_HMARK.c8
-rw-r--r--net/netfilter/xt_hashlimit.c54
-rw-r--r--net/netfilter/xt_ipvs.c4
-rw-r--r--net/netfilter/xt_recent.c43
-rw-r--r--net/netlink/af_netlink.c7
-rw-r--r--net/nfc/Kconfig4
-rw-r--r--net/nfc/core.c33
-rw-r--r--net/nfc/hci/command.c28
-rw-r--r--net/nfc/hci/core.c90
-rw-r--r--net/nfc/hci/llc.c2
-rw-r--r--net/nfc/hci/llc_shdlc.c7
-rw-r--r--net/nfc/llcp/Kconfig4
-rw-r--r--net/nfc/llcp/commands.c148
-rw-r--r--net/nfc/llcp/llcp.c250
-rw-r--r--net/nfc/llcp/llcp.h13
-rw-r--r--net/nfc/llcp/sock.c38
-rw-r--r--net/nfc/nci/Kconfig4
-rw-r--r--net/nfc/nci/core.c29
-rw-r--r--net/nfc/netlink.c157
-rw-r--r--net/nfc/nfc.h6
-rw-r--r--net/nfc/rawsock.c1
-rw-r--r--net/openvswitch/actions.c97
-rw-r--r--net/openvswitch/datapath.c27
-rw-r--r--net/openvswitch/flow.c42
-rw-r--r--net/openvswitch/flow.h8
-rw-r--r--net/openvswitch/vport-netdev.c16
-rw-r--r--net/openvswitch/vport-netdev.h3
-rw-r--r--net/openvswitch/vport.c5
-rw-r--r--net/packet/af_packet.c50
-rw-r--r--net/packet/internal.h1
-rw-r--r--net/phonet/pn_netlink.c6
-rw-r--r--net/rds/ib.h2
-rw-r--r--net/rds/ib_cm.c11
-rw-r--r--net/rds/ib_recv.c33
-rw-r--r--net/rfkill/rfkill-gpio.c2
-rw-r--r--net/rfkill/rfkill-regulator.c6
-rw-r--r--net/sched/Kconfig2
-rw-r--r--net/sched/act_api.c3
-rw-r--r--net/sched/cls_api.c2
-rw-r--r--net/sched/cls_cgroup.c52
-rw-r--r--net/sched/sch_api.c20
-rw-r--r--net/sched/sch_cbq.c3
-rw-r--r--net/sched/sch_generic.c11
-rw-r--r--net/sched/sch_htb.c139
-rw-r--r--net/sched/sch_mq.c4
-rw-r--r--net/sched/sch_mqprio.c4
-rw-r--r--net/sched/sch_qfq.c837
-rw-r--r--net/sctp/Kconfig60
-rw-r--r--net/sctp/associola.c16
-rw-r--r--net/sctp/chunk.c20
-rw-r--r--net/sctp/endpointola.c5
-rw-r--r--net/sctp/inqueue.c2
-rw-r--r--net/sctp/ipv6.c2
-rw-r--r--net/sctp/output.c14
-rw-r--r--net/sctp/outqueue.c24
-rw-r--r--net/sctp/probe.c3
-rw-r--r--net/sctp/proc.c29
-rw-r--r--net/sctp/protocol.c15
-rw-r--r--net/sctp/sm_make_chunk.c24
-rw-r--r--net/sctp/sm_sideeffect.c55
-rw-r--r--net/sctp/sm_statefuns.c24
-rw-r--r--net/sctp/socket.c94
-rw-r--r--net/sctp/sysctl.c63
-rw-r--r--net/sctp/transport.c22
-rw-r--r--net/sctp/tsnmap.c8
-rw-r--r--net/sctp/ulpqueue.c3
-rw-r--r--net/socket.c8
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c17
-rw-r--r--net/sunrpc/backchannel_rqst.c11
-rw-r--r--net/sunrpc/bc_svc.c2
-rw-r--r--net/sunrpc/cache.c4
-rw-r--r--net/sunrpc/clnt.c45
-rw-r--r--net/sunrpc/rpc_pipe.c9
-rw-r--r--net/sunrpc/rpcb_clnt.c6
-rw-r--r--net/sunrpc/sched.c101
-rw-r--r--net/sunrpc/svc.c20
-rw-r--r--net/sunrpc/svc_xprt.c11
-rw-r--r--net/sunrpc/svcsock.c104
-rw-r--r--net/sunrpc/xdr.c5
-rw-r--r--net/sunrpc/xprt.c12
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c10
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c4
-rw-r--r--net/sunrpc/xprtsock.c19
-rw-r--r--net/sysctl_net.c15
-rw-r--r--net/tipc/Kconfig13
-rw-r--r--net/tipc/bcast.c27
-rw-r--r--net/tipc/bearer.c110
-rw-r--r--net/tipc/bearer.h24
-rw-r--r--net/tipc/core.c5
-rw-r--r--net/tipc/discover.c2
-rw-r--r--net/tipc/handler.c1
-rw-r--r--net/tipc/link.c232
-rw-r--r--net/tipc/link.h4
-rw-r--r--net/tipc/name_distr.c2
-rw-r--r--net/tipc/node.c15
-rw-r--r--net/tipc/node.h6
-rw-r--r--net/tipc/port.c32
-rw-r--r--net/tipc/port.h6
-rw-r--r--net/tipc/socket.c411
-rw-r--r--net/tipc/subscr.c2
-rw-r--r--net/unix/diag.c3
-rw-r--r--net/unix/sysctl_net_unix.c4
-rw-r--r--net/wireless/Kconfig5
-rw-r--r--net/wireless/Makefile4
-rw-r--r--net/wireless/ap.c4
-rw-r--r--net/wireless/chan.c313
-rw-r--r--net/wireless/core.c29
-rw-r--r--net/wireless/core.h32
-rw-r--r--net/wireless/ethtool.c15
-rw-r--r--net/wireless/ibss.c36
-rw-r--r--net/wireless/mesh.c59
-rw-r--r--net/wireless/mlme.c116
-rw-r--r--net/wireless/nl80211.c868
-rw-r--r--net/wireless/nl80211.h8
-rw-r--r--net/wireless/rdev-ops.h878
-rw-r--r--net/wireless/reg.c19
-rw-r--r--net/wireless/reg.h2
-rw-r--r--net/wireless/scan.c615
-rw-r--r--net/wireless/sme.c25
-rw-r--r--net/wireless/sysfs.c9
-rw-r--r--net/wireless/trace.c7
-rw-r--r--net/wireless/trace.h2324
-rw-r--r--net/wireless/util.c211
-rw-r--r--net/wireless/wext-compat.c76
-rw-r--r--net/wireless/wext-sme.c19
-rw-r--r--net/xfrm/xfrm_ipcomp.c8
-rw-r--r--net/xfrm/xfrm_policy.c2
-rw-r--r--net/xfrm/xfrm_replay.c17
-rw-r--r--net/xfrm/xfrm_sysctl.c4
-rw-r--r--net/xfrm/xfrm_user.c2
422 files changed, 23564 insertions, 7872 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index ee070722a3a..a292e8050ef 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -242,6 +242,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
* hope the underlying device can handle it.
*/
new_dev->mtu = real_dev->mtu;
+ new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT);
vlan_dev_priv(new_dev)->vlan_id = vlan_id;
vlan_dev_priv(new_dev)->real_dev = real_dev;
@@ -294,7 +295,7 @@ static void vlan_transfer_features(struct net_device *dev,
else
vlandev->hard_header_len = dev->hard_header_len + VLAN_HLEN;
-#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+#if IS_ENABLED(CONFIG_FCOE)
vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
#endif
@@ -529,7 +530,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
switch (args.cmd) {
case SET_VLAN_INGRESS_PRIORITY_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
vlan_dev_set_ingress_priority(dev,
args.u.skb_priority,
@@ -539,7 +540,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
case SET_VLAN_EGRESS_PRIORITY_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
err = vlan_dev_set_egress_priority(dev,
args.u.skb_priority,
@@ -548,7 +549,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
case SET_VLAN_FLAG_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
err = vlan_dev_change_flags(dev,
args.vlan_qos ? args.u.flag : 0,
@@ -557,7 +558,7 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
case SET_VLAN_NAME_TYPE_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
if ((args.u.name_type >= 0) &&
(args.u.name_type < VLAN_NAME_TYPE_HIGHEST)) {
@@ -573,14 +574,14 @@ static int vlan_ioctl_handler(struct net *net, void __user *arg)
case ADD_VLAN_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
err = register_vlan_device(dev, args.u.VID);
break;
case DEL_VLAN_CMD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
unregister_vlan_dev(dev, NULL);
err = 0;
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 402442402af..4a6d31a082b 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -409,7 +409,7 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa)
return err;
}
-#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+#if IS_ENABLED(CONFIG_FCOE)
static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid,
struct scatterlist *sgl, unsigned int sgc)
{
@@ -531,6 +531,10 @@ static const struct header_ops vlan_header_ops = {
.parse = eth_header_parse,
};
+static struct device_type vlan_type = {
+ .name = "vlan",
+};
+
static const struct net_device_ops vlan_netdev_ops;
static int vlan_dev_init(struct net_device *dev)
@@ -564,7 +568,7 @@ static int vlan_dev_init(struct net_device *dev)
if (is_zero_ether_addr(dev->broadcast))
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
-#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+#if IS_ENABLED(CONFIG_FCOE)
dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid;
#endif
@@ -579,6 +583,8 @@ static int vlan_dev_init(struct net_device *dev)
dev->netdev_ops = &vlan_netdev_ops;
+ SET_NETDEV_DEVTYPE(dev, &vlan_type);
+
if (is_vlan_dev(real_dev))
subclass = 1;
@@ -741,7 +747,7 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_do_ioctl = vlan_dev_ioctl,
.ndo_neigh_setup = vlan_dev_neigh_setup,
.ndo_get_stats64 = vlan_dev_get_stats64,
-#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+#if IS_ENABLED(CONFIG_FCOE)
.ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
.ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
.ndo_fcoe_enable = vlan_dev_fcoe_enable,
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 35b8911b1c8..fd05c81cb34 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -39,6 +39,7 @@
#include <linux/inet.h>
#include <linux/idr.h>
#include <linux/file.h>
+#include <linux/highmem.h>
#include <linux/slab.h>
#include <net/9p/9p.h>
#include <linux/parser.h>
@@ -325,7 +326,7 @@ static int p9_get_mapped_pages(struct virtio_chan *chan,
int count = nr_pages;
while (nr_pages) {
s = rest_of_page(data);
- pages[index++] = virt_to_page(data);
+ pages[index++] = kmap_to_page(data);
data += s;
nr_pages--;
}
diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c
index f49da5814bc..350bf62b2ae 100644
--- a/net/atm/atm_sysfs.c
+++ b/net/atm/atm_sysfs.c
@@ -14,49 +14,45 @@ static ssize_t show_type(struct device *cdev,
struct device_attribute *attr, char *buf)
{
struct atm_dev *adev = to_atm_dev(cdev);
- return sprintf(buf, "%s\n", adev->type);
+
+ return scnprintf(buf, PAGE_SIZE, "%s\n", adev->type);
}
static ssize_t show_address(struct device *cdev,
struct device_attribute *attr, char *buf)
{
- char *pos = buf;
struct atm_dev *adev = to_atm_dev(cdev);
- int i;
-
- for (i = 0; i < (ESI_LEN - 1); i++)
- pos += sprintf(pos, "%02x:", adev->esi[i]);
- pos += sprintf(pos, "%02x\n", adev->esi[i]);
- return pos - buf;
+ return scnprintf(buf, PAGE_SIZE, "%pM\n", adev->esi);
}
static ssize_t show_atmaddress(struct device *cdev,
struct device_attribute *attr, char *buf)
{
unsigned long flags;
- char *pos = buf;
struct atm_dev *adev = to_atm_dev(cdev);
struct atm_dev_addr *aaddr;
int bin[] = { 1, 2, 10, 6, 1 }, *fmt = bin;
- int i, j;
+ int i, j, count = 0;
spin_lock_irqsave(&adev->lock, flags);
list_for_each_entry(aaddr, &adev->local, entry) {
for (i = 0, j = 0; i < ATM_ESA_LEN; ++i, ++j) {
if (j == *fmt) {
- pos += sprintf(pos, ".");
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, ".");
++fmt;
j = 0;
}
- pos += sprintf(pos, "%02x",
- aaddr->addr.sas_addr.prv[i]);
+ count += scnprintf(buf + count,
+ PAGE_SIZE - count, "%02x",
+ aaddr->addr.sas_addr.prv[i]);
}
- pos += sprintf(pos, "\n");
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
}
spin_unlock_irqrestore(&adev->lock, flags);
- return pos - buf;
+ return count;
}
static ssize_t show_atmindex(struct device *cdev,
@@ -64,25 +60,21 @@ static ssize_t show_atmindex(struct device *cdev,
{
struct atm_dev *adev = to_atm_dev(cdev);
- return sprintf(buf, "%d\n", adev->number);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", adev->number);
}
static ssize_t show_carrier(struct device *cdev,
struct device_attribute *attr, char *buf)
{
- char *pos = buf;
struct atm_dev *adev = to_atm_dev(cdev);
- pos += sprintf(pos, "%d\n",
- adev->signal == ATM_PHY_SIG_LOST ? 0 : 1);
-
- return pos - buf;
+ return scnprintf(buf, PAGE_SIZE, "%d\n",
+ adev->signal == ATM_PHY_SIG_LOST ? 0 : 1);
}
static ssize_t show_link_rate(struct device *cdev,
struct device_attribute *attr, char *buf)
{
- char *pos = buf;
struct atm_dev *adev = to_atm_dev(cdev);
int link_rate;
@@ -100,9 +92,7 @@ static ssize_t show_link_rate(struct device *cdev,
default:
link_rate = adev->link_rate * 8 * 53;
}
- pos += sprintf(pos, "%d\n", link_rate);
-
- return pos - buf;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", link_rate);
}
static DEVICE_ATTR(address, S_IRUGO, show_address, NULL);
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 4819d31533e..403e71fa88f 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -68,12 +68,15 @@ struct br2684_vcc {
/* keep old push, pop functions for chaining */
void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb);
void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb);
+ void (*old_release_cb)(struct atm_vcc *vcc);
+ struct module *old_owner;
enum br2684_encaps encaps;
struct list_head brvccs;
#ifdef CONFIG_ATM_BR2684_IPFILTER
struct br2684_filter filter;
#endif /* CONFIG_ATM_BR2684_IPFILTER */
unsigned int copies_needed, copies_failed;
+ atomic_t qspace;
};
struct br2684_dev {
@@ -181,18 +184,15 @@ static struct notifier_block atm_dev_notifier = {
static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb)
{
struct br2684_vcc *brvcc = BR2684_VCC(vcc);
- struct net_device *net_dev = skb->dev;
- pr_debug("(vcc %p ; net_dev %p )\n", vcc, net_dev);
+ pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device);
brvcc->old_pop(vcc, skb);
- if (!net_dev)
- return;
-
- if (atm_may_send(vcc, 0))
- netif_wake_queue(net_dev);
-
+ /* If the queue space just went up from zero, wake */
+ if (atomic_inc_return(&brvcc->qspace) == 1)
+ netif_wake_queue(brvcc->device);
}
+
/*
* Send a packet out a particular vcc. Not to useful right now, but paves
* the way for multiple vcc's per itf. Returns true if we can send,
@@ -256,16 +256,30 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
ATM_SKB(skb)->atm_options = atmvcc->atm_options;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
- atmvcc->send(atmvcc, skb);
- if (!atm_may_send(atmvcc, 0)) {
+ if (atomic_dec_return(&brvcc->qspace) < 1) {
+ /* No more please! */
netif_stop_queue(brvcc->device);
- /*check for race with br2684_pop*/
- if (atm_may_send(atmvcc, 0))
- netif_start_queue(brvcc->device);
+ /* We might have raced with br2684_pop() */
+ if (unlikely(atomic_read(&brvcc->qspace) > 0))
+ netif_wake_queue(brvcc->device);
}
- return 1;
+ /* If this fails immediately, the skb will be freed and br2684_pop()
+ will wake the queue if appropriate. Just return an error so that
+ the stats are updated correctly */
+ return !atmvcc->send(atmvcc, skb);
+}
+
+static void br2684_release_cb(struct atm_vcc *atmvcc)
+{
+ struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
+
+ if (atomic_read(&brvcc->qspace) > 0)
+ netif_wake_queue(brvcc->device);
+
+ if (brvcc->old_release_cb)
+ brvcc->old_release_cb(atmvcc);
}
static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb,
@@ -279,6 +293,8 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
{
struct br2684_dev *brdev = BRPRIV(dev);
struct br2684_vcc *brvcc;
+ struct atm_vcc *atmvcc;
+ netdev_tx_t ret = NETDEV_TX_OK;
pr_debug("skb_dst(skb)=%p\n", skb_dst(skb));
read_lock(&devs_lock);
@@ -289,9 +305,26 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
dev->stats.tx_carrier_errors++;
/* netif_stop_queue(dev); */
dev_kfree_skb(skb);
- read_unlock(&devs_lock);
- return NETDEV_TX_OK;
+ goto out_devs;
}
+ atmvcc = brvcc->atmvcc;
+
+ bh_lock_sock(sk_atm(atmvcc));
+
+ if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) ||
+ test_bit(ATM_VF_CLOSE, &atmvcc->flags) ||
+ !test_bit(ATM_VF_READY, &atmvcc->flags)) {
+ dev->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ if (sock_owned_by_user(sk_atm(atmvcc))) {
+ netif_stop_queue(brvcc->device);
+ ret = NETDEV_TX_BUSY;
+ goto out;
+ }
+
if (!br2684_xmit_vcc(skb, dev, brvcc)) {
/*
* We should probably use netif_*_queue() here, but that
@@ -303,8 +336,11 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
dev->stats.tx_errors++;
dev->stats.tx_fifo_errors++;
}
+ out:
+ bh_unlock_sock(sk_atm(atmvcc));
+ out_devs:
read_unlock(&devs_lock);
- return NETDEV_TX_OK;
+ return ret;
}
/*
@@ -377,9 +413,10 @@ static void br2684_close_vcc(struct br2684_vcc *brvcc)
list_del(&brvcc->brvccs);
write_unlock_irq(&devs_lock);
brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */
+ brvcc->atmvcc->release_cb = brvcc->old_release_cb;
brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */
+ module_put(brvcc->old_owner);
kfree(brvcc);
- module_put(THIS_MODULE);
}
/* when AAL5 PDU comes in: */
@@ -504,6 +541,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL);
if (!brvcc)
return -ENOMEM;
+ /*
+ * Allow two packets in the ATM queue. One actually being sent, and one
+ * for the ATM 'TX done' handler to send. It shouldn't take long to get
+ * the next one from the netdev queue, when we need it. More than that
+ * would be bufferbloat.
+ */
+ atomic_set(&brvcc->qspace, 2);
write_lock_irq(&devs_lock);
net_dev = br2684_find_dev(&be.ifspec);
if (net_dev == NULL) {
@@ -546,9 +590,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
brvcc->encaps = (enum br2684_encaps)be.encaps;
brvcc->old_push = atmvcc->push;
brvcc->old_pop = atmvcc->pop;
+ brvcc->old_release_cb = atmvcc->release_cb;
+ brvcc->old_owner = atmvcc->owner;
barrier();
atmvcc->push = br2684_push;
atmvcc->pop = br2684_pop;
+ atmvcc->release_cb = br2684_release_cb;
+ atmvcc->owner = THIS_MODULE;
/* initialize netdev carrier state */
if (atmvcc->dev->signal == ATM_PHY_SIG_LOST)
@@ -687,10 +735,13 @@ static int br2684_ioctl(struct socket *sock, unsigned int cmd,
return -ENOIOCTLCMD;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (cmd == ATM_SETBACKEND)
+ if (cmd == ATM_SETBACKEND) {
+ if (sock->state != SS_CONNECTED)
+ return -EINVAL;
return br2684_regvcc(atmvcc, argp);
- else
+ } else {
return br2684_create(argp);
+ }
#ifdef CONFIG_ATM_BR2684_IPFILTER
case BR2684_SETFILT:
if (atmvcc->push != br2684_push)
diff --git a/net/atm/common.c b/net/atm/common.c
index 0c0ad930a63..806fc0a4005 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -126,10 +126,19 @@ static void vcc_write_space(struct sock *sk)
rcu_read_unlock();
}
+static void vcc_release_cb(struct sock *sk)
+{
+ struct atm_vcc *vcc = atm_sk(sk);
+
+ if (vcc->release_cb)
+ vcc->release_cb(vcc);
+}
+
static struct proto vcc_proto = {
.name = "VCC",
.owner = THIS_MODULE,
.obj_size = sizeof(struct atm_vcc),
+ .release_cb = vcc_release_cb,
};
int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
@@ -156,7 +165,9 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family)
atomic_set(&sk->sk_rmem_alloc, 0);
vcc->push = NULL;
vcc->pop = NULL;
+ vcc->owner = NULL;
vcc->push_oam = NULL;
+ vcc->release_cb = NULL;
vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
vcc->atm_options = vcc->aal_options = 0;
sk->sk_destruct = vcc_sock_destruct;
@@ -175,6 +186,7 @@ static void vcc_destroy_socket(struct sock *sk)
vcc->dev->ops->close(vcc);
if (vcc->push)
vcc->push(vcc, NULL); /* atmarpd has no push */
+ module_put(vcc->owner);
while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
atm_return(vcc, skb->truesize);
diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c
index 226dca98944..8c93267ce96 100644
--- a/net/atm/pppoatm.c
+++ b/net/atm/pppoatm.c
@@ -60,6 +60,8 @@ struct pppoatm_vcc {
struct atm_vcc *atmvcc; /* VCC descriptor */
void (*old_push)(struct atm_vcc *, struct sk_buff *);
void (*old_pop)(struct atm_vcc *, struct sk_buff *);
+ void (*old_release_cb)(struct atm_vcc *);
+ struct module *old_owner;
/* keep old push/pop for detaching */
enum pppoatm_encaps encaps;
atomic_t inflight;
@@ -107,6 +109,24 @@ static void pppoatm_wakeup_sender(unsigned long arg)
ppp_output_wakeup((struct ppp_channel *) arg);
}
+static void pppoatm_release_cb(struct atm_vcc *atmvcc)
+{
+ struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
+
+ /*
+ * As in pppoatm_pop(), it's safe to clear the BLOCKED bit here because
+ * the wakeup *can't* race with pppoatm_send(). They both hold the PPP
+ * channel's ->downl lock. And the potential race with *setting* it,
+ * which leads to the double-check dance in pppoatm_may_send(), doesn't
+ * exist here. In the sock_owned_by_user() case in pppoatm_send(), we
+ * set the BLOCKED bit while the socket is still locked. We know that
+ * ->release_cb() can't be called until that's done.
+ */
+ if (test_and_clear_bit(BLOCKED, &pvcc->blocked))
+ tasklet_schedule(&pvcc->wakeup_tasklet);
+ if (pvcc->old_release_cb)
+ pvcc->old_release_cb(atmvcc);
+}
/*
* This gets called every time the ATM card has finished sending our
* skb. The ->old_pop will take care up normal atm flow control,
@@ -151,12 +171,11 @@ static void pppoatm_unassign_vcc(struct atm_vcc *atmvcc)
pvcc = atmvcc_to_pvcc(atmvcc);
atmvcc->push = pvcc->old_push;
atmvcc->pop = pvcc->old_pop;
+ atmvcc->release_cb = pvcc->old_release_cb;
tasklet_kill(&pvcc->wakeup_tasklet);
ppp_unregister_channel(&pvcc->chan);
atmvcc->user_back = NULL;
kfree(pvcc);
- /* Gee, I hope we have the big kernel lock here... */
- module_put(THIS_MODULE);
}
/* Called when an AAL5 PDU comes in */
@@ -165,9 +184,13 @@ static void pppoatm_push(struct atm_vcc *atmvcc, struct sk_buff *skb)
struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc);
pr_debug("\n");
if (skb == NULL) { /* VCC was closed */
+ struct module *module;
+
pr_debug("removing ATMPPP VCC %p\n", pvcc);
+ module = pvcc->old_owner;
pppoatm_unassign_vcc(atmvcc);
atmvcc->push(atmvcc, NULL); /* Pass along bad news */
+ module_put(module);
return;
}
atm_return(atmvcc, skb->truesize);
@@ -211,7 +234,7 @@ error:
ppp_input_error(&pvcc->chan, 0);
}
-static inline int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
+static int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
{
/*
* It's not clear that we need to bother with using atm_may_send()
@@ -269,10 +292,33 @@ static inline int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size)
static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
{
struct pppoatm_vcc *pvcc = chan_to_pvcc(chan);
+ struct atm_vcc *vcc;
+ int ret;
+
ATM_SKB(skb)->vcc = pvcc->atmvcc;
pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc);
if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT))
(void) skb_pull(skb, 1);
+
+ vcc = ATM_SKB(skb)->vcc;
+ bh_lock_sock(sk_atm(vcc));
+ if (sock_owned_by_user(sk_atm(vcc))) {
+ /*
+ * Needs to happen (and be flushed, hence test_and_) before we unlock
+ * the socket. It needs to be seen by the time our ->release_cb gets
+ * called.
+ */
+ test_and_set_bit(BLOCKED, &pvcc->blocked);
+ goto nospace;
+ }
+ if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
+ test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ !test_bit(ATM_VF_READY, &vcc->flags)) {
+ bh_unlock_sock(sk_atm(vcc));
+ kfree_skb(skb);
+ return DROP_PACKET;
+ }
+
switch (pvcc->encaps) { /* LLC encapsulation needed */
case e_llc:
if (skb_headroom(skb) < LLC_LEN) {
@@ -285,8 +331,10 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
}
consume_skb(skb);
skb = n;
- if (skb == NULL)
+ if (skb == NULL) {
+ bh_unlock_sock(sk_atm(vcc));
return DROP_PACKET;
+ }
} else if (!pppoatm_may_send(pvcc, skb->truesize))
goto nospace;
memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN);
@@ -296,6 +344,7 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
goto nospace;
break;
case e_autodetect:
+ bh_unlock_sock(sk_atm(vcc));
pr_debug("Trying to send without setting encaps!\n");
kfree_skb(skb);
return 1;
@@ -305,9 +354,12 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options;
pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n",
skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev);
- return ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb)
+ ret = ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb)
? DROP_PACKET : 1;
+ bh_unlock_sock(sk_atm(vcc));
+ return ret;
nospace:
+ bh_unlock_sock(sk_atm(vcc));
/*
* We don't have space to send this SKB now, but we might have
* already applied SC_COMP_PROT compression, so may need to undo
@@ -362,6 +414,8 @@ static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
atomic_set(&pvcc->inflight, NONE_INFLIGHT);
pvcc->old_push = atmvcc->push;
pvcc->old_pop = atmvcc->pop;
+ pvcc->old_owner = atmvcc->owner;
+ pvcc->old_release_cb = atmvcc->release_cb;
pvcc->encaps = (enum pppoatm_encaps) be.encaps;
pvcc->chan.private = pvcc;
pvcc->chan.ops = &pppoatm_ops;
@@ -377,7 +431,9 @@ static int pppoatm_assign_vcc(struct atm_vcc *atmvcc, void __user *arg)
atmvcc->user_back = pvcc;
atmvcc->push = pppoatm_push;
atmvcc->pop = pppoatm_pop;
+ atmvcc->release_cb = pppoatm_release_cb;
__module_get(THIS_MODULE);
+ atmvcc->owner = THIS_MODULE;
/* re-process everything received between connection setup and
backend setup */
@@ -406,6 +462,8 @@ static int pppoatm_ioctl(struct socket *sock, unsigned int cmd,
return -ENOIOCTLCMD;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
+ if (sock->state != SS_CONNECTED)
+ return -EINVAL;
return pppoatm_assign_vcc(atmvcc, argp);
}
case PPPIOCGCHAN:
diff --git a/net/batman-adv/Kconfig b/net/batman-adv/Kconfig
index 53f5244e28f..8d8afb134b3 100644
--- a/net/batman-adv/Kconfig
+++ b/net/batman-adv/Kconfig
@@ -6,6 +6,7 @@ config BATMAN_ADV
tristate "B.A.T.M.A.N. Advanced Meshing Protocol"
depends on NET
select CRC16
+ select LIBCRC32C
default n
help
B.A.T.M.A.N. (better approach to mobile ad-hoc networking) is
@@ -25,6 +26,16 @@ config BATMAN_ADV_BLA
more than one mesh node in the same LAN, you can safely remove
this feature and save some space.
+config BATMAN_ADV_DAT
+ bool "Distributed ARP Table"
+ depends on BATMAN_ADV && INET
+ default n
+ help
+ This option enables DAT (Distributed ARP Table), a DHT based
+ mechanism that increases ARP reliability on sparse wireless
+ mesh networks. If you think that your network does not need
+ this option you can safely remove it and save some space.
+
config BATMAN_ADV_DEBUG
bool "B.A.T.M.A.N. debugging"
depends on BATMAN_ADV
diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile
index 8676d2b1d57..e45e3b4e32e 100644
--- a/net/batman-adv/Makefile
+++ b/net/batman-adv/Makefile
@@ -23,6 +23,7 @@ batman-adv-y += bat_iv_ogm.o
batman-adv-y += bitarray.o
batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o
batman-adv-y += debugfs.o
+batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o
batman-adv-y += gateway_client.o
batman-adv-y += gateway_common.o
batman-adv-y += hard-interface.o
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index b02b75dae3a..7d02ebd11a7 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -57,20 +57,22 @@ out:
static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface)
{
struct batadv_ogm_packet *batadv_ogm_packet;
+ unsigned char *ogm_buff;
uint32_t random_seqno;
int res = -ENOMEM;
/* randomize initial seqno to avoid collision */
get_random_bytes(&random_seqno, sizeof(random_seqno));
- atomic_set(&hard_iface->seqno, random_seqno);
+ atomic_set(&hard_iface->bat_iv.ogm_seqno, random_seqno);
- hard_iface->packet_len = BATADV_OGM_HLEN;
- hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC);
-
- if (!hard_iface->packet_buff)
+ hard_iface->bat_iv.ogm_buff_len = BATADV_OGM_HLEN;
+ ogm_buff = kmalloc(hard_iface->bat_iv.ogm_buff_len, GFP_ATOMIC);
+ if (!ogm_buff)
goto out;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff;
+ hard_iface->bat_iv.ogm_buff = ogm_buff;
+
+ batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
batadv_ogm_packet->header.packet_type = BATADV_IV_OGM;
batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION;
batadv_ogm_packet->header.ttl = 2;
@@ -87,15 +89,16 @@ out:
static void batadv_iv_ogm_iface_disable(struct batadv_hard_iface *hard_iface)
{
- kfree(hard_iface->packet_buff);
- hard_iface->packet_buff = NULL;
+ kfree(hard_iface->bat_iv.ogm_buff);
+ hard_iface->bat_iv.ogm_buff = NULL;
}
static void batadv_iv_ogm_iface_update_mac(struct batadv_hard_iface *hard_iface)
{
struct batadv_ogm_packet *batadv_ogm_packet;
+ unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff;
+ batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
memcpy(batadv_ogm_packet->orig,
hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(batadv_ogm_packet->prev_sender,
@@ -106,8 +109,9 @@ static void
batadv_iv_ogm_primary_iface_set(struct batadv_hard_iface *hard_iface)
{
struct batadv_ogm_packet *batadv_ogm_packet;
+ unsigned char *ogm_buff = hard_iface->bat_iv.ogm_buff;
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff;
+ batadv_ogm_packet = (struct batadv_ogm_packet *)ogm_buff;
batadv_ogm_packet->flags = BATADV_PRIMARIES_FIRST_HOP;
batadv_ogm_packet->header.ttl = BATADV_TTL;
}
@@ -119,7 +123,7 @@ batadv_iv_ogm_emit_send_time(const struct batadv_priv *bat_priv)
unsigned int msecs;
msecs = atomic_read(&bat_priv->orig_interval) - BATADV_JITTER;
- msecs += (random32() % 2 * BATADV_JITTER);
+ msecs += random32() % (2 * BATADV_JITTER);
return jiffies + msecs_to_jiffies(msecs);
}
@@ -407,9 +411,11 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
if ((atomic_read(&bat_priv->aggregated_ogms)) &&
(packet_len < BATADV_MAX_AGGREGATION_BYTES))
- skb_size = BATADV_MAX_AGGREGATION_BYTES + ETH_HLEN;
+ skb_size = BATADV_MAX_AGGREGATION_BYTES;
else
- skb_size = packet_len + ETH_HLEN;
+ skb_size = packet_len;
+
+ skb_size += ETH_HLEN + NET_IP_ALIGN;
forw_packet_aggr->skb = dev_alloc_skb(skb_size);
if (!forw_packet_aggr->skb) {
@@ -418,7 +424,7 @@ static void batadv_iv_ogm_aggregate_new(const unsigned char *packet_buff,
kfree(forw_packet_aggr);
goto out;
}
- skb_reserve(forw_packet_aggr->skb, ETH_HLEN);
+ skb_reserve(forw_packet_aggr->skb, ETH_HLEN + NET_IP_ALIGN);
INIT_HLIST_NODE(&forw_packet_aggr->list);
@@ -590,8 +596,10 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node,
static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
+ unsigned char **ogm_buff = &hard_iface->bat_iv.ogm_buff;
struct batadv_ogm_packet *batadv_ogm_packet;
struct batadv_hard_iface *primary_if;
+ int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len;
int vis_server, tt_num_changes = 0;
uint32_t seqno;
uint8_t bandwidth;
@@ -600,17 +608,16 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if)
- tt_num_changes = batadv_tt_append_diff(bat_priv,
- &hard_iface->packet_buff,
- &hard_iface->packet_len,
+ tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff,
+ ogm_buff_len,
BATADV_OGM_HLEN);
- batadv_ogm_packet = (struct batadv_ogm_packet *)hard_iface->packet_buff;
+ batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff);
/* change sequence number to network order */
- seqno = (uint32_t)atomic_read(&hard_iface->seqno);
+ seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->seqno = htonl(seqno);
- atomic_inc(&hard_iface->seqno);
+ atomic_inc(&hard_iface->bat_iv.ogm_seqno);
batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn);
batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc);
@@ -631,8 +638,8 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
}
batadv_slide_own_bcast_window(hard_iface);
- batadv_iv_ogm_queue_add(bat_priv, hard_iface->packet_buff,
- hard_iface->packet_len, hard_iface, 1,
+ batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff,
+ hard_iface->bat_iv.ogm_buff_len, hard_iface, 1,
batadv_iv_ogm_emit_send_time(bat_priv));
if (primary_if)
@@ -1015,7 +1022,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
return;
/* could be changed by schedule_own_packet() */
- if_incoming_seqno = atomic_read(&if_incoming->seqno);
+ if_incoming_seqno = atomic_read(&if_incoming->bat_iv.ogm_seqno);
if (batadv_ogm_packet->flags & BATADV_DIRECTLINK)
has_directlink_flag = 1;
diff --git a/net/batman-adv/bitarray.c b/net/batman-adv/bitarray.c
index aea174cdbfb..5453b17d8df 100644
--- a/net/batman-adv/bitarray.c
+++ b/net/batman-adv/bitarray.c
@@ -79,20 +79,17 @@ int batadv_bit_get_packet(void *priv, unsigned long *seq_bits,
* or the old packet got delayed somewhere in the network. The
* packet should be dropped without calling this function if the
* seqno window is protected.
+ *
+ * seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE
+ * or
+ * seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE
*/
- if (seq_num_diff <= -BATADV_TQ_LOCAL_WINDOW_SIZE ||
- seq_num_diff >= BATADV_EXPECTED_SEQNO_RANGE) {
+ batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+ "Other host probably restarted!\n");
- batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
- "Other host probably restarted!\n");
-
- bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
- if (set_mark)
- batadv_set_bit(seq_bits, 0);
-
- return 1;
- }
+ bitmap_zero(seq_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
+ if (set_mark)
+ batadv_set_bit(seq_bits, 0);
- /* never reached */
- return 0;
+ return 1;
}
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index fd8d5afec0d..5aebe9327d6 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -40,15 +40,11 @@ static void batadv_bla_send_announce(struct batadv_priv *bat_priv,
/* return the index of the claim */
static inline uint32_t batadv_choose_claim(const void *data, uint32_t size)
{
- const unsigned char *key = data;
+ struct batadv_claim *claim = (struct batadv_claim *)data;
uint32_t hash = 0;
- size_t i;
- for (i = 0; i < ETH_ALEN + sizeof(short); i++) {
- hash += key[i];
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
+ hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));
+ hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid));
hash += (hash << 3);
hash ^= (hash >> 11);
@@ -61,15 +57,11 @@ static inline uint32_t batadv_choose_claim(const void *data, uint32_t size)
static inline uint32_t batadv_choose_backbone_gw(const void *data,
uint32_t size)
{
- const unsigned char *key = data;
+ struct batadv_claim *claim = (struct batadv_claim *)data;
uint32_t hash = 0;
- size_t i;
- for (i = 0; i < ETH_ALEN + sizeof(short); i++) {
- hash += key[i];
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
+ hash = batadv_hash_bytes(hash, &claim->addr, sizeof(claim->addr));
+ hash = batadv_hash_bytes(hash, &claim->vid, sizeof(claim->vid));
hash += (hash << 3);
hash ^= (hash >> 11);
@@ -85,8 +77,15 @@ static int batadv_compare_backbone_gw(const struct hlist_node *node,
{
const void *data1 = container_of(node, struct batadv_backbone_gw,
hash_entry);
+ const struct batadv_backbone_gw *gw1 = data1, *gw2 = data2;
+
+ if (!batadv_compare_eth(gw1->orig, gw2->orig))
+ return 0;
+
+ if (gw1->vid != gw2->vid)
+ return 0;
- return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0);
+ return 1;
}
/* compares address and vid of two claims */
@@ -95,8 +94,15 @@ static int batadv_compare_claim(const struct hlist_node *node,
{
const void *data1 = container_of(node, struct batadv_claim,
hash_entry);
+ const struct batadv_claim *cl1 = data1, *cl2 = data2;
+
+ if (!batadv_compare_eth(cl1->addr, cl2->addr))
+ return 0;
+
+ if (cl1->vid != cl2->vid)
+ return 0;
- return (memcmp(data1, data2, ETH_ALEN + sizeof(short)) == 0 ? 1 : 0);
+ return 1;
}
/* free a backbone gw */
@@ -362,7 +368,7 @@ out:
*/
static struct batadv_backbone_gw *
batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
- short vid)
+ short vid, bool own_backbone)
{
struct batadv_backbone_gw *entry;
struct batadv_orig_node *orig_node;
@@ -386,6 +392,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
entry->crc = BATADV_BLA_CRC_INIT;
entry->bat_priv = bat_priv;
atomic_set(&entry->request_sent, 0);
+ atomic_set(&entry->wait_periods, 0);
memcpy(entry->orig, orig, ETH_ALEN);
/* one for the hash, one for returning */
@@ -409,6 +416,16 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig,
"became a backbone gateway");
batadv_orig_node_free_ref(orig_node);
}
+
+ if (own_backbone) {
+ batadv_bla_send_announce(bat_priv, entry);
+
+ /* this will be decreased in the worker thread */
+ atomic_inc(&entry->request_sent);
+ atomic_set(&entry->wait_periods, BATADV_BLA_WAIT_PERIODS);
+ atomic_inc(&bat_priv->bla.num_requests);
+ }
+
return entry;
}
@@ -424,7 +441,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv,
backbone_gw = batadv_bla_get_backbone_gw(bat_priv,
primary_if->net_dev->dev_addr,
- vid);
+ vid, true);
if (unlikely(!backbone_gw))
return;
@@ -632,7 +649,8 @@ static int batadv_handle_announce(struct batadv_priv *bat_priv,
if (memcmp(an_addr, batadv_announce_mac, 4) != 0)
return 0;
- backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid);
+ backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid,
+ false);
if (unlikely(!backbone_gw))
return 1;
@@ -730,7 +748,8 @@ static int batadv_handle_claim(struct batadv_priv *bat_priv,
/* register the gateway if not yet available, and add the claim. */
- backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid);
+ backbone_gw = batadv_bla_get_backbone_gw(bat_priv, backbone_addr, vid,
+ false);
if (unlikely(!backbone_gw))
return 1;
@@ -1140,6 +1159,24 @@ static void batadv_bla_periodic_work(struct work_struct *work)
backbone_gw->lasttime = jiffies;
batadv_bla_send_announce(bat_priv, backbone_gw);
+
+ /* request_sent is only set after creation to avoid
+ * problems when we are not yet known as backbone gw
+ * in the backbone.
+ *
+ * We can reset this now after we waited some periods
+ * to give bridge forward delays and bla group forming
+ * some grace time.
+ */
+
+ if (atomic_read(&backbone_gw->request_sent) == 0)
+ continue;
+
+ if (!atomic_dec_and_test(&backbone_gw->wait_periods))
+ continue;
+
+ atomic_dec(&backbone_gw->bat_priv->bla.num_requests);
+ atomic_set(&backbone_gw->request_sent, 0);
}
rcu_read_unlock();
}
@@ -1212,8 +1249,7 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
/**
* batadv_bla_check_bcast_duplist
* @bat_priv: the bat priv with all the soft interface information
- * @bcast_packet: encapsulated broadcast frame plus batman header
- * @bcast_packet_len: length of encapsulated broadcast frame plus batman header
+ * @skb: contains the bcast_packet to be checked
*
* check if it is on our broadcast list. Another gateway might
* have sent the same packet because it is connected to the same backbone,
@@ -1225,20 +1261,17 @@ int batadv_bla_init(struct batadv_priv *bat_priv)
* the same host however as this might be intended.
*/
int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
- struct batadv_bcast_packet *bcast_packet,
- int bcast_packet_len)
+ struct sk_buff *skb)
{
- int i, length, curr, ret = 0;
- uint8_t *content;
- uint16_t crc;
+ int i, curr, ret = 0;
+ __be32 crc;
+ struct batadv_bcast_packet *bcast_packet;
struct batadv_bcast_duplist_entry *entry;
- length = bcast_packet_len - sizeof(*bcast_packet);
- content = (uint8_t *)bcast_packet;
- content += sizeof(*bcast_packet);
+ bcast_packet = (struct batadv_bcast_packet *)skb->data;
/* calculate the crc ... */
- crc = crc16(0, content, length);
+ crc = batadv_skb_crc32(skb, (u8 *)(bcast_packet + 1));
spin_lock_bh(&bat_priv->bla.bcast_duplist_lock);
@@ -1585,23 +1618,11 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
struct hlist_head *head;
uint32_t i;
bool is_own;
- int ret = 0;
uint8_t *primary_addr;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
-
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
primary_addr = primary_if->net_dev->dev_addr;
seq_printf(seq,
@@ -1628,7 +1649,7 @@ int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
@@ -1643,23 +1664,11 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
int secs, msecs;
uint32_t i;
bool is_own;
- int ret = 0;
uint8_t *primary_addr;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
-
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
primary_addr = primary_if->net_dev->dev_addr;
seq_printf(seq,
@@ -1693,5 +1702,5 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h
index 789cb73bde6..196d9a0254b 100644
--- a/net/batman-adv/bridge_loop_avoidance.h
+++ b/net/batman-adv/bridge_loop_avoidance.h
@@ -31,8 +31,7 @@ int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq,
void *offset);
int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig);
int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
- struct batadv_bcast_packet *bcast_packet,
- int hdr_size);
+ struct sk_buff *skb);
void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
struct batadv_hard_iface *primary_if,
struct batadv_hard_iface *oldif);
@@ -81,8 +80,7 @@ static inline int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv,
static inline int
batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv,
- struct batadv_bcast_packet *bcast_packet,
- int hdr_size)
+ struct sk_buff *skb)
{
return 0;
}
diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c
index 391d4fb2026..6f58ddd53bf 100644
--- a/net/batman-adv/debugfs.c
+++ b/net/batman-adv/debugfs.c
@@ -31,6 +31,7 @@
#include "vis.h"
#include "icmp_socket.h"
#include "bridge_loop_avoidance.h"
+#include "distributed-arp-table.h"
static struct dentry *batadv_debugfs;
@@ -99,15 +100,17 @@ int batadv_debug_log(struct batadv_priv *bat_priv, const char *fmt, ...)
static int batadv_log_open(struct inode *inode, struct file *file)
{
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
nonseekable_open(inode, file);
file->private_data = inode->i_private;
- batadv_inc_module_count();
return 0;
}
static int batadv_log_release(struct inode *inode, struct file *file)
{
- batadv_dec_module_count();
+ module_put(THIS_MODULE);
return 0;
}
@@ -278,6 +281,19 @@ static int batadv_bla_backbone_table_open(struct inode *inode,
#endif
+#ifdef CONFIG_BATMAN_ADV_DAT
+/**
+ * batadv_dat_cache_open - Prepare file handler for reads from dat_chache
+ * @inode: inode which was opened
+ * @file: file handle to be initialized
+ */
+static int batadv_dat_cache_open(struct inode *inode, struct file *file)
+{
+ struct net_device *net_dev = (struct net_device *)inode->i_private;
+ return single_open(file, batadv_dat_cache_seq_print_text, net_dev);
+}
+#endif
+
static int batadv_transtable_local_open(struct inode *inode, struct file *file)
{
struct net_device *net_dev = (struct net_device *)inode->i_private;
@@ -307,7 +323,17 @@ struct batadv_debuginfo batadv_debuginfo_##_name = { \
} \
};
+/* the following attributes are general and therefore they will be directly
+ * placed in the BATADV_DEBUGFS_SUBDIR subdirectory of debugfs
+ */
static BATADV_DEBUGINFO(routing_algos, S_IRUGO, batadv_algorithms_open);
+
+static struct batadv_debuginfo *batadv_general_debuginfos[] = {
+ &batadv_debuginfo_routing_algos,
+ NULL,
+};
+
+/* The following attributes are per soft interface */
static BATADV_DEBUGINFO(originators, S_IRUGO, batadv_originators_open);
static BATADV_DEBUGINFO(gateways, S_IRUGO, batadv_gateways_open);
static BATADV_DEBUGINFO(transtable_global, S_IRUGO,
@@ -317,6 +343,9 @@ static BATADV_DEBUGINFO(bla_claim_table, S_IRUGO, batadv_bla_claim_table_open);
static BATADV_DEBUGINFO(bla_backbone_table, S_IRUGO,
batadv_bla_backbone_table_open);
#endif
+#ifdef CONFIG_BATMAN_ADV_DAT
+static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open);
+#endif
static BATADV_DEBUGINFO(transtable_local, S_IRUGO,
batadv_transtable_local_open);
static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open);
@@ -329,6 +358,9 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
&batadv_debuginfo_bla_claim_table,
&batadv_debuginfo_bla_backbone_table,
#endif
+#ifdef CONFIG_BATMAN_ADV_DAT
+ &batadv_debuginfo_dat_cache,
+#endif
&batadv_debuginfo_transtable_local,
&batadv_debuginfo_vis_data,
NULL,
@@ -336,7 +368,7 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = {
void batadv_debugfs_init(void)
{
- struct batadv_debuginfo *bat_debug;
+ struct batadv_debuginfo **bat_debug;
struct dentry *file;
batadv_debugfs = debugfs_create_dir(BATADV_DEBUGFS_SUBDIR, NULL);
@@ -344,17 +376,23 @@ void batadv_debugfs_init(void)
batadv_debugfs = NULL;
if (!batadv_debugfs)
- goto out;
+ goto err;
- bat_debug = &batadv_debuginfo_routing_algos;
- file = debugfs_create_file(bat_debug->attr.name,
- S_IFREG | bat_debug->attr.mode,
- batadv_debugfs, NULL, &bat_debug->fops);
- if (!file)
- pr_err("Can't add debugfs file: %s\n", bat_debug->attr.name);
+ for (bat_debug = batadv_general_debuginfos; *bat_debug; ++bat_debug) {
+ file = debugfs_create_file(((*bat_debug)->attr).name,
+ S_IFREG | ((*bat_debug)->attr).mode,
+ batadv_debugfs, NULL,
+ &(*bat_debug)->fops);
+ if (!file) {
+ pr_err("Can't add general debugfs file: %s\n",
+ ((*bat_debug)->attr).name);
+ goto err;
+ }
+ }
-out:
return;
+err:
+ debugfs_remove_recursive(batadv_debugfs);
}
void batadv_debugfs_destroy(void)
diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c
new file mode 100644
index 00000000000..183f97a86bb
--- /dev/null
+++ b/net/batman-adv/distributed-arp-table.c
@@ -0,0 +1,1083 @@
+/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors:
+ *
+ * Antonio Quartulli
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+
+#include "main.h"
+#include "hash.h"
+#include "distributed-arp-table.h"
+#include "hard-interface.h"
+#include "originator.h"
+#include "send.h"
+#include "types.h"
+#include "translation-table.h"
+#include "unicast.h"
+
+static void batadv_dat_purge(struct work_struct *work);
+
+/**
+ * batadv_dat_start_timer - initialise the DAT periodic worker
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_dat_start_timer(struct batadv_priv *bat_priv)
+{
+ INIT_DELAYED_WORK(&bat_priv->dat.work, batadv_dat_purge);
+ queue_delayed_work(batadv_event_workqueue, &bat_priv->dat.work,
+ msecs_to_jiffies(10000));
+}
+
+/**
+ * batadv_dat_entry_free_ref - decrements the dat_entry refcounter and possibly
+ * free it
+ * @dat_entry: the oentry to free
+ */
+static void batadv_dat_entry_free_ref(struct batadv_dat_entry *dat_entry)
+{
+ if (atomic_dec_and_test(&dat_entry->refcount))
+ kfree_rcu(dat_entry, rcu);
+}
+
+/**
+ * batadv_dat_to_purge - checks whether a dat_entry has to be purged or not
+ * @dat_entry: the entry to check
+ *
+ * Returns true if the entry has to be purged now, false otherwise
+ */
+static bool batadv_dat_to_purge(struct batadv_dat_entry *dat_entry)
+{
+ return batadv_has_timed_out(dat_entry->last_update,
+ BATADV_DAT_ENTRY_TIMEOUT);
+}
+
+/**
+ * __batadv_dat_purge - delete entries from the DAT local storage
+ * @bat_priv: the bat priv with all the soft interface information
+ * @to_purge: function in charge to decide whether an entry has to be purged or
+ * not. This function takes the dat_entry as argument and has to
+ * returns a boolean value: true is the entry has to be deleted,
+ * false otherwise
+ *
+ * Loops over each entry in the DAT local storage and delete it if and only if
+ * the to_purge function passed as argument returns true
+ */
+static void __batadv_dat_purge(struct batadv_priv *bat_priv,
+ bool (*to_purge)(struct batadv_dat_entry *))
+{
+ spinlock_t *list_lock; /* protects write access to the hash lists */
+ struct batadv_dat_entry *dat_entry;
+ struct hlist_node *node, *node_tmp;
+ struct hlist_head *head;
+ uint32_t i;
+
+ if (!bat_priv->dat.hash)
+ return;
+
+ for (i = 0; i < bat_priv->dat.hash->size; i++) {
+ head = &bat_priv->dat.hash->table[i];
+ list_lock = &bat_priv->dat.hash->list_locks[i];
+
+ spin_lock_bh(list_lock);
+ hlist_for_each_entry_safe(dat_entry, node, node_tmp, head,
+ hash_entry) {
+ /* if an helper function has been passed as parameter,
+ * ask it if the entry has to be purged or not
+ */
+ if (to_purge && !to_purge(dat_entry))
+ continue;
+
+ hlist_del_rcu(node);
+ batadv_dat_entry_free_ref(dat_entry);
+ }
+ spin_unlock_bh(list_lock);
+ }
+}
+
+/**
+ * batadv_dat_purge - periodic task that deletes old entries from the local DAT
+ * hash table
+ * @work: kernel work struct
+ */
+static void batadv_dat_purge(struct work_struct *work)
+{
+ struct delayed_work *delayed_work;
+ struct batadv_priv_dat *priv_dat;
+ struct batadv_priv *bat_priv;
+
+ delayed_work = container_of(work, struct delayed_work, work);
+ priv_dat = container_of(delayed_work, struct batadv_priv_dat, work);
+ bat_priv = container_of(priv_dat, struct batadv_priv, dat);
+
+ __batadv_dat_purge(bat_priv, batadv_dat_to_purge);
+ batadv_dat_start_timer(bat_priv);
+}
+
+/**
+ * batadv_compare_dat - comparing function used in the local DAT hash table
+ * @node: node in the local table
+ * @data2: second object to compare the node to
+ *
+ * Returns 1 if the two entry are the same, 0 otherwise
+ */
+static int batadv_compare_dat(const struct hlist_node *node, const void *data2)
+{
+ const void *data1 = container_of(node, struct batadv_dat_entry,
+ hash_entry);
+
+ return (memcmp(data1, data2, sizeof(__be32)) == 0 ? 1 : 0);
+}
+
+/**
+ * batadv_arp_hw_src - extract the hw_src field from an ARP packet
+ * @skb: ARP packet
+ * @hdr_size: size of the possible header before the ARP packet
+ *
+ * Returns the value of the hw_src field in the ARP packet
+ */
+static uint8_t *batadv_arp_hw_src(struct sk_buff *skb, int hdr_size)
+{
+ uint8_t *addr;
+
+ addr = (uint8_t *)(skb->data + hdr_size);
+ addr += ETH_HLEN + sizeof(struct arphdr);
+
+ return addr;
+}
+
+/**
+ * batadv_arp_ip_src - extract the ip_src field from an ARP packet
+ * @skb: ARP packet
+ * @hdr_size: size of the possible header before the ARP packet
+ *
+ * Returns the value of the ip_src field in the ARP packet
+ */
+static __be32 batadv_arp_ip_src(struct sk_buff *skb, int hdr_size)
+{
+ return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN);
+}
+
+/**
+ * batadv_arp_hw_dst - extract the hw_dst field from an ARP packet
+ * @skb: ARP packet
+ * @hdr_size: size of the possible header before the ARP packet
+ *
+ * Returns the value of the hw_dst field in the ARP packet
+ */
+static uint8_t *batadv_arp_hw_dst(struct sk_buff *skb, int hdr_size)
+{
+ return batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN + 4;
+}
+
+/**
+ * batadv_arp_ip_dst - extract the ip_dst field from an ARP packet
+ * @skb: ARP packet
+ * @hdr_size: size of the possible header before the ARP packet
+ *
+ * Returns the value of the ip_dst field in the ARP packet
+ */
+static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size)
+{
+ return *(__be32 *)(batadv_arp_hw_src(skb, hdr_size) + ETH_ALEN * 2 + 4);
+}
+
+/**
+ * batadv_hash_dat - compute the hash value for an IP address
+ * @data: data to hash
+ * @size: size of the hash table
+ *
+ * Returns the selected index in the hash table for the given data
+ */
+static uint32_t batadv_hash_dat(const void *data, uint32_t size)
+{
+ const unsigned char *key = data;
+ uint32_t hash = 0;
+ size_t i;
+
+ for (i = 0; i < 4; i++) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+
+ hash += (hash << 3);
+ hash ^= (hash >> 11);
+ hash += (hash << 15);
+
+ return hash % size;
+}
+
+/**
+ * batadv_dat_entry_hash_find - looks for a given dat_entry in the local hash
+ * table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ip: search key
+ *
+ * Returns the dat_entry if found, NULL otherwise
+ */
+static struct batadv_dat_entry *
+batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip)
+{
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL;
+ struct batadv_hashtable *hash = bat_priv->dat.hash;
+ uint32_t index;
+
+ if (!hash)
+ return NULL;
+
+ index = batadv_hash_dat(&ip, hash->size);
+ head = &hash->table[index];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) {
+ if (dat_entry->ip != ip)
+ continue;
+
+ if (!atomic_inc_not_zero(&dat_entry->refcount))
+ continue;
+
+ dat_entry_tmp = dat_entry;
+ break;
+ }
+ rcu_read_unlock();
+
+ return dat_entry_tmp;
+}
+
+/**
+ * batadv_dat_entry_add - add a new dat entry or update it if already exists
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ip: ipv4 to add/edit
+ * @mac_addr: mac address to assign to the given ipv4
+ */
+static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip,
+ uint8_t *mac_addr)
+{
+ struct batadv_dat_entry *dat_entry;
+ int hash_added;
+
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip);
+ /* if this entry is already known, just update it */
+ if (dat_entry) {
+ if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr))
+ memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
+ dat_entry->last_update = jiffies;
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "Entry updated: %pI4 %pM\n", &dat_entry->ip,
+ dat_entry->mac_addr);
+ goto out;
+ }
+
+ dat_entry = kmalloc(sizeof(*dat_entry), GFP_ATOMIC);
+ if (!dat_entry)
+ goto out;
+
+ dat_entry->ip = ip;
+ memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN);
+ dat_entry->last_update = jiffies;
+ atomic_set(&dat_entry->refcount, 2);
+
+ hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat,
+ batadv_hash_dat, &dat_entry->ip,
+ &dat_entry->hash_entry);
+
+ if (unlikely(hash_added != 0)) {
+ /* remove the reference for the hash */
+ batadv_dat_entry_free_ref(dat_entry);
+ goto out;
+ }
+
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n",
+ &dat_entry->ip, dat_entry->mac_addr);
+
+out:
+ if (dat_entry)
+ batadv_dat_entry_free_ref(dat_entry);
+}
+
+#ifdef CONFIG_BATMAN_ADV_DEBUG
+
+/**
+ * batadv_dbg_arp - print a debug message containing all the ARP packet details
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: ARP packet
+ * @type: ARP type
+ * @hdr_size: size of the possible header before the ARP packet
+ * @msg: message to print together with the debugging information
+ */
+static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ uint16_t type, int hdr_size, char *msg)
+{
+ struct batadv_unicast_4addr_packet *unicast_4addr_packet;
+ struct batadv_bcast_packet *bcast_pkt;
+ uint8_t *orig_addr;
+ __be32 ip_src, ip_dst;
+
+ if (msg)
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "%s\n", msg);
+
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "ARP MSG = [src: %pM-%pI4 dst: %pM-%pI4]\n",
+ batadv_arp_hw_src(skb, hdr_size), &ip_src,
+ batadv_arp_hw_dst(skb, hdr_size), &ip_dst);
+
+ if (hdr_size == 0)
+ return;
+
+ /* if the ARP packet is encapsulated in a batman packet, let's print
+ * some debug messages
+ */
+ unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
+
+ switch (unicast_4addr_packet->u.header.packet_type) {
+ case BATADV_UNICAST:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "* encapsulated within a UNICAST packet\n");
+ break;
+ case BATADV_UNICAST_4ADDR:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "* encapsulated within a UNICAST_4ADDR packet (src: %pM)\n",
+ unicast_4addr_packet->src);
+ switch (unicast_4addr_packet->subtype) {
+ case BATADV_P_DAT_DHT_PUT:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_PUT\n");
+ break;
+ case BATADV_P_DAT_DHT_GET:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DAT_DHT_GET\n");
+ break;
+ case BATADV_P_DAT_CACHE_REPLY:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "* type: DAT_CACHE_REPLY\n");
+ break;
+ case BATADV_P_DATA:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: DATA\n");
+ break;
+ default:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "* type: Unknown (%u)!\n",
+ unicast_4addr_packet->u.header.packet_type);
+ }
+ break;
+ case BATADV_BCAST:
+ bcast_pkt = (struct batadv_bcast_packet *)unicast_4addr_packet;
+ orig_addr = bcast_pkt->orig;
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "* encapsulated within a BCAST packet (src: %pM)\n",
+ orig_addr);
+ break;
+ default:
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "* encapsulated within an unknown packet type (0x%x)\n",
+ unicast_4addr_packet->u.header.packet_type);
+ }
+}
+
+#else
+
+static void batadv_dbg_arp(struct batadv_priv *bat_priv, struct sk_buff *skb,
+ uint16_t type, int hdr_size, char *msg)
+{
+}
+
+#endif /* CONFIG_BATMAN_ADV_DEBUG */
+
+/**
+ * batadv_is_orig_node_eligible - check whether a node can be a DHT candidate
+ * @res: the array with the already selected candidates
+ * @select: number of already selected candidates
+ * @tmp_max: address of the currently evaluated node
+ * @max: current round max address
+ * @last_max: address of the last selected candidate
+ * @candidate: orig_node under evaluation
+ * @max_orig_node: last selected candidate
+ *
+ * Returns true if the node has been elected as next candidate or false othrwise
+ */
+static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res,
+ int select, batadv_dat_addr_t tmp_max,
+ batadv_dat_addr_t max,
+ batadv_dat_addr_t last_max,
+ struct batadv_orig_node *candidate,
+ struct batadv_orig_node *max_orig_node)
+{
+ bool ret = false;
+ int j;
+
+ /* Check if this node has already been selected... */
+ for (j = 0; j < select; j++)
+ if (res[j].orig_node == candidate)
+ break;
+ /* ..and possibly skip it */
+ if (j < select)
+ goto out;
+ /* sanity check: has it already been selected? This should not happen */
+ if (tmp_max > last_max)
+ goto out;
+ /* check if during this iteration an originator with a closer dht
+ * address has already been found
+ */
+ if (tmp_max < max)
+ goto out;
+ /* this is an hash collision with the temporary selected node. Choose
+ * the one with the lowest address
+ */
+ if ((tmp_max == max) &&
+ (batadv_compare_eth(candidate->orig, max_orig_node->orig) > 0))
+ goto out;
+
+ ret = true;
+out:
+ return ret;
+}
+
+/**
+ * batadv_choose_next_candidate - select the next DHT candidate
+ * @bat_priv: the bat priv with all the soft interface information
+ * @cands: candidates array
+ * @select: number of candidates already present in the array
+ * @ip_key: key to look up in the DHT
+ * @last_max: pointer where the address of the selected candidate will be saved
+ */
+static void batadv_choose_next_candidate(struct batadv_priv *bat_priv,
+ struct batadv_dat_candidate *cands,
+ int select, batadv_dat_addr_t ip_key,
+ batadv_dat_addr_t *last_max)
+{
+ batadv_dat_addr_t max = 0, tmp_max = 0;
+ struct batadv_orig_node *orig_node, *max_orig_node = NULL;
+ struct batadv_hashtable *hash = bat_priv->orig_hash;
+ struct hlist_node *node;
+ struct hlist_head *head;
+ int i;
+
+ /* if no node is eligible as candidate, leave the candidate type as
+ * NOT_FOUND
+ */
+ cands[select].type = BATADV_DAT_CANDIDATE_NOT_FOUND;
+
+ /* iterate over the originator list and find the node with closest
+ * dat_address which has not been selected yet
+ */
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) {
+ /* the dht space is a ring and addresses are unsigned */
+ tmp_max = BATADV_DAT_ADDR_MAX - orig_node->dat_addr +
+ ip_key;
+
+ if (!batadv_is_orig_node_eligible(cands, select,
+ tmp_max, max,
+ *last_max, orig_node,
+ max_orig_node))
+ continue;
+
+ if (!atomic_inc_not_zero(&orig_node->refcount))
+ continue;
+
+ max = tmp_max;
+ if (max_orig_node)
+ batadv_orig_node_free_ref(max_orig_node);
+ max_orig_node = orig_node;
+ }
+ rcu_read_unlock();
+ }
+ if (max_orig_node) {
+ cands[select].type = BATADV_DAT_CANDIDATE_ORIG;
+ cands[select].orig_node = max_orig_node;
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "dat_select_candidates() %d: selected %pM addr=%u dist=%u\n",
+ select, max_orig_node->orig, max_orig_node->dat_addr,
+ max);
+ }
+ *last_max = max;
+}
+
+/**
+ * batadv_dat_select_candidates - selects the nodes which the DHT message has to
+ * be sent to
+ * @bat_priv: the bat priv with all the soft interface information
+ * @ip_dst: ipv4 to look up in the DHT
+ *
+ * An originator O is selected if and only if its DHT_ID value is one of three
+ * closest values (from the LEFT, with wrap around if needed) then the hash
+ * value of the key. ip_dst is the key.
+ *
+ * Returns the candidate array of size BATADV_DAT_CANDIDATE_NUM
+ */
+static struct batadv_dat_candidate *
+batadv_dat_select_candidates(struct batadv_priv *bat_priv, __be32 ip_dst)
+{
+ int select;
+ batadv_dat_addr_t last_max = BATADV_DAT_ADDR_MAX, ip_key;
+ struct batadv_dat_candidate *res;
+
+ if (!bat_priv->orig_hash)
+ return NULL;
+
+ res = kmalloc(BATADV_DAT_CANDIDATES_NUM * sizeof(*res), GFP_ATOMIC);
+ if (!res)
+ return NULL;
+
+ ip_key = (batadv_dat_addr_t)batadv_hash_dat(&ip_dst,
+ BATADV_DAT_ADDR_MAX);
+
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "dat_select_candidates(): IP=%pI4 hash(IP)=%u\n", &ip_dst,
+ ip_key);
+
+ for (select = 0; select < BATADV_DAT_CANDIDATES_NUM; select++)
+ batadv_choose_next_candidate(bat_priv, res, select, ip_key,
+ &last_max);
+
+ return res;
+}
+
+/**
+ * batadv_dat_send_data - send a payload to the selected candidates
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @ip: the DHT key
+ * @packet_subtype: unicast4addr packet subtype to use
+ *
+ * In this function the skb is copied by means of pskb_copy() and is sent as
+ * unicast packet to each of the selected candidates
+ *
+ * Returns true if the packet is sent to at least one candidate, false otherwise
+ */
+static bool batadv_dat_send_data(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, __be32 ip,
+ int packet_subtype)
+{
+ int i;
+ bool ret = false;
+ int send_status;
+ struct batadv_neigh_node *neigh_node = NULL;
+ struct sk_buff *tmp_skb;
+ struct batadv_dat_candidate *cand;
+
+ cand = batadv_dat_select_candidates(bat_priv, ip);
+ if (!cand)
+ goto out;
+
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "DHT_SEND for %pI4\n", &ip);
+
+ for (i = 0; i < BATADV_DAT_CANDIDATES_NUM; i++) {
+ if (cand[i].type == BATADV_DAT_CANDIDATE_NOT_FOUND)
+ continue;
+
+ neigh_node = batadv_orig_node_get_router(cand[i].orig_node);
+ if (!neigh_node)
+ goto free_orig;
+
+ tmp_skb = pskb_copy(skb, GFP_ATOMIC);
+ if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb,
+ cand[i].orig_node,
+ packet_subtype)) {
+ kfree_skb(tmp_skb);
+ goto free_neigh;
+ }
+
+ send_status = batadv_send_skb_packet(tmp_skb,
+ neigh_node->if_incoming,
+ neigh_node->addr);
+ if (send_status == NET_XMIT_SUCCESS) {
+ /* count the sent packet */
+ switch (packet_subtype) {
+ case BATADV_P_DAT_DHT_GET:
+ batadv_inc_counter(bat_priv,
+ BATADV_CNT_DAT_GET_TX);
+ break;
+ case BATADV_P_DAT_DHT_PUT:
+ batadv_inc_counter(bat_priv,
+ BATADV_CNT_DAT_PUT_TX);
+ break;
+ }
+
+ /* packet sent to a candidate: return true */
+ ret = true;
+ }
+free_neigh:
+ batadv_neigh_node_free_ref(neigh_node);
+free_orig:
+ batadv_orig_node_free_ref(cand[i].orig_node);
+ }
+
+out:
+ kfree(cand);
+ return ret;
+}
+
+/**
+ * batadv_dat_hash_free - free the local DAT hash table
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+static void batadv_dat_hash_free(struct batadv_priv *bat_priv)
+{
+ if (!bat_priv->dat.hash)
+ return;
+
+ __batadv_dat_purge(bat_priv, NULL);
+
+ batadv_hash_destroy(bat_priv->dat.hash);
+
+ bat_priv->dat.hash = NULL;
+}
+
+/**
+ * batadv_dat_init - initialise the DAT internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+int batadv_dat_init(struct batadv_priv *bat_priv)
+{
+ if (bat_priv->dat.hash)
+ return 0;
+
+ bat_priv->dat.hash = batadv_hash_new(1024);
+
+ if (!bat_priv->dat.hash)
+ return -ENOMEM;
+
+ batadv_dat_start_timer(bat_priv);
+
+ return 0;
+}
+
+/**
+ * batadv_dat_free - free the DAT internals
+ * @bat_priv: the bat priv with all the soft interface information
+ */
+void batadv_dat_free(struct batadv_priv *bat_priv)
+{
+ cancel_delayed_work_sync(&bat_priv->dat.work);
+
+ batadv_dat_hash_free(bat_priv);
+}
+
+/**
+ * batadv_dat_cache_seq_print_text - print the local DAT hash table
+ * @seq: seq file to print on
+ * @offset: not used
+ */
+int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hashtable *hash = bat_priv->dat.hash;
+ struct batadv_dat_entry *dat_entry;
+ struct batadv_hard_iface *primary_if;
+ struct hlist_node *node;
+ struct hlist_head *head;
+ unsigned long last_seen_jiffies;
+ int last_seen_msecs, last_seen_secs, last_seen_mins;
+ uint32_t i;
+
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
+ goto out;
+
+ seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name);
+ seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC",
+ "last-seen");
+
+ for (i = 0; i < hash->size; i++) {
+ head = &hash->table[i];
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(dat_entry, node, head, hash_entry) {
+ last_seen_jiffies = jiffies - dat_entry->last_update;
+ last_seen_msecs = jiffies_to_msecs(last_seen_jiffies);
+ last_seen_mins = last_seen_msecs / 60000;
+ last_seen_msecs = last_seen_msecs % 60000;
+ last_seen_secs = last_seen_msecs / 1000;
+
+ seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n",
+ &dat_entry->ip, dat_entry->mac_addr,
+ last_seen_mins, last_seen_secs);
+ }
+ rcu_read_unlock();
+ }
+
+out:
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ return 0;
+}
+
+/**
+ * batadv_arp_get_type - parse an ARP packet and gets the type
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to analyse
+ * @hdr_size: size of the possible header before the ARP packet in the skb
+ *
+ * Returns the ARP type if the skb contains a valid ARP packet, 0 otherwise
+ */
+static uint16_t batadv_arp_get_type(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size)
+{
+ struct arphdr *arphdr;
+ struct ethhdr *ethhdr;
+ __be32 ip_src, ip_dst;
+ uint8_t *hw_src, *hw_dst;
+ uint16_t type = 0;
+
+ /* pull the ethernet header */
+ if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN)))
+ goto out;
+
+ ethhdr = (struct ethhdr *)(skb->data + hdr_size);
+
+ if (ethhdr->h_proto != htons(ETH_P_ARP))
+ goto out;
+
+ /* pull the ARP payload */
+ if (unlikely(!pskb_may_pull(skb, hdr_size + ETH_HLEN +
+ arp_hdr_len(skb->dev))))
+ goto out;
+
+ arphdr = (struct arphdr *)(skb->data + hdr_size + ETH_HLEN);
+
+ /* Check whether the ARP packet carries a valid
+ * IP information
+ */
+ if (arphdr->ar_hrd != htons(ARPHRD_ETHER))
+ goto out;
+
+ if (arphdr->ar_pro != htons(ETH_P_IP))
+ goto out;
+
+ if (arphdr->ar_hln != ETH_ALEN)
+ goto out;
+
+ if (arphdr->ar_pln != 4)
+ goto out;
+
+ /* Check for bad reply/request. If the ARP message is not sane, DAT
+ * will simply ignore it
+ */
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+ if (ipv4_is_loopback(ip_src) || ipv4_is_multicast(ip_src) ||
+ ipv4_is_loopback(ip_dst) || ipv4_is_multicast(ip_dst) ||
+ ipv4_is_zeronet(ip_src) || ipv4_is_lbcast(ip_src) ||
+ ipv4_is_zeronet(ip_dst) || ipv4_is_lbcast(ip_dst))
+ goto out;
+
+ hw_src = batadv_arp_hw_src(skb, hdr_size);
+ if (is_zero_ether_addr(hw_src) || is_multicast_ether_addr(hw_src))
+ goto out;
+
+ /* we don't care about the destination MAC address in ARP requests */
+ if (arphdr->ar_op != htons(ARPOP_REQUEST)) {
+ hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+ if (is_zero_ether_addr(hw_dst) ||
+ is_multicast_ether_addr(hw_dst))
+ goto out;
+ }
+
+ type = ntohs(arphdr->ar_op);
+out:
+ return type;
+}
+
+/**
+ * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to
+ * answer using DAT
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to check
+ *
+ * Returns true if the message has been sent to the dht candidates, false
+ * otherwise. In case of true the message has to be enqueued to permit the
+ * fallback
+ */
+bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb)
+{
+ uint16_t type = 0;
+ __be32 ip_dst, ip_src;
+ uint8_t *hw_src;
+ bool ret = false;
+ struct batadv_dat_entry *dat_entry = NULL;
+ struct sk_buff *skb_new;
+ struct batadv_hard_iface *primary_if = NULL;
+
+ if (!atomic_read(&bat_priv->distributed_arp_table))
+ goto out;
+
+ type = batadv_arp_get_type(bat_priv, skb, 0);
+ /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast
+ * message to the selected DHT candidates
+ */
+ if (type != ARPOP_REQUEST)
+ goto out;
+
+ batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST");
+
+ ip_src = batadv_arp_ip_src(skb, 0);
+ hw_src = batadv_arp_hw_src(skb, 0);
+ ip_dst = batadv_arp_ip_dst(skb, 0);
+
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ if (dat_entry) {
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
+ primary_if->soft_iface, ip_dst, hw_src,
+ dat_entry->mac_addr, hw_src);
+ if (!skb_new)
+ goto out;
+
+ skb_reset_mac_header(skb_new);
+ skb_new->protocol = eth_type_trans(skb_new,
+ primary_if->soft_iface);
+ bat_priv->stats.rx_packets++;
+ bat_priv->stats.rx_bytes += skb->len + ETH_HLEN;
+ primary_if->soft_iface->last_rx = jiffies;
+
+ netif_rx(skb_new);
+ batadv_dbg(BATADV_DBG_DAT, bat_priv, "ARP request replied locally\n");
+ ret = true;
+ } else {
+ /* Send the request on the DHT */
+ ret = batadv_dat_send_data(bat_priv, skb, ip_dst,
+ BATADV_P_DAT_DHT_GET);
+ }
+out:
+ if (dat_entry)
+ batadv_dat_entry_free_ref(dat_entry);
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ return ret;
+}
+
+/**
+ * batadv_dat_snoop_incoming_arp_request - snoop the ARP request and try to
+ * answer using the local DAT storage
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to check
+ * @hdr_size: size of the encapsulation header
+ *
+ * Returns true if the request has been answered, false otherwise
+ */
+bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size)
+{
+ uint16_t type;
+ __be32 ip_src, ip_dst;
+ uint8_t *hw_src;
+ struct sk_buff *skb_new;
+ struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_dat_entry *dat_entry = NULL;
+ bool ret = false;
+ int err;
+
+ if (!atomic_read(&bat_priv->distributed_arp_table))
+ goto out;
+
+ type = batadv_arp_get_type(bat_priv, skb, hdr_size);
+ if (type != ARPOP_REQUEST)
+ goto out;
+
+ hw_src = batadv_arp_hw_src(skb, hdr_size);
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+
+ batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+ "Parsing incoming ARP REQUEST");
+
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ if (!dat_entry)
+ goto out;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
+ primary_if->soft_iface, ip_dst, hw_src,
+ dat_entry->mac_addr, hw_src);
+
+ if (!skb_new)
+ goto out;
+
+ /* to preserve backwards compatibility, here the node has to answer
+ * using the same packet type it received for the request. This is due
+ * to that if a node is not using the 4addr packet format it may not
+ * support it.
+ */
+ if (hdr_size == sizeof(struct batadv_unicast_4addr_packet))
+ err = batadv_unicast_4addr_send_skb(bat_priv, skb_new,
+ BATADV_P_DAT_CACHE_REPLY);
+ else
+ err = batadv_unicast_send_skb(bat_priv, skb_new);
+
+ if (!err) {
+ batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX);
+ ret = true;
+ }
+out:
+ if (dat_entry)
+ batadv_dat_entry_free_ref(dat_entry);
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ if (ret)
+ kfree_skb(skb);
+ return ret;
+}
+
+/**
+ * batadv_dat_snoop_outgoing_arp_reply - snoop the ARP reply and fill the DHT
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to check
+ */
+void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb)
+{
+ uint16_t type;
+ __be32 ip_src, ip_dst;
+ uint8_t *hw_src, *hw_dst;
+
+ if (!atomic_read(&bat_priv->distributed_arp_table))
+ return;
+
+ type = batadv_arp_get_type(bat_priv, skb, 0);
+ if (type != ARPOP_REPLY)
+ return;
+
+ batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY");
+
+ hw_src = batadv_arp_hw_src(skb, 0);
+ ip_src = batadv_arp_ip_src(skb, 0);
+ hw_dst = batadv_arp_hw_dst(skb, 0);
+ ip_dst = batadv_arp_ip_dst(skb, 0);
+
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+ batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+
+ /* Send the ARP reply to the candidates for both the IP addresses that
+ * the node got within the ARP reply
+ */
+ batadv_dat_send_data(bat_priv, skb, ip_src, BATADV_P_DAT_DHT_PUT);
+ batadv_dat_send_data(bat_priv, skb, ip_dst, BATADV_P_DAT_DHT_PUT);
+}
+/**
+ * batadv_dat_snoop_incoming_arp_reply - snoop the ARP reply and fill the local
+ * DAT storage only
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: packet to check
+ * @hdr_size: siaze of the encapsulation header
+ */
+bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size)
+{
+ uint16_t type;
+ __be32 ip_src, ip_dst;
+ uint8_t *hw_src, *hw_dst;
+ bool ret = false;
+
+ if (!atomic_read(&bat_priv->distributed_arp_table))
+ goto out;
+
+ type = batadv_arp_get_type(bat_priv, skb, hdr_size);
+ if (type != ARPOP_REPLY)
+ goto out;
+
+ batadv_dbg_arp(bat_priv, skb, type, hdr_size,
+ "Parsing incoming ARP REPLY");
+
+ hw_src = batadv_arp_hw_src(skb, hdr_size);
+ ip_src = batadv_arp_ip_src(skb, hdr_size);
+ hw_dst = batadv_arp_hw_dst(skb, hdr_size);
+ ip_dst = batadv_arp_ip_dst(skb, hdr_size);
+
+ /* Update our internal cache with both the IP addresses the node got
+ * within the ARP reply
+ */
+ batadv_dat_entry_add(bat_priv, ip_src, hw_src);
+ batadv_dat_entry_add(bat_priv, ip_dst, hw_dst);
+
+ /* if this REPLY is directed to a client of mine, let's deliver the
+ * packet to the interface
+ */
+ ret = !batadv_is_my_client(bat_priv, hw_dst);
+out:
+ if (ret)
+ kfree_skb(skb);
+ /* if ret == false -> packet has to be delivered to the interface */
+ return ret;
+}
+
+/**
+ * batadv_dat_drop_broadcast_packet - check if an ARP request has to be dropped
+ * (because the node has already got the reply via DAT) or not
+ * @bat_priv: the bat priv with all the soft interface information
+ * @forw_packet: the broadcast packet
+ *
+ * Returns true if the node can drop the packet, false otherwise
+ */
+bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
+ struct batadv_forw_packet *forw_packet)
+{
+ uint16_t type;
+ __be32 ip_dst;
+ struct batadv_dat_entry *dat_entry = NULL;
+ bool ret = false;
+ const size_t bcast_len = sizeof(struct batadv_bcast_packet);
+
+ if (!atomic_read(&bat_priv->distributed_arp_table))
+ goto out;
+
+ /* If this packet is an ARP_REQUEST and the node already has the
+ * information that it is going to ask, then the packet can be dropped
+ */
+ if (forw_packet->num_packets)
+ goto out;
+
+ type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len);
+ if (type != ARPOP_REQUEST)
+ goto out;
+
+ ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len);
+ dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+ /* check if the node already got this entry */
+ if (!dat_entry) {
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "ARP Request for %pI4: fallback\n", &ip_dst);
+ goto out;
+ }
+
+ batadv_dbg(BATADV_DBG_DAT, bat_priv,
+ "ARP Request for %pI4: fallback prevented\n", &ip_dst);
+ ret = true;
+
+out:
+ if (dat_entry)
+ batadv_dat_entry_free_ref(dat_entry);
+ return ret;
+}
diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h
new file mode 100644
index 00000000000..d060c033e7d
--- /dev/null
+++ b/net/batman-adv/distributed-arp-table.h
@@ -0,0 +1,167 @@
+/* Copyright (C) 2011-2012 B.A.T.M.A.N. contributors:
+ *
+ * Antonio Quartulli
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _NET_BATMAN_ADV_ARP_H_
+#define _NET_BATMAN_ADV_ARP_H_
+
+#ifdef CONFIG_BATMAN_ADV_DAT
+
+#include "types.h"
+#include "originator.h"
+
+#include <linux/if_arp.h>
+
+#define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0)
+
+bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb);
+bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size);
+void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb);
+bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size);
+bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
+ struct batadv_forw_packet *forw_packet);
+
+/**
+ * batadv_dat_init_orig_node_addr - assign a DAT address to the orig_node
+ * @orig_node: the node to assign the DAT address to
+ */
+static inline void
+batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
+{
+ uint32_t addr;
+
+ addr = batadv_choose_orig(orig_node->orig, BATADV_DAT_ADDR_MAX);
+ orig_node->dat_addr = (batadv_dat_addr_t)addr;
+}
+
+/**
+ * batadv_dat_init_own_addr - assign a DAT address to the node itself
+ * @bat_priv: the bat priv with all the soft interface information
+ * @primary_if: a pointer to the primary interface
+ */
+static inline void
+batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *primary_if)
+{
+ uint32_t addr;
+
+ addr = batadv_choose_orig(primary_if->net_dev->dev_addr,
+ BATADV_DAT_ADDR_MAX);
+
+ bat_priv->dat.addr = (batadv_dat_addr_t)addr;
+}
+
+int batadv_dat_init(struct batadv_priv *bat_priv);
+void batadv_dat_free(struct batadv_priv *bat_priv);
+int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset);
+
+/**
+ * batadv_dat_inc_counter - increment the correct DAT packet counter
+ * @bat_priv: the bat priv with all the soft interface information
+ * @subtype: the 4addr subtype of the packet to be counted
+ *
+ * Updates the ethtool statistics for the received packet if it is a DAT subtype
+ */
+static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
+ uint8_t subtype)
+{
+ switch (subtype) {
+ case BATADV_P_DAT_DHT_GET:
+ batadv_inc_counter(bat_priv,
+ BATADV_CNT_DAT_GET_RX);
+ break;
+ case BATADV_P_DAT_DHT_PUT:
+ batadv_inc_counter(bat_priv,
+ BATADV_CNT_DAT_PUT_RX);
+ break;
+ }
+}
+
+#else
+
+static inline bool
+batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb)
+{
+ return false;
+}
+
+static inline bool
+batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size)
+{
+ return false;
+}
+
+static inline bool
+batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb)
+{
+ return false;
+}
+
+static inline bool
+batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int hdr_size)
+{
+ return false;
+}
+
+static inline bool
+batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv,
+ struct batadv_forw_packet *forw_packet)
+{
+ return false;
+}
+
+static inline void
+batadv_dat_init_orig_node_addr(struct batadv_orig_node *orig_node)
+{
+}
+
+static inline void batadv_dat_init_own_addr(struct batadv_priv *bat_priv,
+ struct batadv_hard_iface *iface)
+{
+}
+
+static inline void batadv_arp_change_timeout(struct net_device *soft_iface,
+ const char *name)
+{
+}
+
+static inline int batadv_dat_init(struct batadv_priv *bat_priv)
+{
+ return 0;
+}
+
+static inline void batadv_dat_free(struct batadv_priv *bat_priv)
+{
+}
+
+static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv,
+ uint8_t subtype)
+{
+}
+
+#endif /* CONFIG_BATMAN_ADV_DAT */
+
+#endif /* _NET_BATMAN_ADV_ARP_H_ */
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index 15d67abc10a..dd07c7e3654 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -477,22 +477,11 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
struct batadv_hard_iface *primary_if;
struct batadv_gw_node *gw_node;
struct hlist_node *node;
- int gw_count = 0, ret = 0;
+ int gw_count = 0;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
-
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
- goto out;
- }
seq_printf(seq,
" %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
@@ -519,7 +508,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
static bool batadv_is_type_dhcprequest(struct sk_buff *skb, int header_len)
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index d112fd6750b..f1d37cd8181 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -18,6 +18,7 @@
*/
#include "main.h"
+#include "distributed-arp-table.h"
#include "hard-interface.h"
#include "soft-interface.h"
#include "send.h"
@@ -29,6 +30,7 @@
#include "bridge_loop_avoidance.h"
#include <linux/if_arp.h>
+#include <linux/if_ether.h>
void batadv_hardif_free_rcu(struct rcu_head *rcu)
{
@@ -58,6 +60,45 @@ out:
return hard_iface;
}
+/**
+ * batadv_is_on_batman_iface - check if a device is a batman iface descendant
+ * @net_dev: the device to check
+ *
+ * If the user creates any virtual device on top of a batman-adv interface, it
+ * is important to prevent this new interface to be used to create a new mesh
+ * network (this behaviour would lead to a batman-over-batman configuration).
+ * This function recursively checks all the fathers of the device passed as
+ * argument looking for a batman-adv soft interface.
+ *
+ * Returns true if the device is descendant of a batman-adv mesh interface (or
+ * if it is a batman-adv interface itself), false otherwise
+ */
+static bool batadv_is_on_batman_iface(const struct net_device *net_dev)
+{
+ struct net_device *parent_dev;
+ bool ret;
+
+ /* check if this is a batman-adv mesh interface */
+ if (batadv_softif_is_valid(net_dev))
+ return true;
+
+ /* no more parents..stop recursion */
+ if (net_dev->iflink == net_dev->ifindex)
+ return false;
+
+ /* recurse over the parent device */
+ parent_dev = dev_get_by_index(&init_net, net_dev->iflink);
+ /* if we got a NULL parent_dev there is something broken.. */
+ if (WARN(!parent_dev, "Cannot find parent device"))
+ return false;
+
+ ret = batadv_is_on_batman_iface(parent_dev);
+
+ if (parent_dev)
+ dev_put(parent_dev);
+ return ret;
+}
+
static int batadv_is_valid_iface(const struct net_device *net_dev)
{
if (net_dev->flags & IFF_LOOPBACK)
@@ -70,7 +111,7 @@ static int batadv_is_valid_iface(const struct net_device *net_dev)
return 0;
/* no batman over batman */
- if (batadv_softif_is_valid(net_dev))
+ if (batadv_is_on_batman_iface(net_dev))
return 0;
return 1;
@@ -109,6 +150,8 @@ static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv,
if (!primary_if)
goto out;
+ batadv_dat_init_own_addr(bat_priv, primary_if);
+
skb = bat_priv->vis.my_info->skb_packet;
vis_packet = (struct batadv_vis_packet *)skb->data;
memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN);
@@ -269,7 +312,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
{
struct batadv_priv *bat_priv;
struct net_device *soft_iface;
- __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN);
+ __be16 ethertype = __constant_htons(ETH_P_BATMAN);
int ret;
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
@@ -450,8 +493,8 @@ batadv_hardif_add_interface(struct net_device *net_dev)
/* This can't be called via a bat_priv callback because
* we have no bat_priv yet.
*/
- atomic_set(&hard_iface->seqno, 1);
- hard_iface->packet_buff = NULL;
+ atomic_set(&hard_iface->bat_iv.ogm_seqno, 1);
+ hard_iface->bat_iv.ogm_buff = NULL;
return hard_iface;
diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h
index 977de9c75fc..e05333905af 100644
--- a/net/batman-adv/hash.h
+++ b/net/batman-adv/hash.h
@@ -82,6 +82,28 @@ static inline void batadv_hash_delete(struct batadv_hashtable *hash,
}
/**
+ * batadv_hash_bytes - hash some bytes and add them to the previous hash
+ * @hash: previous hash value
+ * @data: data to be hashed
+ * @size: number of bytes to be hashed
+ *
+ * Returns the new hash value.
+ */
+static inline uint32_t batadv_hash_bytes(uint32_t hash, void *data,
+ uint32_t size)
+{
+ const unsigned char *key = data;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ hash += key[i];
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ return hash;
+}
+
+/**
* batadv_hash_add - adds data to the hashtable
* @hash: storage hash table
* @compare: callback to determine if 2 hash elements are identical
diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c
index bde3cf74750..87ca8095b01 100644
--- a/net/batman-adv/icmp_socket.c
+++ b/net/batman-adv/icmp_socket.c
@@ -42,12 +42,16 @@ static int batadv_socket_open(struct inode *inode, struct file *file)
unsigned int i;
struct batadv_socket_client *socket_client;
+ if (!try_module_get(THIS_MODULE))
+ return -EBUSY;
+
nonseekable_open(inode, file);
socket_client = kmalloc(sizeof(*socket_client), GFP_KERNEL);
-
- if (!socket_client)
+ if (!socket_client) {
+ module_put(THIS_MODULE);
return -ENOMEM;
+ }
for (i = 0; i < ARRAY_SIZE(batadv_socket_client_hash); i++) {
if (!batadv_socket_client_hash[i]) {
@@ -59,6 +63,7 @@ static int batadv_socket_open(struct inode *inode, struct file *file)
if (i == ARRAY_SIZE(batadv_socket_client_hash)) {
pr_err("Error - can't add another packet client: maximum number of clients reached\n");
kfree(socket_client);
+ module_put(THIS_MODULE);
return -EXFULL;
}
@@ -71,7 +76,6 @@ static int batadv_socket_open(struct inode *inode, struct file *file)
file->private_data = socket_client;
- batadv_inc_module_count();
return 0;
}
@@ -96,7 +100,7 @@ static int batadv_socket_release(struct inode *inode, struct file *file)
spin_unlock_bh(&socket_client->lock);
kfree(socket_client);
- batadv_dec_module_count();
+ module_put(THIS_MODULE);
return 0;
}
@@ -173,13 +177,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff,
if (len >= sizeof(struct batadv_icmp_packet_rr))
packet_len = sizeof(struct batadv_icmp_packet_rr);
- skb = dev_alloc_skb(packet_len + ETH_HLEN);
+ skb = dev_alloc_skb(packet_len + ETH_HLEN + NET_IP_ALIGN);
if (!skb) {
len = -ENOMEM;
goto out;
}
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len);
if (copy_from_user(icmp_packet, buff, packet_len)) {
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index b4aa470bc4a..f65a222b7b8 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -17,6 +17,8 @@
* 02110-1301, USA
*/
+#include <linux/crc32c.h>
+#include <linux/highmem.h>
#include "main.h"
#include "sysfs.h"
#include "debugfs.h"
@@ -29,6 +31,7 @@
#include "hard-interface.h"
#include "gateway_client.h"
#include "bridge_loop_avoidance.h"
+#include "distributed-arp-table.h"
#include "vis.h"
#include "hash.h"
#include "bat_algo.h"
@@ -128,6 +131,10 @@ int batadv_mesh_init(struct net_device *soft_iface)
if (ret < 0)
goto err;
+ ret = batadv_dat_init(bat_priv);
+ if (ret < 0)
+ goto err;
+
atomic_set(&bat_priv->gw.reselect, 0);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE);
@@ -155,21 +162,13 @@ void batadv_mesh_free(struct net_device *soft_iface)
batadv_bla_free(bat_priv);
+ batadv_dat_free(bat_priv);
+
free_percpu(bat_priv->bat_counters);
atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE);
}
-void batadv_inc_module_count(void)
-{
- try_module_get(THIS_MODULE);
-}
-
-void batadv_dec_module_count(void)
-{
- module_put(THIS_MODULE);
-}
-
int batadv_is_my_mac(const uint8_t *addr)
{
const struct batadv_hard_iface *hard_iface;
@@ -188,6 +187,42 @@ int batadv_is_my_mac(const uint8_t *addr)
return 0;
}
+/**
+ * batadv_seq_print_text_primary_if_get - called from debugfs table printing
+ * function that requires the primary interface
+ * @seq: debugfs table seq_file struct
+ *
+ * Returns primary interface if found or NULL otherwise.
+ */
+struct batadv_hard_iface *
+batadv_seq_print_text_primary_if_get(struct seq_file *seq)
+{
+ struct net_device *net_dev = (struct net_device *)seq->private;
+ struct batadv_priv *bat_priv = netdev_priv(net_dev);
+ struct batadv_hard_iface *primary_if;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+
+ if (!primary_if) {
+ seq_printf(seq,
+ "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
+ net_dev->name);
+ goto out;
+ }
+
+ if (primary_if->if_status == BATADV_IF_ACTIVE)
+ goto out;
+
+ seq_printf(seq,
+ "BATMAN mesh %s disabled - primary interface not active\n",
+ net_dev->name);
+ batadv_hardif_free_ref(primary_if);
+ primary_if = NULL;
+
+out:
+ return primary_if;
+}
+
static int batadv_recv_unhandled_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if)
{
@@ -274,6 +309,8 @@ static void batadv_recv_handler_init(void)
/* batman icmp packet */
batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet;
+ /* unicast with 4 addresses packet */
+ batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet;
/* unicast packet */
batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet;
/* fragmented unicast packet */
@@ -385,6 +422,38 @@ int batadv_algo_seq_print_text(struct seq_file *seq, void *offset)
return 0;
}
+/**
+ * batadv_skb_crc32 - calculate CRC32 of the whole packet and skip bytes in
+ * the header
+ * @skb: skb pointing to fragmented socket buffers
+ * @payload_ptr: Pointer to position inside the head buffer of the skb
+ * marking the start of the data to be CRC'ed
+ *
+ * payload_ptr must always point to an address in the skb head buffer and not to
+ * a fragment.
+ */
+__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr)
+{
+ u32 crc = 0;
+ unsigned int from;
+ unsigned int to = skb->len;
+ struct skb_seq_state st;
+ const u8 *data;
+ unsigned int len;
+ unsigned int consumed = 0;
+
+ from = (unsigned int)(payload_ptr - skb->data);
+
+ skb_prepare_seq_read(skb, from, to, &st);
+ while ((len = skb_seq_read(consumed, &data, &st)) != 0) {
+ crc = crc32c(crc, data, len);
+ consumed += len;
+ }
+ skb_abort_seq_read(&st);
+
+ return htonl(crc);
+}
+
static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
{
struct batadv_algo_ops *bat_algo_ops;
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index d57b746219d..2f85577086a 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -26,7 +26,7 @@
#define BATADV_DRIVER_DEVICE "batman-adv"
#ifndef BATADV_SOURCE_VERSION
-#define BATADV_SOURCE_VERSION "2012.4.0"
+#define BATADV_SOURCE_VERSION "2012.5.0"
#endif
/* B.A.T.M.A.N. parameters */
@@ -44,6 +44,7 @@
#define BATADV_TT_LOCAL_TIMEOUT 3600000 /* in milliseconds */
#define BATADV_TT_CLIENT_ROAM_TIMEOUT 600000 /* in milliseconds */
#define BATADV_TT_CLIENT_TEMP_TIMEOUT 600000 /* in milliseconds */
+#define BATADV_DAT_ENTRY_TIMEOUT (5*60000) /* 5 mins in milliseconds */
/* sliding packet range of received originator messages in sequence numbers
* (should be a multiple of our word size)
*/
@@ -73,6 +74,11 @@
#define BATADV_LOG_BUF_LEN 8192 /* has to be a power of 2 */
+/* msecs after which an ARP_REQUEST is sent in broadcast as fallback */
+#define ARP_REQ_DELAY 250
+/* numbers of originator to contact for any PUT/GET DHT operation */
+#define BATADV_DAT_CANDIDATES_NUM 3
+
#define BATADV_VIS_INTERVAL 5000 /* 5 seconds */
/* how much worse secondary interfaces may be to be considered as bonding
@@ -89,6 +95,7 @@
#define BATADV_BLA_PERIOD_LENGTH 10000 /* 10 seconds */
#define BATADV_BLA_BACKBONE_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 3)
#define BATADV_BLA_CLAIM_TIMEOUT (BATADV_BLA_PERIOD_LENGTH * 10)
+#define BATADV_BLA_WAIT_PERIODS 3
#define BATADV_DUPLIST_SIZE 16
#define BATADV_DUPLIST_TIMEOUT 500 /* 500 ms */
@@ -117,6 +124,9 @@ enum batadv_uev_type {
#define BATADV_GW_THRESHOLD 50
+#define BATADV_DAT_CANDIDATE_NOT_FOUND 0
+#define BATADV_DAT_CANDIDATE_ORIG 1
+
/* Debug Messages */
#ifdef pr_fmt
#undef pr_fmt
@@ -150,9 +160,9 @@ extern struct workqueue_struct *batadv_event_workqueue;
int batadv_mesh_init(struct net_device *soft_iface);
void batadv_mesh_free(struct net_device *soft_iface);
-void batadv_inc_module_count(void);
-void batadv_dec_module_count(void);
int batadv_is_my_mac(const uint8_t *addr);
+struct batadv_hard_iface *
+batadv_seq_print_text_primary_if_get(struct seq_file *seq);
int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
struct net_device *orig_dev);
@@ -164,14 +174,24 @@ void batadv_recv_handler_unregister(uint8_t packet_type);
int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops);
int batadv_algo_select(struct batadv_priv *bat_priv, char *name);
int batadv_algo_seq_print_text(struct seq_file *seq, void *offset);
+__be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr);
-/* all messages related to routing / flooding / broadcasting / etc */
+/**
+ * enum batadv_dbg_level - available log levels
+ * @BATADV_DBG_BATMAN: OGM and TQ computations related messages
+ * @BATADV_DBG_ROUTES: route added / changed / deleted
+ * @BATADV_DBG_TT: translation table messages
+ * @BATADV_DBG_BLA: bridge loop avoidance messages
+ * @BATADV_DBG_DAT: ARP snooping and DAT related messages
+ * @BATADV_DBG_ALL: the union of all the above log levels
+ */
enum batadv_dbg_level {
BATADV_DBG_BATMAN = BIT(0),
- BATADV_DBG_ROUTES = BIT(1), /* route added / changed / deleted */
- BATADV_DBG_TT = BIT(2), /* translation table operations */
- BATADV_DBG_BLA = BIT(3), /* bridge loop avoidance */
- BATADV_DBG_ALL = 15,
+ BATADV_DBG_ROUTES = BIT(1),
+ BATADV_DBG_TT = BIT(2),
+ BATADV_DBG_BLA = BIT(3),
+ BATADV_DBG_DAT = BIT(4),
+ BATADV_DBG_ALL = 31,
};
#ifdef CONFIG_BATMAN_ADV_DEBUG
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index ac9bdf8f80a..8c32cf1c2de 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -18,6 +18,7 @@
*/
#include "main.h"
+#include "distributed-arp-table.h"
#include "originator.h"
#include "hash.h"
#include "translation-table.h"
@@ -220,9 +221,9 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
atomic_set(&orig_node->refcount, 2);
orig_node->tt_initialised = false;
- orig_node->tt_poss_change = false;
orig_node->bat_priv = bat_priv;
memcpy(orig_node->orig, addr, ETH_ALEN);
+ batadv_dat_init_orig_node_addr(orig_node);
orig_node->router = NULL;
orig_node->tt_crc = 0;
atomic_set(&orig_node->last_ttvn, 0);
@@ -415,23 +416,10 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset)
int last_seen_msecs;
unsigned long last_seen_jiffies;
uint32_t i;
- int ret = 0;
- primary_if = batadv_primary_if_get_selected(bat_priv);
-
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
-
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n",
BATADV_SOURCE_VERSION, primary_if->net_dev->name,
@@ -485,7 +473,7 @@ next:
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node,
diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h
index 2d23a14c220..cb6405bf755 100644
--- a/net/batman-adv/packet.h
+++ b/net/batman-adv/packet.h
@@ -20,17 +20,30 @@
#ifndef _NET_BATMAN_ADV_PACKET_H_
#define _NET_BATMAN_ADV_PACKET_H_
-#define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */
-
enum batadv_packettype {
- BATADV_IV_OGM = 0x01,
- BATADV_ICMP = 0x02,
- BATADV_UNICAST = 0x03,
- BATADV_BCAST = 0x04,
- BATADV_VIS = 0x05,
- BATADV_UNICAST_FRAG = 0x06,
- BATADV_TT_QUERY = 0x07,
- BATADV_ROAM_ADV = 0x08,
+ BATADV_IV_OGM = 0x01,
+ BATADV_ICMP = 0x02,
+ BATADV_UNICAST = 0x03,
+ BATADV_BCAST = 0x04,
+ BATADV_VIS = 0x05,
+ BATADV_UNICAST_FRAG = 0x06,
+ BATADV_TT_QUERY = 0x07,
+ BATADV_ROAM_ADV = 0x08,
+ BATADV_UNICAST_4ADDR = 0x09,
+};
+
+/**
+ * enum batadv_subtype - packet subtype for unicast4addr
+ * @BATADV_P_DATA: user payload
+ * @BATADV_P_DAT_DHT_GET: DHT request message
+ * @BATADV_P_DAT_DHT_PUT: DHT store message
+ * @BATADV_P_DAT_CACHE_REPLY: ARP reply generated by DAT
+ */
+enum batadv_subtype {
+ BATADV_P_DATA = 0x01,
+ BATADV_P_DAT_DHT_GET = 0x02,
+ BATADV_P_DAT_DHT_PUT = 0x03,
+ BATADV_P_DAT_CACHE_REPLY = 0x04,
};
/* this file is included by batctl which needs these defines */
@@ -106,13 +119,16 @@ struct batadv_bla_claim_dst {
uint8_t magic[3]; /* FF:43:05 */
uint8_t type; /* bla_claimframe */
__be16 group; /* group id */
-} __packed;
+};
struct batadv_header {
uint8_t packet_type;
uint8_t version; /* batman version field */
uint8_t ttl;
-} __packed;
+ /* the parent struct has to add a byte after the header to make
+ * everything 4 bytes aligned again
+ */
+};
struct batadv_ogm_packet {
struct batadv_header header;
@@ -137,7 +153,7 @@ struct batadv_icmp_packet {
__be16 seqno;
uint8_t uid;
uint8_t reserved;
-} __packed;
+};
#define BATADV_RR_LEN 16
@@ -153,13 +169,44 @@ struct batadv_icmp_packet_rr {
uint8_t uid;
uint8_t rr_cur;
uint8_t rr[BATADV_RR_LEN][ETH_ALEN];
-} __packed;
+};
+
+/* All packet headers in front of an ethernet header have to be completely
+ * divisible by 2 but not by 4 to make the payload after the ethernet
+ * header again 4 bytes boundary aligned.
+ *
+ * A packing of 2 is necessary to avoid extra padding at the end of the struct
+ * caused by a structure member which is larger than two bytes. Otherwise
+ * the structure would not fulfill the previously mentioned rule to avoid the
+ * misalignment of the payload after the ethernet header. It may also lead to
+ * leakage of information when the padding it not initialized before sending.
+ */
+#pragma pack(2)
struct batadv_unicast_packet {
struct batadv_header header;
uint8_t ttvn; /* destination translation table version number */
uint8_t dest[ETH_ALEN];
-} __packed;
+ /* "4 bytes boundary + 2 bytes" long to make the payload after the
+ * following ethernet header again 4 bytes boundary aligned
+ */
+};
+
+/**
+ * struct batadv_unicast_4addr_packet - extended unicast packet
+ * @u: common unicast packet header
+ * @src: address of the source
+ * @subtype: packet subtype
+ */
+struct batadv_unicast_4addr_packet {
+ struct batadv_unicast_packet u;
+ uint8_t src[ETH_ALEN];
+ uint8_t subtype;
+ uint8_t reserved;
+ /* "4 bytes boundary + 2 bytes" long to make the payload after the
+ * following ethernet header again 4 bytes boundary aligned
+ */
+};
struct batadv_unicast_frag_packet {
struct batadv_header header;
@@ -176,7 +223,12 @@ struct batadv_bcast_packet {
uint8_t reserved;
__be32 seqno;
uint8_t orig[ETH_ALEN];
-} __packed;
+ /* "4 bytes boundary + 2 bytes" long to make the payload after the
+ * following ethernet header again 4 bytes boundary aligned
+ */
+};
+
+#pragma pack()
struct batadv_vis_packet {
struct batadv_header header;
@@ -187,7 +239,7 @@ struct batadv_vis_packet {
uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */
uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */
uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */
-} __packed;
+};
struct batadv_tt_query_packet {
struct batadv_header header;
diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c
index 376b4cc6ca8..1aa1722d018 100644
--- a/net/batman-adv/routing.c
+++ b/net/batman-adv/routing.c
@@ -28,6 +28,7 @@
#include "vis.h"
#include "unicast.h"
#include "bridge_loop_avoidance.h"
+#include "distributed-arp-table.h"
static int batadv_route_unicast_packet(struct sk_buff *skb,
struct batadv_hard_iface *recv_if);
@@ -284,7 +285,6 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
{
struct batadv_hard_iface *primary_if = NULL;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *router = NULL;
struct batadv_icmp_packet_rr *icmp_packet;
int ret = NET_RX_DROP;
@@ -306,10 +306,6 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
if (!orig_node)
goto out;
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- goto out;
-
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
@@ -321,14 +317,12 @@ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv,
icmp_packet->msg_type = BATADV_ECHO_REPLY;
icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr);
- ret = NET_RX_SUCCESS;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+ ret = NET_RX_SUCCESS;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- if (router)
- batadv_neigh_node_free_ref(router);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
return ret;
@@ -339,7 +333,6 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
{
struct batadv_hard_iface *primary_if = NULL;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *router = NULL;
struct batadv_icmp_packet *icmp_packet;
int ret = NET_RX_DROP;
@@ -361,10 +354,6 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
if (!orig_node)
goto out;
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- goto out;
-
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
@@ -376,14 +365,12 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv,
icmp_packet->msg_type = BATADV_TTL_EXCEEDED;
icmp_packet->header.ttl = BATADV_TTL;
- batadv_send_skb_packet(skb, router->if_incoming, router->addr);
- ret = NET_RX_SUCCESS;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+ ret = NET_RX_SUCCESS;
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- if (router)
- batadv_neigh_node_free_ref(router);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
return ret;
@@ -397,7 +384,6 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
struct batadv_icmp_packet_rr *icmp_packet;
struct ethhdr *ethhdr;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *router = NULL;
int hdr_size = sizeof(struct batadv_icmp_packet);
int ret = NET_RX_DROP;
@@ -446,10 +432,6 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
if (!orig_node)
goto out;
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- goto out;
-
/* create a copy of the skb, if needed, to modify it. */
if (skb_cow(skb, ETH_HLEN) < 0)
goto out;
@@ -460,12 +442,10 @@ int batadv_recv_icmp_packet(struct sk_buff *skb,
icmp_packet->header.ttl--;
/* route it */
- batadv_send_skb_packet(skb, router->if_incoming, router->addr);
- ret = NET_RX_SUCCESS;
+ if (batadv_send_skb_to_orig(skb, orig_node, recv_if))
+ ret = NET_RX_SUCCESS;
out:
- if (router)
- batadv_neigh_node_free_ref(router);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
return ret;
@@ -549,25 +529,18 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig,
if (tmp_neigh_node->if_incoming == recv_if)
continue;
- if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+ if (router && tmp_neigh_node->tq_avg <= router->tq_avg)
continue;
- /* if we don't have a router yet
- * or this one is better, choose it.
- */
- if ((!router) ||
- (tmp_neigh_node->tq_avg > router->tq_avg)) {
- /* decrement refcount of
- * previously selected router
- */
- if (router)
- batadv_neigh_node_free_ref(router);
+ if (!atomic_inc_not_zero(&tmp_neigh_node->refcount))
+ continue;
- router = tmp_neigh_node;
- atomic_inc_not_zero(&router->refcount);
- }
+ /* decrement refcount of previously selected router */
+ if (router)
+ batadv_neigh_node_free_ref(router);
- batadv_neigh_node_free_ref(tmp_neigh_node);
+ /* we found a better router (or at least one valid router) */
+ router = tmp_neigh_node;
}
/* use the first candidate if nothing was found. */
@@ -687,21 +660,8 @@ int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_roam_adv_packet *roam_adv_packet;
struct batadv_orig_node *orig_node;
- struct ethhdr *ethhdr;
-
- /* drop packet if it has not necessary minimum size */
- if (unlikely(!pskb_may_pull(skb,
- sizeof(struct batadv_roam_adv_packet))))
- goto out;
-
- ethhdr = (struct ethhdr *)skb_mac_header(skb);
-
- /* packet with unicast indication but broadcast recipient */
- if (is_broadcast_ether_addr(ethhdr->h_dest))
- goto out;
- /* packet with broadcast sender address */
- if (is_broadcast_ether_addr(ethhdr->h_source))
+ if (batadv_check_unicast_packet(skb, sizeof(*roam_adv_packet)) < 0)
goto out;
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX);
@@ -730,12 +690,6 @@ int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if)
BATADV_TT_CLIENT_ROAM,
atomic_read(&orig_node->last_ttvn) + 1);
- /* Roaming phase starts: I have new information but the ttvn has not
- * been incremented yet. This flag will make me check all the incoming
- * packets for the correct destination.
- */
- bat_priv->tt.poss_change = true;
-
batadv_orig_node_free_ref(orig_node);
out:
/* returning NET_RX_DROP will make the caller function kfree the skb */
@@ -907,8 +861,8 @@ static int batadv_route_unicast_packet(struct sk_buff *skb,
skb->len + ETH_HLEN);
/* route it */
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = NET_RX_SUCCESS;
+ if (batadv_send_skb_to_orig(skb, orig_node, recv_if))
+ ret = NET_RX_SUCCESS;
out:
if (neigh_node)
@@ -918,80 +872,161 @@ out:
return ret;
}
+/**
+ * batadv_reroute_unicast_packet - update the unicast header for re-routing
+ * @bat_priv: the bat priv with all the soft interface information
+ * @unicast_packet: the unicast header to be updated
+ * @dst_addr: the payload destination
+ *
+ * Search the translation table for dst_addr and update the unicast header with
+ * the new corresponding information (originator address where the destination
+ * client currently is and its known TTVN)
+ *
+ * Returns true if the packet header has been updated, false otherwise
+ */
+static bool
+batadv_reroute_unicast_packet(struct batadv_priv *bat_priv,
+ struct batadv_unicast_packet *unicast_packet,
+ uint8_t *dst_addr)
+{
+ struct batadv_orig_node *orig_node = NULL;
+ struct batadv_hard_iface *primary_if = NULL;
+ bool ret = false;
+ uint8_t *orig_addr, orig_ttvn;
+
+ if (batadv_is_my_client(bat_priv, dst_addr)) {
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+ orig_addr = primary_if->net_dev->dev_addr;
+ orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
+ } else {
+ orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr);
+ if (!orig_node)
+ goto out;
+
+ if (batadv_compare_eth(orig_node->orig, unicast_packet->dest))
+ goto out;
+
+ orig_addr = orig_node->orig;
+ orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+ }
+
+ /* update the packet header */
+ memcpy(unicast_packet->dest, orig_addr, ETH_ALEN);
+ unicast_packet->ttvn = orig_ttvn;
+
+ ret = true;
+out:
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ if (orig_node)
+ batadv_orig_node_free_ref(orig_node);
+
+ return ret;
+}
+
static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
struct sk_buff *skb) {
- uint8_t curr_ttvn;
+ uint8_t curr_ttvn, old_ttvn;
struct batadv_orig_node *orig_node;
struct ethhdr *ethhdr;
struct batadv_hard_iface *primary_if;
struct batadv_unicast_packet *unicast_packet;
- bool tt_poss_change;
int is_old_ttvn;
- /* I could need to modify it */
- if (skb_cow(skb, sizeof(struct batadv_unicast_packet)) < 0)
+ /* check if there is enough data before accessing it */
+ if (pskb_may_pull(skb, sizeof(*unicast_packet) + ETH_HLEN) < 0)
+ return 0;
+
+ /* create a copy of the skb (in case of for re-routing) to modify it. */
+ if (skb_cow(skb, sizeof(*unicast_packet)) < 0)
return 0;
unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet));
- if (batadv_is_my_mac(unicast_packet->dest)) {
- tt_poss_change = bat_priv->tt.poss_change;
- curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
- } else {
+ /* check if the destination client was served by this node and it is now
+ * roaming. In this case, it means that the node has got a ROAM_ADV
+ * message and that it knows the new destination in the mesh to re-route
+ * the packet to
+ */
+ if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) {
+ if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
+ ethhdr->h_dest))
+ net_ratelimited_function(batadv_dbg, BATADV_DBG_TT,
+ bat_priv,
+ "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n",
+ unicast_packet->dest,
+ ethhdr->h_dest);
+ /* at this point the mesh destination should have been
+ * substituted with the originator address found in the global
+ * table. If not, let the packet go untouched anyway because
+ * there is nothing the node can do
+ */
+ return 1;
+ }
+
+ /* retrieve the TTVN known by this node for the packet destination. This
+ * value is used later to check if the node which sent (or re-routed
+ * last time) the packet had an updated information or not
+ */
+ curr_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn);
+ if (!batadv_is_my_mac(unicast_packet->dest)) {
orig_node = batadv_orig_hash_find(bat_priv,
unicast_packet->dest);
-
+ /* if it is not possible to find the orig_node representing the
+ * destination, the packet can immediately be dropped as it will
+ * not be possible to deliver it
+ */
if (!orig_node)
return 0;
curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
- tt_poss_change = orig_node->tt_poss_change;
batadv_orig_node_free_ref(orig_node);
}
- /* Check whether I have to reroute the packet */
+ /* check if the TTVN contained in the packet is fresher than what the
+ * node knows
+ */
is_old_ttvn = batadv_seq_before(unicast_packet->ttvn, curr_ttvn);
- if (is_old_ttvn || tt_poss_change) {
- /* check if there is enough data before accessing it */
- if (pskb_may_pull(skb, sizeof(struct batadv_unicast_packet) +
- ETH_HLEN) < 0)
- return 0;
+ if (!is_old_ttvn)
+ return 1;
- ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet));
+ old_ttvn = unicast_packet->ttvn;
+ /* the packet was forged based on outdated network information. Its
+ * destination can possibly be updated and forwarded towards the new
+ * target host
+ */
+ if (batadv_reroute_unicast_packet(bat_priv, unicast_packet,
+ ethhdr->h_dest)) {
+ net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv,
+ "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n",
+ unicast_packet->dest, ethhdr->h_dest,
+ old_ttvn, curr_ttvn);
+ return 1;
+ }
- /* we don't have an updated route for this client, so we should
- * not try to reroute the packet!!
- */
- if (batadv_tt_global_client_is_roaming(bat_priv,
- ethhdr->h_dest))
- return 1;
+ /* the packet has not been re-routed: either the destination is
+ * currently served by this node or there is no destination at all and
+ * it is possible to drop the packet
+ */
+ if (!batadv_is_my_client(bat_priv, ethhdr->h_dest))
+ return 0;
- orig_node = batadv_transtable_search(bat_priv, NULL,
- ethhdr->h_dest);
-
- if (!orig_node) {
- if (!batadv_is_my_client(bat_priv, ethhdr->h_dest))
- return 0;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if)
- return 0;
- memcpy(unicast_packet->dest,
- primary_if->net_dev->dev_addr, ETH_ALEN);
- batadv_hardif_free_ref(primary_if);
- } else {
- memcpy(unicast_packet->dest, orig_node->orig,
- ETH_ALEN);
- curr_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
- batadv_orig_node_free_ref(orig_node);
- }
+ /* update the header in order to let the packet be delivered to this
+ * node's soft interface
+ */
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ return 0;
- batadv_dbg(BATADV_DBG_ROUTES, bat_priv,
- "TTVN mismatch (old_ttvn %u new_ttvn %u)! Rerouting unicast packet (for %pM) to %pM\n",
- unicast_packet->ttvn, curr_ttvn, ethhdr->h_dest,
- unicast_packet->dest);
+ memcpy(unicast_packet->dest, primary_if->net_dev->dev_addr, ETH_ALEN);
+
+ batadv_hardif_free_ref(primary_if);
+
+ unicast_packet->ttvn = curr_ttvn;
- unicast_packet->ttvn = curr_ttvn;
- }
return 1;
}
@@ -1000,7 +1035,19 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
{
struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface);
struct batadv_unicast_packet *unicast_packet;
+ struct batadv_unicast_4addr_packet *unicast_4addr_packet;
+ uint8_t *orig_addr;
+ struct batadv_orig_node *orig_node = NULL;
int hdr_size = sizeof(*unicast_packet);
+ bool is4addr;
+
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
+
+ is4addr = unicast_packet->header.packet_type == BATADV_UNICAST_4ADDR;
+ /* the caller function should have already pulled 2 bytes */
+ if (is4addr)
+ hdr_size = sizeof(*unicast_4addr_packet);
if (batadv_check_unicast_packet(skb, hdr_size) < 0)
return NET_RX_DROP;
@@ -1008,12 +1055,28 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
if (!batadv_check_unicast_ttvn(bat_priv, skb))
return NET_RX_DROP;
- unicast_packet = (struct batadv_unicast_packet *)skb->data;
-
/* packet for me */
if (batadv_is_my_mac(unicast_packet->dest)) {
+ if (is4addr) {
+ batadv_dat_inc_counter(bat_priv,
+ unicast_4addr_packet->subtype);
+ orig_addr = unicast_4addr_packet->src;
+ orig_node = batadv_orig_hash_find(bat_priv, orig_addr);
+ }
+
+ if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb,
+ hdr_size))
+ goto rx_success;
+ if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb,
+ hdr_size))
+ goto rx_success;
+
batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size,
- NULL);
+ orig_node);
+
+rx_success:
+ if (orig_node)
+ batadv_orig_node_free_ref(orig_node);
return NET_RX_SUCCESS;
}
@@ -1050,8 +1113,17 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
if (!new_skb)
return NET_RX_SUCCESS;
+ if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb,
+ hdr_size))
+ goto rx_success;
+ if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb,
+ hdr_size))
+ goto rx_success;
+
batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if,
sizeof(struct batadv_unicast_packet), NULL);
+
+rx_success:
return NET_RX_SUCCESS;
}
@@ -1124,14 +1196,8 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
spin_unlock_bh(&orig_node->bcast_seqno_lock);
- /* keep skb linear for crc calculation */
- if (skb_linearize(skb) < 0)
- goto out;
-
- bcast_packet = (struct batadv_bcast_packet *)skb->data;
-
/* check whether this has been sent by another originator before */
- if (batadv_bla_check_bcast_duplist(bat_priv, bcast_packet, skb->len))
+ if (batadv_bla_check_bcast_duplist(bat_priv, skb))
goto out;
/* rebroadcast packet */
@@ -1143,9 +1209,16 @@ int batadv_recv_bcast_packet(struct sk_buff *skb,
if (batadv_bla_is_backbone_gw(skb, orig_node, hdr_size))
goto out;
+ if (batadv_dat_snoop_incoming_arp_request(bat_priv, skb, hdr_size))
+ goto rx_success;
+ if (batadv_dat_snoop_incoming_arp_reply(bat_priv, skb, hdr_size))
+ goto rx_success;
+
/* broadcast for me */
batadv_interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size,
orig_node);
+
+rx_success:
ret = NET_RX_SUCCESS;
goto out;
diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c
index 570a8bce036..4425af9dad4 100644
--- a/net/batman-adv/send.c
+++ b/net/batman-adv/send.c
@@ -18,6 +18,7 @@
*/
#include "main.h"
+#include "distributed-arp-table.h"
#include "send.h"
#include "routing.h"
#include "translation-table.h"
@@ -27,6 +28,8 @@
#include "gateway_common.h"
#include "originator.h"
+#include <linux/if_ether.h>
+
static void batadv_send_outstanding_bcast_packet(struct work_struct *work);
/* send out an already prepared packet to the given address via the
@@ -59,11 +62,11 @@ int batadv_send_skb_packet(struct sk_buff *skb,
ethhdr = (struct ethhdr *)skb_mac_header(skb);
memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN);
memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN);
- ethhdr->h_proto = __constant_htons(BATADV_ETH_P_BATMAN);
+ ethhdr->h_proto = __constant_htons(ETH_P_BATMAN);
skb_set_network_header(skb, ETH_HLEN);
skb->priority = TC_PRIO_CONTROL;
- skb->protocol = __constant_htons(BATADV_ETH_P_BATMAN);
+ skb->protocol = __constant_htons(ETH_P_BATMAN);
skb->dev = hard_iface->net_dev;
@@ -77,6 +80,39 @@ send_skb_err:
return NET_XMIT_DROP;
}
+/**
+ * batadv_send_skb_to_orig - Lookup next-hop and transmit skb.
+ * @skb: Packet to be transmitted.
+ * @orig_node: Final destination of the packet.
+ * @recv_if: Interface used when receiving the packet (can be NULL).
+ *
+ * Looks up the best next-hop towards the passed originator and passes the
+ * skb on for preparation of MAC header. If the packet originated from this
+ * host, NULL can be passed as recv_if and no interface alternating is
+ * attempted.
+ *
+ * Returns TRUE on success; FALSE otherwise.
+ */
+bool batadv_send_skb_to_orig(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ struct batadv_hard_iface *recv_if)
+{
+ struct batadv_priv *bat_priv = orig_node->bat_priv;
+ struct batadv_neigh_node *neigh_node;
+
+ /* batadv_find_router() increases neigh_nodes refcount if found. */
+ neigh_node = batadv_find_router(bat_priv, orig_node, recv_if);
+ if (!neigh_node)
+ return false;
+
+ /* route it */
+ batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
+
+ batadv_neigh_node_free_ref(neigh_node);
+
+ return true;
+}
+
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface)
{
struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface);
@@ -209,6 +245,9 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work)
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_DEACTIVATING)
goto out;
+ if (batadv_dat_drop_broadcast_packet(bat_priv, forw_packet))
+ goto out;
+
/* rebroadcast packet */
rcu_read_lock();
list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h
index 643329b787e..0078dece1ab 100644
--- a/net/batman-adv/send.h
+++ b/net/batman-adv/send.h
@@ -23,6 +23,9 @@
int batadv_send_skb_packet(struct sk_buff *skb,
struct batadv_hard_iface *hard_iface,
const uint8_t *dst_addr);
+bool batadv_send_skb_to_orig(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ struct batadv_hard_iface *recv_if);
void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface);
int batadv_add_bcast_packet_to_list(struct batadv_priv *bat_priv,
const struct sk_buff *skb,
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index b9a28d2dd3e..6b548fde8e0 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -20,6 +20,7 @@
#include "main.h"
#include "soft-interface.h"
#include "hard-interface.h"
+#include "distributed-arp-table.h"
#include "routing.h"
#include "send.h"
#include "debugfs.h"
@@ -33,6 +34,7 @@
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
+#include <linux/if_ether.h>
#include "unicast.h"
#include "bridge_loop_avoidance.h"
@@ -145,14 +147,17 @@ static int batadv_interface_tx(struct sk_buff *skb,
struct batadv_hard_iface *primary_if = NULL;
struct batadv_bcast_packet *bcast_packet;
struct vlan_ethhdr *vhdr;
- __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN);
- static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00,
- 0x00};
+ __be16 ethertype = __constant_htons(ETH_P_BATMAN);
+ static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00,
+ 0x00, 0x00};
+ static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00,
+ 0x00, 0x00};
unsigned int header_len = 0;
int data_len = skb->len, ret;
short vid __maybe_unused = -1;
bool do_bcast = false;
uint32_t seqno;
+ unsigned long brd_delay = 1;
if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
goto dropped;
@@ -168,7 +173,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
break;
/* fall through */
- case BATADV_ETH_P_BATMAN:
+ case ETH_P_BATMAN:
goto dropped;
}
@@ -180,10 +185,16 @@ static int batadv_interface_tx(struct sk_buff *skb,
/* don't accept stp packets. STP does not help in meshes.
* better use the bridge loop avoidance ...
+ *
+ * The same goes for ECTP sent at least by some Cisco Switches,
+ * it might confuse the mesh when used with bridge loop avoidance.
*/
if (batadv_compare_eth(ethhdr->h_dest, stp_addr))
goto dropped;
+ if (batadv_compare_eth(ethhdr->h_dest, ectp_addr))
+ goto dropped;
+
if (is_multicast_ether_addr(ethhdr->h_dest)) {
do_bcast = true;
@@ -216,6 +227,13 @@ static int batadv_interface_tx(struct sk_buff *skb,
if (!primary_if)
goto dropped;
+ /* in case of ARP request, we do not immediately broadcasti the
+ * packet, instead we first wait for DAT to try to retrieve the
+ * correct ARP entry
+ */
+ if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
+ brd_delay = msecs_to_jiffies(ARP_REQ_DELAY);
+
if (batadv_skb_head_push(skb, sizeof(*bcast_packet)) < 0)
goto dropped;
@@ -237,7 +255,7 @@ static int batadv_interface_tx(struct sk_buff *skb,
seqno = atomic_inc_return(&bat_priv->bcast_seqno);
bcast_packet->seqno = htonl(seqno);
- batadv_add_bcast_packet_to_list(bat_priv, skb, 1);
+ batadv_add_bcast_packet_to_list(bat_priv, skb, brd_delay);
/* a copy is stored in the bcast list, therefore removing
* the original skb.
@@ -252,7 +270,12 @@ static int batadv_interface_tx(struct sk_buff *skb,
goto dropped;
}
- ret = batadv_unicast_send_skb(skb, bat_priv);
+ if (batadv_dat_snoop_outgoing_arp_request(bat_priv, skb))
+ goto dropped;
+
+ batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb);
+
+ ret = batadv_unicast_send_skb(bat_priv, skb);
if (ret != 0)
goto dropped_freed;
}
@@ -280,7 +303,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
struct vlan_ethhdr *vhdr;
struct batadv_header *batadv_header = (struct batadv_header *)skb->data;
short vid __maybe_unused = -1;
- __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN);
+ __be16 ethertype = __constant_htons(ETH_P_BATMAN);
bool is_bcast;
is_bcast = (batadv_header->packet_type == BATADV_BCAST);
@@ -303,7 +326,7 @@ void batadv_interface_rx(struct net_device *soft_iface,
break;
/* fall through */
- case BATADV_ETH_P_BATMAN:
+ case ETH_P_BATMAN:
goto dropped;
}
@@ -325,6 +348,12 @@ void batadv_interface_rx(struct net_device *soft_iface,
soft_iface->last_rx = jiffies;
+ /* Let the bridge loop avoidance check the packet. If will
+ * not handle it, we can safely push it up.
+ */
+ if (batadv_bla_rx(bat_priv, skb, vid, is_bcast))
+ goto out;
+
if (orig_node)
batadv_tt_add_temporary_global_entry(bat_priv, orig_node,
ethhdr->h_source);
@@ -332,12 +361,6 @@ void batadv_interface_rx(struct net_device *soft_iface,
if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest))
goto dropped;
- /* Let the bridge loop avoidance check the packet. If will
- * not handle it, we can safely push it up.
- */
- if (batadv_bla_rx(bat_priv, skb, vid, is_bcast))
- goto out;
-
netif_rx(skb);
goto out;
@@ -347,7 +370,51 @@ out:
return;
}
+/* batman-adv network devices have devices nesting below it and are a special
+ * "super class" of normal network devices; split their locks off into a
+ * separate class since they always nest.
+ */
+static struct lock_class_key batadv_netdev_xmit_lock_key;
+static struct lock_class_key batadv_netdev_addr_lock_key;
+
+/**
+ * batadv_set_lockdep_class_one - Set lockdep class for a single tx queue
+ * @dev: device which owns the tx queue
+ * @txq: tx queue to modify
+ * @_unused: always NULL
+ */
+static void batadv_set_lockdep_class_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_unused)
+{
+ lockdep_set_class(&txq->_xmit_lock, &batadv_netdev_xmit_lock_key);
+}
+
+/**
+ * batadv_set_lockdep_class - Set txq and addr_list lockdep class
+ * @dev: network device to modify
+ */
+static void batadv_set_lockdep_class(struct net_device *dev)
+{
+ lockdep_set_class(&dev->addr_list_lock, &batadv_netdev_addr_lock_key);
+ netdev_for_each_tx_queue(dev, batadv_set_lockdep_class_one, NULL);
+}
+
+/**
+ * batadv_softif_init - Late stage initialization of soft interface
+ * @dev: registered network device to modify
+ *
+ * Returns error code on failures
+ */
+static int batadv_softif_init(struct net_device *dev)
+{
+ batadv_set_lockdep_class(dev);
+
+ return 0;
+}
+
static const struct net_device_ops batadv_netdev_ops = {
+ .ndo_init = batadv_softif_init,
.ndo_open = batadv_interface_open,
.ndo_stop = batadv_interface_release,
.ndo_get_stats = batadv_interface_stats,
@@ -414,6 +481,9 @@ struct net_device *batadv_softif_create(const char *name)
atomic_set(&bat_priv->aggregated_ogms, 1);
atomic_set(&bat_priv->bonding, 0);
atomic_set(&bat_priv->bridge_loop_avoidance, 0);
+#ifdef CONFIG_BATMAN_ADV_DAT
+ atomic_set(&bat_priv->distributed_arp_table, 1);
+#endif
atomic_set(&bat_priv->ap_isolation, 0);
atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE);
atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF);
@@ -436,7 +506,6 @@ struct net_device *batadv_softif_create(const char *name)
#endif
bat_priv->tt.last_changeset = NULL;
bat_priv->tt.last_changeset_len = 0;
- bat_priv->tt.poss_change = false;
bat_priv->primary_if = NULL;
bat_priv->num_ifaces = 0;
@@ -556,6 +625,13 @@ static const struct {
{ "tt_response_rx" },
{ "tt_roam_adv_tx" },
{ "tt_roam_adv_rx" },
+#ifdef CONFIG_BATMAN_ADV_DAT
+ { "dat_get_tx" },
+ { "dat_get_rx" },
+ { "dat_put_tx" },
+ { "dat_put_rx" },
+ { "dat_cached_reply_tx" },
+#endif
};
static void batadv_get_strings(struct net_device *dev, uint32_t stringset,
diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c
index 66518c75c21..84a55cb19b0 100644
--- a/net/batman-adv/sysfs.c
+++ b/net/batman-adv/sysfs.c
@@ -20,6 +20,7 @@
#include "main.h"
#include "sysfs.h"
#include "translation-table.h"
+#include "distributed-arp-table.h"
#include "originator.h"
#include "hard-interface.h"
#include "gateway_common.h"
@@ -122,55 +123,6 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \
batadv_store_##_name)
-#define BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func) \
-ssize_t batadv_store_##_name(struct kobject *kobj, \
- struct attribute *attr, char *buff, \
- size_t count) \
-{ \
- struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
- struct batadv_hard_iface *hard_iface; \
- ssize_t length; \
- \
- hard_iface = batadv_hardif_get_by_netdev(net_dev); \
- if (!hard_iface) \
- return 0; \
- \
- length = __batadv_store_uint_attr(buff, count, _min, _max, \
- _post_func, attr, \
- &hard_iface->_name, net_dev); \
- \
- batadv_hardif_free_ref(hard_iface); \
- return length; \
-}
-
-#define BATADV_ATTR_HIF_SHOW_UINT(_name) \
-ssize_t batadv_show_##_name(struct kobject *kobj, \
- struct attribute *attr, char *buff) \
-{ \
- struct net_device *net_dev = batadv_kobj_to_netdev(kobj); \
- struct batadv_hard_iface *hard_iface; \
- ssize_t length; \
- \
- hard_iface = batadv_hardif_get_by_netdev(net_dev); \
- if (!hard_iface) \
- return 0; \
- \
- length = sprintf(buff, "%i\n", atomic_read(&hard_iface->_name));\
- \
- batadv_hardif_free_ref(hard_iface); \
- return length; \
-}
-
-/* Use this, if you are going to set [name] in hard_iface to an
- * unsigned integer value
- */
-#define BATADV_ATTR_HIF_UINT(_name, _mode, _min, _max, _post_func) \
- static BATADV_ATTR_HIF_STORE_UINT(_name, _min, _max, _post_func)\
- static BATADV_ATTR_HIF_SHOW_UINT(_name) \
- static BATADV_ATTR(_name, _mode, batadv_show_##_name, \
- batadv_store_##_name)
-
-
static int batadv_store_bool_attr(char *buff, size_t count,
struct net_device *net_dev,
const char *attr_name, atomic_t *attr)
@@ -469,6 +421,9 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL);
#ifdef CONFIG_BATMAN_ADV_BLA
BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL);
#endif
+#ifdef CONFIG_BATMAN_ADV_DAT
+BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, NULL);
+#endif
BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu);
BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL);
static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode,
@@ -494,6 +449,9 @@ static struct batadv_attribute *batadv_mesh_attrs[] = {
#ifdef CONFIG_BATMAN_ADV_BLA
&batadv_attr_bridge_loop_avoidance,
#endif
+#ifdef CONFIG_BATMAN_ADV_DAT
+ &batadv_attr_distributed_arp_table,
+#endif
&batadv_attr_fragmentation,
&batadv_attr_ap_isolation,
&batadv_attr_vis_mode,
@@ -730,7 +688,7 @@ int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type,
enum batadv_uev_action action, const char *data)
{
int ret = -ENOMEM;
- struct batadv_hard_iface *primary_if = NULL;
+ struct batadv_hard_iface *primary_if;
struct kobject *bat_kobj;
char *uevent_env[4] = { NULL, NULL, NULL, NULL };
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 112edd371b2..22457a7952b 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -238,92 +238,134 @@ static int batadv_tt_local_init(struct batadv_priv *bat_priv)
return 0;
}
+static void batadv_tt_global_free(struct batadv_priv *bat_priv,
+ struct batadv_tt_global_entry *tt_global,
+ const char *message)
+{
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Deleting global tt entry %pM: %s\n",
+ tt_global->common.addr, message);
+
+ batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
+ batadv_choose_orig, tt_global->common.addr);
+ batadv_tt_global_entry_free_ref(tt_global);
+
+}
+
void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
int ifindex)
{
struct batadv_priv *bat_priv = netdev_priv(soft_iface);
- struct batadv_tt_local_entry *tt_local_entry = NULL;
- struct batadv_tt_global_entry *tt_global_entry = NULL;
+ struct batadv_tt_local_entry *tt_local;
+ struct batadv_tt_global_entry *tt_global;
struct hlist_head *head;
struct hlist_node *node;
struct batadv_tt_orig_list_entry *orig_entry;
int hash_added;
+ bool roamed_back = false;
- tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+ tt_local = batadv_tt_local_hash_find(bat_priv, addr);
+ tt_global = batadv_tt_global_hash_find(bat_priv, addr);
- if (tt_local_entry) {
- tt_local_entry->last_seen = jiffies;
- /* possibly unset the BATADV_TT_CLIENT_PENDING flag */
- tt_local_entry->common.flags &= ~BATADV_TT_CLIENT_PENDING;
- goto out;
+ if (tt_local) {
+ tt_local->last_seen = jiffies;
+ if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) {
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Re-adding pending client %pM\n", addr);
+ /* whatever the reason why the PENDING flag was set,
+ * this is a client which was enqueued to be removed in
+ * this orig_interval. Since it popped up again, the
+ * flag can be reset like it was never enqueued
+ */
+ tt_local->common.flags &= ~BATADV_TT_CLIENT_PENDING;
+ goto add_event;
+ }
+
+ if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) {
+ batadv_dbg(BATADV_DBG_TT, bat_priv,
+ "Roaming client %pM came back to its original location\n",
+ addr);
+ /* the ROAM flag is set because this client roamed away
+ * and the node got a roaming_advertisement message. Now
+ * that the client popped up again at its original
+ * location such flag can be unset
+ */
+ tt_local->common.flags &= ~BATADV_TT_CLIENT_ROAM;
+ roamed_back = true;
+ }
+ goto check_roaming;
}
- tt_local_entry = kmalloc(sizeof(*tt_local_entry), GFP_ATOMIC);
- if (!tt_local_entry)
+ tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC);
+ if (!tt_local)
goto out;
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Creating new local tt entry: %pM (ttvn: %d)\n", addr,
(uint8_t)atomic_read(&bat_priv->tt.vn));
- memcpy(tt_local_entry->common.addr, addr, ETH_ALEN);
- tt_local_entry->common.flags = BATADV_NO_FLAGS;
+ memcpy(tt_local->common.addr, addr, ETH_ALEN);
+ tt_local->common.flags = BATADV_NO_FLAGS;
if (batadv_is_wifi_iface(ifindex))
- tt_local_entry->common.flags |= BATADV_TT_CLIENT_WIFI;
- atomic_set(&tt_local_entry->common.refcount, 2);
- tt_local_entry->last_seen = jiffies;
- tt_local_entry->common.added_at = tt_local_entry->last_seen;
+ tt_local->common.flags |= BATADV_TT_CLIENT_WIFI;
+ atomic_set(&tt_local->common.refcount, 2);
+ tt_local->last_seen = jiffies;
+ tt_local->common.added_at = tt_local->last_seen;
/* the batman interface mac address should never be purged */
if (batadv_compare_eth(addr, soft_iface->dev_addr))
- tt_local_entry->common.flags |= BATADV_TT_CLIENT_NOPURGE;
+ tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE;
/* The local entry has to be marked as NEW to avoid to send it in
* a full table response going out before the next ttvn increment
* (consistency check)
*/
- tt_local_entry->common.flags |= BATADV_TT_CLIENT_NEW;
+ tt_local->common.flags |= BATADV_TT_CLIENT_NEW;
hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt,
- batadv_choose_orig,
- &tt_local_entry->common,
- &tt_local_entry->common.hash_entry);
+ batadv_choose_orig, &tt_local->common,
+ &tt_local->common.hash_entry);
if (unlikely(hash_added != 0)) {
/* remove the reference for the hash */
- batadv_tt_local_entry_free_ref(tt_local_entry);
+ batadv_tt_local_entry_free_ref(tt_local);
goto out;
}
- batadv_tt_local_event(bat_priv, addr, tt_local_entry->common.flags);
-
- /* remove address from global hash if present */
- tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
+add_event:
+ batadv_tt_local_event(bat_priv, addr, tt_local->common.flags);
- /* Check whether it is a roaming! */
- if (tt_global_entry) {
+check_roaming:
+ /* Check whether it is a roaming, but don't do anything if the roaming
+ * process has already been handled
+ */
+ if (tt_global && !(tt_global->common.flags & BATADV_TT_CLIENT_ROAM)) {
/* These node are probably going to update their tt table */
- head = &tt_global_entry->orig_list;
+ head = &tt_global->orig_list;
rcu_read_lock();
hlist_for_each_entry_rcu(orig_entry, node, head, list) {
- orig_entry->orig_node->tt_poss_change = true;
-
- batadv_send_roam_adv(bat_priv,
- tt_global_entry->common.addr,
+ batadv_send_roam_adv(bat_priv, tt_global->common.addr,
orig_entry->orig_node);
}
rcu_read_unlock();
- /* The global entry has to be marked as ROAMING and
- * has to be kept for consistency purpose
- */
- tt_global_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
- tt_global_entry->roam_at = jiffies;
+ if (roamed_back) {
+ batadv_tt_global_free(bat_priv, tt_global,
+ "Roaming canceled");
+ tt_global = NULL;
+ } else {
+ /* The global entry has to be marked as ROAMING and
+ * has to be kept for consistency purpose
+ */
+ tt_global->common.flags |= BATADV_TT_CLIENT_ROAM;
+ tt_global->roam_at = jiffies;
+ }
}
+
out:
- if (tt_local_entry)
- batadv_tt_local_entry_free_ref(tt_local_entry);
- if (tt_global_entry)
- batadv_tt_global_entry_free_ref(tt_global_entry);
+ if (tt_local)
+ batadv_tt_local_entry_free_ref(tt_local);
+ if (tt_global)
+ batadv_tt_global_entry_free_ref(tt_global);
}
static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff,
@@ -434,22 +476,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
struct hlist_node *node;
struct hlist_head *head;
uint32_t i;
- int ret = 0;
-
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
seq_printf(seq,
"Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n",
@@ -479,7 +509,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
static void
@@ -501,24 +531,57 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv,
tt_local_entry->common.addr, message);
}
-void batadv_tt_local_remove(struct batadv_priv *bat_priv, const uint8_t *addr,
- const char *message, bool roaming)
+/**
+ * batadv_tt_local_remove - logically remove an entry from the local table
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the MAC address of the client to remove
+ * @message: message to append to the log on deletion
+ * @roaming: true if the deletion is due to a roaming event
+ *
+ * Returns the flags assigned to the local entry before being deleted
+ */
+uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
+ const uint8_t *addr, const char *message,
+ bool roaming)
{
- struct batadv_tt_local_entry *tt_local_entry = NULL;
- uint16_t flags;
+ struct batadv_tt_local_entry *tt_local_entry;
+ uint16_t flags, curr_flags = BATADV_NO_FLAGS;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
if (!tt_local_entry)
goto out;
+ curr_flags = tt_local_entry->common.flags;
+
flags = BATADV_TT_CLIENT_DEL;
- if (roaming)
+ /* if this global entry addition is due to a roaming, the node has to
+ * mark the local entry as "roamed" in order to correctly reroute
+ * packets later
+ */
+ if (roaming) {
flags |= BATADV_TT_CLIENT_ROAM;
+ /* mark the local client as ROAMed */
+ tt_local_entry->common.flags |= BATADV_TT_CLIENT_ROAM;
+ }
+
+ if (!(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW)) {
+ batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
+ message);
+ goto out;
+ }
+ /* if this client has been added right now, it is possible to
+ * immediately purge it
+ */
+ batadv_tt_local_event(bat_priv, tt_local_entry->common.addr,
+ curr_flags | BATADV_TT_CLIENT_DEL);
+ hlist_del_rcu(&tt_local_entry->common.hash_entry);
+ batadv_tt_local_entry_free_ref(tt_local_entry);
- batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags, message);
out:
if (tt_local_entry)
batadv_tt_local_entry_free_ref(tt_local_entry);
+
+ return curr_flags;
}
static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv,
@@ -721,12 +784,23 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
const unsigned char *tt_addr, uint8_t flags,
uint8_t ttvn)
{
- struct batadv_tt_global_entry *tt_global_entry = NULL;
+ struct batadv_tt_global_entry *tt_global_entry;
+ struct batadv_tt_local_entry *tt_local_entry;
int ret = 0;
int hash_added;
struct batadv_tt_common_entry *common;
+ uint16_t local_flags;
tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr);
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr);
+
+ /* if the node already has a local client for this entry, it has to wait
+ * for a roaming advertisement instead of manually messing up the global
+ * table
+ */
+ if ((flags & BATADV_TT_CLIENT_TEMP) && tt_local_entry &&
+ !(tt_local_entry->common.flags & BATADV_TT_CLIENT_NEW))
+ goto out;
if (!tt_global_entry) {
tt_global_entry = kzalloc(sizeof(*tt_global_entry), GFP_ATOMIC);
@@ -738,6 +812,12 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
common->flags = flags;
tt_global_entry->roam_at = 0;
+ /* node must store current time in case of roaming. This is
+ * needed to purge this entry out on timeout (if nobody claims
+ * it)
+ */
+ if (flags & BATADV_TT_CLIENT_ROAM)
+ tt_global_entry->roam_at = jiffies;
atomic_set(&common->refcount, 2);
common->added_at = jiffies;
@@ -755,19 +835,37 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
goto out_remove;
}
} else {
+ common = &tt_global_entry->common;
/* If there is already a global entry, we can use this one for
* our processing.
- * But if we are trying to add a temporary client we can exit
- * directly because the temporary information should never
- * override any already known client state (whatever it is)
+ * But if we are trying to add a temporary client then here are
+ * two options at this point:
+ * 1) the global client is not a temporary client: the global
+ * client has to be left as it is, temporary information
+ * should never override any already known client state
+ * 2) the global client is a temporary client: purge the
+ * originator list and add the new one orig_entry
*/
- if (flags & BATADV_TT_CLIENT_TEMP)
- goto out;
+ if (flags & BATADV_TT_CLIENT_TEMP) {
+ if (!(common->flags & BATADV_TT_CLIENT_TEMP))
+ goto out;
+ if (batadv_tt_global_entry_has_orig(tt_global_entry,
+ orig_node))
+ goto out_remove;
+ batadv_tt_global_del_orig_list(tt_global_entry);
+ goto add_orig_entry;
+ }
/* if the client was temporary added before receiving the first
* OGM announcing it, we have to clear the TEMP flag
*/
- tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_TEMP;
+ common->flags &= ~BATADV_TT_CLIENT_TEMP;
+
+ /* the change can carry possible "attribute" flags like the
+ * TT_CLIENT_WIFI, therefore they have to be copied in the
+ * client entry
+ */
+ tt_global_entry->common.flags |= flags;
/* If there is the BATADV_TT_CLIENT_ROAM flag set, there is only
* one originator left in the list and we previously received a
@@ -776,33 +874,81 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv,
* We should first delete the old originator before adding the
* new one.
*/
- if (tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM) {
+ if (common->flags & BATADV_TT_CLIENT_ROAM) {
batadv_tt_global_del_orig_list(tt_global_entry);
- tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
+ common->flags &= ~BATADV_TT_CLIENT_ROAM;
tt_global_entry->roam_at = 0;
}
}
+add_orig_entry:
/* add the new orig_entry (if needed) or update it */
batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn);
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Creating new global tt entry: %pM (via %pM)\n",
- tt_global_entry->common.addr, orig_node->orig);
+ common->addr, orig_node->orig);
+ ret = 1;
out_remove:
+
/* remove address from local hash if present */
- batadv_tt_local_remove(bat_priv, tt_global_entry->common.addr,
- "global tt received",
- flags & BATADV_TT_CLIENT_ROAM);
- ret = 1;
+ local_flags = batadv_tt_local_remove(bat_priv, tt_addr,
+ "global tt received",
+ !!(flags & BATADV_TT_CLIENT_ROAM));
+ tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI;
+
+ if (!(flags & BATADV_TT_CLIENT_ROAM))
+ /* this is a normal global add. Therefore the client is not in a
+ * roaming state anymore.
+ */
+ tt_global_entry->common.flags &= ~BATADV_TT_CLIENT_ROAM;
+
out:
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
+ if (tt_local_entry)
+ batadv_tt_local_entry_free_ref(tt_local_entry);
return ret;
}
-/* print all orig nodes who announce the address for this global entry.
- * it is assumed that the caller holds rcu_read_lock();
+/* batadv_transtable_best_orig - Get best originator list entry from tt entry
+ * @tt_global_entry: global translation table entry to be analyzed
+ *
+ * This functon assumes the caller holds rcu_read_lock().
+ * Returns best originator list entry or NULL on errors.
+ */
+static struct batadv_tt_orig_list_entry *
+batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry)
+{
+ struct batadv_neigh_node *router = NULL;
+ struct hlist_head *head;
+ struct hlist_node *node;
+ struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL;
+ int best_tq = 0;
+
+ head = &tt_global_entry->orig_list;
+ hlist_for_each_entry_rcu(orig_entry, node, head, list) {
+ router = batadv_orig_node_get_router(orig_entry->orig_node);
+ if (!router)
+ continue;
+
+ if (router->tq_avg > best_tq) {
+ best_entry = orig_entry;
+ best_tq = router->tq_avg;
+ }
+
+ batadv_neigh_node_free_ref(router);
+ }
+
+ return best_entry;
+}
+
+/* batadv_tt_global_print_entry - print all orig nodes who announce the address
+ * for this global entry
+ * @tt_global_entry: global translation table entry to be printed
+ * @seq: debugfs table seq_file struct
+ *
+ * This functon assumes the caller holds rcu_read_lock().
*/
static void
batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry,
@@ -810,21 +956,37 @@ batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry,
{
struct hlist_head *head;
struct hlist_node *node;
- struct batadv_tt_orig_list_entry *orig_entry;
+ struct batadv_tt_orig_list_entry *orig_entry, *best_entry;
struct batadv_tt_common_entry *tt_common_entry;
uint16_t flags;
uint8_t last_ttvn;
tt_common_entry = &tt_global_entry->common;
+ flags = tt_common_entry->flags;
+
+ best_entry = batadv_transtable_best_orig(tt_global_entry);
+ if (best_entry) {
+ last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn);
+ seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n",
+ '*', tt_global_entry->common.addr,
+ best_entry->ttvn, best_entry->orig_node->orig,
+ last_ttvn,
+ (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
+ (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
+ (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
+ }
head = &tt_global_entry->orig_list;
hlist_for_each_entry_rcu(orig_entry, node, head, list) {
- flags = tt_common_entry->flags;
+ if (best_entry == orig_entry)
+ continue;
+
last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn);
- seq_printf(seq, " * %pM (%3u) via %pM (%3u) [%c%c%c]\n",
- tt_global_entry->common.addr, orig_entry->ttvn,
- orig_entry->orig_node->orig, last_ttvn,
+ seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n",
+ '+', tt_global_entry->common.addr,
+ orig_entry->ttvn, orig_entry->orig_node->orig,
+ last_ttvn,
(flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'),
(flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'),
(flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.'));
@@ -842,22 +1004,10 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
struct hlist_node *node;
struct hlist_head *head;
uint32_t i;
- int ret = 0;
- primary_if = batadv_primary_if_get_selected(bat_priv);
- if (!primary_if) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - please specify interfaces to enable it\n",
- net_dev->name);
- goto out;
- }
-
- if (primary_if->if_status != BATADV_IF_ACTIVE) {
- ret = seq_printf(seq,
- "BATMAN mesh %s disabled - primary interface not active\n",
- net_dev->name);
+ primary_if = batadv_seq_print_text_primary_if_get(seq);
+ if (!primary_if)
goto out;
- }
seq_printf(seq,
"Globally announced TT entries received via the mesh %s\n",
@@ -881,7 +1031,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset)
out:
if (primary_if)
batadv_hardif_free_ref(primary_if);
- return ret;
+ return 0;
}
/* deletes the orig list of a tt_global_entry */
@@ -927,21 +1077,6 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv,
spin_unlock_bh(&tt_global_entry->list_lock);
}
-static void
-batadv_tt_global_del_struct(struct batadv_priv *bat_priv,
- struct batadv_tt_global_entry *tt_global_entry,
- const char *message)
-{
- batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Deleting global tt entry %pM: %s\n",
- tt_global_entry->common.addr, message);
-
- batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt,
- batadv_choose_orig, tt_global_entry->common.addr);
- batadv_tt_global_entry_free_ref(tt_global_entry);
-
-}
-
/* If the client is to be deleted, we check if it is the last origantor entry
* within tt_global entry. If yes, we set the BATADV_TT_CLIENT_ROAM flag and the
* timer, otherwise we simply remove the originator scheduled for deletion.
@@ -990,7 +1125,7 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
const unsigned char *addr,
const char *message, bool roaming)
{
- struct batadv_tt_global_entry *tt_global_entry = NULL;
+ struct batadv_tt_global_entry *tt_global_entry;
struct batadv_tt_local_entry *local_entry = NULL;
tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr);
@@ -1002,8 +1137,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
orig_node, message);
if (hlist_empty(&tt_global_entry->orig_list))
- batadv_tt_global_del_struct(bat_priv, tt_global_entry,
- message);
+ batadv_tt_global_free(bat_priv, tt_global_entry,
+ message);
goto out;
}
@@ -1026,7 +1161,7 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv,
if (local_entry) {
/* local entry exists, case 2: client roamed to us. */
batadv_tt_global_del_orig_list(tt_global_entry);
- batadv_tt_global_del_struct(bat_priv, tt_global_entry, message);
+ batadv_tt_global_free(bat_priv, tt_global_entry, message);
} else
/* no local entry exists, case 1: check for roaming */
batadv_tt_global_del_roaming(bat_priv, tt_global_entry,
@@ -1197,15 +1332,12 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
struct batadv_tt_local_entry *tt_local_entry = NULL;
struct batadv_tt_global_entry *tt_global_entry = NULL;
struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *router = NULL;
- struct hlist_head *head;
- struct hlist_node *node;
- struct batadv_tt_orig_list_entry *orig_entry;
- int best_tq;
+ struct batadv_tt_orig_list_entry *best_entry;
if (src && atomic_read(&bat_priv->ap_isolation)) {
tt_local_entry = batadv_tt_local_hash_find(bat_priv, src);
- if (!tt_local_entry)
+ if (!tt_local_entry ||
+ (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING))
goto out;
}
@@ -1220,25 +1352,15 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv,
_batadv_is_ap_isolated(tt_local_entry, tt_global_entry))
goto out;
- best_tq = 0;
-
rcu_read_lock();
- head = &tt_global_entry->orig_list;
- hlist_for_each_entry_rcu(orig_entry, node, head, list) {
- router = batadv_orig_node_get_router(orig_entry->orig_node);
- if (!router)
- continue;
-
- if (router->tq_avg > best_tq) {
- orig_node = orig_entry->orig_node;
- best_tq = router->tq_avg;
- }
- batadv_neigh_node_free_ref(router);
- }
+ best_entry = batadv_transtable_best_orig(tt_global_entry);
/* found anything? */
+ if (best_entry)
+ orig_node = best_entry->orig_node;
if (orig_node && !atomic_inc_not_zero(&orig_node->refcount))
orig_node = NULL;
rcu_read_unlock();
+
out:
if (tt_global_entry)
batadv_tt_global_entry_free_ref(tt_global_entry);
@@ -1471,11 +1593,11 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
tt_tot = tt_len / sizeof(struct batadv_tt_change);
len = tt_query_size + tt_len;
- skb = dev_alloc_skb(len + ETH_HLEN);
+ skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
if (!skb)
goto out;
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len);
tt_response->ttvn = ttvn;
@@ -1496,7 +1618,7 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn,
memcpy(tt_change->addr, tt_common_entry->addr,
ETH_ALEN);
- tt_change->flags = BATADV_NO_FLAGS;
+ tt_change->flags = tt_common_entry->flags;
tt_count++;
tt_change++;
@@ -1520,7 +1642,6 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
{
struct sk_buff *skb = NULL;
struct batadv_tt_query_packet *tt_request;
- struct batadv_neigh_node *neigh_node = NULL;
struct batadv_hard_iface *primary_if;
struct batadv_tt_req_node *tt_req_node = NULL;
int ret = 1;
@@ -1537,11 +1658,11 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
if (!tt_req_node)
goto out;
- skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN);
+ skb = dev_alloc_skb(sizeof(*tt_request) + ETH_HLEN + NET_IP_ALIGN);
if (!skb)
goto out;
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
tt_req_len = sizeof(*tt_request);
tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len);
@@ -1558,23 +1679,15 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv,
if (full_table)
tt_request->flags |= BATADV_TT_FULL_TABLE;
- neigh_node = batadv_orig_node_get_router(dst_orig_node);
- if (!neigh_node)
- goto out;
-
- batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending TT_REQUEST to %pM via %pM [%c]\n",
- dst_orig_node->orig, neigh_node->addr,
- (full_table ? 'F' : '.'));
+ batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n",
+ dst_orig_node->orig, (full_table ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = 0;
+ if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL))
+ ret = 0;
out:
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
if (primary_if)
batadv_hardif_free_ref(primary_if);
if (ret)
@@ -1592,9 +1705,8 @@ static bool
batadv_send_other_tt_response(struct batadv_priv *bat_priv,
struct batadv_tt_query_packet *tt_request)
{
- struct batadv_orig_node *req_dst_orig_node = NULL;
+ struct batadv_orig_node *req_dst_orig_node;
struct batadv_orig_node *res_dst_orig_node = NULL;
- struct batadv_neigh_node *neigh_node = NULL;
struct batadv_hard_iface *primary_if = NULL;
uint8_t orig_ttvn, req_ttvn, ttvn;
int ret = false;
@@ -1620,10 +1732,6 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
if (!res_dst_orig_node)
goto out;
- neigh_node = batadv_orig_node_get_router(res_dst_orig_node);
- if (!neigh_node)
- goto out;
-
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
@@ -1652,11 +1760,11 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
tt_tot = tt_len / sizeof(struct batadv_tt_change);
len = sizeof(*tt_response) + tt_len;
- skb = dev_alloc_skb(len + ETH_HLEN);
+ skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
if (!skb)
goto unlock;
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
packet_pos = skb_put(skb, len);
tt_response = (struct batadv_tt_query_packet *)packet_pos;
tt_response->ttvn = req_ttvn;
@@ -1695,14 +1803,13 @@ batadv_send_other_tt_response(struct batadv_priv *bat_priv,
tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending TT_RESPONSE %pM via %pM for %pM (ttvn: %u)\n",
- res_dst_orig_node->orig, neigh_node->addr,
- req_dst_orig_node->orig, req_ttvn);
+ "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n",
+ res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = true;
+ if (batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL))
+ ret = true;
goto out;
unlock:
@@ -1713,8 +1820,6 @@ out:
batadv_orig_node_free_ref(res_dst_orig_node);
if (req_dst_orig_node)
batadv_orig_node_free_ref(req_dst_orig_node);
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
if (primary_if)
batadv_hardif_free_ref(primary_if);
if (!ret)
@@ -1727,8 +1832,7 @@ static bool
batadv_send_my_tt_response(struct batadv_priv *bat_priv,
struct batadv_tt_query_packet *tt_request)
{
- struct batadv_orig_node *orig_node = NULL;
- struct batadv_neigh_node *neigh_node = NULL;
+ struct batadv_orig_node *orig_node;
struct batadv_hard_iface *primary_if = NULL;
uint8_t my_ttvn, req_ttvn, ttvn;
int ret = false;
@@ -1753,10 +1857,6 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
if (!orig_node)
goto out;
- neigh_node = batadv_orig_node_get_router(orig_node);
- if (!neigh_node)
- goto out;
-
primary_if = batadv_primary_if_get_selected(bat_priv);
if (!primary_if)
goto out;
@@ -1779,11 +1879,11 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
tt_tot = tt_len / sizeof(struct batadv_tt_change);
len = sizeof(*tt_response) + tt_len;
- skb = dev_alloc_skb(len + ETH_HLEN);
+ skb = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
if (!skb)
goto unlock;
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
packet_pos = skb_put(skb, len);
tt_response = (struct batadv_tt_query_packet *)packet_pos;
tt_response->ttvn = req_ttvn;
@@ -1820,14 +1920,14 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv,
tt_response->flags |= BATADV_TT_FULL_TABLE;
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending TT_RESPONSE to %pM via %pM [%c]\n",
- orig_node->orig, neigh_node->addr,
+ "Sending TT_RESPONSE to %pM [%c]\n",
+ orig_node->orig,
(tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.'));
batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = true;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+ ret = true;
goto out;
unlock:
@@ -1835,8 +1935,6 @@ unlock:
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
if (primary_if)
batadv_hardif_free_ref(primary_if);
if (!ret)
@@ -1893,7 +1991,7 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv,
static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv,
struct batadv_tt_query_packet *tt_response)
{
- struct batadv_orig_node *orig_node = NULL;
+ struct batadv_orig_node *orig_node;
orig_node = batadv_orig_hash_find(bat_priv, tt_response->src);
if (!orig_node)
@@ -1935,7 +2033,7 @@ static void batadv_tt_update_changes(struct batadv_priv *bat_priv,
bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr)
{
- struct batadv_tt_local_entry *tt_local_entry = NULL;
+ struct batadv_tt_local_entry *tt_local_entry;
bool ret = false;
tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
@@ -1944,7 +2042,8 @@ bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr)
/* Check if the client has been logically deleted (but is kept for
* consistency purpose)
*/
- if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)
+ if ((tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) ||
+ (tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM))
goto out;
ret = true;
out:
@@ -1995,10 +2094,6 @@ void batadv_handle_tt_response(struct batadv_priv *bat_priv,
/* Recalculate the CRC for this orig_node and store it */
orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node);
- /* Roaming phase is over: tables are in sync again. I can
- * unset the flag
- */
- orig_node->tt_poss_change = false;
out:
if (orig_node)
batadv_orig_node_free_ref(orig_node);
@@ -2104,7 +2199,6 @@ unlock:
static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
struct batadv_orig_node *orig_node)
{
- struct batadv_neigh_node *neigh_node = NULL;
struct sk_buff *skb = NULL;
struct batadv_roam_adv_packet *roam_adv_packet;
int ret = 1;
@@ -2117,11 +2211,11 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
if (!batadv_tt_check_roam_count(bat_priv, client))
goto out;
- skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN);
+ skb = dev_alloc_skb(sizeof(*roam_adv_packet) + ETH_HLEN + NET_IP_ALIGN);
if (!skb)
goto out;
- skb_reserve(skb, ETH_HLEN);
+ skb_reserve(skb, ETH_HLEN + NET_IP_ALIGN);
roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len);
@@ -2137,23 +2231,17 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client,
memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN);
memcpy(roam_adv_packet->client, client, ETH_ALEN);
- neigh_node = batadv_orig_node_get_router(orig_node);
- if (!neigh_node)
- goto out;
-
batadv_dbg(BATADV_DBG_TT, bat_priv,
- "Sending ROAMING_ADV to %pM (client %pM) via %pM\n",
- orig_node->orig, client, neigh_node->addr);
+ "Sending ROAMING_ADV to %pM (client %pM)\n",
+ orig_node->orig, client);
batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX);
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = 0;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+ ret = 0;
out:
- if (neigh_node)
- batadv_neigh_node_free_ref(neigh_node);
- if (ret)
+ if (ret && skb)
kfree_skb(skb);
return;
}
@@ -2289,7 +2377,6 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv,
batadv_dbg(BATADV_DBG_TT, bat_priv,
"Local changes committed, updating to ttvn %u\n",
(uint8_t)atomic_read(&bat_priv->tt.vn));
- bat_priv->tt.poss_change = false;
/* reset the sending counter */
atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX);
@@ -2401,11 +2488,6 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv,
*/
if (orig_node->tt_crc != tt_crc)
goto request_table;
-
- /* Roaming phase is over: tables are in sync again. I can
- * unset the flag
- */
- orig_node->tt_poss_change = false;
} else {
/* if we missed more than one change or our tables are not
* in sync anymore -> request fresh tt data
@@ -2438,18 +2520,51 @@ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
if (!tt_global_entry)
goto out;
- ret = tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM;
+ ret = !!(tt_global_entry->common.flags & BATADV_TT_CLIENT_ROAM);
batadv_tt_global_entry_free_ref(tt_global_entry);
out:
return ret;
}
+/**
+ * batadv_tt_local_client_is_roaming - tells whether the client is roaming
+ * @bat_priv: the bat priv with all the soft interface information
+ * @addr: the MAC address of the local client to query
+ *
+ * Returns true if the local client is known to be roaming (it is not served by
+ * this node anymore) or not. If yes, the client is still present in the table
+ * to keep the latter consistent with the node TTVN
+ */
+bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
+ uint8_t *addr)
+{
+ struct batadv_tt_local_entry *tt_local_entry;
+ bool ret = false;
+
+ tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr);
+ if (!tt_local_entry)
+ goto out;
+
+ ret = tt_local_entry->common.flags & BATADV_TT_CLIENT_ROAM;
+ batadv_tt_local_entry_free_ref(tt_local_entry);
+out:
+ return ret;
+
+}
+
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const unsigned char *addr)
{
bool ret = false;
+ /* if the originator is a backbone node (meaning it belongs to the same
+ * LAN of this node) the temporary client must not be added because to
+ * reach such destination the node must use the LAN instead of the mesh
+ */
+ if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig))
+ goto out;
+
if (!batadv_tt_global_add(bat_priv, orig_node, addr,
BATADV_TT_CLIENT_TEMP,
atomic_read(&orig_node->last_ttvn)))
diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h
index 811fffd4760..46d4451a59e 100644
--- a/net/batman-adv/translation-table.h
+++ b/net/batman-adv/translation-table.h
@@ -24,9 +24,9 @@ int batadv_tt_len(int changes_num);
int batadv_tt_init(struct batadv_priv *bat_priv);
void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr,
int ifindex);
-void batadv_tt_local_remove(struct batadv_priv *bat_priv,
- const uint8_t *addr, const char *message,
- bool roaming);
+uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv,
+ const uint8_t *addr, const char *message,
+ bool roaming);
int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset);
void batadv_tt_global_add_orig(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
@@ -59,6 +59,8 @@ int batadv_tt_append_diff(struct batadv_priv *bat_priv,
int packet_min_len);
bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv,
uint8_t *addr);
+bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv,
+ uint8_t *addr);
bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv,
struct batadv_orig_node *orig_node,
const unsigned char *addr);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index ac1e07a8045..ae9ac9aca8c 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -28,20 +28,41 @@
(ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \
sizeof(struct batadv_bcast_packet)))
+#ifdef CONFIG_BATMAN_ADV_DAT
+
+/* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed,
+ * BATADV_DAT_ADDR_MAX is changed as well.
+ *
+ * *Please be careful: batadv_dat_addr_t must be UNSIGNED*
+ */
+#define batadv_dat_addr_t uint16_t
+
+#endif /* CONFIG_BATMAN_ADV_DAT */
+
+/**
+ * struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data
+ * @ogm_buff: buffer holding the OGM packet
+ * @ogm_buff_len: length of the OGM packet buffer
+ * @ogm_seqno: OGM sequence number - used to identify each OGM
+ */
+struct batadv_hard_iface_bat_iv {
+ unsigned char *ogm_buff;
+ int ogm_buff_len;
+ atomic_t ogm_seqno;
+};
+
struct batadv_hard_iface {
struct list_head list;
int16_t if_num;
char if_status;
struct net_device *net_dev;
- atomic_t seqno;
atomic_t frag_seqno;
- unsigned char *packet_buff;
- int packet_len;
struct kobject *hardif_obj;
atomic_t refcount;
struct packet_type batman_adv_ptype;
struct net_device *soft_iface;
struct rcu_head rcu;
+ struct batadv_hard_iface_bat_iv bat_iv;
};
/**
@@ -63,6 +84,9 @@ struct batadv_orig_node {
uint8_t orig[ETH_ALEN];
uint8_t primary_addr[ETH_ALEN];
struct batadv_neigh_node __rcu *router; /* rcu protected pointer */
+#ifdef CONFIG_BATMAN_ADV_DAT
+ batadv_dat_addr_t dat_addr;
+#endif
unsigned long *bcast_own;
uint8_t *bcast_own_sum;
unsigned long last_seen;
@@ -77,13 +101,6 @@ struct batadv_orig_node {
spinlock_t tt_buff_lock; /* protects tt_buff */
atomic_t tt_size;
bool tt_initialised;
- /* The tt_poss_change flag is used to detect an ongoing roaming phase.
- * If true, then I sent a Roaming_adv to this orig_node and I have to
- * inspect every packet directed to it to check whether it is still
- * the true destination or not. This flag will be reset to false as
- * soon as I receive a new TTVN from this orig_node
- */
- bool tt_poss_change;
uint32_t last_real_seqno;
uint8_t last_ttl;
DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE);
@@ -139,7 +156,7 @@ struct batadv_neigh_node {
#ifdef CONFIG_BATMAN_ADV_BLA
struct batadv_bcast_duplist_entry {
uint8_t orig[ETH_ALEN];
- uint16_t crc;
+ __be32 crc;
unsigned long entrytime;
};
#endif
@@ -162,6 +179,13 @@ enum batadv_counters {
BATADV_CNT_TT_RESPONSE_RX,
BATADV_CNT_TT_ROAM_ADV_TX,
BATADV_CNT_TT_ROAM_ADV_RX,
+#ifdef CONFIG_BATMAN_ADV_DAT
+ BATADV_CNT_DAT_GET_TX,
+ BATADV_CNT_DAT_GET_RX,
+ BATADV_CNT_DAT_PUT_TX,
+ BATADV_CNT_DAT_PUT_RX,
+ BATADV_CNT_DAT_CACHED_REPLY_TX,
+#endif
BATADV_CNT_NUM,
};
@@ -181,7 +205,6 @@ struct batadv_priv_tt {
atomic_t vn;
atomic_t ogm_append_cnt;
atomic_t local_changes;
- bool poss_change;
struct list_head changes_list;
struct batadv_hashtable *local_hash;
struct batadv_hashtable *global_hash;
@@ -228,6 +251,20 @@ struct batadv_priv_vis {
struct batadv_vis_info *my_info;
};
+/**
+ * struct batadv_priv_dat - per mesh interface DAT private data
+ * @addr: node DAT address
+ * @hash: hashtable representing the local ARP cache
+ * @work: work queue callback item for cache purging
+ */
+#ifdef CONFIG_BATMAN_ADV_DAT
+struct batadv_priv_dat {
+ batadv_dat_addr_t addr;
+ struct batadv_hashtable *hash;
+ struct delayed_work work;
+};
+#endif
+
struct batadv_priv {
atomic_t mesh_state;
struct net_device_stats stats;
@@ -237,6 +274,9 @@ struct batadv_priv {
atomic_t fragmentation; /* boolean */
atomic_t ap_isolation; /* boolean */
atomic_t bridge_loop_avoidance; /* boolean */
+#ifdef CONFIG_BATMAN_ADV_DAT
+ atomic_t distributed_arp_table; /* boolean */
+#endif
atomic_t vis_mode; /* VIS_TYPE_* */
atomic_t gw_mode; /* GW_MODE_* */
atomic_t gw_sel_class; /* uint */
@@ -255,7 +295,7 @@ struct batadv_priv {
struct hlist_head forw_bcast_list;
struct batadv_hashtable *orig_hash;
spinlock_t forw_bat_list_lock; /* protects forw_bat_list */
- spinlock_t forw_bcast_list_lock; /* protects */
+ spinlock_t forw_bcast_list_lock; /* protects forw_bcast_list */
struct delayed_work orig_work;
struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */
struct batadv_algo_ops *bat_algo_ops;
@@ -265,6 +305,9 @@ struct batadv_priv {
struct batadv_priv_gw gw;
struct batadv_priv_tt tt;
struct batadv_priv_vis vis;
+#ifdef CONFIG_BATMAN_ADV_DAT
+ struct batadv_priv_dat dat;
+#endif
};
struct batadv_socket_client {
@@ -318,6 +361,7 @@ struct batadv_backbone_gw {
struct hlist_node hash_entry;
struct batadv_priv *bat_priv;
unsigned long lasttime; /* last time we heard of this backbone gw */
+ atomic_t wait_periods;
atomic_t request_sent;
atomic_t refcount;
struct rcu_head rcu;
@@ -437,4 +481,36 @@ struct batadv_algo_ops {
void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet);
};
+/**
+ * struct batadv_dat_entry - it is a single entry of batman-adv ARP backend. It
+ * is used to stored ARP entries needed for the global DAT cache
+ * @ip: the IPv4 corresponding to this DAT/ARP entry
+ * @mac_addr: the MAC address associated to the stored IPv4
+ * @last_update: time in jiffies when this entry was refreshed last time
+ * @hash_entry: hlist node for batadv_priv_dat::hash
+ * @refcount: number of contexts the object is used
+ * @rcu: struct used for freeing in an RCU-safe manner
+ */
+struct batadv_dat_entry {
+ __be32 ip;
+ uint8_t mac_addr[ETH_ALEN];
+ unsigned long last_update;
+ struct hlist_node hash_entry;
+ atomic_t refcount;
+ struct rcu_head rcu;
+};
+
+/**
+ * struct batadv_dat_candidate - candidate destination for DAT operations
+ * @type: the type of the selected candidate. It can one of the following:
+ * - BATADV_DAT_CANDIDATE_NOT_FOUND
+ * - BATADV_DAT_CANDIDATE_ORIG
+ * @orig_node: if type is BATADV_DAT_CANDIDATE_ORIG this field points to the
+ * corresponding originator node structure
+ */
+struct batadv_dat_candidate {
+ int type;
+ struct batadv_orig_node *orig_node;
+};
+
#endif /* _NET_BATMAN_ADV_TYPES_H_ */
diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c
index f39723281ca..10aff49fcf2 100644
--- a/net/batman-adv/unicast.c
+++ b/net/batman-adv/unicast.c
@@ -291,14 +291,118 @@ out:
return ret;
}
-int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv)
+/**
+ * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the
+ * common fields for unicast packets
+ * @skb: packet
+ * @hdr_size: amount of bytes to push at the beginning of the skb
+ * @orig_node: the destination node
+ *
+ * Returns false if the buffer extension was not possible or true otherwise
+ */
+static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size,
+ struct batadv_orig_node *orig_node)
+{
+ struct batadv_unicast_packet *unicast_packet;
+ uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
+
+ if (batadv_skb_head_push(skb, hdr_size) < 0)
+ return false;
+
+ unicast_packet = (struct batadv_unicast_packet *)skb->data;
+ unicast_packet->header.version = BATADV_COMPAT_VERSION;
+ /* batman packet type: unicast */
+ unicast_packet->header.packet_type = BATADV_UNICAST;
+ /* set unicast ttl */
+ unicast_packet->header.ttl = BATADV_TTL;
+ /* copy the destination for faster routing */
+ memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
+ /* set the destination tt version number */
+ unicast_packet->ttvn = ttvn;
+
+ return true;
+}
+
+/**
+ * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise
+ */
+static bool batadv_unicast_prepare_skb(struct sk_buff *skb,
+ struct batadv_orig_node *orig_node)
+{
+ size_t uni_size = sizeof(struct batadv_unicast_packet);
+ return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node);
+}
+
+/**
+ * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr
+ * header
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the skb containing the payload to encapsulate
+ * @orig_node: the destination node
+ * @packet_subtype: the batman 4addr packet subtype to use
+ *
+ * Returns false if the payload could not be encapsulated or true otherwise
+ */
+bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ struct batadv_orig_node *orig,
+ int packet_subtype)
+{
+ struct batadv_hard_iface *primary_if;
+ struct batadv_unicast_4addr_packet *unicast_4addr_packet;
+ bool ret = false;
+
+ primary_if = batadv_primary_if_get_selected(bat_priv);
+ if (!primary_if)
+ goto out;
+
+ /* pull the header space and fill the unicast_packet substructure.
+ * We can do that because the first member of the unicast_4addr_packet
+ * is of type struct unicast_packet
+ */
+ if (!batadv_unicast_push_and_fill_skb(skb,
+ sizeof(*unicast_4addr_packet),
+ orig))
+ goto out;
+
+ unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data;
+ unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR;
+ memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr,
+ ETH_ALEN);
+ unicast_4addr_packet->subtype = packet_subtype;
+ unicast_4addr_packet->reserved = 0;
+
+ ret = true;
+out:
+ if (primary_if)
+ batadv_hardif_free_ref(primary_if);
+ return ret;
+}
+
+/**
+ * batadv_unicast_generic_send_skb - send an skb as unicast
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: payload to send
+ * @packet_type: the batman unicast packet type to use
+ * @packet_subtype: the batman packet subtype. It is ignored if packet_type is
+ * not BATADV_UNICAT_4ADDR
+ *
+ * Returns 1 in case of error or 0 otherwise
+ */
+int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype)
{
struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
struct batadv_unicast_packet *unicast_packet;
struct batadv_orig_node *orig_node;
struct batadv_neigh_node *neigh_node;
int data_len = skb->len;
- int ret = 1;
+ int ret = NET_RX_DROP;
unsigned int dev_mtu;
/* get routing information */
@@ -324,21 +428,23 @@ find_router:
if (!neigh_node)
goto out;
- if (batadv_skb_head_push(skb, sizeof(*unicast_packet)) < 0)
+ switch (packet_type) {
+ case BATADV_UNICAST:
+ batadv_unicast_prepare_skb(skb, orig_node);
+ break;
+ case BATADV_UNICAST_4ADDR:
+ batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
+ packet_subtype);
+ break;
+ default:
+ /* this function supports UNICAST and UNICAST_4ADDR only. It
+ * should never be invoked with any other packet type
+ */
goto out;
+ }
unicast_packet = (struct batadv_unicast_packet *)skb->data;
- unicast_packet->header.version = BATADV_COMPAT_VERSION;
- /* batman packet type: unicast */
- unicast_packet->header.packet_type = BATADV_UNICAST;
- /* set unicast ttl */
- unicast_packet->header.ttl = BATADV_TTL;
- /* copy the destination for faster routing */
- memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
- /* set the destination tt version number */
- unicast_packet->ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn);
-
/* inform the destination node that we are still missing a correct route
* for this client. The destination will receive this packet and will
* try to reroute it because the ttvn contained in the header is less
@@ -348,7 +454,9 @@ find_router:
unicast_packet->ttvn = unicast_packet->ttvn - 1;
dev_mtu = neigh_node->if_incoming->net_dev->mtu;
- if (atomic_read(&bat_priv->fragmentation) &&
+ /* fragmentation mechanism only works for UNICAST (now) */
+ if (packet_type == BATADV_UNICAST &&
+ atomic_read(&bat_priv->fragmentation) &&
data_len + sizeof(*unicast_packet) > dev_mtu) {
/* send frag skb decreases ttl */
unicast_packet->header.ttl++;
@@ -358,16 +466,15 @@ find_router:
goto out;
}
- batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr);
- ret = 0;
- goto out;
+ if (batadv_send_skb_to_orig(skb, orig_node, NULL))
+ ret = 0;
out:
if (neigh_node)
batadv_neigh_node_free_ref(neigh_node);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
- if (ret == 1)
+ if (ret == NET_RX_DROP)
kfree_skb(skb);
return ret;
}
diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h
index 1c46e2eb1ef..61abba58bd8 100644
--- a/net/batman-adv/unicast.h
+++ b/net/batman-adv/unicast.h
@@ -29,10 +29,44 @@ int batadv_frag_reassemble_skb(struct sk_buff *skb,
struct batadv_priv *bat_priv,
struct sk_buff **new_skb);
void batadv_frag_list_free(struct list_head *head);
-int batadv_unicast_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv);
int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv,
struct batadv_hard_iface *hard_iface,
const uint8_t dstaddr[]);
+bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ struct batadv_orig_node *orig_node,
+ int packet_subtype);
+int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb, int packet_type,
+ int packet_subtype);
+
+
+/**
+ * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ */
+static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb)
+{
+ return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST,
+ 0);
+}
+
+/**
+ * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet
+ * @bat_priv: the bat priv with all the soft interface information
+ * @skb: the payload to send
+ * @packet_subtype: the batman 4addr packet subtype to use
+ */
+static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv,
+ struct sk_buff *skb,
+ int packet_subtype)
+{
+ return batadv_unicast_generic_send_skb(bat_priv, skb,
+ BATADV_UNICAST_4ADDR,
+ packet_subtype);
+}
static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu)
{
diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index 5abd1454fb0..0f65a9de5f7 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -396,12 +396,12 @@ batadv_add_packet(struct batadv_priv *bat_priv,
return NULL;
len = sizeof(*packet) + vis_info_len;
- info->skb_packet = dev_alloc_skb(len + ETH_HLEN);
+ info->skb_packet = dev_alloc_skb(len + ETH_HLEN + NET_IP_ALIGN);
if (!info->skb_packet) {
kfree(info);
return NULL;
}
- skb_reserve(info->skb_packet, ETH_HLEN);
+ skb_reserve(info->skb_packet, ETH_HLEN + NET_IP_ALIGN);
packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len);
kref_init(&info->refcount);
@@ -698,15 +698,12 @@ static void batadv_purge_vis_packets(struct batadv_priv *bat_priv)
static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
- struct batadv_neigh_node *router;
struct batadv_hashtable *hash = bat_priv->orig_hash;
struct hlist_node *node;
struct hlist_head *head;
struct batadv_orig_node *orig_node;
struct batadv_vis_packet *packet;
struct sk_buff *skb;
- struct batadv_hard_iface *hard_iface;
- uint8_t dstaddr[ETH_ALEN];
uint32_t i;
@@ -722,30 +719,20 @@ static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv,
if (!(orig_node->flags & BATADV_VIS_SERVER))
continue;
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
- continue;
-
/* don't send it if we already received the packet from
* this node.
*/
if (batadv_recv_list_is_in(bat_priv, &info->recv_list,
- orig_node->orig)) {
- batadv_neigh_node_free_ref(router);
+ orig_node->orig))
continue;
- }
memcpy(packet->target_orig, orig_node->orig, ETH_ALEN);
- hard_iface = router->if_incoming;
- memcpy(dstaddr, router->addr, ETH_ALEN);
-
- batadv_neigh_node_free_ref(router);
-
skb = skb_clone(info->skb_packet, GFP_ATOMIC);
- if (skb)
- batadv_send_skb_packet(skb, hard_iface,
- dstaddr);
+ if (!skb)
+ continue;
+ if (!batadv_send_skb_to_orig(skb, orig_node, NULL))
+ kfree_skb(skb);
}
rcu_read_unlock();
}
@@ -755,7 +742,6 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
struct batadv_vis_info *info)
{
struct batadv_orig_node *orig_node;
- struct batadv_neigh_node *router = NULL;
struct sk_buff *skb;
struct batadv_vis_packet *packet;
@@ -765,17 +751,14 @@ static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv,
if (!orig_node)
goto out;
- router = batadv_orig_node_get_router(orig_node);
- if (!router)
+ skb = skb_clone(info->skb_packet, GFP_ATOMIC);
+ if (!skb)
goto out;
- skb = skb_clone(info->skb_packet, GFP_ATOMIC);
- if (skb)
- batadv_send_skb_packet(skb, router->if_incoming, router->addr);
+ if (!batadv_send_skb_to_orig(skb, orig_node, NULL))
+ kfree_skb(skb);
out:
- if (router)
- batadv_neigh_node_free_ref(router);
if (orig_node)
batadv_orig_node_free_ref(orig_node);
}
@@ -873,12 +856,13 @@ int batadv_vis_init(struct batadv_priv *bat_priv)
if (!bat_priv->vis.my_info)
goto err;
- len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN;
+ len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE;
+ len += ETH_HLEN + NET_IP_ALIGN;
bat_priv->vis.my_info->skb_packet = dev_alloc_skb(len);
if (!bat_priv->vis.my_info->skb_packet)
goto free_info;
- skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN);
+ skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN + NET_IP_ALIGN);
tmp_skb = bat_priv->vis.my_info->skb_packet;
packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet));
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 3537d385035..d3f3f7b1d32 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -11,6 +11,7 @@ menuconfig BT
select CRYPTO_BLKCIPHER
select CRYPTO_AES
select CRYPTO_ECB
+ select CRYPTO_SHA256
help
Bluetooth is low-cost, low-power, short-range wireless technology.
It was designed as a replacement for cables and other short-range
@@ -47,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
source "drivers/bluetooth/Kconfig"
-
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index fa6d94a4602..dea6a287dac 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,4 +10,4 @@ obj-$(CONFIG_BT_HIDP) += hidp/
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
- a2mp.o
+ a2mp.o amp.o
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 0760d1fed6f..2f67d5ecc90 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -16,6 +16,11 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/amp.h>
+
+/* Global AMP Manager list */
+LIST_HEAD(amp_mgr_list);
+DEFINE_MUTEX(amp_mgr_list_lock);
/* A2MP build & send command helper functions */
static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
@@ -37,8 +42,7 @@ static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
return cmd;
}
-static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
- void *data)
+void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
{
struct l2cap_chan *chan = mgr->a2mp_chan;
struct a2mp_cmd *cmd;
@@ -63,6 +67,14 @@ static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len,
kfree(cmd);
}
+u8 __next_ident(struct amp_mgr *mgr)
+{
+ if (++mgr->ident == 0)
+ mgr->ident = 1;
+
+ return mgr->ident;
+}
+
static inline void __a2mp_cl_bredr(struct a2mp_cl *cl)
{
cl->id = 0;
@@ -161,6 +173,83 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
return 0;
}
+static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_discov_rsp *rsp = (void *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct a2mp_cl *cl;
+ u16 ext_feat;
+ bool found = false;
+
+ if (len < sizeof(*rsp))
+ return -EINVAL;
+
+ len -= sizeof(*rsp);
+ skb_pull(skb, sizeof(*rsp));
+
+ ext_feat = le16_to_cpu(rsp->ext_feat);
+
+ BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat);
+
+ /* check that packet is not broken for now */
+ while (ext_feat & A2MP_FEAT_EXT) {
+ if (len < sizeof(ext_feat))
+ return -EINVAL;
+
+ ext_feat = get_unaligned_le16(skb->data);
+ BT_DBG("efm 0x%4.4x", ext_feat);
+ len -= sizeof(ext_feat);
+ skb_pull(skb, sizeof(ext_feat));
+ }
+
+ cl = (void *) skb->data;
+ while (len >= sizeof(*cl)) {
+ BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
+ cl->status);
+
+ if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) {
+ struct a2mp_info_req req;
+
+ found = true;
+ req.id = cl->id;
+ a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr),
+ sizeof(req), &req);
+ }
+
+ len -= sizeof(*cl);
+ cl = (void *) skb_pull(skb, sizeof(*cl));
+ }
+
+ /* Fall back to L2CAP init sequence */
+ if (!found) {
+ struct l2cap_conn *conn = mgr->l2cap_conn;
+ struct l2cap_chan *chan;
+
+ mutex_lock(&conn->chan_lock);
+
+ list_for_each_entry(chan, &conn->chan_l, list) {
+
+ BT_DBG("chan %p state %s", chan,
+ state_to_string(chan->state));
+
+ if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP)
+ continue;
+
+ l2cap_chan_lock(chan);
+
+ if (chan->state == BT_CONNECT)
+ l2cap_send_conn_req(chan);
+
+ l2cap_chan_unlock(chan);
+ }
+
+ mutex_unlock(&conn->chan_lock);
+ }
+
+ return 0;
+}
+
static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
{
@@ -181,7 +270,6 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
{
struct a2mp_info_req *req = (void *) skb->data;
- struct a2mp_info_rsp rsp;
struct hci_dev *hdev;
if (le16_to_cpu(hdr->len) < sizeof(*req))
@@ -189,53 +277,93 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
BT_DBG("id %d", req->id);
- rsp.id = req->id;
- rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
-
hdev = hci_dev_get(req->id);
- if (hdev && hdev->amp_type != HCI_BREDR) {
- rsp.status = 0;
- rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
- rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
- rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
- rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
- rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
+ if (!hdev || hdev->dev_type != HCI_AMP) {
+ struct a2mp_info_rsp rsp;
+
+ rsp.id = req->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp),
+ &rsp);
+
+ goto done;
}
+ mgr->state = READ_LOC_AMP_INFO;
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
+
+done:
if (hdev)
hci_dev_put(hdev);
- a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), &rsp);
-
skb_pull(skb, sizeof(*req));
return 0;
}
+static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data;
+ struct a2mp_amp_assoc_req req;
+ struct amp_ctrl *ctrl;
+
+ if (le16_to_cpu(hdr->len) < sizeof(*rsp))
+ return -EINVAL;
+
+ BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status);
+
+ if (rsp->status)
+ return -EINVAL;
+
+ ctrl = amp_ctrl_add(mgr, rsp->id);
+ if (!ctrl)
+ return -ENOMEM;
+
+ req.id = rsp->id;
+ a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req),
+ &req);
+
+ skb_pull(skb, sizeof(*rsp));
+ return 0;
+}
+
static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
{
struct a2mp_amp_assoc_req *req = (void *) skb->data;
struct hci_dev *hdev;
+ struct amp_mgr *tmp;
if (le16_to_cpu(hdr->len) < sizeof(*req))
return -EINVAL;
BT_DBG("id %d", req->id);
+ /* Make sure that other request is not processed */
+ tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
+
hdev = hci_dev_get(req->id);
- if (!hdev || hdev->amp_type == HCI_BREDR) {
+ if (!hdev || hdev->amp_type == HCI_BREDR || tmp) {
struct a2mp_amp_assoc_rsp rsp;
rsp.id = req->id;
- rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ if (tmp) {
+ rsp.status = A2MP_STATUS_COLLISION_OCCURED;
+ amp_mgr_put(tmp);
+ } else {
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+ }
a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
&rsp);
- goto clean;
+
+ goto done;
}
- /* Placeholder for HCI Read AMP Assoc */
+ amp_read_loc_assoc(hdev, mgr);
-clean:
+done:
if (hdev)
hci_dev_put(hdev);
@@ -243,6 +371,68 @@ clean:
return 0;
}
+static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
+ struct a2mp_cmd *hdr)
+{
+ struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data;
+ u16 len = le16_to_cpu(hdr->len);
+ struct hci_dev *hdev;
+ struct amp_ctrl *ctrl;
+ struct hci_conn *hcon;
+ size_t assoc_len;
+
+ if (len < sizeof(*rsp))
+ return -EINVAL;
+
+ assoc_len = len - sizeof(*rsp);
+
+ BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status,
+ assoc_len);
+
+ if (rsp->status)
+ return -EINVAL;
+
+ /* Save remote ASSOC data */
+ ctrl = amp_ctrl_lookup(mgr, rsp->id);
+ if (ctrl) {
+ u8 *assoc;
+
+ assoc = kzalloc(assoc_len, GFP_KERNEL);
+ if (!assoc) {
+ amp_ctrl_put(ctrl);
+ return -ENOMEM;
+ }
+
+ memcpy(assoc, rsp->amp_assoc, assoc_len);
+ ctrl->assoc = assoc;
+ ctrl->assoc_len = assoc_len;
+ ctrl->assoc_rem_len = assoc_len;
+ ctrl->assoc_len_so_far = 0;
+
+ amp_ctrl_put(ctrl);
+ }
+
+ /* Create Phys Link */
+ hdev = hci_dev_get(rsp->id);
+ if (!hdev)
+ return -EINVAL;
+
+ hcon = phylink_add(hdev, mgr, rsp->id, true);
+ if (!hcon)
+ goto done;
+
+ BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id);
+
+ mgr->bredr_chan->remote_amp_id = rsp->id;
+
+ amp_create_phylink(hdev, mgr, hcon);
+
+done:
+ hci_dev_put(hdev);
+ skb_pull(skb, len);
+ return 0;
+}
+
static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_cmd *hdr)
{
@@ -250,6 +440,8 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_physlink_rsp rsp;
struct hci_dev *hdev;
+ struct hci_conn *hcon;
+ struct amp_ctrl *ctrl;
if (le16_to_cpu(hdr->len) < sizeof(*req))
return -EINVAL;
@@ -265,9 +457,43 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
goto send_rsp;
}
- /* TODO process physlink create */
+ ctrl = amp_ctrl_lookup(mgr, rsp.remote_id);
+ if (!ctrl) {
+ ctrl = amp_ctrl_add(mgr, rsp.remote_id);
+ if (ctrl) {
+ amp_ctrl_get(ctrl);
+ } else {
+ rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
+ goto send_rsp;
+ }
+ }
- rsp.status = A2MP_STATUS_SUCCESS;
+ if (ctrl) {
+ size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req);
+ u8 *assoc;
+
+ assoc = kzalloc(assoc_len, GFP_KERNEL);
+ if (!assoc) {
+ amp_ctrl_put(ctrl);
+ return -ENOMEM;
+ }
+
+ memcpy(assoc, req->amp_assoc, assoc_len);
+ ctrl->assoc = assoc;
+ ctrl->assoc_len = assoc_len;
+ ctrl->assoc_rem_len = assoc_len;
+ ctrl->assoc_len_so_far = 0;
+
+ amp_ctrl_put(ctrl);
+ }
+
+ hcon = phylink_add(hdev, mgr, req->local_id, false);
+ if (hcon) {
+ amp_accept_phylink(hdev, mgr, hcon);
+ rsp.status = A2MP_STATUS_SUCCESS;
+ } else {
+ rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
+ }
send_rsp:
if (hdev)
@@ -286,6 +512,7 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
struct a2mp_physlink_req *req = (void *) skb->data;
struct a2mp_physlink_rsp rsp;
struct hci_dev *hdev;
+ struct hci_conn *hcon;
if (le16_to_cpu(hdr->len) < sizeof(*req))
return -EINVAL;
@@ -296,14 +523,22 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
rsp.remote_id = req->local_id;
rsp.status = A2MP_STATUS_SUCCESS;
- hdev = hci_dev_get(req->local_id);
+ hdev = hci_dev_get(req->remote_id);
if (!hdev) {
rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
goto send_rsp;
}
+ hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst);
+ if (!hcon) {
+ BT_ERR("No phys link exist");
+ rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
+ goto clean;
+ }
+
/* TODO Disconnect Phys Link here */
+clean:
hci_dev_put(hdev);
send_rsp:
@@ -377,10 +612,19 @@ static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
err = a2mp_discphyslink_req(mgr, skb, hdr);
break;
- case A2MP_CHANGE_RSP:
case A2MP_DISCOVER_RSP:
+ err = a2mp_discover_rsp(mgr, skb, hdr);
+ break;
+
case A2MP_GETINFO_RSP:
+ err = a2mp_getinfo_rsp(mgr, skb, hdr);
+ break;
+
case A2MP_GETAMPASSOC_RSP:
+ err = a2mp_getampassoc_rsp(mgr, skb, hdr);
+ break;
+
+ case A2MP_CHANGE_RSP:
case A2MP_CREATEPHYSLINK_RSP:
case A2MP_DISCONNPHYSLINK_RSP:
err = a2mp_cmd_rsp(mgr, skb, hdr);
@@ -455,9 +699,10 @@ static struct l2cap_ops a2mp_chan_ops = {
.new_connection = l2cap_chan_no_new_connection,
.teardown = l2cap_chan_no_teardown,
.ready = l2cap_chan_no_ready,
+ .defer = l2cap_chan_no_defer,
};
-static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
+static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
{
struct l2cap_chan *chan;
int err;
@@ -492,7 +737,10 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
chan->conf_state = 0;
- l2cap_chan_add(conn, chan);
+ if (locked)
+ __l2cap_chan_add(conn, chan);
+ else
+ l2cap_chan_add(conn, chan);
chan->remote_mps = chan->omtu;
chan->mps = chan->omtu;
@@ -503,11 +751,13 @@ static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn)
}
/* AMP Manager functions */
-void amp_mgr_get(struct amp_mgr *mgr)
+struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr)
{
BT_DBG("mgr %p orig refcnt %d", mgr, atomic_read(&mgr->kref.refcount));
kref_get(&mgr->kref);
+
+ return mgr;
}
static void amp_mgr_destroy(struct kref *kref)
@@ -516,6 +766,11 @@ static void amp_mgr_destroy(struct kref *kref)
BT_DBG("mgr %p", mgr);
+ mutex_lock(&amp_mgr_list_lock);
+ list_del(&mgr->list);
+ mutex_unlock(&amp_mgr_list_lock);
+
+ amp_ctrl_list_flush(mgr);
kfree(mgr);
}
@@ -526,7 +781,7 @@ int amp_mgr_put(struct amp_mgr *mgr)
return kref_put(&mgr->kref, &amp_mgr_destroy);
}
-static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
+static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked)
{
struct amp_mgr *mgr;
struct l2cap_chan *chan;
@@ -539,7 +794,7 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
mgr->l2cap_conn = conn;
- chan = a2mp_chan_open(conn);
+ chan = a2mp_chan_open(conn, locked);
if (!chan) {
kfree(mgr);
return NULL;
@@ -552,6 +807,14 @@ static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn)
kref_init(&mgr->kref);
+ /* Remote AMP ctrl list initialization */
+ INIT_LIST_HEAD(&mgr->amp_ctrls);
+ mutex_init(&mgr->amp_ctrls_lock);
+
+ mutex_lock(&amp_mgr_list_lock);
+ list_add(&mgr->list, &amp_mgr_list);
+ mutex_unlock(&amp_mgr_list_lock);
+
return mgr;
}
@@ -560,7 +823,7 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
{
struct amp_mgr *mgr;
- mgr = amp_mgr_create(conn);
+ mgr = amp_mgr_create(conn, false);
if (!mgr) {
BT_ERR("Could not create AMP manager");
return NULL;
@@ -570,3 +833,139 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
return mgr->a2mp_chan;
}
+
+struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
+{
+ struct amp_mgr *mgr;
+
+ mutex_lock(&amp_mgr_list_lock);
+ list_for_each_entry(mgr, &amp_mgr_list, list) {
+ if (mgr->state == state) {
+ amp_mgr_get(mgr);
+ mutex_unlock(&amp_mgr_list_lock);
+ return mgr;
+ }
+ }
+ mutex_unlock(&amp_mgr_list_lock);
+
+ return NULL;
+}
+
+void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
+{
+ struct amp_mgr *mgr;
+ struct a2mp_info_rsp rsp;
+
+ mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO);
+ if (!mgr)
+ return;
+
+ BT_DBG("%s mgr %p", hdev->name, mgr);
+
+ rsp.id = hdev->id;
+ rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
+
+ if (hdev->amp_type != HCI_BREDR) {
+ rsp.status = 0;
+ rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
+ rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
+ rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
+ rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
+ rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
+ }
+
+ a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp);
+ amp_mgr_put(mgr);
+}
+
+void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status)
+{
+ struct amp_mgr *mgr;
+ struct amp_assoc *loc_assoc = &hdev->loc_assoc;
+ struct a2mp_amp_assoc_rsp *rsp;
+ size_t len;
+
+ mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
+ if (!mgr)
+ return;
+
+ BT_DBG("%s mgr %p", hdev->name, mgr);
+
+ len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len;
+ rsp = kzalloc(len, GFP_KERNEL);
+ if (!rsp) {
+ amp_mgr_put(mgr);
+ return;
+ }
+
+ rsp->id = hdev->id;
+
+ if (status) {
+ rsp->status = A2MP_STATUS_INVALID_CTRL_ID;
+ } else {
+ rsp->status = A2MP_STATUS_SUCCESS;
+ memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len);
+ }
+
+ a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp);
+ amp_mgr_put(mgr);
+ kfree(rsp);
+}
+
+void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status)
+{
+ struct amp_mgr *mgr;
+ struct amp_assoc *loc_assoc = &hdev->loc_assoc;
+ struct a2mp_physlink_req *req;
+ struct l2cap_chan *bredr_chan;
+ size_t len;
+
+ mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL);
+ if (!mgr)
+ return;
+
+ len = sizeof(*req) + loc_assoc->len;
+
+ BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len);
+
+ req = kzalloc(len, GFP_KERNEL);
+ if (!req) {
+ amp_mgr_put(mgr);
+ return;
+ }
+
+ bredr_chan = mgr->bredr_chan;
+ if (!bredr_chan)
+ goto clean;
+
+ req->local_id = hdev->id;
+ req->remote_id = bredr_chan->remote_amp_id;
+ memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len);
+
+ a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req);
+
+clean:
+ amp_mgr_put(mgr);
+ kfree(req);
+}
+
+void a2mp_discover_amp(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct amp_mgr *mgr = conn->hcon->amp_mgr;
+ struct a2mp_discov_req req;
+
+ BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr);
+
+ if (!mgr) {
+ mgr = amp_mgr_create(conn, true);
+ if (!mgr)
+ return;
+ }
+
+ mgr->bredr_chan = chan;
+
+ req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
+ req.ext_feat = 0;
+ a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
+}
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index ba033f09196..5355df63d39 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -569,7 +569,6 @@ static int bt_seq_show(struct seq_file *seq, void *v)
{
struct bt_seq_state *s = seq->private;
struct bt_sock_list *l = s->l;
- bdaddr_t src_baswapped, dst_baswapped;
if (v == SEQ_START_TOKEN) {
seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent");
@@ -583,18 +582,17 @@ static int bt_seq_show(struct seq_file *seq, void *v)
} else {
struct sock *sk = sk_entry(v);
struct bt_sock *bt = bt_sk(sk);
- baswap(&src_baswapped, &bt->src);
- baswap(&dst_baswapped, &bt->dst);
- seq_printf(seq, "%pK %-6d %-6u %-6u %-6u %-6lu %pM %pM %-6lu",
+ seq_printf(seq,
+ "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu",
sk,
atomic_read(&sk->sk_refcnt),
sk_rmem_alloc_get(sk),
sk_wmem_alloc_get(sk),
from_kuid(seq_user_ns(seq), sock_i_uid(sk)),
sock_i_ino(sk),
- &src_baswapped,
- &dst_baswapped,
+ &bt->src,
+ &bt->dst,
bt->parent? sock_i_ino(bt->parent): 0LU);
if (l->custom_seq_show) {
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
new file mode 100644
index 00000000000..1b0d92c0643
--- /dev/null
+++ b/net/bluetooth/amp.c
@@ -0,0 +1,471 @@
+/*
+ Copyright (c) 2011,2012 Intel Corp.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 and
+ only version 2 as published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*/
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/amp.h>
+#include <crypto/hash.h>
+
+/* Remote AMP Controllers interface */
+void amp_ctrl_get(struct amp_ctrl *ctrl)
+{
+ BT_DBG("ctrl %p orig refcnt %d", ctrl,
+ atomic_read(&ctrl->kref.refcount));
+
+ kref_get(&ctrl->kref);
+}
+
+static void amp_ctrl_destroy(struct kref *kref)
+{
+ struct amp_ctrl *ctrl = container_of(kref, struct amp_ctrl, kref);
+
+ BT_DBG("ctrl %p", ctrl);
+
+ kfree(ctrl->assoc);
+ kfree(ctrl);
+}
+
+int amp_ctrl_put(struct amp_ctrl *ctrl)
+{
+ BT_DBG("ctrl %p orig refcnt %d", ctrl,
+ atomic_read(&ctrl->kref.refcount));
+
+ return kref_put(&ctrl->kref, &amp_ctrl_destroy);
+}
+
+struct amp_ctrl *amp_ctrl_add(struct amp_mgr *mgr, u8 id)
+{
+ struct amp_ctrl *ctrl;
+
+ ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return NULL;
+
+ kref_init(&ctrl->kref);
+ ctrl->id = id;
+
+ mutex_lock(&mgr->amp_ctrls_lock);
+ list_add(&ctrl->list, &mgr->amp_ctrls);
+ mutex_unlock(&mgr->amp_ctrls_lock);
+
+ BT_DBG("mgr %p ctrl %p", mgr, ctrl);
+
+ return ctrl;
+}
+
+void amp_ctrl_list_flush(struct amp_mgr *mgr)
+{
+ struct amp_ctrl *ctrl, *n;
+
+ BT_DBG("mgr %p", mgr);
+
+ mutex_lock(&mgr->amp_ctrls_lock);
+ list_for_each_entry_safe(ctrl, n, &mgr->amp_ctrls, list) {
+ list_del(&ctrl->list);
+ amp_ctrl_put(ctrl);
+ }
+ mutex_unlock(&mgr->amp_ctrls_lock);
+}
+
+struct amp_ctrl *amp_ctrl_lookup(struct amp_mgr *mgr, u8 id)
+{
+ struct amp_ctrl *ctrl;
+
+ BT_DBG("mgr %p id %d", mgr, id);
+
+ mutex_lock(&mgr->amp_ctrls_lock);
+ list_for_each_entry(ctrl, &mgr->amp_ctrls, list) {
+ if (ctrl->id == id) {
+ amp_ctrl_get(ctrl);
+ mutex_unlock(&mgr->amp_ctrls_lock);
+ return ctrl;
+ }
+ }
+ mutex_unlock(&mgr->amp_ctrls_lock);
+
+ return NULL;
+}
+
+/* Physical Link interface */
+static u8 __next_handle(struct amp_mgr *mgr)
+{
+ if (++mgr->handle == 0)
+ mgr->handle = 1;
+
+ return mgr->handle;
+}
+
+struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
+ u8 remote_id, bool out)
+{
+ bdaddr_t *dst = mgr->l2cap_conn->dst;
+ struct hci_conn *hcon;
+
+ hcon = hci_conn_add(hdev, AMP_LINK, dst);
+ if (!hcon)
+ return NULL;
+
+ BT_DBG("hcon %p dst %pMR", hcon, dst);
+
+ hcon->state = BT_CONNECT;
+ hcon->attempt++;
+ hcon->handle = __next_handle(mgr);
+ hcon->remote_id = remote_id;
+ hcon->amp_mgr = amp_mgr_get(mgr);
+ hcon->out = out;
+
+ return hcon;
+}
+
+/* AMP crypto key generation interface */
+static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
+{
+ int ret = 0;
+ struct crypto_shash *tfm;
+
+ if (!ksize)
+ return -EINVAL;
+
+ tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+ if (IS_ERR(tfm)) {
+ BT_DBG("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm));
+ return PTR_ERR(tfm);
+ }
+
+ ret = crypto_shash_setkey(tfm, key, ksize);
+ if (ret) {
+ BT_DBG("crypto_ahash_setkey failed: err %d", ret);
+ } else {
+ struct {
+ struct shash_desc shash;
+ char ctx[crypto_shash_descsize(tfm)];
+ } desc;
+
+ desc.shash.tfm = tfm;
+ desc.shash.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_digest(&desc.shash, plaintext, psize,
+ output);
+ }
+
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type)
+{
+ struct hci_dev *hdev = conn->hdev;
+ struct link_key *key;
+ u8 keybuf[HCI_AMP_LINK_KEY_SIZE];
+ u8 gamp_key[HCI_AMP_LINK_KEY_SIZE];
+ int err;
+
+ if (!hci_conn_check_link_mode(conn))
+ return -EACCES;
+
+ BT_DBG("conn %p key_type %d", conn, conn->key_type);
+
+ /* Legacy key */
+ if (conn->key_type < 3) {
+ BT_ERR("Legacy key type %d", conn->key_type);
+ return -EACCES;
+ }
+
+ *type = conn->key_type;
+ *len = HCI_AMP_LINK_KEY_SIZE;
+
+ key = hci_find_link_key(hdev, &conn->dst);
+ if (!key) {
+ BT_DBG("No Link key for conn %p dst %pMR", conn, &conn->dst);
+ return -EACCES;
+ }
+
+ /* BR/EDR Link Key concatenated together with itself */
+ memcpy(&keybuf[0], key->val, HCI_LINK_KEY_SIZE);
+ memcpy(&keybuf[HCI_LINK_KEY_SIZE], key->val, HCI_LINK_KEY_SIZE);
+
+ /* Derive Generic AMP Link Key (gamp) */
+ err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key);
+ if (err) {
+ BT_ERR("Could not derive Generic AMP Key: err %d", err);
+ return err;
+ }
+
+ if (conn->key_type == HCI_LK_DEBUG_COMBINATION) {
+ BT_DBG("Use Generic AMP Key (gamp)");
+ memcpy(data, gamp_key, HCI_AMP_LINK_KEY_SIZE);
+ return err;
+ }
+
+ /* Derive Dedicated AMP Link Key: "802b" is 802.11 PAL keyID */
+ return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data);
+}
+
+void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
+{
+ struct hci_cp_read_local_amp_assoc cp;
+ struct amp_assoc *loc_assoc = &hdev->loc_assoc;
+
+ BT_DBG("%s handle %d", hdev->name, phy_handle);
+
+ cp.phy_handle = phy_handle;
+ cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
+ cp.len_so_far = cpu_to_le16(loc_assoc->offset);
+
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
+}
+
+void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
+{
+ struct hci_cp_read_local_amp_assoc cp;
+
+ memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
+ memset(&cp, 0, sizeof(cp));
+
+ cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
+
+ mgr->state = READ_LOC_AMP_ASSOC;
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
+}
+
+void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
+ struct hci_conn *hcon)
+{
+ struct hci_cp_read_local_amp_assoc cp;
+ struct amp_mgr *mgr = hcon->amp_mgr;
+
+ cp.phy_handle = hcon->handle;
+ cp.len_so_far = cpu_to_le16(0);
+ cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
+
+ mgr->state = READ_LOC_AMP_ASSOC_FINAL;
+
+ /* Read Local AMP Assoc final link information data */
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
+}
+
+/* Write AMP Assoc data fragments, returns true with last fragment written*/
+static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
+ struct hci_conn *hcon)
+{
+ struct hci_cp_write_remote_amp_assoc *cp;
+ struct amp_mgr *mgr = hcon->amp_mgr;
+ struct amp_ctrl *ctrl;
+ u16 frag_len, len;
+
+ ctrl = amp_ctrl_lookup(mgr, hcon->remote_id);
+ if (!ctrl)
+ return false;
+
+ if (!ctrl->assoc_rem_len) {
+ BT_DBG("all fragments are written");
+ ctrl->assoc_rem_len = ctrl->assoc_len;
+ ctrl->assoc_len_so_far = 0;
+
+ amp_ctrl_put(ctrl);
+ return true;
+ }
+
+ frag_len = min_t(u16, 248, ctrl->assoc_rem_len);
+ len = frag_len + sizeof(*cp);
+
+ cp = kzalloc(len, GFP_KERNEL);
+ if (!cp) {
+ amp_ctrl_put(ctrl);
+ return false;
+ }
+
+ BT_DBG("hcon %p ctrl %p frag_len %u assoc_len %u rem_len %u",
+ hcon, ctrl, frag_len, ctrl->assoc_len, ctrl->assoc_rem_len);
+
+ cp->phy_handle = hcon->handle;
+ cp->len_so_far = cpu_to_le16(ctrl->assoc_len_so_far);
+ cp->rem_len = cpu_to_le16(ctrl->assoc_rem_len);
+ memcpy(cp->frag, ctrl->assoc, frag_len);
+
+ ctrl->assoc_len_so_far += frag_len;
+ ctrl->assoc_rem_len -= frag_len;
+
+ amp_ctrl_put(ctrl);
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp);
+
+ kfree(cp);
+
+ return false;
+}
+
+void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle)
+{
+ struct hci_conn *hcon;
+
+ BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, handle);
+ if (!hcon)
+ return;
+
+ amp_write_rem_assoc_frag(hdev, hcon);
+}
+
+void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
+{
+ struct hci_conn *hcon;
+
+ BT_DBG("%s phy handle 0x%2.2x", hdev->name, handle);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, handle);
+ if (!hcon)
+ return;
+
+ BT_DBG("%s phy handle 0x%2.2x hcon %p", hdev->name, handle, hcon);
+
+ amp_write_rem_assoc_frag(hdev, hcon);
+}
+
+void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+ struct hci_conn *hcon)
+{
+ struct hci_cp_create_phy_link cp;
+
+ cp.phy_handle = hcon->handle;
+
+ BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
+ hcon->handle);
+
+ if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
+ &cp.key_type)) {
+ BT_DBG("Cannot create link key");
+ return;
+ }
+
+ hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
+}
+
+void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
+ struct hci_conn *hcon)
+{
+ struct hci_cp_accept_phy_link cp;
+
+ cp.phy_handle = hcon->handle;
+
+ BT_DBG("%s hcon %p phy handle 0x%2.2x", hdev->name, hcon,
+ hcon->handle);
+
+ if (phylink_gen_key(mgr->l2cap_conn->hcon, cp.key, &cp.key_len,
+ &cp.key_type)) {
+ BT_DBG("Cannot create link key");
+ return;
+ }
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
+}
+
+void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
+{
+ struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev);
+ struct amp_mgr *mgr = hs_hcon->amp_mgr;
+ struct l2cap_chan *bredr_chan;
+
+ BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr);
+
+ if (!bredr_hdev || !mgr || !mgr->bredr_chan)
+ return;
+
+ bredr_chan = mgr->bredr_chan;
+
+ l2cap_chan_lock(bredr_chan);
+
+ set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags);
+ bredr_chan->remote_amp_id = hs_hcon->remote_id;
+ bredr_chan->local_amp_id = hs_hcon->hdev->id;
+ bredr_chan->hs_hcon = hs_hcon;
+ bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu;
+
+ __l2cap_physical_cfm(bredr_chan, 0);
+
+ l2cap_chan_unlock(bredr_chan);
+
+ hci_dev_put(bredr_hdev);
+}
+
+void amp_create_logical_link(struct l2cap_chan *chan)
+{
+ struct hci_cp_create_accept_logical_link cp;
+ struct hci_conn *hcon;
+ struct hci_dev *hdev;
+
+ BT_DBG("chan %p", chan);
+
+ if (!chan->hs_hcon)
+ return;
+
+ hdev = hci_dev_hold(chan->hs_hcon->hdev);
+ if (!hdev)
+ return;
+
+ BT_DBG("chan %p dst %pMR", chan, chan->conn->dst);
+
+ hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst);
+ if (!hcon)
+ goto done;
+
+ cp.phy_handle = hcon->handle;
+
+ cp.tx_flow_spec.id = chan->local_id;
+ cp.tx_flow_spec.stype = chan->local_stype;
+ cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu);
+ cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
+ cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat);
+ cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to);
+
+ cp.rx_flow_spec.id = chan->remote_id;
+ cp.rx_flow_spec.stype = chan->remote_stype;
+ cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu);
+ cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime);
+ cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat);
+ cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to);
+
+ if (hcon->out)
+ hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp),
+ &cp);
+ else
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp),
+ &cp);
+
+done:
+ hci_dev_put(hdev);
+}
+
+void amp_disconnect_logical_link(struct hci_chan *hchan)
+{
+ struct hci_conn *hcon = hchan->conn;
+ struct hci_cp_disconn_logical_link cp;
+
+ if (hcon->state != BT_CONNECTED) {
+ BT_DBG("hchan %p not connected", hchan);
+ return;
+ }
+
+ cp.log_handle = cpu_to_le16(hchan->handle);
+ hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp);
+}
+
+void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason)
+{
+ BT_DBG("hchan %p", hchan);
+
+ hci_chan_del(hchan);
+}
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 4a6620bc157..a5b63970263 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -182,8 +182,7 @@ static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
a2 = data;
data += ETH_ALEN;
- BT_DBG("mc filter %s -> %s",
- batostr((void *) a1), batostr((void *) a2));
+ BT_DBG("mc filter %pMR -> %pMR", a1, a2);
/* Iterate from a1 to a2 */
set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index 98f86f91d47..e58c8b32589 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -25,7 +25,6 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/export.h>
#include <linux/etherdevice.h>
#include <net/bluetooth/bluetooth.h>
diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c
index 50f0d135eb8..a4a9d4b6816 100644
--- a/net/bluetooth/cmtp/capi.c
+++ b/net/bluetooth/cmtp/capi.c
@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/types.h>
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 6c9c1fd601c..e0a6ebf2baa 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -353,7 +353,7 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock)
BT_DBG("mtu %d", session->mtu);
- sprintf(session->name, "%s", batostr(&bt_sk(sock->sk)->dst));
+ sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst);
session->sock = sock;
session->state = BT_CONFIG;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index aacb802d1ee..1c57482112b 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -20,7 +20,7 @@
SOFTWARE IS DISCLAIMED.
*/
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/types.h>
#include <linux/capability.h>
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b9196a44f75..25bfce0666e 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -130,6 +130,20 @@ void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp);
}
+static void hci_amp_disconn(struct hci_conn *conn, __u8 reason)
+{
+ struct hci_cp_disconn_phy_link cp;
+
+ BT_DBG("hcon %p", conn);
+
+ conn->state = BT_DISCONN;
+
+ cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
+ cp.reason = reason;
+ hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK,
+ sizeof(cp), &cp);
+}
+
static void hci_add_sco(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
@@ -230,11 +244,24 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status)
}
}
+static void hci_conn_disconnect(struct hci_conn *conn)
+{
+ __u8 reason = hci_proto_disconn_ind(conn);
+
+ switch (conn->type) {
+ case ACL_LINK:
+ hci_acl_disconn(conn, reason);
+ break;
+ case AMP_LINK:
+ hci_amp_disconn(conn, reason);
+ break;
+ }
+}
+
static void hci_conn_timeout(struct work_struct *work)
{
struct hci_conn *conn = container_of(work, struct hci_conn,
disc_work.work);
- __u8 reason;
BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
@@ -253,8 +280,7 @@ static void hci_conn_timeout(struct work_struct *work)
break;
case BT_CONFIG:
case BT_CONNECTED:
- reason = hci_proto_disconn_ind(conn);
- hci_acl_disconn(conn, reason);
+ hci_conn_disconnect(conn);
break;
default:
conn->state = BT_CLOSED;
@@ -320,7 +346,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
{
struct hci_conn *conn;
- BT_DBG("%s dst %s", hdev->name, batostr(dst));
+ BT_DBG("%s dst %pMR", hdev->name, dst);
conn = kzalloc(sizeof(struct hci_conn), GFP_KERNEL);
if (!conn)
@@ -437,7 +463,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
int use_src = bacmp(src, BDADDR_ANY);
struct hci_dev *hdev = NULL, *d;
- BT_DBG("%s -> %s", batostr(src), batostr(dst));
+ BT_DBG("%pMR -> %pMR", src, dst);
read_lock(&hci_dev_list_lock);
@@ -476,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
{
struct hci_conn *le;
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags))
+ return ERR_PTR(-ENOTSUPP);
+
le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst);
if (!le) {
le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
@@ -567,7 +596,7 @@ static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u8 dst_type, __u8 sec_level, __u8 auth_type)
{
- BT_DBG("%s dst %s type 0x%x", hdev->name, batostr(dst), type);
+ BT_DBG("%s dst %pMR type 0x%x", hdev->name, dst, type);
switch (type) {
case LE_LINK:
@@ -933,6 +962,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn)
chan->conn = conn;
skb_queue_head_init(&chan->data_q);
+ chan->state = BT_CONNECTED;
list_add_rcu(&chan->list, &conn->chan_list);
@@ -950,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan)
synchronize_rcu();
+ hci_conn_put(conn);
+
skb_queue_purge(&chan->data_q);
kfree(chan);
}
@@ -963,3 +995,35 @@ void hci_chan_list_flush(struct hci_conn *conn)
list_for_each_entry_safe(chan, n, &conn->chan_list, list)
hci_chan_del(chan);
}
+
+static struct hci_chan *__hci_chan_lookup_handle(struct hci_conn *hcon,
+ __u16 handle)
+{
+ struct hci_chan *hchan;
+
+ list_for_each_entry(hchan, &hcon->chan_list, list) {
+ if (hchan->handle == handle)
+ return hchan;
+ }
+
+ return NULL;
+}
+
+struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle)
+{
+ struct hci_conn_hash *h = &hdev->conn_hash;
+ struct hci_conn *hcon;
+ struct hci_chan *hchan = NULL;
+
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(hcon, &h->list, list) {
+ hchan = __hci_chan_lookup_handle(hcon, handle);
+ if (hchan)
+ break;
+ }
+
+ rcu_read_unlock();
+
+ return hchan;
+}
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8de421d28cb..6a2d0387475 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
static void bredr_init(struct hci_dev *hdev)
{
- struct hci_cp_delete_stored_link_key cp;
- __le16 param;
- __u8 flt_type;
-
hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED;
- /* Mandatory initialization */
-
/* Read Local Supported Features */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL);
/* Read Local Version */
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL);
-
- /* Read Buffer Size (ACL mtu, max pkt, etc.) */
- hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
-
- /* Read BD Address */
- hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
-
- /* Read Class of Device */
- hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
-
- /* Read Local Name */
- hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
-
- /* Read Voice Setting */
- hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
-
- /* Optional initialization */
-
- /* Clear Event Filters */
- flt_type = HCI_FLT_CLEAR_ALL;
- hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
-
- /* Connection accept timeout ~20 secs */
- param = __constant_cpu_to_le16(0x7d00);
- hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
-
- bacpy(&cp.bdaddr, BDADDR_ANY);
- cp.delete_all = 1;
- hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
}
static void amp_init(struct hci_dev *hdev)
@@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
}
}
-static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt)
-{
- BT_DBG("%s", hdev->name);
-
- /* Read LE buffer size */
- hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
-}
-
static void hci_scan_req(struct hci_dev *hdev, unsigned long opt)
{
__u8 scan = opt;
@@ -405,7 +362,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup(struct hci_dev *hdev,
struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
- BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+ BT_DBG("cache %p, %pMR", cache, bdaddr);
list_for_each_entry(e, &cache->all, all) {
if (!bacmp(&e->data.bdaddr, bdaddr))
@@ -421,7 +378,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_unknown(struct hci_dev *hdev,
struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
- BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+ BT_DBG("cache %p, %pMR", cache, bdaddr);
list_for_each_entry(e, &cache->unknown, list) {
if (!bacmp(&e->data.bdaddr, bdaddr))
@@ -438,7 +395,7 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev,
struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *e;
- BT_DBG("cache %p bdaddr %s state %d", cache, batostr(bdaddr), state);
+ BT_DBG("cache %p bdaddr %pMR state %d", cache, bdaddr, state);
list_for_each_entry(e, &cache->resolve, list) {
if (!bacmp(bdaddr, BDADDR_ANY) && e->name_state == state)
@@ -475,7 +432,9 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
struct discovery_state *cache = &hdev->discovery;
struct inquiry_entry *ie;
- BT_DBG("cache %p, %s", cache, batostr(&data->bdaddr));
+ BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
+
+ hci_remove_remote_oob_data(hdev, &data->bdaddr);
if (ssp)
*ssp = data->ssp_mode;
@@ -637,6 +596,99 @@ done:
return err;
}
+static u8 create_ad(struct hci_dev *hdev, u8 *ptr)
+{
+ u8 ad_len = 0, flags = 0;
+ size_t name_len;
+
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
+ flags |= LE_AD_GENERAL;
+
+ if (!lmp_bredr_capable(hdev))
+ flags |= LE_AD_NO_BREDR;
+
+ if (lmp_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_CTRL;
+
+ if (lmp_host_le_br_capable(hdev))
+ flags |= LE_AD_SIM_LE_BREDR_HOST;
+
+ if (flags) {
+ BT_DBG("adv flags 0x%02x", flags);
+
+ ptr[0] = 2;
+ ptr[1] = EIR_FLAGS;
+ ptr[2] = flags;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) {
+ ptr[0] = 2;
+ ptr[1] = EIR_TX_POWER;
+ ptr[2] = (u8) hdev->adv_tx_power;
+
+ ad_len += 3;
+ ptr += 3;
+ }
+
+ name_len = strlen(hdev->dev_name);
+ if (name_len > 0) {
+ size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2;
+
+ if (name_len > max_len) {
+ name_len = max_len;
+ ptr[1] = EIR_NAME_SHORT;
+ } else
+ ptr[1] = EIR_NAME_COMPLETE;
+
+ ptr[0] = name_len + 1;
+
+ memcpy(ptr + 2, hdev->dev_name, name_len);
+
+ ad_len += (name_len + 2);
+ ptr += (name_len + 2);
+ }
+
+ return ad_len;
+}
+
+int hci_update_ad(struct hci_dev *hdev)
+{
+ struct hci_cp_le_set_adv_data cp;
+ u8 len;
+ int err;
+
+ hci_dev_lock(hdev);
+
+ if (!lmp_le_capable(hdev)) {
+ err = -EINVAL;
+ goto unlock;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ len = create_ad(hdev, cp.data);
+
+ if (hdev->adv_data_len == len &&
+ memcmp(cp.data, hdev->adv_data, len) == 0) {
+ err = 0;
+ goto unlock;
+ }
+
+ memcpy(hdev->adv_data, cp.data, sizeof(cp.data));
+ hdev->adv_data_len = len;
+
+ cp.length = len;
+ err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp);
+
+unlock:
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
/* ---- HCI ioctl helpers ---- */
int hci_dev_open(__u16 dev)
@@ -687,10 +739,6 @@ int hci_dev_open(__u16 dev)
ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
- if (lmp_host_le_capable(hdev))
- ret = __hci_request(hdev, hci_le_init_req, 0,
- HCI_INIT_TIMEOUT);
-
clear_bit(HCI_INIT, &hdev->flags);
}
@@ -698,6 +746,7 @@ int hci_dev_open(__u16 dev)
hci_dev_hold(hdev);
set_bit(HCI_UP, &hdev->flags);
hci_notify(hdev, HCI_DEV_UP);
+ hci_update_ad(hdev);
if (!test_bit(HCI_SETUP, &hdev->dev_flags) &&
mgmt_valid_hdev(hdev)) {
hci_dev_lock(hdev);
@@ -812,6 +861,9 @@ static int hci_dev_do_close(struct hci_dev *hdev)
/* Clear flags */
hdev->flags = 0;
+ /* Controller radio is available but is currently powered down */
+ hdev->amp_status = 0;
+
memset(hdev->eir, 0, sizeof(hdev->eir));
memset(hdev->dev_class, 0, sizeof(hdev->dev_class));
@@ -1039,10 +1091,17 @@ int hci_get_dev_info(void __user *arg)
di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4);
di.flags = hdev->flags;
di.pkt_type = hdev->pkt_type;
- di.acl_mtu = hdev->acl_mtu;
- di.acl_pkts = hdev->acl_pkts;
- di.sco_mtu = hdev->sco_mtu;
- di.sco_pkts = hdev->sco_pkts;
+ if (lmp_bredr_capable(hdev)) {
+ di.acl_mtu = hdev->acl_mtu;
+ di.acl_pkts = hdev->acl_pkts;
+ di.sco_mtu = hdev->sco_mtu;
+ di.sco_pkts = hdev->sco_pkts;
+ } else {
+ di.acl_mtu = hdev->le_mtu;
+ di.acl_pkts = hdev->le_pkts;
+ di.sco_mtu = 0;
+ di.sco_pkts = 0;
+ }
di.link_policy = hdev->link_policy;
di.link_mode = hdev->link_mode;
@@ -1259,7 +1318,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key,
list_add(&key->list, &hdev->link_keys);
}
- BT_DBG("%s key for %s type %u", hdev->name, batostr(bdaddr), type);
+ BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
/* Some buggy controller combinations generate a changed
* combination key for legacy pairing even when there's no
@@ -1338,7 +1397,7 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
if (!key)
return -ENOENT;
- BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+ BT_DBG("%s removing %pMR", hdev->name, bdaddr);
list_del(&key->list);
kfree(key);
@@ -1354,7 +1413,7 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr)
if (bacmp(bdaddr, &k->bdaddr))
continue;
- BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+ BT_DBG("%s removing %pMR", hdev->name, bdaddr);
list_del(&k->list);
kfree(k);
@@ -1401,7 +1460,7 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
if (!data)
return -ENOENT;
- BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+ BT_DBG("%s removing %pMR", hdev->name, bdaddr);
list_del(&data->list);
kfree(data);
@@ -1440,7 +1499,7 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
memcpy(data->hash, hash, sizeof(data->hash));
memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
- BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
+ BT_DBG("%s for %pMR", hdev->name, bdaddr);
return 0;
}
@@ -1617,6 +1676,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window,
BT_DBG("%s", hdev->name);
+ if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags))
+ return -ENOTSUPP;
+
if (work_busy(&hdev->le_scan))
return -EINPROGRESS;
@@ -1643,6 +1705,8 @@ struct hci_dev *hci_alloc_dev(void)
hdev->esco_type = (ESCO_HV1);
hdev->link_mode = (HCI_LM_ACCEPT);
hdev->io_capability = 0x03; /* No Input No Output */
+ hdev->inq_tx_power = HCI_TX_POWER_INVALID;
+ hdev->adv_tx_power = HCI_TX_POWER_INVALID;
hdev->sniff_max_interval = 800;
hdev->sniff_min_interval = 80;
@@ -1754,11 +1818,11 @@ int hci_register_dev(struct hci_dev *hdev)
if (hdev->dev_type != HCI_AMP)
set_bit(HCI_AUTO_OFF, &hdev->dev_flags);
- schedule_work(&hdev->power_on);
-
hci_notify(hdev, HCI_DEV_REG);
hci_dev_hold(hdev);
+ schedule_work(&hdev->power_on);
+
return id;
err_wqueue:
@@ -1793,6 +1857,8 @@ void hci_unregister_dev(struct hci_dev *hdev)
for (i = 0; i < NUM_REASSEMBLY; i++)
kfree_skb(hdev->reassembly[i]);
+ cancel_work_sync(&hdev->power_on);
+
if (!test_bit(HCI_INIT, &hdev->flags) &&
!test_bit(HCI_SETUP, &hdev->dev_flags)) {
hci_dev_lock(hdev);
@@ -2153,9 +2219,10 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
hdr->dlen = cpu_to_le16(len);
}
-static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
+static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
struct sk_buff *skb, __u16 flags)
{
+ struct hci_conn *conn = chan->conn;
struct hci_dev *hdev = conn->hdev;
struct sk_buff *list;
@@ -2163,7 +2230,18 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
skb->data_len = 0;
bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
- hci_add_acl_hdr(skb, conn->handle, flags);
+
+ switch (hdev->dev_type) {
+ case HCI_BREDR:
+ hci_add_acl_hdr(skb, conn->handle, flags);
+ break;
+ case HCI_AMP:
+ hci_add_acl_hdr(skb, chan->handle, flags);
+ break;
+ default:
+ BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type);
+ return;
+ }
list = skb_shinfo(skb)->frag_list;
if (!list) {
@@ -2202,14 +2280,13 @@ static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue,
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags)
{
- struct hci_conn *conn = chan->conn;
- struct hci_dev *hdev = conn->hdev;
+ struct hci_dev *hdev = chan->conn->hdev;
BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags);
skb->dev = (void *) hdev;
- hci_queue_acl(conn, &chan->data_q, skb, flags);
+ hci_queue_acl(chan, &chan->data_q, skb, flags);
queue_work(hdev->workqueue, &hdev->tx_work);
}
@@ -2311,8 +2388,8 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type)
/* Kill stalled connections */
list_for_each_entry_rcu(c, &h->list, list) {
if (c->type == type && c->sent) {
- BT_ERR("%s killing stalled connection %s",
- hdev->name, batostr(&c->dst));
+ BT_ERR("%s killing stalled connection %pMR",
+ hdev->name, &c->dst);
hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM);
}
}
@@ -2381,6 +2458,9 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type,
case ACL_LINK:
cnt = hdev->acl_cnt;
break;
+ case AMP_LINK:
+ cnt = hdev->block_cnt;
+ break;
case SCO_LINK:
case ESCO_LINK:
cnt = hdev->sco_cnt;
@@ -2510,11 +2590,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
struct hci_chan *chan;
struct sk_buff *skb;
int quote;
+ u8 type;
__check_timeout(hdev, cnt);
+ BT_DBG("%s", hdev->name);
+
+ if (hdev->dev_type == HCI_AMP)
+ type = AMP_LINK;
+ else
+ type = ACL_LINK;
+
while (hdev->block_cnt > 0 &&
- (chan = hci_chan_sent(hdev, ACL_LINK, &quote))) {
+ (chan = hci_chan_sent(hdev, type, &quote))) {
u32 priority = (skb_peek(&chan->data_q))->priority;
while (quote > 0 && (skb = skb_peek(&chan->data_q))) {
int blocks;
@@ -2547,14 +2635,19 @@ static void hci_sched_acl_blk(struct hci_dev *hdev)
}
if (cnt != hdev->block_cnt)
- hci_prio_recalculate(hdev, ACL_LINK);
+ hci_prio_recalculate(hdev, type);
}
static void hci_sched_acl(struct hci_dev *hdev)
{
BT_DBG("%s", hdev->name);
- if (!hci_conn_num(hdev, ACL_LINK))
+ /* No ACL link over BR/EDR controller */
+ if (!hci_conn_num(hdev, ACL_LINK) && hdev->dev_type == HCI_BREDR)
+ return;
+
+ /* No AMP link over AMP controller */
+ if (!hci_conn_num(hdev, AMP_LINK) && hdev->dev_type == HCI_AMP)
return;
switch (hdev->flow_ctl_mode) {
@@ -2717,14 +2810,6 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
if (conn) {
hci_conn_enter_active_mode(conn, BT_POWER_FORCE_ACTIVE_OFF);
- hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
- !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
- hci_dev_unlock(hdev);
-
/* Send to upper protocol */
l2cap_recv_acldata(conn, skb, flags);
return;
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 2022b43c735..81b44481d0d 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -24,12 +24,13 @@
/* Bluetooth HCI event handling. */
-#include <linux/export.h>
#include <asm/unaligned.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/mgmt.h>
+#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/amp.h>
/* Handle HCI Event packets */
@@ -201,6 +202,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
BIT(HCI_PERIODIC_INQ));
hdev->discovery.state = DISCOVERY_STOPPED;
+ hdev->inq_tx_power = HCI_TX_POWER_INVALID;
+ hdev->adv_tx_power = HCI_TX_POWER_INVALID;
+
+ memset(hdev->adv_data, 0, sizeof(hdev->adv_data));
+ hdev->adv_data_len = 0;
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -223,6 +229,9 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_unlock(hdev);
+ if (!status && !test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+
hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status);
}
@@ -438,7 +447,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb)
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
- void *sent;
+ struct hci_cp_write_ssp_mode *sent;
BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -446,10 +455,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
if (!sent)
return;
+ if (!status) {
+ if (sent->mode)
+ hdev->host_features[0] |= LMP_HOST_SSP;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_SSP;
+ }
+
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status);
+ mgmt_ssp_enable_complete(hdev, sent->mode, status);
else if (!status) {
- if (*((u8 *) sent))
+ if (sent->mode)
set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
else
clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
@@ -458,10 +474,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
{
- if (hdev->features[6] & LMP_EXT_INQ)
+ if (lmp_ext_inq_capable(hdev))
return 2;
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
return 1;
if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 &&
@@ -505,28 +521,30 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
if (hdev->hci_ver < BLUETOOTH_VER_1_2)
return;
- events[4] |= 0x01; /* Flow Specification Complete */
- events[4] |= 0x02; /* Inquiry Result with RSSI */
- events[4] |= 0x04; /* Read Remote Extended Features Complete */
- events[5] |= 0x08; /* Synchronous Connection Complete */
- events[5] |= 0x10; /* Synchronous Connection Changed */
+ if (lmp_bredr_capable(hdev)) {
+ events[4] |= 0x01; /* Flow Specification Complete */
+ events[4] |= 0x02; /* Inquiry Result with RSSI */
+ events[4] |= 0x04; /* Read Remote Extended Features Complete */
+ events[5] |= 0x08; /* Synchronous Connection Complete */
+ events[5] |= 0x10; /* Synchronous Connection Changed */
+ }
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
events[4] |= 0x02; /* Inquiry Result with RSSI */
if (lmp_sniffsubr_capable(hdev))
events[5] |= 0x20; /* Sniff Subrating */
- if (hdev->features[5] & LMP_PAUSE_ENC)
+ if (lmp_pause_enc_capable(hdev))
events[5] |= 0x80; /* Encryption Key Refresh Complete */
- if (hdev->features[6] & LMP_EXT_INQ)
+ if (lmp_ext_inq_capable(hdev))
events[5] |= 0x40; /* Extended Inquiry Result */
if (lmp_no_flush_capable(hdev))
events[7] |= 0x01; /* Enhanced Flush Complete */
- if (hdev->features[7] & LMP_LSTO)
+ if (lmp_lsto_capable(hdev))
events[6] |= 0x80; /* Link Supervision Timeout Changed */
if (lmp_ssp_capable(hdev)) {
@@ -546,6 +564,53 @@ static void hci_setup_event_mask(struct hci_dev *hdev)
events[7] |= 0x20; /* LE Meta-Event */
hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events);
+
+ if (lmp_le_capable(hdev)) {
+ memset(events, 0, sizeof(events));
+ events[0] = 0x1f;
+ hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK,
+ sizeof(events), events);
+ }
+}
+
+static void bredr_setup(struct hci_dev *hdev)
+{
+ struct hci_cp_delete_stored_link_key cp;
+ __le16 param;
+ __u8 flt_type;
+
+ /* Read Buffer Size (ACL mtu, max pkt, etc.) */
+ hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL);
+
+ /* Read Class of Device */
+ hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL);
+
+ /* Read Local Name */
+ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL);
+
+ /* Read Voice Setting */
+ hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL);
+
+ /* Clear Event Filters */
+ flt_type = HCI_FLT_CLEAR_ALL;
+ hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type);
+
+ /* Connection accept timeout ~20 secs */
+ param = __constant_cpu_to_le16(0x7d00);
+ hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, &param);
+
+ bacpy(&cp.bdaddr, BDADDR_ANY);
+ cp.delete_all = 1;
+ hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp);
+}
+
+static void le_setup(struct hci_dev *hdev)
+{
+ /* Read LE Buffer Size */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL);
+
+ /* Read LE Advertising Channel TX Power */
+ hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
}
static void hci_setup(struct hci_dev *hdev)
@@ -553,6 +618,15 @@ static void hci_setup(struct hci_dev *hdev)
if (hdev->dev_type != HCI_BREDR)
return;
+ /* Read BD Address */
+ hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL);
+
+ if (lmp_bredr_capable(hdev))
+ bredr_setup(hdev);
+
+ if (lmp_le_capable(hdev))
+ le_setup(hdev);
+
hci_setup_event_mask(hdev);
if (hdev->hci_ver > BLUETOOTH_VER_1_1)
@@ -573,13 +647,13 @@ static void hci_setup(struct hci_dev *hdev)
}
}
- if (hdev->features[3] & LMP_RSSI_INQ)
+ if (lmp_inq_rssi_capable(hdev))
hci_setup_inquiry_mode(hdev);
- if (hdev->features[7] & LMP_INQ_TX_PWR)
+ if (lmp_inq_tx_pwr_capable(hdev))
hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL);
- if (hdev->features[7] & LMP_EXTFEATURES) {
+ if (lmp_ext_feat_capable(hdev)) {
struct hci_cp_read_local_ext_features cp;
cp.page = 0x01;
@@ -626,11 +700,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev)
if (lmp_rswitch_capable(hdev))
link_policy |= HCI_LP_RSWITCH;
- if (hdev->features[0] & LMP_HOLD)
+ if (lmp_hold_capable(hdev))
link_policy |= HCI_LP_HOLD;
if (lmp_sniff_capable(hdev))
link_policy |= HCI_LP_SNIFF;
- if (hdev->features[1] & LMP_PARK)
+ if (lmp_park_capable(hdev))
link_policy |= HCI_LP_PARK;
cp.policy = cpu_to_le16(link_policy);
@@ -720,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev)
if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ cp.simul = lmp_le_br_capable(hdev);
}
- if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE))
+ if (cp.le != lmp_host_le_capable(hdev))
hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp),
&cp);
}
@@ -846,7 +920,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
if (rp->status)
- return;
+ goto a2mp_rsp;
hdev->amp_status = rp->amp_status;
hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
@@ -860,6 +934,46 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status);
+
+a2mp_rsp:
+ a2mp_send_getinfo_rsp(hdev);
+}
+
+static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data;
+ struct amp_assoc *assoc = &hdev->loc_assoc;
+ size_t rem_len, frag_len;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (rp->status)
+ goto a2mp_rsp;
+
+ frag_len = skb->len - sizeof(*rp);
+ rem_len = __le16_to_cpu(rp->rem_len);
+
+ if (rem_len > frag_len) {
+ BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len);
+
+ memcpy(assoc->data + assoc->offset, rp->frag, frag_len);
+ assoc->offset += frag_len;
+
+ /* Read other fragments */
+ amp_read_loc_assoc_frag(hdev, rp->phy_handle);
+
+ return;
+ }
+
+ memcpy(assoc->data + assoc->offset, rp->frag, rem_len);
+ assoc->len = assoc->offset + rem_len;
+ assoc->offset = 0;
+
+a2mp_rsp:
+ /* Send A2MP Rsp when all fragments are received */
+ a2mp_send_getampassoc_rsp(hdev, rp->status);
+ a2mp_send_create_phy_link_req(hdev, rp->status);
}
static void hci_cc_delete_stored_link_key(struct hci_dev *hdev,
@@ -976,6 +1090,31 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status);
}
+static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_le_read_adv_tx_power *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
+
+ if (!rp->status) {
+ hdev->adv_tx_power = rp->tx_power;
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+ }
+
+ hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status);
+}
+
+static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status);
+}
+
static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_rp_user_confirm_reply *rp = (void *) skb->data;
@@ -1051,6 +1190,33 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
hci_dev_unlock(hdev);
}
+static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ __u8 *sent, status = *((__u8 *) skb->data);
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE);
+ if (!sent)
+ return;
+
+ hci_dev_lock(hdev);
+
+ if (!status) {
+ if (*sent)
+ set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+ else
+ clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags);
+ }
+
+ hci_dev_unlock(hdev);
+
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ hci_update_ad(hdev);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status);
+}
+
static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1165,6 +1331,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
hdev->host_features[0] |= LMP_HOST_LE;
else
hdev->host_features[0] &= ~LMP_HOST_LE;
+
+ if (sent->simul)
+ hdev->host_features[0] |= LMP_HOST_LE_BREDR;
+ else
+ hdev->host_features[0] &= ~LMP_HOST_LE_BREDR;
}
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
@@ -1174,6 +1345,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status);
}
+static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data;
+
+ BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x",
+ hdev->name, rp->status, rp->phy_handle);
+
+ if (rp->status)
+ return;
+
+ amp_write_rem_assoc_continue(hdev, rp->phy_handle);
+}
+
static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status)
{
BT_DBG("%s status 0x%2.2x", hdev->name, status);
@@ -1210,7 +1395,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
- BT_DBG("%s bdaddr %s hcon %p", hdev->name, batostr(&cp->bdaddr), conn);
+ BT_DBG("%s bdaddr %pMR hcon %p", hdev->name, &cp->bdaddr, conn);
if (status) {
if (conn && conn->state == BT_CONNECT) {
@@ -1639,8 +1824,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
return;
}
- BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst),
- conn);
+ BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn);
conn->state = BT_CLOSED;
mgmt_connect_failed(hdev, &conn->dst, conn->type,
@@ -1657,6 +1841,52 @@ static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status)
BT_DBG("%s status 0x%2.2x", hdev->name, status);
}
+static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_create_phy_link *cp;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ if (status) {
+ struct hci_conn *hcon;
+
+ hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
+ if (hcon)
+ hci_conn_del(hcon);
+ } else {
+ amp_write_remote_assoc(hdev, cp->phy_handle);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_accept_phy_link *cp;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK);
+ if (!cp)
+ return;
+
+ amp_write_remote_assoc(hdev, cp->phy_handle);
+}
+
+static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status)
+{
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -1817,15 +2047,53 @@ unlock:
hci_conn_check_pending(hdev);
}
+void hci_conn_accept(struct hci_conn *conn, int mask)
+{
+ struct hci_dev *hdev = conn->hdev;
+
+ BT_DBG("conn %p", conn);
+
+ conn->state = BT_CONFIG;
+
+ if (!lmp_esco_capable(hdev)) {
+ struct hci_cp_accept_conn_req cp;
+
+ bacpy(&cp.bdaddr, &conn->dst);
+
+ if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+ cp.role = 0x00; /* Become master */
+ else
+ cp.role = 0x01; /* Remain slave */
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
+ } else /* lmp_esco_capable(hdev)) */ {
+ struct hci_cp_accept_sync_conn_req cp;
+
+ bacpy(&cp.bdaddr, &conn->dst);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+
+ cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
+ cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.content_format = cpu_to_le16(hdev->voice_setting);
+ cp.retrans_effort = 0xff;
+
+ hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
+ sizeof(cp), &cp);
+ }
+}
+
static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_conn_request *ev = (void *) skb->data;
int mask = hdev->link_mode;
+ __u8 flags = 0;
- BT_DBG("%s bdaddr %s type 0x%x", hdev->name, batostr(&ev->bdaddr),
+ BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr,
ev->link_type);
- mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type);
+ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type,
+ &flags);
if ((mask & HCI_LM_ACCEPT) &&
!hci_blacklist_lookup(hdev, &ev->bdaddr)) {
@@ -1851,12 +2119,13 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
memcpy(conn->dev_class, ev->dev_class, 3);
- conn->state = BT_CONNECT;
hci_dev_unlock(hdev);
- if (ev->link_type == ACL_LINK || !lmp_esco_capable(hdev)) {
+ if (ev->link_type == ACL_LINK ||
+ (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) {
struct hci_cp_accept_conn_req cp;
+ conn->state = BT_CONNECT;
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -1867,8 +2136,9 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp),
&cp);
- } else {
+ } else if (!(flags & HCI_PROTO_DEFER)) {
struct hci_cp_accept_sync_conn_req cp;
+ conn->state = BT_CONNECT;
bacpy(&cp.bdaddr, &ev->bdaddr);
cp.pkt_type = cpu_to_le16(conn->pkt_type);
@@ -1881,6 +2151,10 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
+ } else {
+ conn->state = BT_CONNECT2;
+ hci_proto_connect_cfm(conn, 0);
+ hci_conn_put(conn);
}
} else {
/* Connection rejected */
@@ -2314,6 +2588,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_read_local_amp_info(hdev, skb);
break;
+ case HCI_OP_READ_LOCAL_AMP_ASSOC:
+ hci_cc_read_local_amp_assoc(hdev, skb);
+ break;
+
case HCI_OP_DELETE_STORED_LINK_KEY:
hci_cc_delete_stored_link_key(hdev, skb);
break;
@@ -2350,6 +2628,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_read_buffer_size(hdev, skb);
break;
+ case HCI_OP_LE_READ_ADV_TX_POWER:
+ hci_cc_le_read_adv_tx_power(hdev, skb);
+ break;
+
+ case HCI_OP_LE_SET_EVENT_MASK:
+ hci_cc_le_set_event_mask(hdev, skb);
+ break;
+
case HCI_OP_USER_CONFIRM_REPLY:
hci_cc_user_confirm_reply(hdev, skb);
break;
@@ -2370,6 +2656,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_le_set_scan_param(hdev, skb);
break;
+ case HCI_OP_LE_SET_ADV_ENABLE:
+ hci_cc_le_set_adv_enable(hdev, skb);
+ break;
+
case HCI_OP_LE_SET_SCAN_ENABLE:
hci_cc_le_set_scan_enable(hdev, skb);
break;
@@ -2386,6 +2676,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cc_write_le_host_supported(hdev, skb);
break;
+ case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
+ hci_cc_write_remote_amp_assoc(hdev, skb);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
@@ -2394,7 +2688,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (ev->opcode != HCI_OP_NOP)
del_timer(&hdev->cmd_timer);
- if (ev->ncmd) {
+ if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) {
atomic_set(&hdev->cmd_cnt, 1);
if (!skb_queue_empty(&hdev->cmd_q))
queue_work(hdev->workqueue, &hdev->cmd_work);
@@ -2467,6 +2761,18 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_le_start_enc(hdev, ev->status);
break;
+ case HCI_OP_CREATE_PHY_LINK:
+ hci_cs_create_phylink(hdev, ev->status);
+ break;
+
+ case HCI_OP_ACCEPT_PHY_LINK:
+ hci_cs_accept_phylink(hdev, ev->status);
+ break;
+
+ case HCI_OP_CREATE_LOGICAL_LINK:
+ hci_cs_create_logical_link(hdev, ev->status);
+ break;
+
default:
BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode);
break;
@@ -2574,6 +2880,27 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
queue_work(hdev->workqueue, &hdev->tx_work);
}
+static struct hci_conn *__hci_conn_lookup_handle(struct hci_dev *hdev,
+ __u16 handle)
+{
+ struct hci_chan *chan;
+
+ switch (hdev->dev_type) {
+ case HCI_BREDR:
+ return hci_conn_hash_lookup_handle(hdev, handle);
+ case HCI_AMP:
+ chan = hci_chan_lookup_handle(hdev, handle);
+ if (chan)
+ return chan->conn;
+ break;
+ default:
+ BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type);
+ break;
+ }
+
+ return NULL;
+}
+
static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_num_comp_blocks *ev = (void *) skb->data;
@@ -2595,13 +2922,13 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
for (i = 0; i < ev->num_hndl; i++) {
struct hci_comp_blocks_info *info = &ev->handles[i];
- struct hci_conn *conn;
+ struct hci_conn *conn = NULL;
__u16 handle, block_count;
handle = __le16_to_cpu(info->handle);
block_count = __le16_to_cpu(info->blocks);
- conn = hci_conn_hash_lookup_handle(hdev, handle);
+ conn = __hci_conn_lookup_handle(hdev, handle);
if (!conn)
continue;
@@ -2609,6 +2936,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb)
switch (conn->type) {
case ACL_LINK:
+ case AMP_LINK:
hdev->block_cnt += block_count;
if (hdev->block_cnt > hdev->num_blocks)
hdev->block_cnt = hdev->num_blocks;
@@ -2705,13 +3033,13 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
key = hci_find_link_key(hdev, &ev->bdaddr);
if (!key) {
- BT_DBG("%s link key not found for %s", hdev->name,
- batostr(&ev->bdaddr));
+ BT_DBG("%s link key not found for %pMR", hdev->name,
+ &ev->bdaddr);
goto not_found;
}
- BT_DBG("%s found key type %u for %s", hdev->name, key->type,
- batostr(&ev->bdaddr));
+ BT_DBG("%s found key type %u for %pMR", hdev->name, key->type,
+ &ev->bdaddr);
if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) &&
key->type == HCI_LK_DEBUG_COMBINATION) {
@@ -3419,6 +3747,130 @@ unlock:
hci_dev_unlock(hdev);
}
+static void hci_phy_link_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_phy_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon, *bredr_hcon;
+
+ BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle,
+ ev->status);
+
+ hci_dev_lock(hdev);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (!hcon) {
+ hci_dev_unlock(hdev);
+ return;
+ }
+
+ if (ev->status) {
+ hci_conn_del(hcon);
+ hci_dev_unlock(hdev);
+ return;
+ }
+
+ bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon;
+
+ hcon->state = BT_CONNECTED;
+ bacpy(&hcon->dst, &bredr_hcon->dst);
+
+ hci_conn_hold(hcon);
+ hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
+ hci_conn_put(hcon);
+
+ hci_conn_hold_device(hcon);
+ hci_conn_add_sysfs(hcon);
+
+ amp_physical_cfm(bredr_hcon, hcon);
+
+ hci_dev_unlock(hdev);
+}
+
+static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_logical_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon;
+ struct hci_chan *hchan;
+ struct amp_mgr *mgr;
+
+ BT_DBG("%s log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x",
+ hdev->name, le16_to_cpu(ev->handle), ev->phy_handle,
+ ev->status);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (!hcon)
+ return;
+
+ /* Create AMP hchan */
+ hchan = hci_chan_create(hcon);
+ if (!hchan)
+ return;
+
+ hchan->handle = le16_to_cpu(ev->handle);
+
+ BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan);
+
+ mgr = hcon->amp_mgr;
+ if (mgr && mgr->bredr_chan) {
+ struct l2cap_chan *bredr_chan = mgr->bredr_chan;
+
+ l2cap_chan_lock(bredr_chan);
+
+ bredr_chan->conn->mtu = hdev->block_mtu;
+ l2cap_logical_cfm(bredr_chan, hchan, 0);
+ hci_conn_hold(hcon);
+
+ l2cap_chan_unlock(bredr_chan);
+ }
+}
+
+static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_disconn_logical_link_complete *ev = (void *) skb->data;
+ struct hci_chan *hchan;
+
+ BT_DBG("%s log handle 0x%4.4x status 0x%2.2x", hdev->name,
+ le16_to_cpu(ev->handle), ev->status);
+
+ if (ev->status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle));
+ if (!hchan)
+ goto unlock;
+
+ amp_destroy_logical_link(hchan, ev->reason);
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ struct hci_ev_disconn_phy_link_complete *ev = (void *) skb->data;
+ struct hci_conn *hcon;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
+
+ if (ev->status)
+ return;
+
+ hci_dev_lock(hdev);
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (hcon) {
+ hcon->state = BT_CLOSED;
+ hci_conn_del(hcon);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_conn_complete *ev = (void *) skb->data;
@@ -3558,6 +4010,22 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_channel_selected *ev = (void *) skb->data;
+ struct hci_conn *hcon;
+
+ BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle);
+
+ skb_pull(skb, sizeof(*ev));
+
+ hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
+ if (!hcon)
+ return;
+
+ amp_read_loc_assoc_final_data(hdev, hcon);
+}
+
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_event_hdr *hdr = (void *) skb->data;
@@ -3722,10 +4190,30 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_le_meta_evt(hdev, skb);
break;
+ case HCI_EV_CHANNEL_SELECTED:
+ hci_chan_selected_evt(hdev, skb);
+ break;
+
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
hci_remote_oob_data_request_evt(hdev, skb);
break;
+ case HCI_EV_PHY_LINK_COMPLETE:
+ hci_phy_link_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_LOGICAL_LINK_COMPLETE:
+ hci_loglink_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE:
+ hci_disconn_loglink_complete_evt(hdev, skb);
+ break;
+
+ case HCI_EV_DISCONN_PHY_LINK_COMPLETE:
+ hci_disconn_phylink_complete_evt(hdev, skb);
+ break;
+
case HCI_EV_NUM_COMP_BLOCKS:
hci_num_comp_blocks_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index a20e61c3653..55cceee02a8 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -38,7 +38,7 @@ static ssize_t show_link_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_conn *conn = to_hci_conn(dev);
- return sprintf(buf, "%s\n", batostr(&conn->dst));
+ return sprintf(buf, "%pMR\n", &conn->dst);
}
static ssize_t show_link_features(struct device *dev,
@@ -224,7 +224,7 @@ static ssize_t show_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hci_dev *hdev = to_hci_dev(dev);
- return sprintf(buf, "%s\n", batostr(&hdev->bdaddr));
+ return sprintf(buf, "%pMR\n", &hdev->bdaddr);
}
static ssize_t show_features(struct device *dev,
@@ -406,8 +406,8 @@ static int inquiry_cache_show(struct seq_file *f, void *p)
list_for_each_entry(e, &cache->all, all) {
struct inquiry_data *data = &e->data;
- seq_printf(f, "%s %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
- batostr(&data->bdaddr),
+ seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+ &data->bdaddr,
data->pscan_rep_mode, data->pscan_period_mode,
data->pscan_mode, data->dev_class[2],
data->dev_class[1], data->dev_class[0],
@@ -440,7 +440,7 @@ static int blacklist_show(struct seq_file *f, void *p)
hci_dev_lock(hdev);
list_for_each_entry(b, &hdev->blacklist, list)
- seq_printf(f, "%s\n", batostr(&b->bdaddr));
+ seq_printf(f, "%pMR\n", &b->bdaddr);
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index ccd985da651..a7352ff3fd1 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -931,9 +931,13 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->version = req->version;
hid->country = req->country;
- strncpy(hid->name, req->name, 128);
- strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64);
- strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64);
+ strncpy(hid->name, req->name, sizeof(req->name) - 1);
+
+ snprintf(hid->phys, sizeof(hid->phys), "%pMR",
+ &bt_sk(session->ctrl_sock->sk)->src);
+
+ snprintf(hid->uniq, sizeof(hid->uniq), "%pMR",
+ &bt_sk(session->ctrl_sock->sk)->dst);
hid->dev.parent = &session->conn->dev;
hid->ll_driver = &hidp_hid_driver;
@@ -941,6 +945,13 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->hid_get_raw_report = hidp_get_raw_report;
hid->hid_output_raw_report = hidp_output_raw_report;
+ /* True if device is blacklisted in drivers/hid/hid-core.c */
+ if (hid_ignore(hid)) {
+ hid_destroy_device(session->hid);
+ session->hid = NULL;
+ return -ENODEV;
+ }
+
return 0;
fault:
@@ -1013,7 +1024,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
if (req->rd_size > 0) {
err = hidp_setup_hid(session, req);
- if (err)
+ if (err && err != -ENODEV)
goto purge;
}
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a91239dcda4..22e65832284 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -38,6 +38,7 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/smp.h>
#include <net/bluetooth/a2mp.h>
+#include <net/bluetooth/amp.h>
bool disable_ertm;
@@ -48,19 +49,19 @@ static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
- u8 code, u8 ident, u16 dlen, void *data);
+ u8 code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
- void *data);
+ void *data);
static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
-static void l2cap_send_disconn_req(struct l2cap_conn *conn,
- struct l2cap_chan *chan, int err);
+static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
- struct sk_buff_head *skbs, u8 event);
+ struct sk_buff_head *skbs, u8 event);
/* ---- L2CAP channels ---- */
-static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16 cid)
+static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
+ u16 cid)
{
struct l2cap_chan *c;
@@ -71,7 +72,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, u16
return NULL;
}
-static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
+static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn,
+ u16 cid)
{
struct l2cap_chan *c;
@@ -84,7 +86,8 @@ static struct l2cap_chan *__l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16
/* Find channel with given SCID.
* Returns locked channel. */
-static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 cid)
+static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn,
+ u16 cid)
{
struct l2cap_chan *c;
@@ -97,7 +100,25 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, u16 ci
return c;
}
-static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident)
+/* Find channel with given DCID.
+ * Returns locked channel.
+ */
+static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn,
+ u16 cid)
+{
+ struct l2cap_chan *c;
+
+ mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_dcid(conn, cid);
+ if (c)
+ l2cap_chan_lock(c);
+ mutex_unlock(&conn->chan_lock);
+
+ return c;
+}
+
+static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn,
+ u8 ident)
{
struct l2cap_chan *c;
@@ -108,6 +129,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8
return NULL;
}
+static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn,
+ u8 ident)
+{
+ struct l2cap_chan *c;
+
+ mutex_lock(&conn->chan_lock);
+ c = __l2cap_get_chan_by_ident(conn, ident);
+ if (c)
+ l2cap_chan_lock(c);
+ mutex_unlock(&conn->chan_lock);
+
+ return c;
+}
+
static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src)
{
struct l2cap_chan *c;
@@ -178,7 +213,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
static void __l2cap_state_change(struct l2cap_chan *chan, int state)
{
BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state),
- state_to_string(state));
+ state_to_string(state));
chan->state = state;
chan->ops->state_change(chan, state);
@@ -361,7 +396,7 @@ static void l2cap_seq_list_append(struct l2cap_seq_list *seq_list, u16 seq)
static void l2cap_chan_timeout(struct work_struct *work)
{
struct l2cap_chan *chan = container_of(work, struct l2cap_chan,
- chan_timer.work);
+ chan_timer.work);
struct l2cap_conn *conn = chan->conn;
int reason;
@@ -373,7 +408,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
reason = ECONNREFUSED;
else if (chan->state == BT_CONNECT &&
- chan->sec_level != BT_SECURITY_SDP)
+ chan->sec_level != BT_SECURITY_SDP)
reason = ECONNREFUSED;
else
reason = ETIMEDOUT;
@@ -455,7 +490,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
-static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
__le16_to_cpu(chan->psm), chan->dcid);
@@ -504,7 +539,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE;
chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME;
chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT;
- chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO;
+ chan->local_flush_to = L2CAP_EFS_DEFAULT_FLUSH_TO;
l2cap_chan_hold(chan);
@@ -527,6 +562,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
if (conn) {
+ struct amp_mgr *mgr = conn->hcon->amp_mgr;
/* Delete from channel list */
list_del(&chan->list);
@@ -536,10 +572,19 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
if (chan->chan_type != L2CAP_CHAN_CONN_FIX_A2MP)
hci_conn_put(conn->hcon);
+
+ if (mgr && mgr->bredr_chan == chan)
+ mgr->bredr_chan = NULL;
}
- if (chan->ops->teardown)
- chan->ops->teardown(chan, err);
+ if (chan->hs_hchan) {
+ struct hci_chan *hs_hchan = chan->hs_hchan;
+
+ BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan);
+ amp_disconnect_logical_link(hs_hchan);
+ }
+
+ chan->ops->teardown(chan, err);
if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
return;
@@ -573,28 +618,27 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
struct l2cap_conn *conn = chan->conn;
struct sock *sk = chan->sk;
- BT_DBG("chan %p state %s sk %p", chan,
- state_to_string(chan->state), sk);
+ BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state),
+ sk);
switch (chan->state) {
case BT_LISTEN:
- if (chan->ops->teardown)
- chan->ops->teardown(chan, 0);
+ chan->ops->teardown(chan, 0);
break;
case BT_CONNECTED:
case BT_CONFIG:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
+ conn->hcon->type == ACL_LINK) {
__set_chan_timer(chan, sk->sk_sndtimeo);
- l2cap_send_disconn_req(conn, chan, reason);
+ l2cap_send_disconn_req(chan, reason);
} else
l2cap_chan_del(chan, reason);
break;
case BT_CONNECT2:
if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
+ conn->hcon->type == ACL_LINK) {
struct l2cap_conn_rsp rsp;
__u16 result;
@@ -609,7 +653,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
rsp.result = cpu_to_le16(result);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
- sizeof(rsp), &rsp);
+ sizeof(rsp), &rsp);
}
l2cap_chan_del(chan, reason);
@@ -621,8 +665,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
default:
- if (chan->ops->teardown)
- chan->ops->teardown(chan, 0);
+ chan->ops->teardown(chan, 0);
break;
}
}
@@ -691,7 +734,8 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn)
return id;
}
-static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, void *data)
+static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
+ void *data)
{
struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
u8 flags;
@@ -712,16 +756,31 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
hci_send_acl(conn->hchan, skb, flags);
}
+static bool __chan_is_moving(struct l2cap_chan *chan)
+{
+ return chan->move_state != L2CAP_MOVE_STABLE &&
+ chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
+}
+
static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
{
struct hci_conn *hcon = chan->conn->hcon;
u16 flags;
BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len,
- skb->priority);
+ skb->priority);
+
+ if (chan->hs_hcon && !__chan_is_moving(chan)) {
+ if (chan->hs_hchan)
+ hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE);
+ else
+ kfree_skb(skb);
+
+ return;
+ }
if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
- lmp_no_flush_capable(hcon->hdev))
+ lmp_no_flush_capable(hcon->hdev))
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -895,6 +954,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan,
if (!control->sframe)
return;
+ if (__chan_is_moving(chan))
+ return;
+
if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) &&
!control->poll)
control->final = 1;
@@ -946,7 +1008,26 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan)
return !test_bit(CONF_CONNECT_PEND, &chan->conf_state);
}
-static void l2cap_send_conn_req(struct l2cap_chan *chan)
+static bool __amp_capable(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+
+ if (enable_hs &&
+ hci_amp_capable() &&
+ chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED &&
+ conn->fixed_chan_mask & L2CAP_FC_A2MP)
+ return true;
+ else
+ return false;
+}
+
+static bool l2cap_check_efs(struct l2cap_chan *chan)
+{
+ /* Check EFS parameters */
+ return true;
+}
+
+void l2cap_send_conn_req(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
struct l2cap_conn_req req;
@@ -961,6 +1042,76 @@ static void l2cap_send_conn_req(struct l2cap_chan *chan)
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
}
+static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id)
+{
+ struct l2cap_create_chan_req req;
+ req.scid = cpu_to_le16(chan->scid);
+ req.psm = chan->psm;
+ req.amp_id = amp_id;
+
+ chan->ident = l2cap_get_ident(chan->conn);
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ,
+ sizeof(req), &req);
+}
+
+static void l2cap_move_setup(struct l2cap_chan *chan)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("chan %p", chan);
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ __clear_retrans_timer(chan);
+ __clear_monitor_timer(chan);
+ __clear_ack_timer(chan);
+
+ chan->retry_count = 0;
+ skb_queue_walk(&chan->tx_q, skb) {
+ if (bt_cb(skb)->control.retries)
+ bt_cb(skb)->control.retries = 1;
+ else
+ break;
+ }
+
+ chan->expected_tx_seq = chan->buffer_seq;
+
+ clear_bit(CONN_REJ_ACT, &chan->conn_state);
+ clear_bit(CONN_SREJ_ACT, &chan->conn_state);
+ l2cap_seq_list_clear(&chan->retrans_list);
+ l2cap_seq_list_clear(&chan->srej_list);
+ skb_queue_purge(&chan->srej_q);
+
+ chan->tx_state = L2CAP_TX_STATE_XMIT;
+ chan->rx_state = L2CAP_RX_STATE_MOVE;
+
+ set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+}
+
+static void l2cap_move_done(struct l2cap_chan *chan)
+{
+ u8 move_role = chan->move_role;
+ BT_DBG("chan %p", chan);
+
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
+ if (chan->mode != L2CAP_MODE_ERTM)
+ return;
+
+ switch (move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL);
+ chan->rx_state = L2CAP_RX_STATE_WAIT_F;
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ chan->rx_state = L2CAP_RX_STATE_WAIT_P;
+ break;
+ }
+}
+
static void l2cap_chan_ready(struct l2cap_chan *chan)
{
/* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -972,6 +1123,16 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
chan->ops->ready(chan);
}
+static void l2cap_start_connection(struct l2cap_chan *chan)
+{
+ if (__amp_capable(chan)) {
+ BT_DBG("chan %p AMP capable: discover AMPs", chan);
+ a2mp_discover_amp(chan);
+ } else {
+ l2cap_send_conn_req(chan);
+ }
+}
+
static void l2cap_do_start(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
@@ -986,8 +1147,9 @@ static void l2cap_do_start(struct l2cap_chan *chan)
return;
if (l2cap_chan_check_security(chan) &&
- __l2cap_no_conn_pending(chan))
- l2cap_send_conn_req(chan);
+ __l2cap_no_conn_pending(chan)) {
+ l2cap_start_connection(chan);
+ }
} else {
struct l2cap_info_req req;
req.type = __constant_cpu_to_le16(L2CAP_IT_FEAT_MASK);
@@ -997,8 +1159,8 @@ static void l2cap_do_start(struct l2cap_chan *chan)
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
- l2cap_send_cmd(conn, conn->info_ident,
- L2CAP_INFO_REQ, sizeof(req), &req);
+ l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
+ sizeof(req), &req);
}
}
@@ -1018,9 +1180,10 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
}
}
-static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *chan, int err)
+static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err)
{
struct sock *sk = chan->sk;
+ struct l2cap_conn *conn = chan->conn;
struct l2cap_disconn_req req;
if (!conn)
@@ -1033,14 +1196,14 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
}
if (chan->chan_type == L2CAP_CHAN_CONN_FIX_A2MP) {
- __l2cap_state_change(chan, BT_DISCONN);
+ l2cap_state_change(chan, BT_DISCONN);
return;
}
req.dcid = cpu_to_le16(chan->dcid);
req.scid = cpu_to_le16(chan->scid);
- l2cap_send_cmd(conn, l2cap_get_ident(conn),
- L2CAP_DISCONN_REQ, sizeof(req), &req);
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ,
+ sizeof(req), &req);
lock_sock(sk);
__l2cap_state_change(chan, BT_DISCONN);
@@ -1069,20 +1232,20 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
if (chan->state == BT_CONNECT) {
if (!l2cap_chan_check_security(chan) ||
- !__l2cap_no_conn_pending(chan)) {
+ !__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
}
if (!l2cap_mode_supported(chan->mode, conn->feat_mask)
- && test_bit(CONF_STATE2_DEVICE,
+ && test_bit(CONF_STATE2_DEVICE,
&chan->conf_state)) {
l2cap_chan_close(chan, ECONNRESET);
l2cap_chan_unlock(chan);
continue;
}
- l2cap_send_conn_req(chan);
+ l2cap_start_connection(chan);
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
@@ -1094,11 +1257,9 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
lock_sock(sk);
if (test_bit(BT_SK_DEFER_SETUP,
&bt_sk(sk)->flags)) {
- struct sock *parent = bt_sk(sk)->parent;
rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
- if (parent)
- parent->sk_data_ready(parent, 0);
+ chan->ops->defer(chan);
} else {
__l2cap_state_change(chan, BT_CONFIG);
@@ -1112,17 +1273,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
}
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
- sizeof(rsp), &rsp);
+ sizeof(rsp), &rsp);
if (test_bit(CONF_REQ_SENT, &chan->conf_state) ||
- rsp.result != L2CAP_CR_SUCCESS) {
+ rsp.result != L2CAP_CR_SUCCESS) {
l2cap_chan_unlock(chan);
continue;
}
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, buf), buf);
+ l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
@@ -1204,8 +1365,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
- bt_accept_enqueue(parent, sk);
-
l2cap_chan_add(conn, chan);
l2cap_chan_ready(chan);
@@ -1270,7 +1429,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
list_for_each_entry(chan, &conn->chan_l, list) {
if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags))
- __l2cap_chan_set_err(chan, err);
+ l2cap_chan_set_err(chan, err);
}
mutex_unlock(&conn->chan_lock);
@@ -1279,7 +1438,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
static void l2cap_info_timeout(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
- info_timer.work);
+ info_timer.work);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -1333,7 +1492,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
static void security_timeout(struct work_struct *work)
{
struct l2cap_conn *conn = container_of(work, struct l2cap_conn,
- security_timer.work);
+ security_timer.work);
BT_DBG("conn %p", conn);
@@ -1355,7 +1514,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
if (!hchan)
return NULL;
- conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC);
+ conn = kzalloc(sizeof(struct l2cap_conn), GFP_KERNEL);
if (!conn) {
hci_chan_del(hchan);
return NULL;
@@ -1367,10 +1526,22 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan);
- if (hcon->hdev->le_mtu && hcon->type == LE_LINK)
- conn->mtu = hcon->hdev->le_mtu;
- else
+ switch (hcon->type) {
+ case AMP_LINK:
+ conn->mtu = hcon->hdev->block_mtu;
+ break;
+
+ case LE_LINK:
+ if (hcon->hdev->le_mtu) {
+ conn->mtu = hcon->hdev->le_mtu;
+ break;
+ }
+ /* fall through */
+
+ default:
conn->mtu = hcon->hdev->acl_mtu;
+ break;
+ }
conn->src = &hcon->hdev->bdaddr;
conn->dst = &hcon->dst;
@@ -1448,7 +1619,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
__u8 auth_type;
int err;
- BT_DBG("%s -> %s (type %u) psm 0x%2.2x", batostr(src), batostr(dst),
+ BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst,
dst_type, __le16_to_cpu(psm));
hdev = hci_get_route(dst, src);
@@ -1461,7 +1632,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
/* PSM must be odd and lsb of upper byte must be 0 */
if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
- chan->chan_type != L2CAP_CHAN_RAW) {
+ chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
}
@@ -1657,6 +1828,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan,
BT_DBG("chan %p, skbs %p", chan, skbs);
+ if (__chan_is_moving(chan))
+ return;
+
skb_queue_splice_tail_init(skbs, &chan->tx_q);
while (!skb_queue_empty(&chan->tx_q)) {
@@ -1699,6 +1873,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return 0;
+ if (__chan_is_moving(chan))
+ return 0;
+
while (chan->tx_send_head &&
chan->unacked_frames < chan->remote_tx_win &&
chan->tx_state == L2CAP_TX_STATE_XMIT) {
@@ -1764,13 +1941,16 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state))
return;
+ if (__chan_is_moving(chan))
+ return;
+
while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) {
seq = l2cap_seq_list_pop(&chan->retrans_list);
skb = l2cap_ertm_seq_in_queue(&chan->tx_q, seq);
if (!skb) {
BT_DBG("Error: Can't retransmit seq %d, frame missing",
- seq);
+ seq);
continue;
}
@@ -1780,7 +1960,7 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
if (chan->max_tx != 0 &&
bt_cb(skb)->control.retries > chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
l2cap_seq_list_clear(&chan->retrans_list);
break;
}
@@ -1795,9 +1975,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan)
/* Cloned sk_buffs are read-only, so we need a
* writeable copy
*/
- tx_skb = skb_copy(skb, GFP_ATOMIC);
+ tx_skb = skb_copy(skb, GFP_KERNEL);
} else {
- tx_skb = skb_clone(skb, GFP_ATOMIC);
+ tx_skb = skb_clone(skb, GFP_KERNEL);
}
if (!tx_skb) {
@@ -1855,7 +2035,7 @@ static void l2cap_retransmit_all(struct l2cap_chan *chan,
if (chan->unacked_frames) {
skb_queue_walk(&chan->tx_q, skb) {
if (bt_cb(skb)->control.txseq == control->reqseq ||
- skb == chan->tx_send_head)
+ skb == chan->tx_send_head)
break;
}
@@ -2106,7 +2286,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
/* PDU size is derived from the HCI MTU */
pdu_len = chan->conn->mtu;
- pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
+ /* Constrain PDU size for BR/EDR connections */
+ if (!chan->hs_hcon)
+ pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD);
/* Adjust for largest possible L2CAP overhead. */
if (chan->fcs)
@@ -2156,7 +2338,7 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
}
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
- u32 priority)
+ u32 priority)
{
struct sk_buff *skb;
int err;
@@ -2484,7 +2666,7 @@ static void l2cap_tx_state_wait_f(struct l2cap_chan *chan,
__set_monitor_timer(chan);
chan->retry_count++;
} else {
- l2cap_send_disconn_req(chan->conn, chan, ECONNABORTED);
+ l2cap_send_disconn_req(chan, ECONNABORTED);
}
break;
default:
@@ -2543,7 +2725,7 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
/* Don't send frame to the socket it came from */
if (skb->sk == sk)
continue;
- nskb = skb_clone(skb, GFP_ATOMIC);
+ nskb = skb_clone(skb, GFP_KERNEL);
if (!nskb)
continue;
@@ -2569,7 +2751,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
count = min_t(unsigned int, conn->mtu, len);
- skb = bt_skb_alloc(count, GFP_ATOMIC);
+ skb = bt_skb_alloc(count, GFP_KERNEL);
if (!skb)
return NULL;
@@ -2599,7 +2781,7 @@ static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn, u8 code,
while (len) {
count = min_t(unsigned int, conn->mtu, len);
- *frag = bt_skb_alloc(count, GFP_ATOMIC);
+ *frag = bt_skb_alloc(count, GFP_KERNEL);
if (!*frag)
goto fail;
@@ -2618,7 +2800,8 @@ fail:
return NULL;
}
-static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
+static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen,
+ unsigned long *val)
{
struct l2cap_conf_opt *opt = *ptr;
int len;
@@ -2692,7 +2875,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
efs.msdu = cpu_to_le16(chan->local_msdu);
efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime);
efs.acc_lat = __constant_cpu_to_le32(L2CAP_DEFAULT_ACC_LAT);
- efs.flush_to = __constant_cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO);
+ efs.flush_to = __constant_cpu_to_le32(L2CAP_EFS_DEFAULT_FLUSH_TO);
break;
case L2CAP_MODE_STREAMING:
@@ -2709,7 +2892,7 @@ static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
}
l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
- (unsigned long) &efs);
+ (unsigned long) &efs);
}
static void l2cap_ack_timeout(struct work_struct *work)
@@ -2749,6 +2932,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan)
skb_queue_head_init(&chan->tx_q);
+ chan->local_amp_id = 0;
+ chan->move_id = 0;
+ chan->move_state = L2CAP_MOVE_STABLE;
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+
if (chan->mode != L2CAP_MODE_ERTM)
return 0;
@@ -2795,16 +2983,54 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan)
return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
}
+static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
+ struct l2cap_conf_rfc *rfc)
+{
+ if (chan->local_amp_id && chan->hs_hcon) {
+ u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to;
+
+ /* Class 1 devices have must have ERTM timeouts
+ * exceeding the Link Supervision Timeout. The
+ * default Link Supervision Timeout for AMP
+ * controllers is 10 seconds.
+ *
+ * Class 1 devices use 0xffffffff for their
+ * best-effort flush timeout, so the clamping logic
+ * will result in a timeout that meets the above
+ * requirement. ERTM timeouts are 16-bit values, so
+ * the maximum timeout is 65.535 seconds.
+ */
+
+ /* Convert timeout to milliseconds and round */
+ ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000);
+
+ /* This is the recommended formula for class 2 devices
+ * that start ERTM timers when packets are sent to the
+ * controller.
+ */
+ ertm_to = 3 * ertm_to + 500;
+
+ if (ertm_to > 0xffff)
+ ertm_to = 0xffff;
+
+ rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
+ rfc->monitor_timeout = rfc->retrans_timeout;
+ } else {
+ rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
+ rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ }
+}
+
static inline void l2cap_txwin_setup(struct l2cap_chan *chan)
{
if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW &&
- __l2cap_ews_supported(chan)) {
+ __l2cap_ews_supported(chan)) {
/* use extended control field */
set_bit(FLAG_EXT_CTRL, &chan->flags);
chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW;
} else {
chan->tx_win = min_t(u16, chan->tx_win,
- L2CAP_DEFAULT_TX_WINDOW);
+ L2CAP_DEFAULT_TX_WINDOW);
chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW;
}
chan->ack_win = chan->tx_win;
@@ -2844,7 +3070,7 @@ done:
switch (chan->mode) {
case L2CAP_MODE_BASIC:
if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) &&
- !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
+ !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING))
break;
rfc.mode = L2CAP_MODE_BASIC;
@@ -2855,44 +3081,42 @@ done:
rfc.max_pdu_size = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc);
break;
case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM;
rfc.max_transmit = chan->max_tx;
- rfc.retrans_timeout = 0;
- rfc.monitor_timeout = 0;
+
+ __l2cap_set_ertm_timeouts(chan, &rfc);
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
- L2CAP_EXT_HDR_SIZE -
- L2CAP_SDULEN_SIZE -
- L2CAP_FCS_SIZE);
+ L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
l2cap_txwin_setup(chan);
rfc.txwin_size = min_t(u16, chan->tx_win,
- L2CAP_DEFAULT_TX_WINDOW);
+ L2CAP_DEFAULT_TX_WINDOW);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan);
- if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
- break;
-
- if (chan->fcs == L2CAP_FCS_NONE ||
- test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
- chan->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
- }
-
if (test_bit(FLAG_EXT_CTRL, &chan->flags))
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
- chan->tx_win);
+ chan->tx_win);
+
+ if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
+ chan->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
+ chan->fcs);
+ }
break;
case L2CAP_MODE_STREAMING:
@@ -2904,25 +3128,23 @@ done:
rfc.monitor_timeout = 0;
size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu -
- L2CAP_EXT_HDR_SIZE -
- L2CAP_SDULEN_SIZE -
- L2CAP_FCS_SIZE);
+ L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE -
+ L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
- (unsigned long) &rfc);
+ (unsigned long) &rfc);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
l2cap_add_opt_efs(&ptr, chan);
- if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS))
- break;
-
- if (chan->fcs == L2CAP_FCS_NONE ||
- test_bit(CONF_NO_FCS_RECV, &chan->conf_state)) {
- chan->fcs = L2CAP_FCS_NONE;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs);
- }
+ if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
+ if (chan->fcs == L2CAP_FCS_NONE ||
+ test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
+ chan->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
+ chan->fcs);
+ }
break;
}
@@ -2974,7 +3196,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_CONF_FCS:
if (val == L2CAP_FCS_NONE)
- set_bit(CONF_NO_FCS_RECV, &chan->conf_state);
+ set_bit(CONF_RECV_NO_FCS, &chan->conf_state);
break;
case L2CAP_CONF_EFS:
@@ -3011,7 +3233,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
case L2CAP_MODE_ERTM:
if (!test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) {
chan->mode = l2cap_select_mode(rfc.mode,
- chan->conn->feat_mask);
+ chan->conn->feat_mask);
break;
}
@@ -3036,8 +3258,8 @@ done:
if (chan->num_conf_rsp == 1)
return -ECONNREFUSED;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
}
if (result == L2CAP_CONF_SUCCESS) {
@@ -3054,8 +3276,8 @@ done:
if (remote_efs) {
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != chan->local_stype) {
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype) {
result = L2CAP_CONF_UNACCEPT;
@@ -3063,8 +3285,8 @@ done:
return -ECONNREFUSED;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
- sizeof(efs),
- (unsigned long) &efs);
+ sizeof(efs),
+ (unsigned long) &efs);
} else {
/* Send PENDING Conf Rsp */
result = L2CAP_CONF_PENDING;
@@ -3087,51 +3309,45 @@ done:
chan->remote_max_tx = rfc.max_transmit;
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
- chan->conn->mtu -
- L2CAP_EXT_HDR_SIZE -
- L2CAP_SDULEN_SIZE -
- L2CAP_FCS_SIZE);
+ chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size;
- rfc.retrans_timeout =
- __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
- rfc.monitor_timeout =
- __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
+ __l2cap_set_ertm_timeouts(chan, &rfc);
set_bit(CONF_MODE_DONE, &chan->conf_state);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ sizeof(rfc), (unsigned long) &rfc);
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
chan->remote_id = efs.id;
chan->remote_stype = efs.stype;
chan->remote_msdu = le16_to_cpu(efs.msdu);
chan->remote_flush_to =
- le32_to_cpu(efs.flush_to);
+ le32_to_cpu(efs.flush_to);
chan->remote_acc_lat =
- le32_to_cpu(efs.acc_lat);
+ le32_to_cpu(efs.acc_lat);
chan->remote_sdu_itime =
le32_to_cpu(efs.sdu_itime);
l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
- sizeof(efs), (unsigned long) &efs);
+ sizeof(efs),
+ (unsigned long) &efs);
}
break;
case L2CAP_MODE_STREAMING:
size = min_t(u16, le16_to_cpu(rfc.max_pdu_size),
- chan->conn->mtu -
- L2CAP_EXT_HDR_SIZE -
- L2CAP_SDULEN_SIZE -
- L2CAP_FCS_SIZE);
+ chan->conn->mtu - L2CAP_EXT_HDR_SIZE -
+ L2CAP_SDULEN_SIZE - L2CAP_FCS_SIZE);
rfc.max_pdu_size = cpu_to_le16(size);
chan->remote_mps = size;
set_bit(CONF_MODE_DONE, &chan->conf_state);
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
break;
@@ -3152,7 +3368,8 @@ done:
return ptr - data;
}
-static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, void *data, u16 *result)
+static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
+ void *data, u16 *result)
{
struct l2cap_conf_req *req = data;
void *ptr = req->data;
@@ -3179,7 +3396,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
case L2CAP_CONF_FLUSH_TO:
chan->flush_to = val;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
- 2, chan->flush_to);
+ 2, chan->flush_to);
break;
case L2CAP_CONF_RFC:
@@ -3187,13 +3404,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
memcpy(&rfc, (void *)val, olen);
if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state) &&
- rfc.mode != chan->mode)
+ rfc.mode != chan->mode)
return -ECONNREFUSED;
chan->fcs = 0;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ sizeof(rfc), (unsigned long) &rfc);
break;
case L2CAP_CONF_EWS:
@@ -3207,12 +3424,19 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
memcpy(&efs, (void *)val, olen);
if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != L2CAP_SERV_NOTRAFIC &&
- efs.stype != chan->local_stype)
+ efs.stype != L2CAP_SERV_NOTRAFIC &&
+ efs.stype != chan->local_stype)
return -ECONNREFUSED;
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
- sizeof(efs), (unsigned long) &efs);
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
+ (unsigned long) &efs);
+ break;
+
+ case L2CAP_CONF_FCS:
+ if (*result == L2CAP_CONF_PENDING)
+ if (val == L2CAP_FCS_NONE)
+ set_bit(CONF_RECV_NO_FCS,
+ &chan->conf_state);
break;
}
}
@@ -3235,10 +3459,10 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
chan->local_msdu = le16_to_cpu(efs.msdu);
chan->local_sdu_itime =
- le32_to_cpu(efs.sdu_itime);
+ le32_to_cpu(efs.sdu_itime);
chan->local_acc_lat = le32_to_cpu(efs.acc_lat);
chan->local_flush_to =
- le32_to_cpu(efs.flush_to);
+ le32_to_cpu(efs.flush_to);
}
break;
@@ -3253,7 +3477,8 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi
return ptr - data;
}
-static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data, u16 result, u16 flags)
+static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
+ u16 result, u16 flags)
{
struct l2cap_conf_rsp *rsp = data;
void *ptr = rsp->data;
@@ -3272,19 +3497,27 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
struct l2cap_conn_rsp rsp;
struct l2cap_conn *conn = chan->conn;
u8 buf[128];
+ u8 rsp_code;
rsp.scid = cpu_to_le16(chan->dcid);
rsp.dcid = cpu_to_le16(chan->scid);
rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, chan->ident,
- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+
+ if (chan->hs_hcon)
+ rsp_code = L2CAP_CREATE_CHAN_RSP;
+ else
+ rsp_code = L2CAP_CONN_RSP;
+
+ BT_DBG("chan %p rsp_code %u", chan, rsp_code);
+
+ l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp);
if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state))
return;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, buf), buf);
+ l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
@@ -3339,7 +3572,8 @@ static void l2cap_conf_rfc_get(struct l2cap_chan *chan, void *rsp, int len)
}
}
-static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_command_rej(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
@@ -3347,7 +3581,7 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
- cmd->ident == conn->info_ident) {
+ cmd->ident == conn->info_ident) {
cancel_delayed_work(&conn->info_timer);
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
@@ -3359,7 +3593,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
return 0;
}
-static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u8 *data, u8 rsp_code, u8 amp_id)
{
struct l2cap_conn_req *req = (struct l2cap_conn_req *) data;
struct l2cap_conn_rsp rsp;
@@ -3386,7 +3622,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
/* Check if the ACL is secure enough (if not SDP) */
if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) &&
- !hci_conn_check_link_mode(conn->hcon)) {
+ !hci_conn_check_link_mode(conn->hcon)) {
conn->disc_reason = HCI_ERROR_AUTH_FAILURE;
result = L2CAP_CR_SEC_BLOCK;
goto response;
@@ -3410,8 +3646,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
bacpy(&bt_sk(sk)->dst, conn->dst);
chan->psm = psm;
chan->dcid = scid;
-
- bt_accept_enqueue(parent, sk);
+ chan->local_amp_id = amp_id;
__l2cap_chan_add(conn, chan);
@@ -3427,10 +3662,19 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
__l2cap_state_change(chan, BT_CONNECT2);
result = L2CAP_CR_PEND;
status = L2CAP_CS_AUTHOR_PEND;
- parent->sk_data_ready(parent, 0);
+ chan->ops->defer(chan);
} else {
- __l2cap_state_change(chan, BT_CONFIG);
- result = L2CAP_CR_SUCCESS;
+ /* Force pending result for AMP controllers.
+ * The connection will succeed after the
+ * physical link is up.
+ */
+ if (amp_id) {
+ __l2cap_state_change(chan, BT_CONNECT2);
+ result = L2CAP_CR_PEND;
+ } else {
+ __l2cap_state_change(chan, BT_CONFIG);
+ result = L2CAP_CR_SUCCESS;
+ }
status = L2CAP_CS_NO_INFO;
}
} else {
@@ -3453,7 +3697,7 @@ sendresp:
rsp.dcid = cpu_to_le16(dcid);
rsp.result = cpu_to_le16(result);
rsp.status = cpu_to_le16(status);
- l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, cmd->ident, rsp_code, sizeof(rsp), &rsp);
if (result == L2CAP_CR_PEND && status == L2CAP_CS_NO_INFO) {
struct l2cap_info_req info;
@@ -3464,23 +3708,42 @@ sendresp:
schedule_delayed_work(&conn->info_timer, L2CAP_INFO_TIMEOUT);
- l2cap_send_cmd(conn, conn->info_ident,
- L2CAP_INFO_REQ, sizeof(info), &info);
+ l2cap_send_cmd(conn, conn->info_ident, L2CAP_INFO_REQ,
+ sizeof(info), &info);
}
if (chan && !test_bit(CONF_REQ_SENT, &chan->conf_state) &&
- result == L2CAP_CR_SUCCESS) {
+ result == L2CAP_CR_SUCCESS) {
u8 buf[128];
set_bit(CONF_REQ_SENT, &chan->conf_state);
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, buf), buf);
+ l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
+ return chan;
+}
+
+static int l2cap_connect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
+{
+ struct hci_dev *hdev = conn->hcon->hdev;
+ struct hci_conn *hcon = conn->hcon;
+
+ hci_dev_lock(hdev);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
+ !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
+ mgmt_device_connected(hdev, &hcon->dst, hcon->type,
+ hcon->dst_type, 0, NULL, 0,
+ hcon->dev_class);
+ hci_dev_unlock(hdev);
+
+ l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
return 0;
}
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data;
u16 scid, dcid, result, status;
@@ -3494,7 +3757,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
status = __le16_to_cpu(rsp->status);
BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x",
- dcid, scid, result, status);
+ dcid, scid, result, status);
mutex_lock(&conn->chan_lock);
@@ -3527,7 +3790,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
break;
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, req), req);
+ l2cap_build_conf_req(chan, req), req);
chan->num_conf_req++;
break;
@@ -3555,11 +3818,29 @@ static inline void set_default_fcs(struct l2cap_chan *chan)
*/
if (chan->mode != L2CAP_MODE_ERTM && chan->mode != L2CAP_MODE_STREAMING)
chan->fcs = L2CAP_FCS_NONE;
- else if (!test_bit(CONF_NO_FCS_RECV, &chan->conf_state))
+ else if (!test_bit(CONF_RECV_NO_FCS, &chan->conf_state))
chan->fcs = L2CAP_FCS_CRC16;
}
-static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
+static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data,
+ u8 ident, u16 flags)
+{
+ struct l2cap_conn *conn = chan->conn;
+
+ BT_DBG("conn %p chan %p ident %d flags 0x%4.4x", conn, chan, ident,
+ flags);
+
+ clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
+ set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
+
+ l2cap_send_cmd(conn, ident, L2CAP_CONF_RSP,
+ l2cap_build_conf_rsp(chan, data,
+ L2CAP_CONF_SUCCESS, flags), data);
+}
+
+static inline int l2cap_config_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
struct l2cap_conf_req *req = (struct l2cap_conf_req *) data;
u16 dcid, flags;
@@ -3584,7 +3865,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
rej.dcid = cpu_to_le16(chan->dcid);
l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ,
- sizeof(rej), &rej);
+ sizeof(rej), &rej);
goto unlock;
}
@@ -3592,8 +3873,8 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
len = cmd_len - sizeof(*req);
if (len < 0 || chan->conf_len + len > sizeof(chan->conf_req)) {
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(chan, rsp,
- L2CAP_CONF_REJECT, flags), rsp);
+ l2cap_build_conf_rsp(chan, rsp,
+ L2CAP_CONF_REJECT, flags), rsp);
goto unlock;
}
@@ -3604,18 +3885,19 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (flags & L2CAP_CONF_FLAG_CONTINUATION) {
/* Incomplete config. Send empty response. */
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(chan, rsp,
- L2CAP_CONF_SUCCESS, flags), rsp);
+ l2cap_build_conf_rsp(chan, rsp,
+ L2CAP_CONF_SUCCESS, flags), rsp);
goto unlock;
}
/* Complete config. */
len = l2cap_parse_conf_req(chan, rsp);
if (len < 0) {
- l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto unlock;
}
+ chan->ident = cmd->ident;
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
chan->num_conf_rsp++;
@@ -3633,7 +3915,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
err = l2cap_ertm_init(chan);
if (err < 0)
- l2cap_send_disconn_req(chan->conn, chan, -err);
+ l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
@@ -3643,23 +3925,22 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
u8 buf[64];
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(chan, buf), buf);
+ l2cap_build_conf_req(chan, buf), buf);
chan->num_conf_req++;
}
/* Got Conf Rsp PENDING from remote side and asume we sent
Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
- test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
+ test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
/* check compatibility */
- clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
- set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
-
- l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(chan, rsp,
- L2CAP_CONF_SUCCESS, flags), rsp);
+ /* Send rsp for BR/EDR channel */
+ if (!chan->hs_hcon)
+ l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags);
+ else
+ chan->ident = cmd->ident;
}
unlock:
@@ -3667,7 +3948,8 @@ unlock:
return err;
}
-static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_config_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_conf_rsp *rsp = (struct l2cap_conf_rsp *)data;
u16 scid, flags, result;
@@ -3699,20 +3981,21 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
char buf[64];
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
- buf, &result);
+ buf, &result);
if (len < 0) {
- l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
- /* check compatibility */
-
- clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state);
- set_bit(CONF_OUTPUT_DONE, &chan->conf_state);
-
- l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP,
- l2cap_build_conf_rsp(chan, buf,
- L2CAP_CONF_SUCCESS, 0x0000), buf);
+ if (!chan->hs_hcon) {
+ l2cap_send_efs_conf_rsp(chan, buf, cmd->ident,
+ 0);
+ } else {
+ if (l2cap_check_efs(chan)) {
+ amp_create_logical_link(chan);
+ chan->ident = cmd->ident;
+ }
+ }
}
goto done;
@@ -3721,21 +4004,21 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
char req[64];
if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
- l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
/* throw out any old stored conf requests */
result = L2CAP_CONF_SUCCESS;
len = l2cap_parse_conf_rsp(chan, rsp->data, len,
- req, &result);
+ req, &result);
if (len < 0) {
- l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
l2cap_send_cmd(conn, l2cap_get_ident(conn),
- L2CAP_CONF_REQ, len, req);
+ L2CAP_CONF_REQ, len, req);
chan->num_conf_req++;
if (result != L2CAP_CONF_SUCCESS)
goto done;
@@ -3746,7 +4029,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
l2cap_chan_set_err(chan, ECONNRESET);
__set_chan_timer(chan, L2CAP_DISC_REJ_TIMEOUT);
- l2cap_send_disconn_req(conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto done;
}
@@ -3763,7 +4046,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
err = l2cap_ertm_init(chan);
if (err < 0)
- l2cap_send_disconn_req(chan->conn, chan, -err);
+ l2cap_send_disconn_req(chan, -err);
else
l2cap_chan_ready(chan);
}
@@ -3773,7 +4056,8 @@ done:
return err;
}
-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_disconn_req *req = (struct l2cap_disconn_req *) data;
struct l2cap_disconn_rsp rsp;
@@ -3819,7 +4103,8 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_disconn_rsp *rsp = (struct l2cap_disconn_rsp *) data;
u16 dcid, scid;
@@ -3853,7 +4138,8 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
return 0;
}
-static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_info_req *req = (struct l2cap_info_req *) data;
u16 type;
@@ -3870,14 +4156,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS);
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
- | L2CAP_FEAT_FCS;
+ | L2CAP_FEAT_FCS;
if (enable_hs)
feat_mask |= L2CAP_FEAT_EXT_FLOW
- | L2CAP_FEAT_EXT_WINDOW;
+ | L2CAP_FEAT_EXT_WINDOW;
put_unaligned_le32(feat_mask, rsp->data);
- l2cap_send_cmd(conn, cmd->ident,
- L2CAP_INFO_RSP, sizeof(buf), buf);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
+ buf);
} else if (type == L2CAP_IT_FIXED_CHAN) {
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
@@ -3890,20 +4176,21 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
rsp->type = __constant_cpu_to_le16(L2CAP_IT_FIXED_CHAN);
rsp->result = __constant_cpu_to_le16(L2CAP_IR_SUCCESS);
memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
- l2cap_send_cmd(conn, cmd->ident,
- L2CAP_INFO_RSP, sizeof(buf), buf);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
+ buf);
} else {
struct l2cap_info_rsp rsp;
rsp.type = cpu_to_le16(type);
rsp.result = __constant_cpu_to_le16(L2CAP_IR_NOTSUPP);
- l2cap_send_cmd(conn, cmd->ident,
- L2CAP_INFO_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(rsp),
+ &rsp);
}
return 0;
}
-static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data)
+static inline int l2cap_information_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) data;
u16 type, result;
@@ -3915,7 +4202,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
/* L2CAP Info req/rsp are unbound to channels, add extra checks */
if (cmd->ident != conn->info_ident ||
- conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
+ conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE)
return 0;
cancel_delayed_work(&conn->info_timer);
@@ -3940,7 +4227,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
conn->info_ident = l2cap_get_ident(conn);
l2cap_send_cmd(conn, conn->info_ident,
- L2CAP_INFO_REQ, sizeof(req), &req);
+ L2CAP_INFO_REQ, sizeof(req), &req);
} else {
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -3961,12 +4248,14 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
return 0;
}
-static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u16 cmd_len,
- void *data)
+static int l2cap_create_channel_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_create_chan_req *req = data;
struct l2cap_create_chan_rsp rsp;
+ struct l2cap_chan *chan;
+ struct hci_dev *hdev;
u16 psm, scid;
if (cmd_len != sizeof(*req))
@@ -3980,56 +4269,119 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn,
BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id);
- /* Placeholder: Always reject */
+ /* For controller id 0 make BR/EDR connection */
+ if (req->amp_id == HCI_BREDR_ID) {
+ l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+ req->amp_id);
+ return 0;
+ }
+
+ /* Validate AMP controller id */
+ hdev = hci_dev_get(req->amp_id);
+ if (!hdev)
+ goto error;
+
+ if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) {
+ hci_dev_put(hdev);
+ goto error;
+ }
+
+ chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP,
+ req->amp_id);
+ if (chan) {
+ struct amp_mgr *mgr = conn->hcon->amp_mgr;
+ struct hci_conn *hs_hcon;
+
+ hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst);
+ if (!hs_hcon) {
+ hci_dev_put(hdev);
+ return -EFAULT;
+ }
+
+ BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon);
+
+ mgr->bredr_chan = chan;
+ chan->hs_hcon = hs_hcon;
+ chan->fcs = L2CAP_FCS_NONE;
+ conn->mtu = hdev->block_mtu;
+ }
+
+ hci_dev_put(hdev);
+
+ return 0;
+
+error:
rsp.dcid = 0;
rsp.scid = cpu_to_le16(scid);
- rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP);
rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP,
sizeof(rsp), &rsp);
- return 0;
+ return -EFAULT;
}
-static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, void *data)
+static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id)
{
- BT_DBG("conn %p", conn);
+ struct l2cap_move_chan_req req;
+ u8 ident;
- return l2cap_connect_rsp(conn, cmd, data);
+ BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id);
+
+ ident = l2cap_get_ident(chan->conn);
+ chan->ident = ident;
+
+ req.icid = cpu_to_le16(chan->scid);
+ req.dest_amp_id = dest_amp_id;
+
+ l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req),
+ &req);
+
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
}
-static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident,
- u16 icid, u16 result)
+static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result)
{
struct l2cap_move_chan_rsp rsp;
- BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ BT_DBG("chan %p, result 0x%4.4x", chan, result);
- rsp.icid = cpu_to_le16(icid);
+ rsp.icid = cpu_to_le16(chan->dcid);
rsp.result = cpu_to_le16(result);
- l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp);
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP,
+ sizeof(rsp), &rsp);
}
-static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn,
- struct l2cap_chan *chan,
- u16 icid, u16 result)
+static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result)
{
struct l2cap_move_chan_cfm cfm;
- u8 ident;
- BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ BT_DBG("chan %p, result 0x%4.4x", chan, result);
- ident = l2cap_get_ident(conn);
- if (chan)
- chan->ident = ident;
+ chan->ident = l2cap_get_ident(chan->conn);
- cfm.icid = cpu_to_le16(icid);
+ cfm.icid = cpu_to_le16(chan->scid);
cfm.result = cpu_to_le16(result);
- l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm);
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM,
+ sizeof(cfm), &cfm);
+
+ __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT);
+}
+
+static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid)
+{
+ struct l2cap_move_chan_cfm cfm;
+
+ BT_DBG("conn %p, icid 0x%4.4x", conn, icid);
+
+ cfm.icid = cpu_to_le16(icid);
+ cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED);
+
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM,
+ sizeof(cfm), &cfm);
}
static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
@@ -4043,11 +4395,289 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident,
l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp);
}
+static void __release_logical_link(struct l2cap_chan *chan)
+{
+ chan->hs_hchan = NULL;
+ chan->hs_hcon = NULL;
+
+ /* Placeholder - release the logical link */
+}
+
+static void l2cap_logical_fail(struct l2cap_chan *chan)
+{
+ /* Logical link setup failed */
+ if (chan->state != BT_CONNECTED) {
+ /* Create channel failure, disconnect */
+ l2cap_send_disconn_req(chan, ECONNRESET);
+ return;
+ }
+
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ l2cap_move_done(chan);
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP);
+ break;
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP ||
+ chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) {
+ /* Remote has only sent pending or
+ * success responses, clean up
+ */
+ l2cap_move_done(chan);
+ }
+
+ /* Other amp move states imply that the move
+ * has already aborted
+ */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+}
+
+static void l2cap_logical_finish_create(struct l2cap_chan *chan,
+ struct hci_chan *hchan)
+{
+ struct l2cap_conf_rsp rsp;
+
+ chan->hs_hchan = hchan;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0);
+
+ if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) {
+ int err;
+
+ set_default_fcs(chan);
+
+ err = l2cap_ertm_init(chan);
+ if (err < 0)
+ l2cap_send_disconn_req(chan, -err);
+ else
+ l2cap_chan_ready(chan);
+ }
+}
+
+static void l2cap_logical_finish_move(struct l2cap_chan *chan,
+ struct hci_chan *hchan)
+{
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ BT_DBG("move_state %d", chan->move_state);
+
+ switch (chan->move_state) {
+ case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+ /* Move confirm will be sent after a success
+ * response is received
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ break;
+ case L2CAP_MOVE_WAIT_LOGICAL_CFM:
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ } else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
+ }
+ break;
+ default:
+ /* Move was not in expected state, free the channel */
+ __release_logical_link(chan);
+
+ chan->move_state = L2CAP_MOVE_STABLE;
+ }
+}
+
+/* Call with chan locked */
+void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan,
+ u8 status)
+{
+ BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status);
+
+ if (status) {
+ l2cap_logical_fail(chan);
+ __release_logical_link(chan);
+ return;
+ }
+
+ if (chan->state != BT_CONNECTED) {
+ /* Ignore logical link if channel is on BR/EDR */
+ if (chan->local_amp_id)
+ l2cap_logical_finish_create(chan, hchan);
+ } else {
+ l2cap_logical_finish_move(chan, hchan);
+ }
+}
+
+void l2cap_move_start(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ if (chan->local_amp_id == HCI_BREDR_ID) {
+ if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED)
+ return;
+ chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+ chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+ /* Placeholder - start physical link setup */
+ } else {
+ chan->move_role = L2CAP_MOVE_ROLE_INITIATOR;
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ chan->move_id = 0;
+ l2cap_move_setup(chan);
+ l2cap_send_move_chan_req(chan, 0);
+ }
+}
+
+static void l2cap_do_create(struct l2cap_chan *chan, int result,
+ u8 local_amp_id, u8 remote_amp_id)
+{
+ BT_DBG("chan %p state %s %u -> %u", chan, state_to_string(chan->state),
+ local_amp_id, remote_amp_id);
+
+ chan->fcs = L2CAP_FCS_NONE;
+
+ /* Outgoing channel on AMP */
+ if (chan->state == BT_CONNECT) {
+ if (result == L2CAP_CR_SUCCESS) {
+ chan->local_amp_id = local_amp_id;
+ l2cap_send_create_chan_req(chan, remote_amp_id);
+ } else {
+ /* Revert to BR/EDR connect */
+ l2cap_send_conn_req(chan);
+ }
+
+ return;
+ }
+
+ /* Incoming channel on AMP */
+ if (__l2cap_no_conn_pending(chan)) {
+ struct l2cap_conn_rsp rsp;
+ char buf[128];
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+
+ if (result == L2CAP_CR_SUCCESS) {
+ /* Send successful response */
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ } else {
+ /* Send negative response */
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+ }
+
+ l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+
+ if (result == L2CAP_CR_SUCCESS) {
+ __l2cap_state_change(chan, BT_CONFIG);
+ set_bit(CONF_REQ_SENT, &chan->conf_state);
+ l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
+ L2CAP_CONF_REQ,
+ l2cap_build_conf_req(chan, buf), buf);
+ chan->num_conf_req++;
+ }
+ }
+}
+
+static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id,
+ u8 remote_amp_id)
+{
+ l2cap_move_setup(chan);
+ chan->move_id = local_amp_id;
+ chan->move_state = L2CAP_MOVE_WAIT_RSP;
+
+ l2cap_send_move_chan_req(chan, remote_amp_id);
+}
+
+static void l2cap_do_move_respond(struct l2cap_chan *chan, int result)
+{
+ struct hci_chan *hchan = NULL;
+
+ /* Placeholder - get hci_chan for logical link */
+
+ if (hchan) {
+ if (hchan->state == BT_CONNECTED) {
+ /* Logical link is ready to go */
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS);
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ } else {
+ /* Wait for logical link to be ready */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ }
+ } else {
+ /* Logical link not available */
+ l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED);
+ }
+}
+
+static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result)
+{
+ if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) {
+ u8 rsp_result;
+ if (result == -EINVAL)
+ rsp_result = L2CAP_MR_BAD_ID;
+ else
+ rsp_result = L2CAP_MR_NOT_ALLOWED;
+
+ l2cap_send_move_chan_rsp(chan, rsp_result);
+ }
+
+ chan->move_role = L2CAP_MOVE_ROLE_NONE;
+ chan->move_state = L2CAP_MOVE_STABLE;
+
+ /* Restart data transmission */
+ l2cap_ertm_send(chan);
+}
+
+/* Invoke with locked chan */
+void __l2cap_physical_cfm(struct l2cap_chan *chan, int result)
+{
+ u8 local_amp_id = chan->local_amp_id;
+ u8 remote_amp_id = chan->remote_amp_id;
+
+ BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d",
+ chan, result, local_amp_id, remote_amp_id);
+
+ if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) {
+ l2cap_chan_unlock(chan);
+ return;
+ }
+
+ if (chan->state != BT_CONNECTED) {
+ l2cap_do_create(chan, result, local_amp_id, remote_amp_id);
+ } else if (result != L2CAP_MR_SUCCESS) {
+ l2cap_do_move_cancel(chan, result);
+ } else {
+ switch (chan->move_role) {
+ case L2CAP_MOVE_ROLE_INITIATOR:
+ l2cap_do_move_initiate(chan, local_amp_id,
+ remote_amp_id);
+ break;
+ case L2CAP_MOVE_ROLE_RESPONDER:
+ l2cap_do_move_respond(chan, result);
+ break;
+ default:
+ l2cap_do_move_cancel(chan, result);
+ break;
+ }
+ }
+}
+
static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_req *req = data;
+ struct l2cap_move_chan_rsp rsp;
+ struct l2cap_chan *chan;
u16 icid = 0;
u16 result = L2CAP_MR_NOT_ALLOWED;
@@ -4061,15 +4691,206 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
if (!enable_hs)
return -EINVAL;
- /* Placeholder: Always refuse */
- l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+ if (!chan) {
+ rsp.icid = cpu_to_le16(icid);
+ rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED);
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP,
+ sizeof(rsp), &rsp);
+ return 0;
+ }
+
+ chan->ident = cmd->ident;
+
+ if (chan->scid < L2CAP_CID_DYN_START ||
+ chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
+ (chan->mode != L2CAP_MODE_ERTM &&
+ chan->mode != L2CAP_MODE_STREAMING)) {
+ result = L2CAP_MR_NOT_ALLOWED;
+ goto send_move_response;
+ }
+
+ if (chan->local_amp_id == req->dest_amp_id) {
+ result = L2CAP_MR_SAME_ID;
+ goto send_move_response;
+ }
+
+ if (req->dest_amp_id) {
+ struct hci_dev *hdev;
+ hdev = hci_dev_get(req->dest_amp_id);
+ if (!hdev || hdev->dev_type != HCI_AMP ||
+ !test_bit(HCI_UP, &hdev->flags)) {
+ if (hdev)
+ hci_dev_put(hdev);
+
+ result = L2CAP_MR_BAD_ID;
+ goto send_move_response;
+ }
+ hci_dev_put(hdev);
+ }
+
+ /* Detect a move collision. Only send a collision response
+ * if this side has "lost", otherwise proceed with the move.
+ * The winner has the larger bd_addr.
+ */
+ if ((__chan_is_moving(chan) ||
+ chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
+ bacmp(conn->src, conn->dst) > 0) {
+ result = L2CAP_MR_COLLISION;
+ goto send_move_response;
+ }
+
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ l2cap_move_setup(chan);
+ chan->move_id = req->dest_amp_id;
+ icid = chan->dcid;
+
+ if (!req->dest_amp_id) {
+ /* Moving to BR/EDR */
+ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ result = L2CAP_MR_PEND;
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
+ result = L2CAP_MR_SUCCESS;
+ }
+ } else {
+ chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
+ /* Placeholder - uncomment when amp functions are available */
+ /*amp_accept_physical(chan, req->dest_amp_id);*/
+ result = L2CAP_MR_PEND;
+ }
+
+send_move_response:
+ l2cap_send_move_chan_rsp(chan, result);
+
+ l2cap_chan_unlock(chan);
return 0;
}
-static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result)
+{
+ struct l2cap_chan *chan;
+ struct hci_chan *hchan = NULL;
+
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan) {
+ l2cap_send_move_chan_cfm_icid(conn, icid);
+ return;
+ }
+
+ __clear_chan_timer(chan);
+ if (result == L2CAP_MR_PEND)
+ __set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT);
+
+ switch (chan->move_state) {
+ case L2CAP_MOVE_WAIT_LOGICAL_COMP:
+ /* Move confirm will be sent when logical link
+ * is complete.
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ break;
+ case L2CAP_MOVE_WAIT_RSP_SUCCESS:
+ if (result == L2CAP_MR_PEND) {
+ break;
+ } else if (test_bit(CONN_LOCAL_BUSY,
+ &chan->conn_state)) {
+ chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
+ } else {
+ /* Logical link is up or moving to BR/EDR,
+ * proceed with move
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP;
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ }
+ break;
+ case L2CAP_MOVE_WAIT_RSP:
+ /* Moving to AMP */
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Remote is ready, send confirm immediately
+ * after logical link is ready
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM;
+ } else {
+ /* Both logical link and move success
+ * are required to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP;
+ }
+
+ /* Placeholder - get hci_chan for logical link */
+ if (!hchan) {
+ /* Logical link not available */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ break;
+ }
+
+ /* If the logical link is not yet connected, do not
+ * send confirmation.
+ */
+ if (hchan->state != BT_CONNECTED)
+ break;
+
+ /* Logical link is already ready to go */
+
+ chan->hs_hcon = hchan->conn;
+ chan->hs_hcon->l2cap_data = chan->conn;
+
+ if (result == L2CAP_MR_SUCCESS) {
+ /* Can confirm now */
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED);
+ } else {
+ /* Now only need move success
+ * to confirm
+ */
+ chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS;
+ }
+
+ l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS);
+ break;
+ default:
+ /* Any other amp move state means the move failed. */
+ chan->move_id = chan->local_amp_id;
+ l2cap_move_done(chan);
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+ }
+
+ l2cap_chan_unlock(chan);
+}
+
+static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid,
+ u16 result)
+{
+ struct l2cap_chan *chan;
+
+ chan = l2cap_get_chan_by_ident(conn, ident);
+ if (!chan) {
+ /* Could not locate channel, icid is best guess */
+ l2cap_send_move_chan_cfm_icid(conn, icid);
+ return;
+ }
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) {
+ if (result == L2CAP_MR_COLLISION) {
+ chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
+ } else {
+ /* Cleanup - cancel move */
+ chan->move_id = chan->local_amp_id;
+ l2cap_move_done(chan);
+ }
+ }
+
+ l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED);
+
+ l2cap_chan_unlock(chan);
+}
+
+static int l2cap_move_channel_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_move_chan_rsp *rsp = data;
u16 icid, result;
@@ -4082,17 +4903,20 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
- /* Placeholder: Always unconfirmed */
- l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED);
+ if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND)
+ l2cap_move_continue(conn, icid, result);
+ else
+ l2cap_move_fail(conn, cmd->ident, icid, result);
return 0;
}
-static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd,
- u16 cmd_len, void *data)
+static int l2cap_move_channel_confirm(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd,
+ u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm *cfm = data;
+ struct l2cap_chan *chan;
u16 icid, result;
if (cmd_len != sizeof(*cfm))
@@ -4103,8 +4927,29 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result);
+ chan = l2cap_get_chan_by_dcid(conn, icid);
+ if (!chan) {
+ /* Spec requires a response even if the icid was not found */
+ l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+ return 0;
+ }
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) {
+ if (result == L2CAP_MC_CONFIRMED) {
+ chan->local_amp_id = chan->move_id;
+ if (!chan->local_amp_id)
+ __release_logical_link(chan);
+ } else {
+ chan->move_id = chan->local_amp_id;
+ }
+
+ l2cap_move_done(chan);
+ }
+
l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid);
+ l2cap_chan_unlock(chan);
+
return 0;
}
@@ -4113,6 +4958,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
u16 cmd_len, void *data)
{
struct l2cap_move_chan_cfm_rsp *rsp = data;
+ struct l2cap_chan *chan;
u16 icid;
if (cmd_len != sizeof(*rsp))
@@ -4122,11 +4968,28 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x", icid);
+ chan = l2cap_get_chan_by_scid(conn, icid);
+ if (!chan)
+ return 0;
+
+ __clear_chan_timer(chan);
+
+ if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) {
+ chan->local_amp_id = chan->move_id;
+
+ if (!chan->local_amp_id && chan->hs_hchan)
+ __release_logical_link(chan);
+
+ l2cap_move_done(chan);
+ }
+
+ l2cap_chan_unlock(chan);
+
return 0;
}
static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
- u16 to_multiplier)
+ u16 to_multiplier)
{
u16 max_latency;
@@ -4147,7 +5010,8 @@ static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
}
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u8 *data)
+ struct l2cap_cmd_hdr *cmd,
+ u8 *data)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_conn_param_update_req *req;
@@ -4169,7 +5033,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
to_multiplier = __le16_to_cpu(req->to_multiplier);
BT_DBG("min 0x%4.4x max 0x%4.4x latency: 0x%4.4x Timeout: 0x%4.4x",
- min, max, latency, to_multiplier);
+ min, max, latency, to_multiplier);
memset(&rsp, 0, sizeof(rsp));
@@ -4180,7 +5044,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
rsp.result = __constant_cpu_to_le16(L2CAP_CONN_PARAM_ACCEPTED);
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP,
- sizeof(rsp), &rsp);
+ sizeof(rsp), &rsp);
if (!err)
hci_le_conn_update(hcon, min, max, latency, to_multiplier);
@@ -4189,7 +5053,8 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
}
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data)
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
int err = 0;
@@ -4203,7 +5068,8 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
break;
case L2CAP_CONN_RSP:
- err = l2cap_connect_rsp(conn, cmd, data);
+ case L2CAP_CREATE_CHAN_RSP:
+ err = l2cap_connect_create_rsp(conn, cmd, data);
break;
case L2CAP_CONF_REQ:
@@ -4241,10 +5107,6 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
err = l2cap_create_channel_req(conn, cmd, cmd_len, data);
break;
- case L2CAP_CREATE_CHAN_RSP:
- err = l2cap_create_channel_rsp(conn, cmd, data);
- break;
-
case L2CAP_MOVE_CHAN_REQ:
err = l2cap_move_channel_req(conn, cmd, cmd_len, data);
break;
@@ -4271,7 +5133,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
}
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u8 *data)
+ struct l2cap_cmd_hdr *cmd, u8 *data)
{
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
@@ -4290,7 +5152,7 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
}
static inline void l2cap_sig_channel(struct l2cap_conn *conn,
- struct sk_buff *skb)
+ struct sk_buff *skb)
{
u8 *data = skb->data;
int len = skb->len;
@@ -4307,7 +5169,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
cmd_len = le16_to_cpu(cmd.len);
- BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, cmd.ident);
+ BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len,
+ cmd.ident);
if (cmd_len > len || !cmd.ident) {
BT_DBG("corrupted command");
@@ -4326,7 +5189,8 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn,
/* FIXME: Map err to a valid reason */
rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD);
- l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej);
+ l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ,
+ sizeof(rej), &rej);
}
data += cmd_len;
@@ -4391,8 +5255,8 @@ static void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan)
}
}
-static void append_skb_frag(struct sk_buff *skb,
- struct sk_buff *new_frag, struct sk_buff **last_frag)
+static void append_skb_frag(struct sk_buff *skb, struct sk_buff *new_frag,
+ struct sk_buff **last_frag)
{
/* skb->len reflects data in skb as well as all fragments
* skb->data_len reflects only data in fragments
@@ -4492,6 +5356,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb,
return err;
}
+static int l2cap_resegment(struct l2cap_chan *chan)
+{
+ /* Placeholder */
+ return 0;
+}
+
void l2cap_chan_busy(struct l2cap_chan *chan, int busy)
{
u8 event;
@@ -4546,7 +5416,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@@ -4560,7 +5430,7 @@ static void l2cap_handle_srej(struct l2cap_chan *chan,
if (chan->max_tx != 0 && bt_cb(skb)->control.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@@ -4604,7 +5474,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
if (control->reqseq == chan->next_tx_seq) {
BT_DBG("Invalid reqseq %d, disconnecting", control->reqseq);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@@ -4613,7 +5483,7 @@ static void l2cap_handle_rej(struct l2cap_chan *chan,
if (chan->max_tx && skb &&
bt_cb(skb)->control.retries >= chan->max_tx) {
BT_DBG("Retry limit exceeded (%d)", chan->max_tx);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
return;
}
@@ -4641,7 +5511,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
if (chan->rx_state == L2CAP_RX_STATE_SREJ_SENT) {
if (__seq_offset(chan, txseq, chan->last_acked_seq) >=
- chan->tx_win) {
+ chan->tx_win) {
/* See notes below regarding "double poll" and
* invalid packets.
*/
@@ -4682,8 +5552,7 @@ static u8 l2cap_classify_txseq(struct l2cap_chan *chan, u16 txseq)
}
if (__seq_offset(chan, txseq, chan->last_acked_seq) <
- __seq_offset(chan, chan->expected_tx_seq,
- chan->last_acked_seq)){
+ __seq_offset(chan, chan->expected_tx_seq, chan->last_acked_seq)) {
BT_DBG("Duplicate - expected_tx_seq later than txseq");
return L2CAP_TXSEQ_DUPLICATE;
}
@@ -4798,8 +5667,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
break;
case L2CAP_TXSEQ_INVALID:
default:
- l2cap_send_disconn_req(chan->conn, chan,
- ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
@@ -4808,8 +5676,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan,
if (control->final) {
clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
- if (!test_and_clear_bit(CONN_REJ_ACT,
- &chan->conn_state)) {
+ if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) &&
+ !__chan_is_moving(chan)) {
control->final = 0;
l2cap_retransmit_all(chan, control);
}
@@ -4932,8 +5800,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
break;
case L2CAP_TXSEQ_INVALID:
default:
- l2cap_send_disconn_req(chan->conn, chan,
- ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
break;
}
break;
@@ -4998,6 +5865,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan,
return err;
}
+static int l2cap_finish_move(struct l2cap_chan *chan)
+{
+ BT_DBG("chan %p", chan);
+
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ return l2cap_resegment(chan);
+}
+
+static int l2cap_rx_state_wait_p(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err;
+
+ BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb,
+ event);
+
+ if (!control->poll)
+ return -EPROTO;
+
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ err = l2cap_finish_move(chan);
+ if (err)
+ return err;
+
+ set_bit(CONN_SEND_FBIT, &chan->conn_state);
+ l2cap_send_i_or_rr_or_rnr(chan);
+
+ if (event == L2CAP_EV_RECV_IFRAME)
+ return -EPROTO;
+
+ return l2cap_rx_state_recv(chan, control, NULL, event);
+}
+
+static int l2cap_rx_state_wait_f(struct l2cap_chan *chan,
+ struct l2cap_ctrl *control,
+ struct sk_buff *skb, u8 event)
+{
+ int err;
+
+ if (!control->final)
+ return -EPROTO;
+
+ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state);
+
+ chan->rx_state = L2CAP_RX_STATE_RECV;
+ l2cap_process_reqseq(chan, control->reqseq);
+
+ if (!skb_queue_empty(&chan->tx_q))
+ chan->tx_send_head = skb_peek(&chan->tx_q);
+ else
+ chan->tx_send_head = NULL;
+
+ /* Rewind next_tx_seq to the point expected
+ * by the receiver.
+ */
+ chan->next_tx_seq = control->reqseq;
+ chan->unacked_frames = 0;
+
+ if (chan->hs_hcon)
+ chan->conn->mtu = chan->hs_hcon->hdev->block_mtu;
+ else
+ chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu;
+
+ err = l2cap_resegment(chan);
+
+ if (!err)
+ err = l2cap_rx_state_recv(chan, control, skb, event);
+
+ return err;
+}
+
static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq)
{
/* Make sure reqseq is for a packet that has been sent but not acked */
@@ -5024,6 +5981,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
err = l2cap_rx_state_srej_sent(chan, control, skb,
event);
break;
+ case L2CAP_RX_STATE_WAIT_P:
+ err = l2cap_rx_state_wait_p(chan, control, skb, event);
+ break;
+ case L2CAP_RX_STATE_WAIT_F:
+ err = l2cap_rx_state_wait_f(chan, control, skb, event);
+ break;
default:
/* shut it down */
break;
@@ -5032,7 +5995,7 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
BT_DBG("Invalid reqseq %d (next_tx_seq %d, expected_ack_seq %d",
control->reqseq, chan->next_tx_seq,
chan->expected_ack_seq);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
}
return err;
@@ -5101,7 +6064,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
len -= L2CAP_FCS_SIZE;
if (len > chan->mps) {
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
@@ -5126,8 +6089,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
}
if (err)
- l2cap_send_disconn_req(chan->conn, chan,
- ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
} else {
const u8 rx_func_to_event[4] = {
L2CAP_EV_RECV_RR, L2CAP_EV_RECV_REJ,
@@ -5143,8 +6105,8 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
control->super);
if (len != 0) {
- BT_ERR("%d", len);
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ BT_ERR("Trailing bytes: %d in sframe", len);
+ l2cap_send_disconn_req(chan, ECONNRESET);
goto drop;
}
@@ -5155,7 +6117,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
event = rx_func_to_event[control->super];
if (l2cap_rx(chan, control, skb, event))
- l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
+ l2cap_send_disconn_req(chan, ECONNRESET);
}
return 0;
@@ -5323,7 +6285,7 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
int exact = 0, lm1 = 0, lm2 = 0;
struct l2cap_chan *c;
- BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+ BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
/* Find listening sockets and check their link_mode */
read_lock(&chan_list_lock);
@@ -5353,15 +6315,15 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
{
struct l2cap_conn *conn;
- BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+ BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (!status) {
conn = l2cap_conn_add(hcon, status);
if (conn)
l2cap_conn_ready(conn);
- } else
+ } else {
l2cap_conn_del(hcon, bt_to_errno(status));
-
+ }
}
int l2cap_disconn_ind(struct hci_conn *hcon)
@@ -5437,13 +6399,13 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
continue;
}
- if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) {
+ if (!__l2cap_no_conn_pending(chan)) {
l2cap_chan_unlock(chan);
continue;
}
if (!status && (chan->state == BT_CONNECTED ||
- chan->state == BT_CONFIG)) {
+ chan->state == BT_CONFIG)) {
struct sock *sk = chan->sk;
clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
@@ -5456,7 +6418,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (chan->state == BT_CONNECT) {
if (!status) {
- l2cap_send_conn_req(chan);
+ l2cap_start_connection(chan);
} else {
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
}
@@ -5470,11 +6432,9 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
if (!status) {
if (test_bit(BT_SK_DEFER_SETUP,
&bt_sk(sk)->flags)) {
- struct sock *parent = bt_sk(sk)->parent;
res = L2CAP_CR_PEND;
stat = L2CAP_CS_AUTHOR_PEND;
- if (parent)
- parent->sk_data_ready(parent, 0);
+ chan->ops->defer(chan);
} else {
__l2cap_state_change(chan, BT_CONFIG);
res = L2CAP_CR_SUCCESS;
@@ -5494,7 +6454,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
rsp.result = cpu_to_le16(res);
rsp.status = cpu_to_le16(stat);
l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
- sizeof(rsp), &rsp);
+ sizeof(rsp), &rsp);
if (!test_bit(CONF_REQ_SENT, &chan->conf_state) &&
res == L2CAP_CR_SUCCESS) {
@@ -5519,6 +6479,12 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
{
struct l2cap_conn *conn = hcon->l2cap_data;
+ struct l2cap_hdr *hdr;
+ int len;
+
+ /* For AMP controller do not create l2cap conn */
+ if (!conn && hcon->hdev->dev_type != HCI_BREDR)
+ goto drop;
if (!conn)
conn = l2cap_conn_add(hcon, 0);
@@ -5528,10 +6494,10 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
- if (!(flags & ACL_CONT)) {
- struct l2cap_hdr *hdr;
- int len;
-
+ switch (flags) {
+ case ACL_START:
+ case ACL_START_NO_FLUSH:
+ case ACL_COMPLETE:
if (conn->rx_len) {
BT_ERR("Unexpected start frame (len %d)", skb->len);
kfree_skb(conn->rx_skb);
@@ -5560,20 +6526,22 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
if (skb->len > len) {
BT_ERR("Frame is too long (len %d, expected len %d)",
- skb->len, len);
+ skb->len, len);
l2cap_conn_unreliable(conn, ECOMM);
goto drop;
}
/* Allocate skb for the complete frame (with header) */
- conn->rx_skb = bt_skb_alloc(len, GFP_ATOMIC);
+ conn->rx_skb = bt_skb_alloc(len, GFP_KERNEL);
if (!conn->rx_skb)
goto drop;
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len = len - skb->len;
- } else {
+ break;
+
+ case ACL_CONT:
BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
if (!conn->rx_len) {
@@ -5584,7 +6552,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
if (skb->len > conn->rx_len) {
BT_ERR("Fragment is too long (len %d, expected %d)",
- skb->len, conn->rx_len);
+ skb->len, conn->rx_len);
kfree_skb(conn->rx_skb);
conn->rx_skb = NULL;
conn->rx_len = 0;
@@ -5593,7 +6561,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
}
skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
- skb->len);
+ skb->len);
conn->rx_len -= skb->len;
if (!conn->rx_len) {
@@ -5601,6 +6569,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
l2cap_recv_frame(conn, conn->rx_skb);
conn->rx_skb = NULL;
}
+ break;
}
drop:
@@ -5617,12 +6586,11 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p)
list_for_each_entry(c, &chan_list, global_l) {
struct sock *sk = c->sk;
- seq_printf(f, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
- batostr(&bt_sk(sk)->src),
- batostr(&bt_sk(sk)->dst),
- c->state, __le16_to_cpu(c->psm),
- c->scid, c->dcid, c->imtu, c->omtu,
- c->sec_level, c->mode);
+ seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n",
+ &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ c->state, __le16_to_cpu(c->psm),
+ c->scid, c->dcid, c->imtu, c->omtu,
+ c->sec_level, c->mode);
}
read_unlock(&chan_list_lock);
@@ -5653,8 +6621,8 @@ int __init l2cap_init(void)
return err;
if (bt_debugfs) {
- l2cap_debugfs = debugfs_create_file("l2cap", 0444,
- bt_debugfs, NULL, &l2cap_debugfs_fops);
+ l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
+ NULL, &l2cap_debugfs_fops);
if (!l2cap_debugfs)
BT_ERR("Failed to create L2CAP debug file");
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 083f2bf065d..1bcfb8422fd 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -40,7 +40,8 @@ static struct bt_sock_list l2cap_sk_list = {
static const struct proto_ops l2cap_sock_ops;
static void l2cap_sock_init(struct sock *sk, struct sock *parent);
-static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio);
+static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
+ int proto, gfp_t prio);
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
@@ -106,7 +107,8 @@ done:
return err;
}
-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
+ int alen, int flags)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -134,7 +136,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
lock_sock(sk);
err = bt_sock_wait_state(sk, BT_CONNECTED,
- sock_sndtimeo(sk, flags & O_NONBLOCK));
+ sock_sndtimeo(sk, flags & O_NONBLOCK));
release_sock(sk);
@@ -185,7 +187,8 @@ done:
return err;
}
-static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
+ int flags)
{
DECLARE_WAITQUEUE(wait, current);
struct sock *sk = sock->sk, *nsk;
@@ -241,7 +244,8 @@ done:
return err;
}
-static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr,
+ int *len, int peer)
{
struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
struct sock *sk = sock->sk;
@@ -266,7 +270,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
return 0;
}
-static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
+ char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -309,7 +314,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
break;
case BT_SECURITY_HIGH:
opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
- L2CAP_LM_SECURE;
+ L2CAP_LM_SECURE;
break;
default:
opt = 0;
@@ -353,7 +358,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
return err;
}
-static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
+static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -377,19 +383,20 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
switch (optname) {
case BT_SECURITY:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
- chan->chan_type != L2CAP_CHAN_RAW) {
+ chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
}
memset(&sec, 0, sizeof(sec));
- if (chan->conn)
+ if (chan->conn) {
sec.level = chan->conn->hcon->sec_level;
- else
- sec.level = chan->sec_level;
- if (sk->sk_state == BT_CONNECTED)
- sec.key_size = chan->conn->hcon->enc_key_size;
+ if (sk->sk_state == BT_CONNECTED)
+ sec.key_size = chan->conn->hcon->enc_key_size;
+ } else {
+ sec.level = chan->sec_level;
+ }
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
@@ -411,14 +418,14 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
case BT_FLUSHABLE:
if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags),
- (u32 __user *) optval))
+ (u32 __user *) optval))
err = -EFAULT;
break;
case BT_POWER:
if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_STREAM
- && sk->sk_type != SOCK_RAW) {
+ && sk->sk_type != SOCK_RAW) {
err = -EINVAL;
break;
}
@@ -466,7 +473,8 @@ static bool l2cap_valid_mtu(struct l2cap_chan *chan, u16 mtu)
return true;
}
-static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, unsigned int optlen)
+static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
+ char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -529,6 +537,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
chan->fcs = opts.fcs;
chan->max_tx = opts.max_tx;
chan->tx_win = opts.txwin_size;
+ chan->flush_to = opts.flush_to;
break;
case L2CAP_LM:
@@ -564,7 +573,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
return err;
}
-static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
+static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -587,7 +597,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
switch (optname) {
case BT_SECURITY:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
- chan->chan_type != L2CAP_CHAN_RAW) {
+ chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
}
@@ -601,7 +611,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
if (sec.level < BT_SECURITY_LOW ||
- sec.level > BT_SECURITY_HIGH) {
+ sec.level > BT_SECURITY_HIGH) {
err = -EINVAL;
break;
}
@@ -627,7 +637,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
/* or for ACL link */
} else if ((sk->sk_state == BT_CONNECT2 &&
- test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) ||
sk->sk_state == BT_CONNECTED) {
if (!l2cap_chan_check_security(chan))
set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
@@ -684,7 +694,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
case BT_POWER:
if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED &&
- chan->chan_type != L2CAP_CHAN_RAW) {
+ chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
break;
}
@@ -720,12 +730,17 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
}
if (chan->mode != L2CAP_MODE_ERTM &&
- chan->mode != L2CAP_MODE_STREAMING) {
+ chan->mode != L2CAP_MODE_STREAMING) {
err = -EOPNOTSUPP;
break;
}
chan->chan_policy = (u8) opt;
+
+ if (sk->sk_state == BT_CONNECTED &&
+ chan->move_role == L2CAP_MOVE_ROLE_NONE)
+ l2cap_move_start(chan);
+
break;
default:
@@ -737,7 +752,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
return err;
}
-static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
+static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
@@ -762,7 +778,8 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
return err;
}
-static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
+static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
{
struct sock *sk = sock->sk;
struct l2cap_pinfo *pi = l2cap_pi(sk);
@@ -866,7 +883,7 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
err = bt_sock_wait_state(sk, BT_CLOSED,
- sk->sk_lingertime);
+ sk->sk_lingertime);
}
if (!err && sk->sk_err)
@@ -930,7 +947,7 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
}
sk = l2cap_sock_alloc(sock_net(parent), NULL, BTPROTO_L2CAP,
- GFP_ATOMIC);
+ GFP_ATOMIC);
if (!sk)
return NULL;
@@ -938,6 +955,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan)
l2cap_sock_init(sk, parent);
+ bt_accept_enqueue(parent, sk);
+
return l2cap_pi(sk)->chan;
}
@@ -1068,6 +1087,15 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
release_sock(sk);
}
+static void l2cap_sock_defer_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+ struct sock *parent = bt_sk(sk)->parent;
+
+ if (parent)
+ parent->sk_data_ready(parent, 0);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -1076,6 +1104,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.teardown = l2cap_sock_teardown_cb,
.state_change = l2cap_sock_state_change_cb,
.ready = l2cap_sock_ready_cb,
+ .defer = l2cap_sock_defer_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
};
@@ -1083,7 +1112,8 @@ static void l2cap_sock_destruct(struct sock *sk)
{
BT_DBG("sk %p", sk);
- l2cap_chan_put(l2cap_pi(sk)->chan);
+ if (l2cap_pi(sk)->chan)
+ l2cap_chan_put(l2cap_pi(sk)->chan);
if (l2cap_pi(sk)->rx_busy_skb) {
kfree_skb(l2cap_pi(sk)->rx_busy_skb);
l2cap_pi(sk)->rx_busy_skb = NULL;
@@ -1159,7 +1189,8 @@ static struct proto l2cap_proto = {
.obj_size = sizeof(struct l2cap_pinfo)
};
-static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int proto, gfp_t prio)
+static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock,
+ int proto, gfp_t prio)
{
struct sock *sk;
struct l2cap_chan *chan;
@@ -1204,7 +1235,7 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol,
sock->state = SS_UNCONNECTED;
if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM &&
- sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
+ sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
@@ -1261,7 +1292,8 @@ int __init l2cap_init_sockets(void)
goto error;
}
- err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list, NULL);
+ err = bt_procfs_init(THIS_MODULE, &init_net, "l2cap", &l2cap_sk_list,
+ NULL);
if (err < 0) {
BT_ERR("Failed to create L2CAP proc file");
bt_sock_unregister(BTPROTO_L2CAP);
diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c
index e1c97527e16..b3fbc73516c 100644
--- a/net/bluetooth/lib.c
+++ b/net/bluetooth/lib.c
@@ -41,20 +41,6 @@ void baswap(bdaddr_t *dst, bdaddr_t *src)
}
EXPORT_SYMBOL(baswap);
-char *batostr(bdaddr_t *ba)
-{
- static char str[2][18];
- static int i = 1;
-
- i ^= 1;
- sprintf(str[i], "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
- ba->b[5], ba->b[4], ba->b[3],
- ba->b[2], ba->b[1], ba->b[0]);
-
- return str[i];
-}
-EXPORT_SYMBOL(batostr);
-
/* Bluetooth error codes to Unix errno mapping */
int bt_to_errno(__u16 code)
{
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index aa2ea0a8142..f559b966279 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev));
@@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
hdr = (void *) skb_put(skb, sizeof(*hdr));
- hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
+ hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
@@ -326,7 +326,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
struct hci_dev *d;
size_t rp_len;
u16 count;
- int i, err;
+ int err;
BT_DBG("sock %p", sk);
@@ -347,9 +347,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
return -ENOMEM;
}
- rp->num_controllers = cpu_to_le16(count);
-
- i = 0;
+ count = 0;
list_for_each_entry(d, &hci_dev_list, list) {
if (test_bit(HCI_SETUP, &d->dev_flags))
continue;
@@ -357,10 +355,13 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data,
if (!mgmt_valid_hdev(d))
continue;
- rp->index[i++] = cpu_to_le16(d->id);
+ rp->index[count++] = cpu_to_le16(d->id);
BT_DBG("Added hci%u", d->id);
}
+ rp->num_controllers = cpu_to_le16(count);
+ rp_len = sizeof(*rp) + (2 * count);
+
read_unlock(&hci_dev_list_lock);
err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
@@ -376,15 +377,15 @@ static u32 get_supported_settings(struct hci_dev *hdev)
u32 settings = 0;
settings |= MGMT_SETTING_POWERED;
- settings |= MGMT_SETTING_CONNECTABLE;
- settings |= MGMT_SETTING_FAST_CONNECTABLE;
- settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_PAIRABLE;
if (lmp_ssp_capable(hdev))
settings |= MGMT_SETTING_SSP;
if (lmp_bredr_capable(hdev)) {
+ settings |= MGMT_SETTING_CONNECTABLE;
+ settings |= MGMT_SETTING_FAST_CONNECTABLE;
+ settings |= MGMT_SETTING_DISCOVERABLE;
settings |= MGMT_SETTING_BREDR;
settings |= MGMT_SETTING_LINK_SECURITY;
}
@@ -484,7 +485,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data)
ptr += (name_len + 2);
}
- if (hdev->inq_tx_power) {
+ if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) {
ptr[0] = 2;
ptr[1] = EIR_TX_POWER;
ptr[2] = (u8) hdev->inq_tx_power;
@@ -565,7 +566,7 @@ static int update_eir(struct hci_dev *hdev)
if (!hdev_is_powered(hdev))
return 0;
- if (!(hdev->features[6] & LMP_EXT_INQ))
+ if (!lmp_ext_inq_capable(hdev))
return 0;
if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
@@ -832,7 +833,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len,
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
- hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
+ hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
@@ -867,6 +868,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
timeout = __le16_to_cpu(cp->timeout);
if (!cp->val && timeout > 0)
return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
@@ -962,6 +967,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
@@ -1060,6 +1069,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data,
BT_DBG("request for %s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
@@ -1213,7 +1226,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
}
val = !!cp->val;
- enabled = !!(hdev->host_features[0] & LMP_HOST_LE);
+ enabled = lmp_host_le_capable(hdev);
if (!hdev_is_powered(hdev) || val == enabled) {
bool changed = false;
@@ -1249,7 +1262,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
if (val) {
hci_cp.le = val;
- hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+ hci_cp.simul = lmp_le_br_capable(hdev);
}
err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp),
@@ -1366,6 +1379,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
continue;
list_del(&match->list);
+ kfree(match);
found++;
}
@@ -2594,6 +2608,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name);
+ if (!lmp_bredr_capable(hdev))
+ return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
+ MGMT_STATUS_NOT_SUPPORTED);
+
if (!hdev_is_powered(hdev))
return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
MGMT_STATUS_NOT_POWERED);
@@ -2871,6 +2889,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_free(cmd);
}
+static int set_bredr_scan(struct hci_dev *hdev)
+{
+ u8 scan = 0;
+
+ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
+ scan |= SCAN_PAGE;
+ if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
+ scan |= SCAN_INQUIRY;
+
+ if (!scan)
+ return 0;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+}
+
int mgmt_powered(struct hci_dev *hdev, u8 powered)
{
struct cmd_lookup match = { NULL, hdev };
@@ -2882,17 +2915,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
if (powered) {
- u8 scan = 0;
-
- if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
- scan |= SCAN_PAGE;
- if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
- scan |= SCAN_INQUIRY;
-
- if (scan)
- hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-
- if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) &&
+ !lmp_host_ssp_capable(hdev)) {
u8 ssp = 1;
hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
@@ -2902,15 +2926,24 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
struct hci_cp_write_le_host_supported cp;
cp.le = 1;
- cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
-
- hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
- sizeof(cp), &cp);
+ cp.simul = lmp_le_br_capable(hdev);
+
+ /* Check first if we already have the right
+ * host state (host features set)
+ */
+ if (cp.le != lmp_host_le_capable(hdev) ||
+ cp.simul != lmp_host_le_br_capable(hdev))
+ hci_send_cmd(hdev,
+ HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
}
- update_class(hdev);
- update_name(hdev, hdev->dev_name);
- update_eir(hdev);
+ if (lmp_bredr_capable(hdev)) {
+ set_bredr_scan(hdev);
+ update_class(hdev);
+ update_name(hdev, hdev->dev_name);
+ update_eir(hdev);
+ }
} else {
u8 status = MGMT_STATUS_NOT_POWERED;
mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
@@ -3125,6 +3158,9 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
struct pending_cmd *cmd;
int err;
+ mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
+ hdev);
+
cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
if (!cmd)
return -ENOENT;
@@ -3137,8 +3173,6 @@ int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
mgmt_pending_remove(cmd);
- mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
- hdev);
return err;
}
@@ -3358,7 +3392,7 @@ static int clear_eir(struct hci_dev *hdev)
{
struct hci_cp_write_eir cp;
- if (!(hdev->features[6] & LMP_EXT_INQ))
+ if (!lmp_ext_inq_capable(hdev))
return 0;
memset(hdev->eir, 0, sizeof(hdev->eir));
@@ -3490,7 +3524,12 @@ send_event:
err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
sizeof(ev), cmd ? cmd->sk : NULL);
- update_eir(hdev);
+ /* EIR is taken care of separately when powering on the
+ * adapter so only update them here if this is a name change
+ * unrelated to power on.
+ */
+ if (!test_bit(HCI_INIT, &hdev->flags))
+ update_eir(hdev);
failed:
if (cmd)
@@ -3585,9 +3624,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
if (cfm_name)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME);
if (!ssp)
- ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
+ ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING);
if (eir_len > 0)
memcpy(ev->eir, eir, eir_len);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index c75107ef892..201fdf73720 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -377,8 +377,8 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
int err = 0;
u8 dlci;
- BT_DBG("dlc %p state %ld %s %s channel %d",
- d, d->state, batostr(src), batostr(dst), channel);
+ BT_DBG("dlc %p state %ld %pMR -> %pMR channel %d",
+ d, d->state, src, dst, channel);
if (channel < 1 || channel > 30)
return -EINVAL;
@@ -676,7 +676,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
struct socket *sock;
struct sock *sk;
- BT_DBG("%s %s", batostr(src), batostr(dst));
+ BT_DBG("%pMR -> %pMR", src, dst);
*err = rfcomm_l2sock_create(&sock);
if (*err < 0)
@@ -709,7 +709,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
bacpy(&addr.l2_bdaddr, dst);
addr.l2_family = AF_BLUETOOTH;
- addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
+ addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
addr.l2_cid = 0;
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
if (*err == 0 || *err == -EINPROGRESS)
@@ -1987,7 +1987,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
/* Bind socket */
bacpy(&addr.l2_bdaddr, ba);
addr.l2_family = AF_BLUETOOTH;
- addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
+ addr.l2_psm = __constant_cpu_to_le16(RFCOMM_PSM);
addr.l2_cid = 0;
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
if (err < 0) {
@@ -2125,11 +2125,10 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x)
list_for_each_entry(d, &s->dlcs, list) {
struct sock *sk = s->sock->sk;
- seq_printf(f, "%s %s %ld %d %d %d %d\n",
- batostr(&bt_sk(sk)->src),
- batostr(&bt_sk(sk)->dst),
- d->state, d->dlci, d->mtu,
- d->rx_credits, d->tx_credits);
+ seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n",
+ &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ d->state, d->dlci, d->mtu,
+ d->rx_credits, d->tx_credits);
}
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index b3226f3658c..ce3f6658f4b 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -334,7 +334,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
struct sock *sk = sock->sk;
int err = 0;
- BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
+ BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
@@ -467,7 +467,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
long timeo;
int err = 0;
- lock_sock(sk);
+ lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
if (sk->sk_type != SOCK_STREAM) {
err = -EINVAL;
@@ -504,7 +504,7 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f
release_sock(sk);
timeo = schedule_timeout(timeo);
- lock_sock(sk);
+ lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
@@ -975,10 +975,9 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p)
read_lock(&rfcomm_sk_list.lock);
sk_for_each(sk, node, &rfcomm_sk_list.head) {
- seq_printf(f, "%s %s %d %d\n",
- batostr(&bt_sk(sk)->src),
- batostr(&bt_sk(sk)->dst),
- sk->sk_state, rfcomm_pi(sk)->channel);
+ seq_printf(f, "%pMR %pMR %d %d\n",
+ &bt_sk(sk)->src, &bt_sk(sk)->dst,
+ sk->sk_state, rfcomm_pi(sk)->channel);
}
read_unlock(&rfcomm_sk_list.lock);
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index ccc248791d5..bd6fd0f43d2 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -166,7 +166,7 @@ static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
{
struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
- return sprintf(buf, "%s\n", batostr(&dev->dst));
+ return sprintf(buf, "%pMR\n", &dev->dst);
}
static ssize_t show_channel(struct device *tty_dev, struct device_attribute *attr, char *buf)
@@ -663,8 +663,8 @@ static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
if (!dev)
return -ENODEV;
- BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst),
- dev->channel, dev->port.count);
+ BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
+ dev->channel, dev->port.count);
spin_lock_irqsave(&dev->port.lock, flags);
if (++dev->port.count > 1) {
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index dc42b917aaa..57f250c20e3 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -131,15 +131,6 @@ static int sco_conn_del(struct hci_conn *hcon, int err)
sco_sock_clear_timer(sk);
sco_chan_del(sk, err);
bh_unlock_sock(sk);
-
- sco_conn_lock(conn);
- conn->sk = NULL;
- sco_pi(sk)->conn = NULL;
- sco_conn_unlock(conn);
-
- if (conn->hcon)
- hci_conn_put(conn->hcon);
-
sco_sock_kill(sk);
}
@@ -172,7 +163,7 @@ static int sco_connect(struct sock *sk)
struct hci_dev *hdev;
int err, type;
- BT_DBG("%s -> %s", batostr(src), batostr(dst));
+ BT_DBG("%pMR -> %pMR", src, dst);
hdev = hci_get_route(dst, src);
if (!hdev)
@@ -361,7 +352,7 @@ static void __sco_sock_close(struct sock *sk)
case BT_CONNECTED:
case BT_CONFIG:
- if (sco_pi(sk)->conn) {
+ if (sco_pi(sk)->conn->hcon) {
sk->sk_state = BT_DISCONN;
sco_sock_set_timer(sk, SCO_DISCONN_TIMEOUT);
hci_conn_put(sco_pi(sk)->conn->hcon);
@@ -397,6 +388,7 @@ static void sco_sock_init(struct sock *sk, struct sock *parent)
if (parent) {
sk->sk_type = parent->sk_type;
+ bt_sk(sk)->flags = bt_sk(parent)->flags;
security_sk_clone(parent, sk);
}
}
@@ -460,7 +452,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le
struct sock *sk = sock->sk;
int err = 0;
- BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
+ BT_DBG("sk %p %pMR", sk, &sa->sco_bdaddr);
if (!addr || addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
@@ -662,16 +654,57 @@ static int sco_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err;
}
+static int sco_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sco_pinfo *pi = sco_pi(sk);
+
+ lock_sock(sk);
+
+ if (sk->sk_state == BT_CONNECT2 &&
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
+ hci_conn_accept(pi->conn->hcon, 0);
+ sk->sk_state = BT_CONFIG;
+
+ release_sock(sk);
+ return 0;
+ }
+
+ release_sock(sk);
+
+ return bt_sock_recvmsg(iocb, sock, msg, len, flags);
+}
+
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int err = 0;
+ u32 opt;
BT_DBG("sk %p", sk);
lock_sock(sk);
switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (opt)
+ set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ else
+ clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -753,6 +786,19 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
lock_sock(sk);
switch (optname) {
+
+ case BT_DEFER_SETUP:
+ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags),
+ (u32 __user *) optval))
+ err = -EFAULT;
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -830,6 +876,16 @@ static void sco_chan_del(struct sock *sk, int err)
BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+ if (conn) {
+ sco_conn_lock(conn);
+ conn->sk = NULL;
+ sco_pi(sk)->conn = NULL;
+ sco_conn_unlock(conn);
+
+ if (conn->hcon)
+ hci_conn_put(conn->hcon);
+ }
+
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
@@ -874,7 +930,10 @@ static void sco_conn_ready(struct sco_conn *conn)
hci_conn_hold(conn->hcon);
__sco_chan_add(conn, sk, parent);
- sk->sk_state = BT_CONNECTED;
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
+ sk->sk_state = BT_CONNECT2;
+ else
+ sk->sk_state = BT_CONNECTED;
/* Wake up parent */
parent->sk_data_ready(parent, 1);
@@ -887,13 +946,13 @@ done:
}
/* ----- SCO interface with lower layer (HCI) ----- */
-int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
{
struct sock *sk;
struct hlist_node *node;
int lm = 0;
- BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+ BT_DBG("hdev %s, bdaddr %pMR", hdev->name, bdaddr);
/* Find listening sockets */
read_lock(&sco_sk_list.lock);
@@ -904,6 +963,9 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
lm |= HCI_LM_ACCEPT;
+
+ if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))
+ *flags |= HCI_PROTO_DEFER;
break;
}
}
@@ -914,7 +976,7 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
void sco_connect_cfm(struct hci_conn *hcon, __u8 status)
{
- BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+ BT_DBG("hcon %p bdaddr %pMR status %d", hcon, &hcon->dst, status);
if (!status) {
struct sco_conn *conn;
@@ -959,8 +1021,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p)
read_lock(&sco_sk_list.lock);
sk_for_each(sk, node, &sco_sk_list.head) {
- seq_printf(f, "%s %s %d\n", batostr(&bt_sk(sk)->src),
- batostr(&bt_sk(sk)->dst), sk->sk_state);
+ seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src,
+ &bt_sk(sk)->dst, sk->sk_state);
}
read_unlock(&sco_sk_list.lock);
@@ -992,7 +1054,7 @@ static const struct proto_ops sco_sock_ops = {
.accept = sco_sock_accept,
.getname = sco_sock_getname,
.sendmsg = sco_sock_sendmsg,
- .recvmsg = bt_sock_recvmsg,
+ .recvmsg = sco_sock_recvmsg,
.poll = bt_sock_poll,
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 2ac8d50861e..68a9587c969 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -167,7 +167,7 @@ static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
lh->len = cpu_to_le16(sizeof(code) + dlen);
- lh->cid = cpu_to_le16(L2CAP_CID_SMP);
+ lh->cid = __constant_cpu_to_le16(L2CAP_CID_SMP);
memcpy(skb_put(skb, sizeof(code)), &code, sizeof(code));
@@ -267,7 +267,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags);
mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type,
- hcon->dst_type, reason);
+ hcon->dst_type, HCI_ERROR_AUTH_FAILURE);
cancel_delayed_work_sync(&conn->security_timer);
diff --git a/net/bridge/Makefile b/net/bridge/Makefile
index d0359ea8ee7..e859098f5ee 100644
--- a/net/bridge/Makefile
+++ b/net/bridge/Makefile
@@ -12,6 +12,6 @@ bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o
bridge-$(CONFIG_BRIDGE_NETFILTER) += br_netfilter.o
-bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o
+bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 070e8a68cfc..7c78e264019 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -313,6 +313,8 @@ static const struct net_device_ops br_netdev_ops = {
.ndo_fdb_add = br_fdb_add,
.ndo_fdb_del = br_fdb_delete,
.ndo_fdb_dump = br_fdb_dump,
+ .ndo_bridge_getlink = br_getlink,
+ .ndo_bridge_setlink = br_setlink,
};
static void br_dev_free(struct net_device *dev)
@@ -356,7 +358,7 @@ void br_dev_setup(struct net_device *dev)
br->bridge_id.prio[0] = 0x80;
br->bridge_id.prio[1] = 0x00;
- memcpy(br->group_addr, br_group_address, ETH_ALEN);
+ memcpy(br->group_addr, eth_reserved_addr_base, ETH_ALEN);
br->stp_enabled = BR_NO_STP;
br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 1c8fdc3558c..37fe693471a 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -366,11 +366,11 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
err = netdev_set_master(dev, br->dev);
if (err)
- goto err3;
+ goto err4;
err = netdev_rx_handler_register(dev, br_handle_frame, p);
if (err)
- goto err4;
+ goto err5;
dev->priv_flags |= IFF_BRIDGE_PORT;
@@ -402,8 +402,10 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0;
-err4:
+err5:
netdev_set_master(dev, NULL);
+err4:
+ br_netpoll_disable(p);
err3:
sysfs_remove_link(br->ifobj, p->dev->name);
err2:
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 76f15fda021..4b34207419b 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -19,9 +19,6 @@
#include <linux/export.h>
#include "br_private.h"
-/* Bridge group multicast address 802.1d (pg 51). */
-const u8 br_group_address[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 };
-
/* Hook for brouter */
br_should_route_hook_t __rcu *br_should_route_hook __read_mostly;
EXPORT_SYMBOL(br_should_route_hook);
@@ -127,18 +124,6 @@ static int br_handle_local_finish(struct sk_buff *skb)
return 0; /* process further */
}
-/* Does address match the link local multicast address.
- * 01:80:c2:00:00:0X
- */
-static inline int is_link_local(const unsigned char *dest)
-{
- __be16 *a = (__be16 *)dest;
- static const __be16 *b = (const __be16 *)br_group_address;
- static const __be16 m = cpu_to_be16(0xfff0);
-
- return ((a[0] ^ b[0]) | (a[1] ^ b[1]) | ((a[2] ^ b[2]) & m)) == 0;
-}
-
/*
* Return NULL if skb is handled
* note: already called with rcu_read_lock
@@ -162,7 +147,7 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
p = br_port_get_rcu(skb->dev);
- if (unlikely(is_link_local(dest))) {
+ if (unlikely(is_link_local_ether_addr(dest))) {
/*
* See IEEE 802.1D Table 7-10 Reserved addresses
*
diff --git a/net/bridge/br_ioctl.c b/net/bridge/br_ioctl.c
index 7222fe1d546..cd8c3a44ab7 100644
--- a/net/bridge/br_ioctl.c
+++ b/net/bridge/br_ioctl.c
@@ -85,13 +85,14 @@ static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
/* called with RTNL */
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
{
+ struct net *net = dev_net(br->dev);
struct net_device *dev;
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
- dev = __dev_get_by_index(dev_net(br->dev), ifindex);
+ dev = __dev_get_by_index(net, ifindex);
if (dev == NULL)
return -EINVAL;
@@ -178,25 +179,25 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
case BRCTL_SET_BRIDGE_FORWARD_DELAY:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
return br_set_forward_delay(br, args[1]);
case BRCTL_SET_BRIDGE_HELLO_TIME:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
return br_set_hello_time(br, args[1]);
case BRCTL_SET_BRIDGE_MAX_AGE:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
return br_set_max_age(br, args[1]);
case BRCTL_SET_AGEING_TIME:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
br->ageing_time = clock_t_to_jiffies(args[1]);
@@ -236,14 +237,14 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
}
case BRCTL_SET_BRIDGE_STP_STATE:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
br_stp_set_enabled(br, args[1]);
return 0;
case BRCTL_SET_BRIDGE_PRIORITY:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
spin_lock_bh(&br->lock);
@@ -256,7 +257,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
struct net_bridge_port *p;
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
spin_lock_bh(&br->lock);
@@ -273,7 +274,7 @@ static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
struct net_bridge_port *p;
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
spin_lock_bh(&br->lock);
@@ -330,7 +331,7 @@ static int old_deviceless(struct net *net, void __user *uarg)
{
char buf[IFNAMSIZ];
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
@@ -360,7 +361,7 @@ int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uar
{
char buf[IFNAMSIZ];
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(buf, uarg, IFNAMSIZ))
diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c
new file mode 100644
index 00000000000..acc9f4cc18f
--- /dev/null
+++ b/net/bridge/br_mdb.c
@@ -0,0 +1,493 @@
+#include <linux/err.h>
+#include <linux/igmp.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/rculist.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <net/ip.h>
+#include <net/netlink.h>
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#endif
+
+#include "br_private.h"
+
+static int br_rports_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
+ struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct net_bridge_port *p;
+ struct hlist_node *n;
+ struct nlattr *nest;
+
+ if (!br->multicast_router || hlist_empty(&br->router_list))
+ return 0;
+
+ nest = nla_nest_start(skb, MDBA_ROUTER);
+ if (nest == NULL)
+ return -EMSGSIZE;
+
+ hlist_for_each_entry_rcu(p, n, &br->router_list, rlist) {
+ if (p && nla_put_u32(skb, MDBA_ROUTER_PORT, p->dev->ifindex))
+ goto fail;
+ }
+
+ nla_nest_end(skb, nest);
+ return 0;
+fail:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int br_mdb_fill_info(struct sk_buff *skb, struct netlink_callback *cb,
+ struct net_device *dev)
+{
+ struct net_bridge *br = netdev_priv(dev);
+ struct net_bridge_mdb_htable *mdb;
+ struct nlattr *nest, *nest2;
+ int i, err = 0;
+ int idx = 0, s_idx = cb->args[1];
+
+ if (br->multicast_disabled)
+ return 0;
+
+ mdb = rcu_dereference(br->mdb);
+ if (!mdb)
+ return 0;
+
+ nest = nla_nest_start(skb, MDBA_MDB);
+ if (nest == NULL)
+ return -EMSGSIZE;
+
+ for (i = 0; i < mdb->max; i++) {
+ struct hlist_node *h;
+ struct net_bridge_mdb_entry *mp;
+ struct net_bridge_port_group *p, **pp;
+ struct net_bridge_port *port;
+
+ hlist_for_each_entry_rcu(mp, h, &mdb->mhash[i], hlist[mdb->ver]) {
+ if (idx < s_idx)
+ goto skip;
+
+ nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
+ if (nest2 == NULL) {
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ for (pp = &mp->ports;
+ (p = rcu_dereference(*pp)) != NULL;
+ pp = &p->next) {
+ port = p->port;
+ if (port) {
+ struct br_mdb_entry e;
+ e.ifindex = port->dev->ifindex;
+ e.state = p->state;
+ if (p->addr.proto == htons(ETH_P_IP))
+ e.addr.u.ip4 = p->addr.u.ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (p->addr.proto == htons(ETH_P_IPV6))
+ e.addr.u.ip6 = p->addr.u.ip6;
+#endif
+ e.addr.proto = p->addr.proto;
+ if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(e), &e)) {
+ nla_nest_cancel(skb, nest2);
+ err = -EMSGSIZE;
+ goto out;
+ }
+ }
+ }
+ nla_nest_end(skb, nest2);
+ skip:
+ idx++;
+ }
+ }
+
+out:
+ cb->args[1] = idx;
+ nla_nest_end(skb, nest);
+ return err;
+}
+
+static int br_mdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net_device *dev;
+ struct net *net = sock_net(skb->sk);
+ struct nlmsghdr *nlh = NULL;
+ int idx = 0, s_idx;
+
+ s_idx = cb->args[0];
+
+ rcu_read_lock();
+
+ /* In theory this could be wrapped to 0... */
+ cb->seq = net->dev_base_seq + br_mdb_rehash_seq;
+
+ for_each_netdev_rcu(net, dev) {
+ if (dev->priv_flags & IFF_EBRIDGE) {
+ struct br_port_msg *bpm;
+
+ if (idx < s_idx)
+ goto skip;
+
+ nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, RTM_GETMDB,
+ sizeof(*bpm), NLM_F_MULTI);
+ if (nlh == NULL)
+ break;
+
+ bpm = nlmsg_data(nlh);
+ bpm->ifindex = dev->ifindex;
+ if (br_mdb_fill_info(skb, cb, dev) < 0)
+ goto out;
+ if (br_rports_fill_info(skb, cb, dev) < 0)
+ goto out;
+
+ cb->args[1] = 0;
+ nlmsg_end(skb, nlh);
+ skip:
+ idx++;
+ }
+ }
+
+out:
+ if (nlh)
+ nlmsg_end(skb, nlh);
+ rcu_read_unlock();
+ cb->args[0] = idx;
+ return skb->len;
+}
+
+static int nlmsg_populate_mdb_fill(struct sk_buff *skb,
+ struct net_device *dev,
+ struct br_mdb_entry *entry, u32 pid,
+ u32 seq, int type, unsigned int flags)
+{
+ struct nlmsghdr *nlh;
+ struct br_port_msg *bpm;
+ struct nlattr *nest, *nest2;
+
+ nlh = nlmsg_put(skb, pid, seq, type, sizeof(*bpm), NLM_F_MULTI);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ bpm = nlmsg_data(nlh);
+ bpm->family = AF_BRIDGE;
+ bpm->ifindex = dev->ifindex;
+ nest = nla_nest_start(skb, MDBA_MDB);
+ if (nest == NULL)
+ goto cancel;
+ nest2 = nla_nest_start(skb, MDBA_MDB_ENTRY);
+ if (nest2 == NULL)
+ goto end;
+
+ if (nla_put(skb, MDBA_MDB_ENTRY_INFO, sizeof(*entry), entry))
+ goto end;
+
+ nla_nest_end(skb, nest2);
+ nla_nest_end(skb, nest);
+ return nlmsg_end(skb, nlh);
+
+end:
+ nla_nest_end(skb, nest);
+cancel:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+static inline size_t rtnl_mdb_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct br_port_msg))
+ + nla_total_size(sizeof(struct br_mdb_entry));
+}
+
+static void __br_mdb_notify(struct net_device *dev, struct br_mdb_entry *entry,
+ int type)
+{
+ struct net *net = dev_net(dev);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(rtnl_mdb_nlmsg_size(), GFP_ATOMIC);
+ if (!skb)
+ goto errout;
+
+ err = nlmsg_populate_mdb_fill(skb, dev, entry, 0, 0, type, NTF_SELF);
+ if (err < 0) {
+ kfree_skb(skb);
+ goto errout;
+ }
+
+ rtnl_notify(skb, net, 0, RTNLGRP_MDB, NULL, GFP_ATOMIC);
+ return;
+errout:
+ rtnl_set_sk_err(net, RTNLGRP_MDB, err);
+}
+
+void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
+ struct br_ip *group, int type)
+{
+ struct br_mdb_entry entry;
+
+ entry.ifindex = port->dev->ifindex;
+ entry.addr.proto = group->proto;
+ entry.addr.u.ip4 = group->u.ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+ entry.addr.u.ip6 = group->u.ip6;
+#endif
+ __br_mdb_notify(dev, &entry, type);
+}
+
+static bool is_valid_mdb_entry(struct br_mdb_entry *entry)
+{
+ if (entry->ifindex == 0)
+ return false;
+
+ if (entry->addr.proto == htons(ETH_P_IP)) {
+ if (!ipv4_is_multicast(entry->addr.u.ip4))
+ return false;
+ if (ipv4_is_local_multicast(entry->addr.u.ip4))
+ return false;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else if (entry->addr.proto == htons(ETH_P_IPV6)) {
+ if (!ipv6_is_transient_multicast(&entry->addr.u.ip6))
+ return false;
+#endif
+ } else
+ return false;
+ if (entry->state != MDB_PERMANENT && entry->state != MDB_TEMPORARY)
+ return false;
+
+ return true;
+}
+
+static int br_mdb_parse(struct sk_buff *skb, struct nlmsghdr *nlh,
+ struct net_device **pdev, struct br_mdb_entry **pentry)
+{
+ struct net *net = sock_net(skb->sk);
+ struct br_mdb_entry *entry;
+ struct br_port_msg *bpm;
+ struct nlattr *tb[MDBA_SET_ENTRY_MAX+1];
+ struct net_device *dev;
+ int err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ err = nlmsg_parse(nlh, sizeof(*bpm), tb, MDBA_SET_ENTRY, NULL);
+ if (err < 0)
+ return err;
+
+ bpm = nlmsg_data(nlh);
+ if (bpm->ifindex == 0) {
+ pr_info("PF_BRIDGE: br_mdb_parse() with invalid ifindex\n");
+ return -EINVAL;
+ }
+
+ dev = __dev_get_by_index(net, bpm->ifindex);
+ if (dev == NULL) {
+ pr_info("PF_BRIDGE: br_mdb_parse() with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ if (!(dev->priv_flags & IFF_EBRIDGE)) {
+ pr_info("PF_BRIDGE: br_mdb_parse() with non-bridge\n");
+ return -EOPNOTSUPP;
+ }
+
+ *pdev = dev;
+
+ if (!tb[MDBA_SET_ENTRY] ||
+ nla_len(tb[MDBA_SET_ENTRY]) != sizeof(struct br_mdb_entry)) {
+ pr_info("PF_BRIDGE: br_mdb_parse() with invalid attr\n");
+ return -EINVAL;
+ }
+
+ entry = nla_data(tb[MDBA_SET_ENTRY]);
+ if (!is_valid_mdb_entry(entry)) {
+ pr_info("PF_BRIDGE: br_mdb_parse() with invalid entry\n");
+ return -EINVAL;
+ }
+
+ *pentry = entry;
+ return 0;
+}
+
+static int br_mdb_add_group(struct net_bridge *br, struct net_bridge_port *port,
+ struct br_ip *group, unsigned char state)
+{
+ struct net_bridge_mdb_entry *mp;
+ struct net_bridge_port_group *p;
+ struct net_bridge_port_group __rcu **pp;
+ struct net_bridge_mdb_htable *mdb;
+ int err;
+
+ mdb = mlock_dereference(br->mdb, br);
+ mp = br_mdb_ip_get(mdb, group);
+ if (!mp) {
+ mp = br_multicast_new_group(br, port, group);
+ err = PTR_ERR(mp);
+ if (IS_ERR(mp))
+ return err;
+ }
+
+ for (pp = &mp->ports;
+ (p = mlock_dereference(*pp, br)) != NULL;
+ pp = &p->next) {
+ if (p->port == port)
+ return -EEXIST;
+ if ((unsigned long)p->port < (unsigned long)port)
+ break;
+ }
+
+ p = br_multicast_new_port_group(port, group, *pp, state);
+ if (unlikely(!p))
+ return -ENOMEM;
+ rcu_assign_pointer(*pp, p);
+
+ br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
+ return 0;
+}
+
+static int __br_mdb_add(struct net *net, struct net_bridge *br,
+ struct br_mdb_entry *entry)
+{
+ struct br_ip ip;
+ struct net_device *dev;
+ struct net_bridge_port *p;
+ int ret;
+
+ if (!netif_running(br->dev) || br->multicast_disabled)
+ return -EINVAL;
+
+ dev = __dev_get_by_index(net, entry->ifindex);
+ if (!dev)
+ return -ENODEV;
+
+ p = br_port_get_rtnl(dev);
+ if (!p || p->br != br || p->state == BR_STATE_DISABLED)
+ return -EINVAL;
+
+ ip.proto = entry->addr.proto;
+ if (ip.proto == htons(ETH_P_IP))
+ ip.u.ip4 = entry->addr.u.ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ ip.u.ip6 = entry->addr.u.ip6;
+#endif
+
+ spin_lock_bh(&br->multicast_lock);
+ ret = br_mdb_add_group(br, p, &ip, entry->state);
+ spin_unlock_bh(&br->multicast_lock);
+ return ret;
+}
+
+static int br_mdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct br_mdb_entry *entry;
+ struct net_device *dev;
+ struct net_bridge *br;
+ int err;
+
+ err = br_mdb_parse(skb, nlh, &dev, &entry);
+ if (err < 0)
+ return err;
+
+ br = netdev_priv(dev);
+
+ err = __br_mdb_add(net, br, entry);
+ if (!err)
+ __br_mdb_notify(dev, entry, RTM_NEWMDB);
+ return err;
+}
+
+static int __br_mdb_del(struct net_bridge *br, struct br_mdb_entry *entry)
+{
+ struct net_bridge_mdb_htable *mdb;
+ struct net_bridge_mdb_entry *mp;
+ struct net_bridge_port_group *p;
+ struct net_bridge_port_group __rcu **pp;
+ struct br_ip ip;
+ int err = -EINVAL;
+
+ if (!netif_running(br->dev) || br->multicast_disabled)
+ return -EINVAL;
+
+ if (timer_pending(&br->multicast_querier_timer))
+ return -EBUSY;
+
+ ip.proto = entry->addr.proto;
+ if (ip.proto == htons(ETH_P_IP))
+ ip.u.ip4 = entry->addr.u.ip4;
+#if IS_ENABLED(CONFIG_IPV6)
+ else
+ ip.u.ip6 = entry->addr.u.ip6;
+#endif
+
+ spin_lock_bh(&br->multicast_lock);
+ mdb = mlock_dereference(br->mdb, br);
+
+ mp = br_mdb_ip_get(mdb, &ip);
+ if (!mp)
+ goto unlock;
+
+ for (pp = &mp->ports;
+ (p = mlock_dereference(*pp, br)) != NULL;
+ pp = &p->next) {
+ if (!p->port || p->port->dev->ifindex != entry->ifindex)
+ continue;
+
+ if (p->port->state == BR_STATE_DISABLED)
+ goto unlock;
+
+ rcu_assign_pointer(*pp, p->next);
+ hlist_del_init(&p->mglist);
+ del_timer(&p->timer);
+ call_rcu_bh(&p->rcu, br_multicast_free_pg);
+ err = 0;
+
+ if (!mp->ports && !mp->mglist &&
+ netif_running(br->dev))
+ mod_timer(&mp->timer, jiffies);
+ break;
+ }
+
+unlock:
+ spin_unlock_bh(&br->multicast_lock);
+ return err;
+}
+
+static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+{
+ struct net_device *dev;
+ struct br_mdb_entry *entry;
+ struct net_bridge *br;
+ int err;
+
+ err = br_mdb_parse(skb, nlh, &dev, &entry);
+ if (err < 0)
+ return err;
+
+ br = netdev_priv(dev);
+
+ err = __br_mdb_del(br, entry);
+ if (!err)
+ __br_mdb_notify(dev, entry, RTM_DELMDB);
+ return err;
+}
+
+void br_mdb_init(void)
+{
+ rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, NULL);
+ rtnl_register(PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, NULL);
+ rtnl_register(PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, NULL);
+}
+
+void br_mdb_uninit(void)
+{
+ rtnl_unregister(PF_BRIDGE, RTM_GETMDB);
+ rtnl_unregister(PF_BRIDGE, RTM_NEWMDB);
+ rtnl_unregister(PF_BRIDGE, RTM_DELMDB);
+}
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 241743417f4..6d6f26531de 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -27,25 +27,13 @@
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
#include <net/mld.h>
-#include <net/addrconf.h>
#include <net/ip6_checksum.h>
#endif
#include "br_private.h"
-#define mlock_dereference(X, br) \
- rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
-
static void br_multicast_start_querier(struct net_bridge *br);
-
-#if IS_ENABLED(CONFIG_IPV6)
-static inline int ipv6_is_transient_multicast(const struct in6_addr *addr)
-{
- if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr))
- return 1;
- return 0;
-}
-#endif
+unsigned int br_mdb_rehash_seq;
static inline int br_ip_equal(const struct br_ip *a, const struct br_ip *b)
{
@@ -103,8 +91,8 @@ static struct net_bridge_mdb_entry *__br_mdb_ip_get(
return NULL;
}
-static struct net_bridge_mdb_entry *br_mdb_ip_get(
- struct net_bridge_mdb_htable *mdb, struct br_ip *dst)
+struct net_bridge_mdb_entry *br_mdb_ip_get(struct net_bridge_mdb_htable *mdb,
+ struct br_ip *dst)
{
if (!mdb)
return NULL;
@@ -207,7 +195,7 @@ static int br_mdb_copy(struct net_bridge_mdb_htable *new,
return maxlen > elasticity ? -EINVAL : 0;
}
-static void br_multicast_free_pg(struct rcu_head *head)
+void br_multicast_free_pg(struct rcu_head *head)
{
struct net_bridge_port_group *p =
container_of(head, struct net_bridge_port_group, rcu);
@@ -291,7 +279,7 @@ static void br_multicast_port_group_expired(unsigned long data)
spin_lock(&br->multicast_lock);
if (!netif_running(br->dev) || timer_pending(&pg->timer) ||
- hlist_unhashed(&pg->mglist))
+ hlist_unhashed(&pg->mglist) || pg->state & MDB_PERMANENT)
goto out;
br_multicast_del_pg(br, pg);
@@ -338,6 +326,7 @@ static int br_mdb_rehash(struct net_bridge_mdb_htable __rcu **mdbp, int max,
return err;
}
+ br_mdb_rehash_seq++;
call_rcu_bh(&mdb->rcu, br_mdb_free);
out:
@@ -582,9 +571,8 @@ err:
return mp;
}
-static struct net_bridge_mdb_entry *br_multicast_new_group(
- struct net_bridge *br, struct net_bridge_port *port,
- struct br_ip *group)
+struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
+ struct net_bridge_port *port, struct br_ip *group)
{
struct net_bridge_mdb_htable *mdb;
struct net_bridge_mdb_entry *mp;
@@ -631,6 +619,28 @@ out:
return mp;
}
+struct net_bridge_port_group *br_multicast_new_port_group(
+ struct net_bridge_port *port,
+ struct br_ip *group,
+ struct net_bridge_port_group __rcu *next,
+ unsigned char state)
+{
+ struct net_bridge_port_group *p;
+
+ p = kzalloc(sizeof(*p), GFP_ATOMIC);
+ if (unlikely(!p))
+ return NULL;
+
+ p->addr = *group;
+ p->port = port;
+ p->state = state;
+ rcu_assign_pointer(p->next, next);
+ hlist_add_head(&p->mglist, &port->mglist);
+ setup_timer(&p->timer, br_multicast_port_group_expired,
+ (unsigned long)p);
+ return p;
+}
+
static int br_multicast_add_group(struct net_bridge *br,
struct net_bridge_port *port,
struct br_ip *group)
@@ -666,19 +676,11 @@ static int br_multicast_add_group(struct net_bridge *br,
break;
}
- p = kzalloc(sizeof(*p), GFP_ATOMIC);
- err = -ENOMEM;
+ p = br_multicast_new_port_group(port, group, *pp, MDB_TEMPORARY);
if (unlikely(!p))
goto err;
-
- p->addr = *group;
- p->port = port;
- p->next = *pp;
- hlist_add_head(&p->mglist, &port->mglist);
- setup_timer(&p->timer, br_multicast_port_group_expired,
- (unsigned long)p);
-
rcu_assign_pointer(*pp, p);
+ br_mdb_notify(br->dev, port, group, RTM_NEWMDB);
found:
mod_timer(&p->timer, now + br->multicast_membership_interval);
@@ -1138,7 +1140,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
struct sk_buff *skb)
{
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
- struct mld_msg *mld = (struct mld_msg *) icmp6_hdr(skb);
+ struct mld_msg *mld;
struct net_bridge_mdb_entry *mp;
struct mld2_query *mld2q;
struct net_bridge_port_group *p;
@@ -1172,7 +1174,7 @@ static int br_ip6_multicast_query(struct net_bridge *br,
mld2q = (struct mld2_query *)icmp6_hdr(skb);
if (!mld2q->mld2q_nsrcs)
group = &mld2q->mld2q_mca;
- max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(mld2q->mld2q_mrc) : 1;
+ max_delay = mld2q->mld2q_mrc ? MLDV2_MRC(ntohs(mld2q->mld2q_mrc)) : 1;
}
if (!group)
@@ -1225,6 +1227,28 @@ static void br_multicast_leave_group(struct net_bridge *br,
if (!mp)
goto out;
+ if (port && (port->flags & BR_MULTICAST_FAST_LEAVE)) {
+ struct net_bridge_port_group __rcu **pp;
+
+ for (pp = &mp->ports;
+ (p = mlock_dereference(*pp, br)) != NULL;
+ pp = &p->next) {
+ if (p->port != port)
+ continue;
+
+ rcu_assign_pointer(*pp, p->next);
+ hlist_del_init(&p->mglist);
+ del_timer(&p->timer);
+ call_rcu_bh(&p->rcu, br_multicast_free_pg);
+ br_mdb_notify(br->dev, port, group, RTM_DELMDB);
+
+ if (!mp->ports && !mp->mglist &&
+ netif_running(br->dev))
+ mod_timer(&mp->timer, jiffies);
+ }
+ goto out;
+ }
+
now = jiffies;
time = now + br->multicast_last_member_count *
br->multicast_last_member_interval;
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 093f527276a..5dc66abcc9e 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -20,16 +20,44 @@
#include "br_private.h"
#include "br_private_stp.h"
+static inline size_t br_port_info_size(void)
+{
+ return nla_total_size(1) /* IFLA_BRPORT_STATE */
+ + nla_total_size(2) /* IFLA_BRPORT_PRIORITY */
+ + nla_total_size(4) /* IFLA_BRPORT_COST */
+ + nla_total_size(1) /* IFLA_BRPORT_MODE */
+ + nla_total_size(1) /* IFLA_BRPORT_GUARD */
+ + nla_total_size(1) /* IFLA_BRPORT_PROTECT */
+ + 0;
+}
+
static inline size_t br_nlmsg_size(void)
{
return NLMSG_ALIGN(sizeof(struct ifinfomsg))
- + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
- + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
- + nla_total_size(4) /* IFLA_MASTER */
- + nla_total_size(4) /* IFLA_MTU */
- + nla_total_size(4) /* IFLA_LINK */
- + nla_total_size(1) /* IFLA_OPERSTATE */
- + nla_total_size(1); /* IFLA_PROTINFO */
+ + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ + nla_total_size(4) /* IFLA_MASTER */
+ + nla_total_size(4) /* IFLA_MTU */
+ + nla_total_size(4) /* IFLA_LINK */
+ + nla_total_size(1) /* IFLA_OPERSTATE */
+ + nla_total_size(br_port_info_size()); /* IFLA_PROTINFO */
+}
+
+static int br_port_fill_attrs(struct sk_buff *skb,
+ const struct net_bridge_port *p)
+{
+ u8 mode = !!(p->flags & BR_HAIRPIN_MODE);
+
+ if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) ||
+ nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) ||
+ nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) ||
+ nla_put_u8(skb, IFLA_BRPORT_MODE, mode) ||
+ nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) ||
+ nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) ||
+ nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)))
+ return -EMSGSIZE;
+
+ return 0;
}
/*
@@ -67,10 +95,18 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por
(dev->addr_len &&
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
(dev->ifindex != dev->iflink &&
- nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
- (event == RTM_NEWLINK &&
- nla_put_u8(skb, IFLA_PROTINFO, port->state)))
+ nla_put_u32(skb, IFLA_LINK, dev->iflink)))
goto nla_put_failure;
+
+ if (event == RTM_NEWLINK) {
+ struct nlattr *nest
+ = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
+
+ if (nest == NULL || br_port_fill_attrs(skb, port) < 0)
+ goto nla_put_failure;
+ nla_nest_end(skb, nest);
+ }
+
return nlmsg_end(skb, nlh);
nla_put_failure:
@@ -111,89 +147,134 @@ errout:
/*
* Dump information about all ports, in response to GETLINK
*/
-static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
+int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
+ struct net_device *dev)
+{
+ int err = 0;
+ struct net_bridge_port *port = br_port_get_rcu(dev);
+
+ /* not a bridge port */
+ if (!port)
+ goto out;
+
+ err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI);
+out:
+ return err;
+}
+
+static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = {
+ [IFLA_BRPORT_STATE] = { .type = NLA_U8 },
+ [IFLA_BRPORT_COST] = { .type = NLA_U32 },
+ [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
+ [IFLA_BRPORT_MODE] = { .type = NLA_U8 },
+ [IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
+ [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
+};
+
+/* Change the state of the port and notify spanning tree */
+static int br_set_port_state(struct net_bridge_port *p, u8 state)
+{
+ if (state > BR_STATE_BLOCKING)
+ return -EINVAL;
+
+ /* if kernel STP is running, don't allow changes */
+ if (p->br->stp_enabled == BR_KERNEL_STP)
+ return -EBUSY;
+
+ if (!netif_running(p->dev) ||
+ (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED))
+ return -ENETDOWN;
+
+ p->state = state;
+ br_log_state(p);
+ br_port_state_selection(p->br);
+ return 0;
+}
+
+/* Set/clear or port flags based on attribute */
+static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
+ int attrtype, unsigned long mask)
+{
+ if (tb[attrtype]) {
+ u8 flag = nla_get_u8(tb[attrtype]);
+ if (flag)
+ p->flags |= mask;
+ else
+ p->flags &= ~mask;
+ }
+}
+
+/* Process bridge protocol info on port */
+static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
{
- struct net *net = sock_net(skb->sk);
- struct net_device *dev;
- int idx;
-
- idx = 0;
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
- struct net_bridge_port *port = br_port_get_rcu(dev);
-
- /* not a bridge port */
- if (!port || idx < cb->args[0])
- goto skip;
-
- if (br_fill_ifinfo(skb, port,
- NETLINK_CB(cb->skb).portid,
- cb->nlh->nlmsg_seq, RTM_NEWLINK,
- NLM_F_MULTI) < 0)
- break;
-skip:
- ++idx;
+ int err;
+
+ br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
+ br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
+ br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE);
+
+ if (tb[IFLA_BRPORT_COST]) {
+ err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
+ if (err)
+ return err;
+ }
+
+ if (tb[IFLA_BRPORT_PRIORITY]) {
+ err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY]));
+ if (err)
+ return err;
}
- rcu_read_unlock();
- cb->args[0] = idx;
- return skb->len;
+ if (tb[IFLA_BRPORT_STATE]) {
+ err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE]));
+ if (err)
+ return err;
+ }
+ return 0;
}
-/*
- * Change state of port (ie from forwarding to blocking etc)
- * Used by spanning tree in user space.
- */
-static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
+/* Change state and parameters on port. */
+int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
{
- struct net *net = sock_net(skb->sk);
struct ifinfomsg *ifm;
struct nlattr *protinfo;
- struct net_device *dev;
struct net_bridge_port *p;
- u8 new_state;
-
- if (nlmsg_len(nlh) < sizeof(*ifm))
- return -EINVAL;
+ struct nlattr *tb[IFLA_BRPORT_MAX + 1];
+ int err;
ifm = nlmsg_data(nlh);
- if (ifm->ifi_family != AF_BRIDGE)
- return -EPFNOSUPPORT;
protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO);
- if (!protinfo || nla_len(protinfo) < sizeof(u8))
- return -EINVAL;
-
- new_state = nla_get_u8(protinfo);
- if (new_state > BR_STATE_BLOCKING)
- return -EINVAL;
-
- dev = __dev_get_by_index(net, ifm->ifi_index);
- if (!dev)
- return -ENODEV;
+ if (!protinfo)
+ return 0;
p = br_port_get_rtnl(dev);
if (!p)
return -EINVAL;
- /* if kernel STP is running, don't allow changes */
- if (p->br->stp_enabled == BR_KERNEL_STP)
- return -EBUSY;
-
- if (!netif_running(dev) ||
- (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED))
- return -ENETDOWN;
-
- p->state = new_state;
- br_log_state(p);
+ if (protinfo->nla_type & NLA_F_NESTED) {
+ err = nla_parse_nested(tb, IFLA_BRPORT_MAX,
+ protinfo, ifla_brport_policy);
+ if (err)
+ return err;
+
+ spin_lock_bh(&p->br->lock);
+ err = br_setport(p, tb);
+ spin_unlock_bh(&p->br->lock);
+ } else {
+ /* Binary compatability with old RSTP */
+ if (nla_len(protinfo) < sizeof(u8))
+ return -EINVAL;
- spin_lock_bh(&p->br->lock);
- br_port_state_selection(p->br);
- spin_unlock_bh(&p->br->lock);
+ spin_lock_bh(&p->br->lock);
+ err = br_set_port_state(p, nla_get_u8(protinfo));
+ spin_unlock_bh(&p->br->lock);
+ }
- br_ifinfo_notify(RTM_NEWLINK, p);
+ if (err == 0)
+ br_ifinfo_notify(RTM_NEWLINK, p);
- return 0;
+ return err;
}
static int br_validate(struct nlattr *tb[], struct nlattr *data[])
@@ -220,31 +301,19 @@ int __init br_netlink_init(void)
{
int err;
+ br_mdb_init();
err = rtnl_link_register(&br_link_ops);
- if (err < 0)
- goto err1;
-
- err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL,
- br_dump_ifinfo, NULL);
- if (err)
- goto err2;
- err = __rtnl_register(PF_BRIDGE, RTM_SETLINK,
- br_rtm_setlink, NULL, NULL);
if (err)
- goto err3;
+ goto out;
return 0;
-
-err3:
- rtnl_unregister_all(PF_BRIDGE);
-err2:
- rtnl_link_unregister(&br_link_ops);
-err1:
+out:
+ br_mdb_uninit();
return err;
}
void __exit br_netlink_fini(void)
{
+ br_mdb_uninit();
rtnl_link_unregister(&br_link_ops);
- rtnl_unregister_all(PF_BRIDGE);
}
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 9b278c4ebee..711094aed41 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -83,6 +83,7 @@ struct net_bridge_port_group {
struct rcu_head rcu;
struct timer_list timer;
struct br_ip addr;
+ unsigned char state;
};
struct net_bridge_mdb_entry
@@ -135,6 +136,9 @@ struct net_bridge_port
unsigned long flags;
#define BR_HAIRPIN_MODE 0x00000001
+#define BR_BPDU_GUARD 0x00000002
+#define BR_ROOT_BLOCK 0x00000004
+#define BR_MULTICAST_FAST_LEAVE 0x00000008
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
u32 multicast_startup_queries_sent;
@@ -158,7 +162,9 @@ struct net_bridge_port
static inline struct net_bridge_port *br_port_get_rcu(const struct net_device *dev)
{
- struct net_bridge_port *port = rcu_dereference(dev->rx_handler_data);
+ struct net_bridge_port *port =
+ rcu_dereference_rtnl(dev->rx_handler_data);
+
return br_port_exists(dev) ? port : NULL;
}
@@ -288,7 +294,6 @@ struct br_input_skb_cb {
pr_debug("%s: " format, (br)->dev->name, ##args)
extern struct notifier_block br_device_notifier;
-extern const u8 br_group_address[ETH_ALEN];
/* called under bridge lock */
static inline int br_is_root_bridge(const struct net_bridge *br)
@@ -407,6 +412,7 @@ extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __us
/* br_multicast.c */
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+extern unsigned int br_mdb_rehash_seq;
extern int br_multicast_rcv(struct net_bridge *br,
struct net_bridge_port *port,
struct sk_buff *skb);
@@ -429,6 +435,34 @@ extern int br_multicast_set_port_router(struct net_bridge_port *p,
extern int br_multicast_toggle(struct net_bridge *br, unsigned long val);
extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val);
extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val);
+extern struct net_bridge_mdb_entry *br_mdb_ip_get(
+ struct net_bridge_mdb_htable *mdb,
+ struct br_ip *dst);
+extern struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br,
+ struct net_bridge_port *port, struct br_ip *group);
+extern void br_multicast_free_pg(struct rcu_head *head);
+extern struct net_bridge_port_group *br_multicast_new_port_group(
+ struct net_bridge_port *port,
+ struct br_ip *group,
+ struct net_bridge_port_group *next,
+ unsigned char state);
+extern void br_mdb_init(void);
+extern void br_mdb_uninit(void);
+extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
+ struct br_ip *group, int type);
+
+#define mlock_dereference(X, br) \
+ rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock))
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/addrconf.h>
+static inline int ipv6_is_transient_multicast(const struct in6_addr *addr)
+{
+ if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr))
+ return 1;
+ return 0;
+}
+#endif
static inline bool br_multicast_is_router(struct net_bridge *br)
{
@@ -492,6 +526,12 @@ static inline bool br_multicast_is_router(struct net_bridge *br)
{
return 0;
}
+static inline void br_mdb_init(void)
+{
+}
+static inline void br_mdb_uninit(void)
+{
+}
#endif
/* br_netfilter.c */
@@ -553,6 +593,9 @@ extern struct rtnl_link_ops br_link_ops;
extern int br_netlink_init(void);
extern void br_netlink_fini(void);
extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
+extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg);
+extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq,
+ struct net_device *dev);
#ifdef CONFIG_SYSFS
/* br_sysfs_if.c */
@@ -566,10 +609,10 @@ extern void br_sysfs_delbr(struct net_device *dev);
#else
-#define br_sysfs_addif(p) (0)
-#define br_sysfs_renameif(p) (0)
-#define br_sysfs_addbr(dev) (0)
-#define br_sysfs_delbr(dev) do { } while(0)
+static inline int br_sysfs_addif(struct net_bridge_port *p) { return 0; }
+static inline int br_sysfs_renameif(struct net_bridge_port *p) { return 0; }
+static inline int br_sysfs_addbr(struct net_device *dev) { return 0; }
+static inline void br_sysfs_delbr(struct net_device *dev) { return; }
#endif /* CONFIG_SYSFS */
#endif
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index af9a12099ba..b01849a7431 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -100,6 +100,21 @@ static int br_should_become_root_port(const struct net_bridge_port *p,
return 0;
}
+static void br_root_port_block(const struct net_bridge *br,
+ struct net_bridge_port *p)
+{
+
+ br_notice(br, "port %u(%s) tried to become root port (blocked)",
+ (unsigned int) p->port_no, p->dev->name);
+
+ p->state = BR_STATE_LISTENING;
+ br_log_state(p);
+ br_ifinfo_notify(RTM_NEWLINK, p);
+
+ if (br->forward_delay > 0)
+ mod_timer(&p->forward_delay_timer, jiffies + br->forward_delay);
+}
+
/* called under bridge lock */
static void br_root_selection(struct net_bridge *br)
{
@@ -107,7 +122,12 @@ static void br_root_selection(struct net_bridge *br)
u16 root_port = 0;
list_for_each_entry(p, &br->port_list, list) {
- if (br_should_become_root_port(p, root_port))
+ if (!br_should_become_root_port(p, root_port))
+ continue;
+
+ if (p->flags & BR_ROOT_BLOCK)
+ br_root_port_block(br, p);
+ else
root_port = p->port_no;
}
diff --git a/net/bridge/br_stp_bpdu.c b/net/bridge/br_stp_bpdu.c
index fd30a6022de..7f884e3fb95 100644
--- a/net/bridge/br_stp_bpdu.c
+++ b/net/bridge/br_stp_bpdu.c
@@ -170,6 +170,13 @@ void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb,
if (!ether_addr_equal(dest, br->group_addr))
goto out;
+ if (p->flags & BR_BPDU_GUARD) {
+ br_notice(br, "BPDU received on blocked port %u(%s)\n",
+ (unsigned int) p->port_no, p->dev->name);
+ br_stp_disable_port(p);
+ goto out;
+ }
+
buf = skb_pull(skb, 3);
if (buf[0] == BPDU_TYPE_CONFIG) {
diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c
index c5c059333ea..5913a3a0047 100644
--- a/net/bridge/br_sysfs_br.c
+++ b/net/bridge/br_sysfs_br.c
@@ -14,6 +14,7 @@
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
#include <linux/if_bridge.h>
#include <linux/rtnetlink.h>
#include <linux/spinlock.h>
@@ -36,7 +37,7 @@ static ssize_t store_bridge_parm(struct device *d,
unsigned long val;
int err;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
val = simple_strtoul(buf, &endp, 0);
@@ -132,7 +133,7 @@ static ssize_t store_stp_state(struct device *d,
char *endp;
unsigned long val;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
val = simple_strtoul(buf, &endp, 0);
@@ -165,7 +166,7 @@ static ssize_t store_group_fwd_mask(struct device *d,
char *endp;
unsigned long val;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
val = simple_strtoul(buf, &endp, 0);
@@ -297,23 +298,18 @@ static ssize_t store_group_addr(struct device *d,
const char *buf, size_t len)
{
struct net_bridge *br = to_bridge(d);
- unsigned int new_addr[6];
+ u8 new_addr[6];
int i;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
- if (sscanf(buf, "%x:%x:%x:%x:%x:%x",
+ if (sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&new_addr[0], &new_addr[1], &new_addr[2],
&new_addr[3], &new_addr[4], &new_addr[5]) != 6)
return -EINVAL;
- /* Must be 01:80:c2:00:00:0X */
- for (i = 0; i < 5; i++)
- if (new_addr[i] != br_group_address[i])
- return -EINVAL;
-
- if (new_addr[5] & ~0xf)
+ if (!is_link_local_ether_addr(new_addr))
return -EINVAL;
if (new_addr[5] == 1 || /* 802.3x Pause address */
@@ -337,7 +333,7 @@ static ssize_t store_flush(struct device *d,
{
struct net_bridge *br = to_bridge(d);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(br->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
br_fdb_flush(br);
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 13b36bdc76a..a1ef1b6e14d 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -34,6 +34,28 @@ const struct brport_attribute brport_attr_##_name = { \
.store = _store, \
};
+#define BRPORT_ATTR_FLAG(_name, _mask) \
+static ssize_t show_##_name(struct net_bridge_port *p, char *buf) \
+{ \
+ return sprintf(buf, "%d\n", !!(p->flags & _mask)); \
+} \
+static int store_##_name(struct net_bridge_port *p, unsigned long v) \
+{ \
+ unsigned long flags = p->flags; \
+ if (v) \
+ flags |= _mask; \
+ else \
+ flags &= ~_mask; \
+ if (flags != p->flags) { \
+ p->flags = flags; \
+ br_ifinfo_notify(RTM_NEWLINK, p); \
+ } \
+ return 0; \
+} \
+static BRPORT_ATTR(_name, S_IRUGO | S_IWUSR, \
+ show_##_name, store_##_name)
+
+
static ssize_t show_path_cost(struct net_bridge_port *p, char *buf)
{
return sprintf(buf, "%d\n", p->path_cost);
@@ -133,21 +155,9 @@ static int store_flush(struct net_bridge_port *p, unsigned long v)
}
static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
-static ssize_t show_hairpin_mode(struct net_bridge_port *p, char *buf)
-{
- int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0;
- return sprintf(buf, "%d\n", hairpin_mode);
-}
-static int store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
-{
- if (v)
- p->flags |= BR_HAIRPIN_MODE;
- else
- p->flags &= ~BR_HAIRPIN_MODE;
- return 0;
-}
-static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR,
- show_hairpin_mode, store_hairpin_mode);
+BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE);
+BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
+BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -162,6 +172,8 @@ static int store_multicast_router(struct net_bridge_port *p,
}
static BRPORT_ATTR(multicast_router, S_IRUGO | S_IWUSR, show_multicast_router,
store_multicast_router);
+
+BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE);
#endif
static const struct brport_attribute *brport_attrs[] = {
@@ -181,8 +193,11 @@ static const struct brport_attribute *brport_attrs[] = {
&brport_attr_hold_timer,
&brport_attr_flush,
&brport_attr_hairpin_mode,
+ &brport_attr_bpdu_guard,
+ &brport_attr_root_block,
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
&brport_attr_multicast_router,
+ &brport_attr_multicast_fast_leave,
#endif
NULL
};
@@ -209,7 +224,7 @@ static ssize_t brport_store(struct kobject * kobj,
char *endp;
unsigned long val;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(dev_net(p->dev)->user_ns, CAP_NET_ADMIN))
return -EPERM;
val = simple_strtoul(buf, &endp, 0);
diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c
index fd7cbf5aa89..3ebc8cbc91f 100644
--- a/net/caif/caif_usb.c
+++ b/net/caif/caif_usb.c
@@ -126,20 +126,16 @@ static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
struct net_device *dev = arg;
struct caif_dev_common common;
struct cflayer *layer, *link_support;
- struct usbnet *usbnet = netdev_priv(dev);
- struct usb_device *usbdev = usbnet->udev;
- struct ethtool_drvinfo drvinfo;
+ struct usbnet *usbnet;
+ struct usb_device *usbdev;
- /*
- * Quirks: High-jack ethtool to find if we have a NCM device,
- * and find it's VID/PID.
- */
- if (dev->ethtool_ops == NULL || dev->ethtool_ops->get_drvinfo == NULL)
+ /* Check whether we have a NCM device, and find its VID/PID. */
+ if (!(dev->dev.parent && dev->dev.parent->driver &&
+ strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
return 0;
- dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
- if (strncmp(drvinfo.driver, "cdc_ncm", 7) != 0)
- return 0;
+ usbnet = netdev_priv(dev);
+ usbdev = usbnet->udev;
pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
le16_to_cpu(usbdev->descriptor.idVendor),
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index 44f270fc2d0..a376ec1ac0a 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -515,8 +515,7 @@ static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
client_layer : NULL);
}
- if (req != NULL)
- kfree(req);
+ kfree(req);
spin_unlock_bh(&cfctrl->info_list_lock);
}
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 6f747582718..969b7cdff59 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -1084,6 +1084,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->sk = sk;
op->ifindex = ifindex;
+ /* ifindex for timeout events w/o previous frame reception */
+ op->rx_ifindex = ifindex;
+
/* initialize uninitialized (kzalloc) structure */
hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
op->timer.function = bcm_rx_timeout_handler;
diff --git a/net/can/gw.c b/net/can/gw.c
index 1f5c9785a26..574dda78eb0 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -751,6 +751,9 @@ static int cgw_create_job(struct sk_buff *skb, struct nlmsghdr *nlh,
struct cgw_job *gwj;
int err = 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (nlmsg_len(nlh) < sizeof(*r))
return -EINVAL;
@@ -839,6 +842,9 @@ static int cgw_remove_job(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct can_can_gw ccgw;
int err = 0;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (nlmsg_len(nlh) < sizeof(*r))
return -EINVAL;
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index a8020293f34..ee71ea26777 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -305,7 +305,6 @@ ceph_parse_options(char *options, const char *dev_name,
/* start with defaults */
opt->flags = CEPH_OPT_DEFAULT;
- opt->osd_timeout = CEPH_OSD_TIMEOUT_DEFAULT;
opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT;
opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */
opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; /* seconds */
@@ -391,7 +390,7 @@ ceph_parse_options(char *options, const char *dev_name,
/* misc */
case Opt_osdtimeout:
- opt->osd_timeout = intval;
+ pr_warning("ignoring deprecated osdtimeout option\n");
break;
case Opt_osdkeepalivetimeout:
opt->osd_keepalive_timeout = intval;
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 159aa8bef9e..5ccf87ed8d6 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -506,6 +506,7 @@ static void reset_connection(struct ceph_connection *con)
{
/* reset connection, out_queue, msg_ and connect_seq */
/* discard existing out_queue and msg_seq */
+ dout("reset_connection %p\n", con);
ceph_msg_remove_list(&con->out_queue);
ceph_msg_remove_list(&con->out_sent);
@@ -561,7 +562,7 @@ void ceph_con_open(struct ceph_connection *con,
mutex_lock(&con->mutex);
dout("con_open %p %s\n", con, ceph_pr_addr(&addr->in_addr));
- BUG_ON(con->state != CON_STATE_CLOSED);
+ WARN_ON(con->state != CON_STATE_CLOSED);
con->state = CON_STATE_PREOPEN;
con->peer_name.type = (__u8) entity_type;
@@ -1506,13 +1507,6 @@ static int process_banner(struct ceph_connection *con)
return 0;
}
-static void fail_protocol(struct ceph_connection *con)
-{
- reset_connection(con);
- BUG_ON(con->state != CON_STATE_NEGOTIATING);
- con->state = CON_STATE_CLOSED;
-}
-
static int process_connect(struct ceph_connection *con)
{
u64 sup_feat = con->msgr->supported_features;
@@ -1530,7 +1524,7 @@ static int process_connect(struct ceph_connection *con)
ceph_pr_addr(&con->peer_addr.in_addr),
sup_feat, server_feat, server_feat & ~sup_feat);
con->error_msg = "missing required protocol features";
- fail_protocol(con);
+ reset_connection(con);
return -1;
case CEPH_MSGR_TAG_BADPROTOVER:
@@ -1541,7 +1535,7 @@ static int process_connect(struct ceph_connection *con)
le32_to_cpu(con->out_connect.protocol_version),
le32_to_cpu(con->in_reply.protocol_version));
con->error_msg = "protocol version mismatch";
- fail_protocol(con);
+ reset_connection(con);
return -1;
case CEPH_MSGR_TAG_BADAUTHORIZER:
@@ -1631,11 +1625,11 @@ static int process_connect(struct ceph_connection *con)
ceph_pr_addr(&con->peer_addr.in_addr),
req_feat, server_feat, req_feat & ~server_feat);
con->error_msg = "missing required protocol features";
- fail_protocol(con);
+ reset_connection(con);
return -1;
}
- BUG_ON(con->state != CON_STATE_NEGOTIATING);
+ WARN_ON(con->state != CON_STATE_NEGOTIATING);
con->state = CON_STATE_OPEN;
con->peer_global_seq = le32_to_cpu(con->in_reply.global_seq);
@@ -2132,7 +2126,6 @@ more:
if (ret < 0)
goto out;
- BUG_ON(con->state != CON_STATE_CONNECTING);
con->state = CON_STATE_NEGOTIATING;
/*
@@ -2160,7 +2153,7 @@ more:
goto more;
}
- BUG_ON(con->state != CON_STATE_OPEN);
+ WARN_ON(con->state != CON_STATE_OPEN);
if (con->in_base_pos < 0) {
/*
@@ -2244,22 +2237,62 @@ bad_tag:
/*
- * Atomically queue work on a connection. Bump @con reference to
- * avoid races with connection teardown.
+ * Atomically queue work on a connection after the specified delay.
+ * Bump @con reference to avoid races with connection teardown.
+ * Returns 0 if work was queued, or an error code otherwise.
*/
-static void queue_con(struct ceph_connection *con)
+static int queue_con_delay(struct ceph_connection *con, unsigned long delay)
{
if (!con->ops->get(con)) {
- dout("queue_con %p ref count 0\n", con);
- return;
+ dout("%s %p ref count 0\n", __func__, con);
+
+ return -ENOENT;
}
- if (!queue_delayed_work(ceph_msgr_wq, &con->work, 0)) {
- dout("queue_con %p - already queued\n", con);
+ if (!queue_delayed_work(ceph_msgr_wq, &con->work, delay)) {
+ dout("%s %p - already queued\n", __func__, con);
con->ops->put(con);
- } else {
- dout("queue_con %p\n", con);
+
+ return -EBUSY;
+ }
+
+ dout("%s %p %lu\n", __func__, con, delay);
+
+ return 0;
+}
+
+static void queue_con(struct ceph_connection *con)
+{
+ (void) queue_con_delay(con, 0);
+}
+
+static bool con_sock_closed(struct ceph_connection *con)
+{
+ if (!test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags))
+ return false;
+
+#define CASE(x) \
+ case CON_STATE_ ## x: \
+ con->error_msg = "socket closed (con state " #x ")"; \
+ break;
+
+ switch (con->state) {
+ CASE(CLOSED);
+ CASE(PREOPEN);
+ CASE(CONNECTING);
+ CASE(NEGOTIATING);
+ CASE(OPEN);
+ CASE(STANDBY);
+ default:
+ pr_warning("%s con %p unrecognized state %lu\n",
+ __func__, con, con->state);
+ con->error_msg = "unrecognized con state";
+ BUG();
+ break;
}
+#undef CASE
+
+ return true;
}
/*
@@ -2273,37 +2306,19 @@ static void con_work(struct work_struct *work)
mutex_lock(&con->mutex);
restart:
- if (test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) {
- switch (con->state) {
- case CON_STATE_CONNECTING:
- con->error_msg = "connection failed";
- break;
- case CON_STATE_NEGOTIATING:
- con->error_msg = "negotiation failed";
- break;
- case CON_STATE_OPEN:
- con->error_msg = "socket closed";
- break;
- default:
- dout("unrecognized con state %d\n", (int)con->state);
- con->error_msg = "unrecognized con state";
- BUG();
- }
+ if (con_sock_closed(con))
goto fault;
- }
if (test_and_clear_bit(CON_FLAG_BACKOFF, &con->flags)) {
dout("con_work %p backing off\n", con);
- if (queue_delayed_work(ceph_msgr_wq, &con->work,
- round_jiffies_relative(con->delay))) {
- dout("con_work %p backoff %lu\n", con, con->delay);
- mutex_unlock(&con->mutex);
- return;
- } else {
- con->ops->put(con);
+ ret = queue_con_delay(con, round_jiffies_relative(con->delay));
+ if (ret) {
dout("con_work %p FAILED to back off %lu\n", con,
con->delay);
+ BUG_ON(ret == -ENOENT);
+ set_bit(CON_FLAG_BACKOFF, &con->flags);
}
+ goto done;
}
if (con->state == CON_STATE_STANDBY) {
@@ -2355,12 +2370,12 @@ fault:
static void ceph_fault(struct ceph_connection *con)
__releases(con->mutex)
{
- pr_err("%s%lld %s %s\n", ENTITY_NAME(con->peer_name),
+ pr_warning("%s%lld %s %s\n", ENTITY_NAME(con->peer_name),
ceph_pr_addr(&con->peer_addr.in_addr), con->error_msg);
dout("fault %p state %lu to peer %s\n",
con, con->state, ceph_pr_addr(&con->peer_addr.in_addr));
- BUG_ON(con->state != CON_STATE_CONNECTING &&
+ WARN_ON(con->state != CON_STATE_CONNECTING &&
con->state != CON_STATE_NEGOTIATING &&
con->state != CON_STATE_OPEN);
@@ -2397,24 +2412,8 @@ static void ceph_fault(struct ceph_connection *con)
con->delay = BASE_DELAY_INTERVAL;
else if (con->delay < MAX_DELAY_INTERVAL)
con->delay *= 2;
- con->ops->get(con);
- if (queue_delayed_work(ceph_msgr_wq, &con->work,
- round_jiffies_relative(con->delay))) {
- dout("fault queued %p delay %lu\n", con, con->delay);
- } else {
- con->ops->put(con);
- dout("fault failed to queue %p delay %lu, backoff\n",
- con, con->delay);
- /*
- * In many cases we see a socket state change
- * while con_work is running and end up
- * queuing (non-delayed) work, such that we
- * can't backoff with a delay. Set a flag so
- * that when con_work restarts we schedule the
- * delay then.
- */
- set_bit(CON_FLAG_BACKOFF, &con->flags);
- }
+ set_bit(CON_FLAG_BACKOFF, &con->flags);
+ queue_con(con);
}
out_unlock:
@@ -2749,7 +2748,8 @@ static int ceph_con_in_msg_alloc(struct ceph_connection *con, int *skip)
msg = con->ops->alloc_msg(con, hdr, skip);
mutex_lock(&con->mutex);
if (con->state != CON_STATE_OPEN) {
- ceph_msg_put(msg);
+ if (msg)
+ ceph_msg_put(msg);
return -EAGAIN;
}
con->in_msg = msg;
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index c1d756cc744..eb9a4447876 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -221,6 +221,7 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc,
kref_init(&req->r_kref);
init_completion(&req->r_completion);
init_completion(&req->r_safe_completion);
+ RB_CLEAR_NODE(&req->r_node);
INIT_LIST_HEAD(&req->r_unsafe_item);
INIT_LIST_HEAD(&req->r_linger_item);
INIT_LIST_HEAD(&req->r_linger_osd);
@@ -580,7 +581,7 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc,
dout("__kick_osd_requests osd%d\n", osd->o_osd);
err = __reset_osd(osdc, osd);
- if (err == -EAGAIN)
+ if (err)
return;
list_for_each_entry(req, &osd->o_requests, r_osd_item) {
@@ -607,14 +608,6 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc,
}
}
-static void kick_osd_requests(struct ceph_osd_client *osdc,
- struct ceph_osd *kickosd)
-{
- mutex_lock(&osdc->request_mutex);
- __kick_osd_requests(osdc, kickosd);
- mutex_unlock(&osdc->request_mutex);
-}
-
/*
* If the osd connection drops, we need to resubmit all requests.
*/
@@ -628,7 +621,9 @@ static void osd_reset(struct ceph_connection *con)
dout("osd_reset osd%d\n", osd->o_osd);
osdc = osd->o_osdc;
down_read(&osdc->map_sem);
- kick_osd_requests(osdc, osd);
+ mutex_lock(&osdc->request_mutex);
+ __kick_osd_requests(osdc, osd);
+ mutex_unlock(&osdc->request_mutex);
send_queued(osdc);
up_read(&osdc->map_sem);
}
@@ -647,6 +642,7 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum)
atomic_set(&osd->o_ref, 1);
osd->o_osdc = osdc;
osd->o_osd = onum;
+ RB_CLEAR_NODE(&osd->o_node);
INIT_LIST_HEAD(&osd->o_requests);
INIT_LIST_HEAD(&osd->o_linger_requests);
INIT_LIST_HEAD(&osd->o_osd_lru);
@@ -750,6 +746,7 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
if (list_empty(&osd->o_requests) &&
list_empty(&osd->o_linger_requests)) {
__remove_osd(osdc, osd);
+ ret = -ENODEV;
} else if (memcmp(&osdc->osdmap->osd_addr[osd->o_osd],
&osd->o_con.peer_addr,
sizeof(osd->o_con.peer_addr)) == 0 &&
@@ -876,9 +873,9 @@ static void __unregister_request(struct ceph_osd_client *osdc,
req->r_osd = NULL;
}
+ list_del_init(&req->r_req_lru_item);
ceph_osdc_put_request(req);
- list_del_init(&req->r_req_lru_item);
if (osdc->num_requests == 0) {
dout(" no requests, canceling timeout\n");
__cancel_osd_timeout(osdc);
@@ -910,8 +907,8 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc,
struct ceph_osd_request *req)
{
dout("__unregister_linger_request %p\n", req);
+ list_del_init(&req->r_linger_item);
if (req->r_osd) {
- list_del_init(&req->r_linger_item);
list_del_init(&req->r_linger_osd);
if (list_empty(&req->r_osd->o_requests) &&
@@ -1090,12 +1087,10 @@ static void handle_timeout(struct work_struct *work)
{
struct ceph_osd_client *osdc =
container_of(work, struct ceph_osd_client, timeout_work.work);
- struct ceph_osd_request *req, *last_req = NULL;
+ struct ceph_osd_request *req;
struct ceph_osd *osd;
- unsigned long timeout = osdc->client->options->osd_timeout * HZ;
unsigned long keepalive =
osdc->client->options->osd_keepalive_timeout * HZ;
- unsigned long last_stamp = 0;
struct list_head slow_osds;
dout("timeout\n");
down_read(&osdc->map_sem);
@@ -1105,37 +1100,6 @@ static void handle_timeout(struct work_struct *work)
mutex_lock(&osdc->request_mutex);
/*
- * reset osds that appear to be _really_ unresponsive. this
- * is a failsafe measure.. we really shouldn't be getting to
- * this point if the system is working properly. the monitors
- * should mark the osd as failed and we should find out about
- * it from an updated osd map.
- */
- while (timeout && !list_empty(&osdc->req_lru)) {
- req = list_entry(osdc->req_lru.next, struct ceph_osd_request,
- r_req_lru_item);
-
- /* hasn't been long enough since we sent it? */
- if (time_before(jiffies, req->r_stamp + timeout))
- break;
-
- /* hasn't been long enough since it was acked? */
- if (req->r_request->ack_stamp == 0 ||
- time_before(jiffies, req->r_request->ack_stamp + timeout))
- break;
-
- BUG_ON(req == last_req && req->r_stamp == last_stamp);
- last_req = req;
- last_stamp = req->r_stamp;
-
- osd = req->r_osd;
- BUG_ON(!osd);
- pr_warning(" tid %llu timed out on osd%d, will reset osd\n",
- req->r_tid, osd->o_osd);
- __kick_osd_requests(osdc, osd);
- }
-
- /*
* ping osds that are a bit slow. this ensures that if there
* is a break in the TCP connection we will notice, and reopen
* a connection with that osd (from the fault callback).
@@ -1306,7 +1270,7 @@ static void reset_changed_osds(struct ceph_osd_client *osdc)
* Requeue requests whose mapping to an OSD has changed. If requests map to
* no osd, request a new map.
*
- * Caller should hold map_sem for read and request_mutex.
+ * Caller should hold map_sem for read.
*/
static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
{
@@ -1320,6 +1284,24 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
for (p = rb_first(&osdc->requests); p; ) {
req = rb_entry(p, struct ceph_osd_request, r_node);
p = rb_next(p);
+
+ /*
+ * For linger requests that have not yet been
+ * registered, move them to the linger list; they'll
+ * be sent to the osd in the loop below. Unregister
+ * the request before re-registering it as a linger
+ * request to ensure the __map_request() below
+ * will decide it needs to be sent.
+ */
+ if (req->r_linger && list_empty(&req->r_linger_item)) {
+ dout("%p tid %llu restart on osd%d\n",
+ req, req->r_tid,
+ req->r_osd ? req->r_osd->o_osd : -1);
+ __unregister_request(osdc, req);
+ __register_linger_request(osdc, req);
+ continue;
+ }
+
err = __map_request(osdc, req, force_resend);
if (err < 0)
continue; /* error */
@@ -1334,17 +1316,6 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
req->r_flags |= CEPH_OSD_FLAG_RETRY;
}
}
- if (req->r_linger && list_empty(&req->r_linger_item)) {
- /*
- * register as a linger so that we will
- * re-submit below and get a new tid
- */
- dout("%p tid %llu restart on osd%d\n",
- req, req->r_tid,
- req->r_osd ? req->r_osd->o_osd : -1);
- __register_linger_request(osdc, req);
- __unregister_request(osdc, req);
- }
}
list_for_each_entry_safe(req, nreq, &osdc->req_linger,
@@ -1352,6 +1323,7 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
dout("linger req=%p req->r_osd=%p\n", req, req->r_osd);
err = __map_request(osdc, req, force_resend);
+ dout("__map_request returned %d\n", err);
if (err == 0)
continue; /* no change and no osd was specified */
if (err < 0)
@@ -1364,8 +1336,8 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
dout("kicking lingering %p tid %llu osd%d\n", req, req->r_tid,
req->r_osd ? req->r_osd->o_osd : -1);
- __unregister_linger_request(osdc, req);
__register_request(osdc, req);
+ __unregister_linger_request(osdc, req);
}
mutex_unlock(&osdc->request_mutex);
@@ -1373,6 +1345,7 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend)
dout("%d requests for down osds, need new map\n", needmap);
ceph_monc_request_next_osdmap(&osdc->client->monc);
}
+ reset_changed_osds(osdc);
}
@@ -1429,7 +1402,6 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)
osdc->osdmap = newmap;
}
kick_requests(osdc, 0);
- reset_changed_osds(osdc);
} else {
dout("ignoring incremental map %u len %d\n",
epoch, maplen);
@@ -1599,6 +1571,7 @@ int ceph_osdc_create_event(struct ceph_osd_client *osdc,
event->data = data;
event->osdc = osdc;
INIT_LIST_HEAD(&event->osd_node);
+ RB_CLEAR_NODE(&event->node);
kref_init(&event->kref); /* one ref for us */
kref_get(&event->kref); /* one ref for the caller */
init_completion(&event->completion);
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index 5433fb0eb3c..de73214b5d2 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -469,6 +469,22 @@ static struct ceph_pg_pool_info *__lookup_pg_pool(struct rb_root *root, int id)
return NULL;
}
+const char *ceph_pg_pool_name_by_id(struct ceph_osdmap *map, u64 id)
+{
+ struct ceph_pg_pool_info *pi;
+
+ if (id == CEPH_NOPOOL)
+ return NULL;
+
+ if (WARN_ON_ONCE(id > (u64) INT_MAX))
+ return NULL;
+
+ pi = __lookup_pg_pool(&map->pg_pools, (int) id);
+
+ return pi ? pi->name : NULL;
+}
+EXPORT_SYMBOL(ceph_pg_pool_name_by_id);
+
int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name)
{
struct rb_node *rbp;
@@ -645,10 +661,12 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end)
ceph_decode_32_safe(p, end, max, bad);
while (max--) {
ceph_decode_need(p, end, 4 + 1 + sizeof(pi->v), bad);
+ err = -ENOMEM;
pi = kzalloc(sizeof(*pi), GFP_NOFS);
if (!pi)
goto bad;
pi->id = ceph_decode_32(p);
+ err = -EINVAL;
ev = ceph_decode_8(p); /* encoding version */
if (ev > CEPH_PG_POOL_VERSION) {
pr_warning("got unknown v %d > %d of ceph_pg_pool\n",
@@ -664,8 +682,13 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end)
__insert_pg_pool(&map->pg_pools, pi);
}
- if (version >= 5 && __decode_pool_names(p, end, map) < 0)
- goto bad;
+ if (version >= 5) {
+ err = __decode_pool_names(p, end, map);
+ if (err < 0) {
+ dout("fail to decode pool names");
+ goto bad;
+ }
+ }
ceph_decode_32_safe(p, end, map->pool_max, bad);
@@ -745,7 +768,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end)
return map;
bad:
- dout("osdmap_decode fail\n");
+ dout("osdmap_decode fail err %d\n", err);
ceph_osdmap_destroy(map);
return ERR_PTR(err);
}
@@ -839,6 +862,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
if (ev > CEPH_PG_POOL_VERSION) {
pr_warning("got unknown v %d > %d of ceph_pg_pool\n",
ev, CEPH_PG_POOL_VERSION);
+ err = -EINVAL;
goto bad;
}
pi = __lookup_pg_pool(&map->pg_pools, pool);
@@ -855,8 +879,11 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
if (err < 0)
goto bad;
}
- if (version >= 5 && __decode_pool_names(p, end, map) < 0)
- goto bad;
+ if (version >= 5) {
+ err = __decode_pool_names(p, end, map);
+ if (err < 0)
+ goto bad;
+ }
/* old_pool */
ceph_decode_32_safe(p, end, len, bad);
@@ -932,15 +959,13 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end,
(void) __remove_pg_mapping(&map->pg_temp, pgid);
/* insert */
- if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32)) {
- err = -EINVAL;
+ err = -EINVAL;
+ if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32))
goto bad;
- }
+ err = -ENOMEM;
pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS);
- if (!pg) {
- err = -ENOMEM;
+ if (!pg)
goto bad;
- }
pg->pgid = pgid;
pg->len = pglen;
for (j = 0; j < pglen; j++)
diff --git a/net/core/dev.c b/net/core/dev.c
index 09cb3f6dc40..f64e439b4a0 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -176,8 +176,10 @@
#define PTYPE_HASH_MASK (PTYPE_HASH_SIZE - 1)
static DEFINE_SPINLOCK(ptype_lock);
+static DEFINE_SPINLOCK(offload_lock);
static struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
static struct list_head ptype_all __read_mostly; /* Taps */
+static struct list_head offload_base __read_mostly;
/*
* The @dev_base_head list is protected by @dev_base_lock and the rtnl
@@ -201,6 +203,8 @@ static struct list_head ptype_all __read_mostly; /* Taps */
DEFINE_RWLOCK(dev_base_lock);
EXPORT_SYMBOL(dev_base_lock);
+seqcount_t devnet_rename_seq;
+
static inline void dev_base_seq_inc(struct net *net)
{
while (++net->dev_base_seq == 0);
@@ -470,6 +474,82 @@ void dev_remove_pack(struct packet_type *pt)
}
EXPORT_SYMBOL(dev_remove_pack);
+
+/**
+ * dev_add_offload - register offload handlers
+ * @po: protocol offload declaration
+ *
+ * Add protocol offload handlers to the networking stack. The passed
+ * &proto_offload is linked into kernel lists and may not be freed until
+ * it has been removed from the kernel lists.
+ *
+ * This call does not sleep therefore it can not
+ * guarantee all CPU's that are in middle of receiving packets
+ * will see the new offload handlers (until the next received packet).
+ */
+void dev_add_offload(struct packet_offload *po)
+{
+ struct list_head *head = &offload_base;
+
+ spin_lock(&offload_lock);
+ list_add_rcu(&po->list, head);
+ spin_unlock(&offload_lock);
+}
+EXPORT_SYMBOL(dev_add_offload);
+
+/**
+ * __dev_remove_offload - remove offload handler
+ * @po: packet offload declaration
+ *
+ * Remove a protocol offload handler that was previously added to the
+ * kernel offload handlers by dev_add_offload(). The passed &offload_type
+ * is removed from the kernel lists and can be freed or reused once this
+ * function returns.
+ *
+ * The packet type might still be in use by receivers
+ * and must not be freed until after all the CPU's have gone
+ * through a quiescent state.
+ */
+void __dev_remove_offload(struct packet_offload *po)
+{
+ struct list_head *head = &offload_base;
+ struct packet_offload *po1;
+
+ spin_lock(&offload_lock);
+
+ list_for_each_entry(po1, head, list) {
+ if (po == po1) {
+ list_del_rcu(&po->list);
+ goto out;
+ }
+ }
+
+ pr_warn("dev_remove_offload: %p not found\n", po);
+out:
+ spin_unlock(&offload_lock);
+}
+EXPORT_SYMBOL(__dev_remove_offload);
+
+/**
+ * dev_remove_offload - remove packet offload handler
+ * @po: packet offload declaration
+ *
+ * Remove a packet offload handler that was previously added to the kernel
+ * offload handlers by dev_add_offload(). The passed &offload_type is
+ * removed from the kernel lists and can be freed or reused once this
+ * function returns.
+ *
+ * This call sleeps to guarantee that no CPU is looking at the packet
+ * type after return.
+ */
+void dev_remove_offload(struct packet_offload *po)
+{
+ __dev_remove_offload(po);
+
+ synchronize_net();
+}
+EXPORT_SYMBOL(dev_remove_offload);
+
/******************************************************************************
Device Boot-time Settings Routines
@@ -1013,22 +1093,31 @@ int dev_change_name(struct net_device *dev, const char *newname)
if (dev->flags & IFF_UP)
return -EBUSY;
- if (strncmp(newname, dev->name, IFNAMSIZ) == 0)
+ write_seqcount_begin(&devnet_rename_seq);
+
+ if (strncmp(newname, dev->name, IFNAMSIZ) == 0) {
+ write_seqcount_end(&devnet_rename_seq);
return 0;
+ }
memcpy(oldname, dev->name, IFNAMSIZ);
err = dev_get_valid_name(net, dev, newname);
- if (err < 0)
+ if (err < 0) {
+ write_seqcount_end(&devnet_rename_seq);
return err;
+ }
rollback:
ret = device_rename(&dev->dev, dev->name);
if (ret) {
memcpy(dev->name, oldname, IFNAMSIZ);
+ write_seqcount_end(&devnet_rename_seq);
return ret;
}
+ write_seqcount_end(&devnet_rename_seq);
+
write_lock_bh(&dev_base_lock);
hlist_del_rcu(&dev->name_hlist);
write_unlock_bh(&dev_base_lock);
@@ -1046,6 +1135,7 @@ rollback:
/* err >= 0 after dev_alloc_name() or stores the first errno */
if (err >= 0) {
err = ret;
+ write_seqcount_begin(&devnet_rename_seq);
memcpy(dev->name, oldname, IFNAMSIZ);
goto rollback;
} else {
@@ -1075,10 +1165,8 @@ int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
return -EINVAL;
if (!len) {
- if (dev->ifalias) {
- kfree(dev->ifalias);
- dev->ifalias = NULL;
- }
+ kfree(dev->ifalias);
+ dev->ifalias = NULL;
return 0;
}
@@ -1666,7 +1754,7 @@ static inline int deliver_skb(struct sk_buff *skb,
static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb)
{
- if (ptype->af_packet_priv == NULL)
+ if (!ptype->af_packet_priv || !skb->sk)
return false;
if (ptype->id_match)
@@ -1994,7 +2082,7 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
- struct packet_type *ptype;
+ struct packet_offload *ptype;
__be16 type = skb->protocol;
int vlan_depth = ETH_HLEN;
int err;
@@ -2023,18 +2111,17 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb,
}
rcu_read_lock();
- list_for_each_entry_rcu(ptype,
- &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) {
- if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
+ list_for_each_entry_rcu(ptype, &offload_base, list) {
+ if (ptype->type == type && ptype->callbacks.gso_segment) {
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
- err = ptype->gso_send_check(skb);
+ err = ptype->callbacks.gso_send_check(skb);
segs = ERR_PTR(err);
if (err || skb_gso_ok(skb, features))
break;
__skb_push(skb, (skb->data -
skb_network_header(skb)));
}
- segs = ptype->gso_segment(skb, features);
+ segs = ptype->callbacks.gso_segment(skb, features);
break;
}
}
@@ -2237,6 +2324,13 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
skb->vlan_tci = 0;
}
+ /* If encapsulation offload request, verify we are testing
+ * hardware encapsulation features instead of standard
+ * features for the netdev
+ */
+ if (skb->encapsulation)
+ features &= dev->hw_enc_features;
+
if (netif_needs_gso(skb, features)) {
if (unlikely(dev_gso_segment(skb, features)))
goto out_kfree_skb;
@@ -2252,8 +2346,12 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
* checksumming here.
*/
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- skb_set_transport_header(skb,
- skb_checksum_start_offset(skb));
+ if (skb->encapsulation)
+ skb_set_inner_transport_header(skb,
+ skb_checksum_start_offset(skb));
+ else
+ skb_set_transport_header(skb,
+ skb_checksum_start_offset(skb));
if (!(features & NETIF_F_ALL_CSUM) &&
skb_checksum_help(skb))
goto out_kfree_skb;
@@ -2818,8 +2916,10 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
if (unlikely(tcpu != next_cpu) &&
(tcpu == RPS_NO_CPU || !cpu_online(tcpu) ||
((int)(per_cpu(softnet_data, tcpu).input_queue_head -
- rflow->last_qtail)) >= 0))
+ rflow->last_qtail)) >= 0)) {
+ tcpu = next_cpu;
rflow = set_rps_cpu(dev, skb, rflow, next_cpu);
+ }
if (tcpu != RPS_NO_CPU && cpu_online(tcpu)) {
*rflowp = rflow;
@@ -3444,11 +3544,13 @@ static void flush_backlog(void *arg)
static int napi_gro_complete(struct sk_buff *skb)
{
- struct packet_type *ptype;
+ struct packet_offload *ptype;
__be16 type = skb->protocol;
- struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+ struct list_head *head = &offload_base;
int err = -ENOENT;
+ BUILD_BUG_ON(sizeof(struct napi_gro_cb) > sizeof(skb->cb));
+
if (NAPI_GRO_CB(skb)->count == 1) {
skb_shinfo(skb)->gso_size = 0;
goto out;
@@ -3456,10 +3558,10 @@ static int napi_gro_complete(struct sk_buff *skb)
rcu_read_lock();
list_for_each_entry_rcu(ptype, head, list) {
- if (ptype->type != type || ptype->dev || !ptype->gro_complete)
+ if (ptype->type != type || !ptype->callbacks.gro_complete)
continue;
- err = ptype->gro_complete(skb);
+ err = ptype->callbacks.gro_complete(skb);
break;
}
rcu_read_unlock();
@@ -3503,12 +3605,34 @@ void napi_gro_flush(struct napi_struct *napi, bool flush_old)
}
EXPORT_SYMBOL(napi_gro_flush);
-enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
+static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
+{
+ struct sk_buff *p;
+ unsigned int maclen = skb->dev->hard_header_len;
+
+ for (p = napi->gro_list; p; p = p->next) {
+ unsigned long diffs;
+
+ diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev;
+ diffs |= p->vlan_tci ^ skb->vlan_tci;
+ if (maclen == ETH_HLEN)
+ diffs |= compare_ether_header(skb_mac_header(p),
+ skb_gro_mac_header(skb));
+ else if (!diffs)
+ diffs = memcmp(skb_mac_header(p),
+ skb_gro_mac_header(skb),
+ maclen);
+ NAPI_GRO_CB(p)->same_flow = !diffs;
+ NAPI_GRO_CB(p)->flush = 0;
+ }
+}
+
+static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff **pp = NULL;
- struct packet_type *ptype;
+ struct packet_offload *ptype;
__be16 type = skb->protocol;
- struct list_head *head = &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
+ struct list_head *head = &offload_base;
int same_flow;
int mac_len;
enum gro_result ret;
@@ -3519,9 +3643,11 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
if (skb_is_gso(skb) || skb_has_frag_list(skb))
goto normal;
+ gro_list_prepare(napi, skb);
+
rcu_read_lock();
list_for_each_entry_rcu(ptype, head, list) {
- if (ptype->type != type || ptype->dev || !ptype->gro_receive)
+ if (ptype->type != type || !ptype->callbacks.gro_receive)
continue;
skb_set_network_header(skb, skb_gro_offset(skb));
@@ -3531,7 +3657,7 @@ enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
NAPI_GRO_CB(skb)->flush = 0;
NAPI_GRO_CB(skb)->free = 0;
- pp = ptype->gro_receive(&napi->gro_list, skb);
+ pp = ptype->callbacks.gro_receive(&napi->gro_list, skb);
break;
}
rcu_read_unlock();
@@ -3594,34 +3720,9 @@ normal:
ret = GRO_NORMAL;
goto pull;
}
-EXPORT_SYMBOL(dev_gro_receive);
-
-static inline gro_result_t
-__napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
-{
- struct sk_buff *p;
- unsigned int maclen = skb->dev->hard_header_len;
-
- for (p = napi->gro_list; p; p = p->next) {
- unsigned long diffs;
-
- diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev;
- diffs |= p->vlan_tci ^ skb->vlan_tci;
- if (maclen == ETH_HLEN)
- diffs |= compare_ether_header(skb_mac_header(p),
- skb_gro_mac_header(skb));
- else if (!diffs)
- diffs = memcmp(skb_mac_header(p),
- skb_gro_mac_header(skb),
- maclen);
- NAPI_GRO_CB(p)->same_flow = !diffs;
- NAPI_GRO_CB(p)->flush = 0;
- }
- return dev_gro_receive(napi, skb);
-}
-gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
+static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
{
switch (ret) {
case GRO_NORMAL:
@@ -3647,7 +3748,6 @@ gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
return ret;
}
-EXPORT_SYMBOL(napi_skb_finish);
static void skb_gro_reset_offset(struct sk_buff *skb)
{
@@ -3670,7 +3770,7 @@ gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
skb_gro_reset_offset(skb);
- return napi_skb_finish(__napi_gro_receive(napi, skb), skb);
+ return napi_skb_finish(dev_gro_receive(napi, skb), skb);
}
EXPORT_SYMBOL(napi_gro_receive);
@@ -3699,7 +3799,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
}
EXPORT_SYMBOL(napi_get_frags);
-gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb,
+static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb,
gro_result_t ret)
{
switch (ret) {
@@ -3724,7 +3824,6 @@ gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb,
return ret;
}
-EXPORT_SYMBOL(napi_frags_finish);
static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
@@ -3769,7 +3868,7 @@ gro_result_t napi_gro_frags(struct napi_struct *napi)
if (!skb)
return GRO_DROP;
- return napi_frags_finish(napi, skb, __napi_gro_receive(napi, skb));
+ return napi_frags_finish(napi, skb, dev_gro_receive(napi, skb));
}
EXPORT_SYMBOL(napi_gro_frags);
@@ -4071,6 +4170,7 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
{
struct net_device *dev;
struct ifreq ifr;
+ unsigned seq;
/*
* Fetch the caller's info block.
@@ -4079,6 +4179,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
return -EFAULT;
+retry:
+ seq = read_seqcount_begin(&devnet_rename_seq);
rcu_read_lock();
dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex);
if (!dev) {
@@ -4088,6 +4190,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg)
strcpy(ifr.ifr_name, dev->name);
rcu_read_unlock();
+ if (read_seqcount_retry(&devnet_rename_seq, seq))
+ goto retry;
if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
return -EFAULT;
@@ -4880,7 +4984,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
else
dev->mtu = new_mtu;
- if (!err && dev->flags & IFF_UP)
+ if (!err)
call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
return err;
}
@@ -5200,7 +5304,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCGMIIPHY:
case SIOCGMIIREG:
case SIOCSIFNAME:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
dev_load(net, ifr.ifr_name);
rtnl_lock();
@@ -5221,16 +5325,25 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
* - require strict serialization.
* - do not return a value
*/
+ case SIOCSIFMAP:
+ case SIOCSIFTXQLEN:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ /* fall through */
+ /*
+ * These ioctl calls:
+ * - require local superuser power.
+ * - require strict serialization.
+ * - do not return a value
+ */
case SIOCSIFFLAGS:
case SIOCSIFMETRIC:
case SIOCSIFMTU:
- case SIOCSIFMAP:
case SIOCSIFHWADDR:
case SIOCSIFSLAVE:
case SIOCADDMULTI:
case SIOCDELMULTI:
case SIOCSIFHWBROADCAST:
- case SIOCSIFTXQLEN:
case SIOCSMIIREG:
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
@@ -5239,7 +5352,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCBRADDIF:
case SIOCBRDELIF:
case SIOCSHWTSTAMP:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* fall through */
case SIOCBONDSLAVEINFOQUERY:
@@ -6008,6 +6121,14 @@ struct netdev_queue *dev_ingress_queue_create(struct net_device *dev)
static const struct ethtool_ops default_ethtool_ops;
+void netdev_set_default_ethtool_ops(struct net_device *dev,
+ const struct ethtool_ops *ops)
+{
+ if (dev->ethtool_ops == &default_ethtool_ops)
+ dev->ethtool_ops = ops;
+}
+EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops);
+
/**
* alloc_netdev_mqs - allocate network device
* @sizeof_priv: size of private data to allocate space for
@@ -6264,7 +6385,6 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
goto out;
/* Ensure the device has been registrered */
- err = -EINVAL;
if (dev->reg_state != NETREG_REGISTERED)
goto out;
@@ -6319,6 +6439,9 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
dev_uc_flush(dev);
dev_mc_flush(dev);
+ /* Send a netdev-removed uevent to the old namespace */
+ kobject_uevent(&dev->dev.kobj, KOBJ_REMOVE);
+
/* Actually switch the network namespace */
dev_net_set(dev, net);
@@ -6330,6 +6453,9 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
dev->iflink = dev->ifindex;
}
+ /* Send a netdev-add uevent to the new namespace */
+ kobject_uevent(&dev->dev.kobj, KOBJ_ADD);
+
/* Fixup kobjects */
err = device_rename(&dev->dev, dev->name);
WARN_ON(err);
@@ -6662,6 +6788,8 @@ static int __init net_dev_init(void)
for (i = 0; i < PTYPE_HASH_SIZE; i++)
INIT_LIST_HEAD(&ptype_base[i]);
+ INIT_LIST_HEAD(&offload_base);
+
if (register_pernet_subsys(&netdev_net_ops))
goto out;
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 87cc17db2d5..b079c7bbc15 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -319,7 +319,8 @@ int dev_addr_del(struct net_device *dev, const unsigned char *addr,
*/
ha = list_first_entry(&dev->dev_addrs.list,
struct netdev_hw_addr, list);
- if (ha->addr == dev->dev_addr && ha->refcount == 1)
+ if (!memcmp(ha->addr, addr, dev->addr_len) &&
+ ha->type == addr_type && ha->refcount == 1)
return -ENOENT;
err = __hw_addr_del(&dev->dev_addrs, addr, dev->addr_len,
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 4d64cc2e3fa..a8705432e4b 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -1460,7 +1460,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_GEEE:
break;
default:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
}
diff --git a/net/core/filter.c b/net/core/filter.c
index 3d92ebb7fbc..c23543cba13 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -39,6 +39,7 @@
#include <linux/reciprocal_div.h>
#include <linux/ratelimit.h>
#include <linux/seccomp.h>
+#include <linux/if_vlan.h>
/* No hurry in this branch
*
@@ -341,6 +342,12 @@ load_b:
case BPF_S_ANC_CPU:
A = raw_smp_processor_id();
continue;
+ case BPF_S_ANC_VLAN_TAG:
+ A = vlan_tx_tag_get(skb);
+ continue;
+ case BPF_S_ANC_VLAN_TAG_PRESENT:
+ A = !!vlan_tx_tag_present(skb);
+ continue;
case BPF_S_ANC_NLATTR: {
struct nlattr *nla;
@@ -600,6 +607,8 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
ANCILLARY(RXHASH);
ANCILLARY(CPU);
ANCILLARY(ALU_XOR_X);
+ ANCILLARY(VLAN_TAG);
+ ANCILLARY(VLAN_TAG_PRESENT);
}
}
ftest->code = code;
@@ -751,3 +760,133 @@ int sk_detach_filter(struct sock *sk)
return ret;
}
EXPORT_SYMBOL_GPL(sk_detach_filter);
+
+static void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to)
+{
+ static const u16 decodes[] = {
+ [BPF_S_ALU_ADD_K] = BPF_ALU|BPF_ADD|BPF_K,
+ [BPF_S_ALU_ADD_X] = BPF_ALU|BPF_ADD|BPF_X,
+ [BPF_S_ALU_SUB_K] = BPF_ALU|BPF_SUB|BPF_K,
+ [BPF_S_ALU_SUB_X] = BPF_ALU|BPF_SUB|BPF_X,
+ [BPF_S_ALU_MUL_K] = BPF_ALU|BPF_MUL|BPF_K,
+ [BPF_S_ALU_MUL_X] = BPF_ALU|BPF_MUL|BPF_X,
+ [BPF_S_ALU_DIV_X] = BPF_ALU|BPF_DIV|BPF_X,
+ [BPF_S_ALU_MOD_K] = BPF_ALU|BPF_MOD|BPF_K,
+ [BPF_S_ALU_MOD_X] = BPF_ALU|BPF_MOD|BPF_X,
+ [BPF_S_ALU_AND_K] = BPF_ALU|BPF_AND|BPF_K,
+ [BPF_S_ALU_AND_X] = BPF_ALU|BPF_AND|BPF_X,
+ [BPF_S_ALU_OR_K] = BPF_ALU|BPF_OR|BPF_K,
+ [BPF_S_ALU_OR_X] = BPF_ALU|BPF_OR|BPF_X,
+ [BPF_S_ALU_XOR_K] = BPF_ALU|BPF_XOR|BPF_K,
+ [BPF_S_ALU_XOR_X] = BPF_ALU|BPF_XOR|BPF_X,
+ [BPF_S_ALU_LSH_K] = BPF_ALU|BPF_LSH|BPF_K,
+ [BPF_S_ALU_LSH_X] = BPF_ALU|BPF_LSH|BPF_X,
+ [BPF_S_ALU_RSH_K] = BPF_ALU|BPF_RSH|BPF_K,
+ [BPF_S_ALU_RSH_X] = BPF_ALU|BPF_RSH|BPF_X,
+ [BPF_S_ALU_NEG] = BPF_ALU|BPF_NEG,
+ [BPF_S_LD_W_ABS] = BPF_LD|BPF_W|BPF_ABS,
+ [BPF_S_LD_H_ABS] = BPF_LD|BPF_H|BPF_ABS,
+ [BPF_S_LD_B_ABS] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_PROTOCOL] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_PKTTYPE] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_IFINDEX] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_NLATTR] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_NLATTR_NEST] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_MARK] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_QUEUE] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_HATYPE] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_RXHASH] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_CPU] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_ALU_XOR_X] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_SECCOMP_LD_W] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_VLAN_TAG] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_ANC_VLAN_TAG_PRESENT] = BPF_LD|BPF_B|BPF_ABS,
+ [BPF_S_LD_W_LEN] = BPF_LD|BPF_W|BPF_LEN,
+ [BPF_S_LD_W_IND] = BPF_LD|BPF_W|BPF_IND,
+ [BPF_S_LD_H_IND] = BPF_LD|BPF_H|BPF_IND,
+ [BPF_S_LD_B_IND] = BPF_LD|BPF_B|BPF_IND,
+ [BPF_S_LD_IMM] = BPF_LD|BPF_IMM,
+ [BPF_S_LDX_W_LEN] = BPF_LDX|BPF_W|BPF_LEN,
+ [BPF_S_LDX_B_MSH] = BPF_LDX|BPF_B|BPF_MSH,
+ [BPF_S_LDX_IMM] = BPF_LDX|BPF_IMM,
+ [BPF_S_MISC_TAX] = BPF_MISC|BPF_TAX,
+ [BPF_S_MISC_TXA] = BPF_MISC|BPF_TXA,
+ [BPF_S_RET_K] = BPF_RET|BPF_K,
+ [BPF_S_RET_A] = BPF_RET|BPF_A,
+ [BPF_S_ALU_DIV_K] = BPF_ALU|BPF_DIV|BPF_K,
+ [BPF_S_LD_MEM] = BPF_LD|BPF_MEM,
+ [BPF_S_LDX_MEM] = BPF_LDX|BPF_MEM,
+ [BPF_S_ST] = BPF_ST,
+ [BPF_S_STX] = BPF_STX,
+ [BPF_S_JMP_JA] = BPF_JMP|BPF_JA,
+ [BPF_S_JMP_JEQ_K] = BPF_JMP|BPF_JEQ|BPF_K,
+ [BPF_S_JMP_JEQ_X] = BPF_JMP|BPF_JEQ|BPF_X,
+ [BPF_S_JMP_JGE_K] = BPF_JMP|BPF_JGE|BPF_K,
+ [BPF_S_JMP_JGE_X] = BPF_JMP|BPF_JGE|BPF_X,
+ [BPF_S_JMP_JGT_K] = BPF_JMP|BPF_JGT|BPF_K,
+ [BPF_S_JMP_JGT_X] = BPF_JMP|BPF_JGT|BPF_X,
+ [BPF_S_JMP_JSET_K] = BPF_JMP|BPF_JSET|BPF_K,
+ [BPF_S_JMP_JSET_X] = BPF_JMP|BPF_JSET|BPF_X,
+ };
+ u16 code;
+
+ code = filt->code;
+
+ to->code = decodes[code];
+ to->jt = filt->jt;
+ to->jf = filt->jf;
+
+ if (code == BPF_S_ALU_DIV_K) {
+ /*
+ * When loaded this rule user gave us X, which was
+ * translated into R = r(X). Now we calculate the
+ * RR = r(R) and report it back. If next time this
+ * value is loaded and RRR = r(RR) is calculated
+ * then the R == RRR will be true.
+ *
+ * One exception. X == 1 translates into R == 0 and
+ * we can't calculate RR out of it with r().
+ */
+
+ if (filt->k == 0)
+ to->k = 1;
+ else
+ to->k = reciprocal_value(filt->k);
+
+ BUG_ON(reciprocal_value(to->k) != filt->k);
+ } else
+ to->k = filt->k;
+}
+
+int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len)
+{
+ struct sk_filter *filter;
+ int i, ret;
+
+ lock_sock(sk);
+ filter = rcu_dereference_protected(sk->sk_filter,
+ sock_owned_by_user(sk));
+ ret = 0;
+ if (!filter)
+ goto out;
+ ret = filter->len;
+ if (!len)
+ goto out;
+ ret = -EINVAL;
+ if (len < filter->len)
+ goto out;
+
+ ret = -EFAULT;
+ for (i = 0; i < filter->len; i++) {
+ struct sock_filter fb;
+
+ sk_decode_filter(&filter->insns[i], &fb);
+ if (copy_to_user(&ubuf[i], &fb, sizeof(fb)))
+ goto out;
+ }
+
+ ret = filter->len;
+out:
+ release_sock(sk);
+ return ret;
+}
diff --git a/net/core/flow.c b/net/core/flow.c
index e318c7e9804..b0901ee5a00 100644
--- a/net/core/flow.c
+++ b/net/core/flow.c
@@ -327,11 +327,9 @@ static void flow_cache_flush_tasklet(unsigned long data)
static void flow_cache_flush_per_cpu(void *data)
{
struct flow_flush_info *info = data;
- int cpu;
struct tasklet_struct *tasklet;
- cpu = smp_processor_id();
- tasklet = &per_cpu_ptr(info->cache->percpu, cpu)->flush_tasklet;
+ tasklet = this_cpu_ptr(&info->cache->percpu->flush_tasklet);
tasklet->data = (unsigned long)info;
tasklet_schedule(tasklet);
}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 22571488730..c815f285e5a 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -1787,8 +1787,7 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, parms->queue_len_bytes) ||
/* approximative value for deprecated QUEUE_LEN (in packets) */
nla_put_u32(skb, NDTPA_QUEUE_LEN,
- DIV_ROUND_UP(parms->queue_len_bytes,
- SKB_TRUESIZE(ETH_FRAME_LEN))) ||
+ parms->queue_len_bytes / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
nla_put_u32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen) ||
nla_put_u32(skb, NDTPA_APP_PROBES, parms->app_probes) ||
nla_put_u32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes) ||
@@ -2770,6 +2769,8 @@ EXPORT_SYMBOL(neigh_app_ns);
#endif /* CONFIG_ARPD */
#ifdef CONFIG_SYSCTL
+static int zero;
+static int unres_qlen_max = INT_MAX / SKB_TRUESIZE(ETH_FRAME_LEN);
static int proc_unres_qlen(ctl_table *ctl, int write, void __user *buffer,
size_t *lenp, loff_t *ppos)
@@ -2777,9 +2778,13 @@ static int proc_unres_qlen(ctl_table *ctl, int write, void __user *buffer,
int size, ret;
ctl_table tmp = *ctl;
+ tmp.extra1 = &zero;
+ tmp.extra2 = &unres_qlen_max;
tmp.data = &size;
- size = DIV_ROUND_UP(*(int *)ctl->data, SKB_TRUESIZE(ETH_FRAME_LEN));
- ret = proc_dointvec(&tmp, write, buffer, lenp, ppos);
+
+ size = *(int *)ctl->data / SKB_TRUESIZE(ETH_FRAME_LEN);
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
if (write && !ret)
*(int *)ctl->data = size * SKB_TRUESIZE(ETH_FRAME_LEN);
return ret;
@@ -2865,7 +2870,8 @@ static struct neigh_sysctl_table {
.procname = "unres_qlen_bytes",
.maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = proc_dointvec,
+ .extra1 = &zero,
+ .proc_handler = proc_dointvec_minmax,
},
[NEIGH_VAR_PROXY_QLEN] = {
.procname = "proxy_qlen",
@@ -2987,6 +2993,10 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].extra1 = dev;
}
+ /* Don't export sysctls to unprivileged users */
+ if (neigh_parms_net(p)->user_ns != &init_user_ns)
+ t->neigh_vars[0].procname = NULL;
+
snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s",
p_name, dev_name_source);
t->sysctl_header =
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index bcf02f608cb..28c5f5aa7ca 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -18,11 +18,9 @@
#include <net/sock.h>
#include <net/net_namespace.h>
#include <linux/rtnetlink.h>
-#include <linux/wireless.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
#include <linux/jiffies.h>
-#include <net/wext.h>
#include "net-sysfs.h"
@@ -73,11 +71,12 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len,
int (*set)(struct net_device *, unsigned long))
{
- struct net_device *net = to_net_dev(dev);
+ struct net_device *netdev = to_net_dev(dev);
+ struct net *net = dev_net(netdev);
unsigned long new;
int ret = -EINVAL;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
ret = kstrtoul(buf, 0, &new);
@@ -87,8 +86,8 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr,
if (!rtnl_trylock())
return restart_syscall();
- if (dev_isalive(net)) {
- if ((ret = (*set)(net, new)) == 0)
+ if (dev_isalive(netdev)) {
+ if ((ret = (*set)(netdev, new)) == 0)
ret = len;
}
rtnl_unlock();
@@ -264,6 +263,9 @@ static ssize_t store_tx_queue_len(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
return netdev_store(dev, attr, buf, len, change_tx_queue_len);
}
@@ -271,10 +273,11 @@ static ssize_t store_ifalias(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
struct net_device *netdev = to_net_dev(dev);
+ struct net *net = dev_net(netdev);
size_t count = len;
ssize_t ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* ignore trailing newline */
@@ -429,6 +432,17 @@ static struct attribute_group netstat_group = {
.name = "statistics",
.attrs = netstat_attrs,
};
+
+#if IS_ENABLED(CONFIG_WIRELESS_EXT) || IS_ENABLED(CONFIG_CFG80211)
+static struct attribute *wireless_attrs[] = {
+ NULL
+};
+
+static struct attribute_group wireless_group = {
+ .name = "wireless",
+ .attrs = wireless_attrs,
+};
+#endif
#endif /* CONFIG_SYSFS */
#ifdef CONFIG_RPS
@@ -1320,7 +1334,6 @@ struct kobj_ns_type_operations net_ns_type_operations = {
};
EXPORT_SYMBOL_GPL(net_ns_type_operations);
-#ifdef CONFIG_HOTPLUG
static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
{
struct net_device *dev = to_net_dev(d);
@@ -1339,7 +1352,6 @@ static int netdev_uevent(struct device *d, struct kobj_uevent_env *env)
exit:
return retval;
}
-#endif
/*
* netdev_release -- destroy and free a dead device.
@@ -1368,9 +1380,7 @@ static struct class net_class = {
#ifdef CONFIG_SYSFS
.dev_attrs = net_class_attributes,
#endif /* CONFIG_SYSFS */
-#ifdef CONFIG_HOTPLUG
.dev_uevent = netdev_uevent,
-#endif
.ns_type = &net_ns_type_operations,
.namespace = net_namespace,
};
@@ -1409,6 +1419,15 @@ int netdev_register_kobject(struct net_device *net)
groups++;
*groups++ = &netstat_group;
+
+#if IS_ENABLED(CONFIG_WIRELESS_EXT) || IS_ENABLED(CONFIG_CFG80211)
+ if (net->ieee80211_ptr)
+ *groups++ = &wireless_group;
+#if IS_ENABLED(CONFIG_WIRELESS_EXT)
+ else if (net->wireless_handlers)
+ *groups++ = &wireless_group;
+#endif
+#endif
#endif /* CONFIG_SYSFS */
error = device_add(dev);
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 42f1e1c7514..8acce01b6da 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -13,6 +13,7 @@
#include <linux/proc_fs.h>
#include <linux/file.h>
#include <linux/export.h>
+#include <linux/user_namespace.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
@@ -145,7 +146,7 @@ static void ops_free_list(const struct pernet_operations *ops,
/*
* setup_net runs the initializers for the network namespace object.
*/
-static __net_init int setup_net(struct net *net)
+static __net_init int setup_net(struct net *net, struct user_namespace *user_ns)
{
/* Must be called with net_mutex held */
const struct pernet_operations *ops, *saved_ops;
@@ -155,6 +156,7 @@ static __net_init int setup_net(struct net *net)
atomic_set(&net->count, 1);
atomic_set(&net->passive, 1);
net->dev_base_seq = 1;
+ net->user_ns = user_ns;
#ifdef NETNS_REFCNT_DEBUG
atomic_set(&net->use_count, 0);
@@ -232,7 +234,8 @@ void net_drop_ns(void *p)
net_free(ns);
}
-struct net *copy_net_ns(unsigned long flags, struct net *old_net)
+struct net *copy_net_ns(unsigned long flags,
+ struct user_namespace *user_ns, struct net *old_net)
{
struct net *net;
int rv;
@@ -243,8 +246,11 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
net = net_alloc();
if (!net)
return ERR_PTR(-ENOMEM);
+
+ get_user_ns(user_ns);
+
mutex_lock(&net_mutex);
- rv = setup_net(net);
+ rv = setup_net(net, user_ns);
if (rv == 0) {
rtnl_lock();
list_add_tail_rcu(&net->list, &net_namespace_list);
@@ -252,6 +258,7 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
}
mutex_unlock(&net_mutex);
if (rv < 0) {
+ put_user_ns(user_ns);
net_drop_ns(net);
return ERR_PTR(rv);
}
@@ -308,6 +315,7 @@ static void cleanup_net(struct work_struct *work)
/* Finally it is safe to free my network namespace structure */
list_for_each_entry_safe(net, tmp, &net_exit_list, exit_list) {
list_del_init(&net->exit_list);
+ put_user_ns(net->user_ns);
net_drop_ns(net);
}
}
@@ -347,13 +355,6 @@ struct net *get_net_ns_by_fd(int fd)
}
#else
-struct net *copy_net_ns(unsigned long flags, struct net *old_net)
-{
- if (flags & CLONE_NEWNET)
- return ERR_PTR(-EINVAL);
- return old_net;
-}
-
struct net *get_net_ns_by_fd(int fd)
{
return ERR_PTR(-EINVAL);
@@ -380,6 +381,21 @@ struct net *get_net_ns_by_pid(pid_t pid)
}
EXPORT_SYMBOL_GPL(get_net_ns_by_pid);
+static __net_init int net_ns_net_init(struct net *net)
+{
+ return proc_alloc_inum(&net->proc_inum);
+}
+
+static __net_exit void net_ns_net_exit(struct net *net)
+{
+ proc_free_inum(net->proc_inum);
+}
+
+static struct pernet_operations __net_initdata net_ns_ops = {
+ .init = net_ns_net_init,
+ .exit = net_ns_net_exit,
+};
+
static int __init net_ns_init(void)
{
struct net_generic *ng;
@@ -402,7 +418,7 @@ static int __init net_ns_init(void)
rcu_assign_pointer(init_net.gen, ng);
mutex_lock(&net_mutex);
- if (setup_net(&init_net))
+ if (setup_net(&init_net, &init_user_ns))
panic("Could not setup the initial network namespace");
rtnl_lock();
@@ -411,6 +427,8 @@ static int __init net_ns_init(void)
mutex_unlock(&net_mutex);
+ register_pernet_subsys(&net_ns_ops);
+
return 0;
}
@@ -629,16 +647,29 @@ static void netns_put(void *ns)
static int netns_install(struct nsproxy *nsproxy, void *ns)
{
+ struct net *net = ns;
+
+ if (!ns_capable(net->user_ns, CAP_SYS_ADMIN) ||
+ !nsown_capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
put_net(nsproxy->net_ns);
- nsproxy->net_ns = get_net(ns);
+ nsproxy->net_ns = get_net(net);
return 0;
}
+static unsigned int netns_inum(void *ns)
+{
+ struct net *net = ns;
+ return net->proc_inum;
+}
+
const struct proc_ns_operations netns_operations = {
.name = "net",
.type = CLONE_NEWNET,
.get = netns_get,
.put = netns_put,
.install = netns_install,
+ .inum = netns_inum,
};
#endif
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 77a0388fc3b..3151acf5ec1 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -674,7 +674,8 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
if ((delim = strchr(cur, '@')) == NULL)
goto parse_failed;
*delim = 0;
- np->local_port = simple_strtol(cur, NULL, 10);
+ if (kstrtou16(cur, 10, &np->local_port))
+ goto parse_failed;
cur = delim;
}
cur++;
@@ -705,7 +706,8 @@ int netpoll_parse_options(struct netpoll *np, char *opt)
*delim = 0;
if (*cur == ' ' || *cur == '\t')
np_info(np, "warning: whitespace is not allowed\n");
- np->remote_port = simple_strtol(cur, NULL, 10);
+ if (kstrtou16(cur, 10, &np->remote_port))
+ goto parse_failed;
cur = delim;
}
cur++;
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 79285a36035..5e67defe2cb 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -27,11 +27,7 @@
#include <linux/fdtable.h>
-#define PRIOIDX_SZ 128
-
-static unsigned long prioidx_map[PRIOIDX_SZ];
-static DEFINE_SPINLOCK(prioidx_map_lock);
-static atomic_t max_prioidx = ATOMIC_INIT(0);
+#define PRIOMAP_MIN_SZ 128
static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgrp)
{
@@ -39,136 +35,157 @@ static inline struct cgroup_netprio_state *cgrp_netprio_state(struct cgroup *cgr
struct cgroup_netprio_state, css);
}
-static int get_prioidx(u32 *prio)
-{
- unsigned long flags;
- u32 prioidx;
-
- spin_lock_irqsave(&prioidx_map_lock, flags);
- prioidx = find_first_zero_bit(prioidx_map, sizeof(unsigned long) * PRIOIDX_SZ);
- if (prioidx == sizeof(unsigned long) * PRIOIDX_SZ) {
- spin_unlock_irqrestore(&prioidx_map_lock, flags);
- return -ENOSPC;
- }
- set_bit(prioidx, prioidx_map);
- if (atomic_read(&max_prioidx) < prioidx)
- atomic_set(&max_prioidx, prioidx);
- spin_unlock_irqrestore(&prioidx_map_lock, flags);
- *prio = prioidx;
- return 0;
-}
-
-static void put_prioidx(u32 idx)
+/*
+ * Extend @dev->priomap so that it's large enough to accomodate
+ * @target_idx. @dev->priomap.priomap_len > @target_idx after successful
+ * return. Must be called under rtnl lock.
+ */
+static int extend_netdev_table(struct net_device *dev, u32 target_idx)
{
- unsigned long flags;
-
- spin_lock_irqsave(&prioidx_map_lock, flags);
- clear_bit(idx, prioidx_map);
- spin_unlock_irqrestore(&prioidx_map_lock, flags);
-}
+ struct netprio_map *old, *new;
+ size_t new_sz, new_len;
-static int extend_netdev_table(struct net_device *dev, u32 new_len)
-{
- size_t new_size = sizeof(struct netprio_map) +
- ((sizeof(u32) * new_len));
- struct netprio_map *new_priomap = kzalloc(new_size, GFP_KERNEL);
- struct netprio_map *old_priomap;
+ /* is the existing priomap large enough? */
+ old = rtnl_dereference(dev->priomap);
+ if (old && old->priomap_len > target_idx)
+ return 0;
- old_priomap = rtnl_dereference(dev->priomap);
+ /*
+ * Determine the new size. Let's keep it power-of-two. We start
+ * from PRIOMAP_MIN_SZ and double it until it's large enough to
+ * accommodate @target_idx.
+ */
+ new_sz = PRIOMAP_MIN_SZ;
+ while (true) {
+ new_len = (new_sz - offsetof(struct netprio_map, priomap)) /
+ sizeof(new->priomap[0]);
+ if (new_len > target_idx)
+ break;
+ new_sz *= 2;
+ /* overflowed? */
+ if (WARN_ON(new_sz < PRIOMAP_MIN_SZ))
+ return -ENOSPC;
+ }
- if (!new_priomap) {
+ /* allocate & copy */
+ new = kzalloc(new_sz, GFP_KERNEL);
+ if (!new) {
pr_warn("Unable to alloc new priomap!\n");
return -ENOMEM;
}
- if (old_priomap)
- memcpy(new_priomap->priomap, old_priomap->priomap,
- old_priomap->priomap_len *
- sizeof(old_priomap->priomap[0]));
+ if (old)
+ memcpy(new->priomap, old->priomap,
+ old->priomap_len * sizeof(old->priomap[0]));
- new_priomap->priomap_len = new_len;
+ new->priomap_len = new_len;
- rcu_assign_pointer(dev->priomap, new_priomap);
- if (old_priomap)
- kfree_rcu(old_priomap, rcu);
+ /* install the new priomap */
+ rcu_assign_pointer(dev->priomap, new);
+ if (old)
+ kfree_rcu(old, rcu);
return 0;
}
-static int write_update_netdev_table(struct net_device *dev)
+/**
+ * netprio_prio - return the effective netprio of a cgroup-net_device pair
+ * @cgrp: cgroup part of the target pair
+ * @dev: net_device part of the target pair
+ *
+ * Should be called under RCU read or rtnl lock.
+ */
+static u32 netprio_prio(struct cgroup *cgrp, struct net_device *dev)
+{
+ struct netprio_map *map = rcu_dereference_rtnl(dev->priomap);
+
+ if (map && cgrp->id < map->priomap_len)
+ return map->priomap[cgrp->id];
+ return 0;
+}
+
+/**
+ * netprio_set_prio - set netprio on a cgroup-net_device pair
+ * @cgrp: cgroup part of the target pair
+ * @dev: net_device part of the target pair
+ * @prio: prio to set
+ *
+ * Set netprio to @prio on @cgrp-@dev pair. Should be called under rtnl
+ * lock and may fail under memory pressure for non-zero @prio.
+ */
+static int netprio_set_prio(struct cgroup *cgrp, struct net_device *dev,
+ u32 prio)
{
- int ret = 0;
- u32 max_len;
struct netprio_map *map;
+ int ret;
- max_len = atomic_read(&max_prioidx) + 1;
+ /* avoid extending priomap for zero writes */
map = rtnl_dereference(dev->priomap);
- if (!map || map->priomap_len < max_len)
- ret = extend_netdev_table(dev, max_len);
+ if (!prio && (!map || map->priomap_len <= cgrp->id))
+ return 0;
- return ret;
+ ret = extend_netdev_table(dev, cgrp->id);
+ if (ret)
+ return ret;
+
+ map = rtnl_dereference(dev->priomap);
+ map->priomap[cgrp->id] = prio;
+ return 0;
}
-static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp)
+static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp)
{
struct cgroup_netprio_state *cs;
- int ret = -EINVAL;
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
if (!cs)
return ERR_PTR(-ENOMEM);
- if (cgrp->parent && cgrp_netprio_state(cgrp->parent)->prioidx)
- goto out;
-
- ret = get_prioidx(&cs->prioidx);
- if (ret < 0) {
- pr_warn("No space in priority index array\n");
- goto out;
- }
-
return &cs->css;
-out:
- kfree(cs);
- return ERR_PTR(ret);
}
-static void cgrp_destroy(struct cgroup *cgrp)
+static int cgrp_css_online(struct cgroup *cgrp)
{
- struct cgroup_netprio_state *cs;
+ struct cgroup *parent = cgrp->parent;
struct net_device *dev;
- struct netprio_map *map;
+ int ret = 0;
+
+ if (!parent)
+ return 0;
- cs = cgrp_netprio_state(cgrp);
rtnl_lock();
+ /*
+ * Inherit prios from the parent. As all prios are set during
+ * onlining, there is no need to clear them on offline.
+ */
for_each_netdev(&init_net, dev) {
- map = rtnl_dereference(dev->priomap);
- if (map && cs->prioidx < map->priomap_len)
- map->priomap[cs->prioidx] = 0;
+ u32 prio = netprio_prio(parent, dev);
+
+ ret = netprio_set_prio(cgrp, dev, prio);
+ if (ret)
+ break;
}
rtnl_unlock();
- put_prioidx(cs->prioidx);
- kfree(cs);
+ return ret;
+}
+
+static void cgrp_css_free(struct cgroup *cgrp)
+{
+ kfree(cgrp_netprio_state(cgrp));
}
static u64 read_prioidx(struct cgroup *cgrp, struct cftype *cft)
{
- return (u64)cgrp_netprio_state(cgrp)->prioidx;
+ return cgrp->id;
}
static int read_priomap(struct cgroup *cont, struct cftype *cft,
struct cgroup_map_cb *cb)
{
struct net_device *dev;
- u32 prioidx = cgrp_netprio_state(cont)->prioidx;
- u32 priority;
- struct netprio_map *map;
rcu_read_lock();
- for_each_netdev_rcu(&init_net, dev) {
- map = rcu_dereference(dev->priomap);
- priority = (map && prioidx < map->priomap_len) ? map->priomap[prioidx] : 0;
- cb->fill(cb, dev->name, priority);
- }
+ for_each_netdev_rcu(&init_net, dev)
+ cb->fill(cb, dev->name, netprio_prio(cont, dev));
rcu_read_unlock();
return 0;
}
@@ -176,66 +193,24 @@ static int read_priomap(struct cgroup *cont, struct cftype *cft,
static int write_priomap(struct cgroup *cgrp, struct cftype *cft,
const char *buffer)
{
- char *devname = kstrdup(buffer, GFP_KERNEL);
- int ret = -EINVAL;
- u32 prioidx = cgrp_netprio_state(cgrp)->prioidx;
- unsigned long priority;
- char *priostr;
+ char devname[IFNAMSIZ + 1];
struct net_device *dev;
- struct netprio_map *map;
-
- if (!devname)
- return -ENOMEM;
-
- /*
- * Minimally sized valid priomap string
- */
- if (strlen(devname) < 3)
- goto out_free_devname;
-
- priostr = strstr(devname, " ");
- if (!priostr)
- goto out_free_devname;
-
- /*
- *Separate the devname from the associated priority
- *and advance the priostr pointer to the priority value
- */
- *priostr = '\0';
- priostr++;
-
- /*
- * If the priostr points to NULL, we're at the end of the passed
- * in string, and its not a valid write
- */
- if (*priostr == '\0')
- goto out_free_devname;
-
- ret = kstrtoul(priostr, 10, &priority);
- if (ret < 0)
- goto out_free_devname;
+ u32 prio;
+ int ret;
- ret = -ENODEV;
+ if (sscanf(buffer, "%"__stringify(IFNAMSIZ)"s %u", devname, &prio) != 2)
+ return -EINVAL;
dev = dev_get_by_name(&init_net, devname);
if (!dev)
- goto out_free_devname;
+ return -ENODEV;
rtnl_lock();
- ret = write_update_netdev_table(dev);
- if (ret < 0)
- goto out_put_dev;
- map = rtnl_dereference(dev->priomap);
- if (map)
- map->priomap[prioidx] = priority;
+ ret = netprio_set_prio(cgrp, dev, prio);
-out_put_dev:
rtnl_unlock();
dev_put(dev);
-
-out_free_devname:
- kfree(devname);
return ret;
}
@@ -248,7 +223,7 @@ static int update_netprio(const void *v, struct file *file, unsigned n)
return 0;
}
-void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
+static void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
{
struct task_struct *p;
void *v;
@@ -276,22 +251,13 @@ static struct cftype ss_files[] = {
struct cgroup_subsys net_prio_subsys = {
.name = "net_prio",
- .create = cgrp_create,
- .destroy = cgrp_destroy,
+ .css_alloc = cgrp_css_alloc,
+ .css_online = cgrp_css_online,
+ .css_free = cgrp_css_free,
.attach = net_prio_attach,
.subsys_id = net_prio_subsys_id,
.base_cftypes = ss_files,
.module = THIS_MODULE,
-
- /*
- * net_prio has artificial limit on the number of cgroups and
- * disallows nesting making it impossible to co-mount it with other
- * hierarchical subsystems. Remove the artificially low PRIOIDX_SZ
- * limit and properly nest configuration such that children follow
- * their parents' configurations by default and are allowed to
- * override and remove the following.
- */
- .broken_hierarchy = true,
};
static int netprio_device_event(struct notifier_block *unused,
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index d1dc14c2aac..b29dacf900f 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -419,20 +419,6 @@ struct pktgen_thread {
#define REMOVE 1
#define FIND 0
-static inline ktime_t ktime_now(void)
-{
- struct timespec ts;
- ktime_get_ts(&ts);
-
- return timespec_to_ktime(ts);
-}
-
-/* This works even if 32 bit because of careful byte order choice */
-static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
-{
- return cmp1.tv64 < cmp2.tv64;
-}
-
static const char version[] =
"Packet Generator for packet performance testing. "
"Version: " VERSION "\n";
@@ -675,7 +661,7 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
seq_puts(seq, "\n");
/* not really stopped, more like last-running-at */
- stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at;
+ stopped = pkt_dev->running ? ktime_get() : pkt_dev->stopped_at;
idle = pkt_dev->idle_acc;
do_div(idle, NSEC_PER_USEC);
@@ -2141,12 +2127,12 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
return;
}
- start_time = ktime_now();
+ start_time = ktime_get();
if (remaining < 100000) {
/* for small delays (<100us), just loop until limit is reached */
do {
- end_time = ktime_now();
- } while (ktime_lt(end_time, spin_until));
+ end_time = ktime_get();
+ } while (ktime_compare(end_time, spin_until) < 0);
} else {
/* see do_nanosleep */
hrtimer_init_sleeper(&t, current);
@@ -2162,7 +2148,7 @@ static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
hrtimer_cancel(&t.timer);
} while (t.task && pkt_dev->running && !signal_pending(current));
__set_current_state(TASK_RUNNING);
- end_time = ktime_now();
+ end_time = ktime_get();
}
pkt_dev->idle_acc += ktime_to_ns(ktime_sub(end_time, start_time));
@@ -2427,11 +2413,7 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)
}
} else { /* IPV6 * */
- if (pkt_dev->min_in6_daddr.s6_addr32[0] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[1] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[2] == 0 &&
- pkt_dev->min_in6_daddr.s6_addr32[3] == 0) ;
- else {
+ if (!ipv6_addr_any(&pkt_dev->min_in6_daddr)) {
int i;
/* Only random destinations yet */
@@ -2916,8 +2898,7 @@ static void pktgen_run(struct pktgen_thread *t)
pktgen_clear_counters(pkt_dev);
pkt_dev->running = 1; /* Cranke yeself! */
pkt_dev->skb = NULL;
- pkt_dev->started_at =
- pkt_dev->next_tx = ktime_now();
+ pkt_dev->started_at = pkt_dev->next_tx = ktime_get();
set_pkt_overhead(pkt_dev);
@@ -3076,7 +3057,7 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
kfree_skb(pkt_dev->skb);
pkt_dev->skb = NULL;
- pkt_dev->stopped_at = ktime_now();
+ pkt_dev->stopped_at = ktime_get();
pkt_dev->running = 0;
show_results(pkt_dev, nr_frags);
@@ -3095,7 +3076,7 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
continue;
if (best == NULL)
best = pkt_dev;
- else if (ktime_lt(pkt_dev->next_tx, best->next_tx))
+ else if (ktime_compare(pkt_dev->next_tx, best->next_tx) < 0)
best = pkt_dev;
}
if_unlock(t);
@@ -3180,14 +3161,14 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
static void pktgen_resched(struct pktgen_dev *pkt_dev)
{
- ktime_t idle_start = ktime_now();
+ ktime_t idle_start = ktime_get();
schedule();
- pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
}
static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
{
- ktime_t idle_start = ktime_now();
+ ktime_t idle_start = ktime_get();
while (atomic_read(&(pkt_dev->skb->users)) != 1) {
if (signal_pending(current))
@@ -3198,7 +3179,7 @@ static void pktgen_wait_for_skb(struct pktgen_dev *pkt_dev)
else
cpu_relax();
}
- pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_get(), idle_start));
}
static void pktgen_xmit(struct pktgen_dev *pkt_dev)
@@ -3220,7 +3201,7 @@ static void pktgen_xmit(struct pktgen_dev *pkt_dev)
* "never transmit"
*/
if (unlikely(pkt_dev->delay == ULLONG_MAX)) {
- pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX);
+ pkt_dev->next_tx = ktime_add_ns(ktime_get(), ULONG_MAX);
return;
}
diff --git a/net/core/request_sock.c b/net/core/request_sock.c
index c31d9e8668c..4425148d2b5 100644
--- a/net/core/request_sock.c
+++ b/net/core/request_sock.c
@@ -186,8 +186,6 @@ void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req,
struct fastopen_queue *fastopenq =
inet_csk(lsk)->icsk_accept_queue.fastopenq;
- BUG_ON(!spin_is_locked(&sk->sk_lock.slock) && !sock_owned_by_user(sk));
-
tcp_sk(sk)->fastopen_rsk = NULL;
spin_lock_bh(&fastopenq->lock);
fastopenq->qlen--;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 76d4c2c3c89..1868625af25 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -128,7 +128,7 @@ static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex)
if (tab == NULL || tab[msgindex].doit == NULL)
tab = rtnl_msg_handlers[PF_UNSPEC];
- return tab ? tab[msgindex].doit : NULL;
+ return tab[msgindex].doit;
}
static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
@@ -143,7 +143,7 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex)
if (tab == NULL || tab[msgindex].dumpit == NULL)
tab = rtnl_msg_handlers[PF_UNSPEC];
- return tab ? tab[msgindex].dumpit : NULL;
+ return tab[msgindex].dumpit;
}
static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex)
@@ -158,7 +158,7 @@ static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex)
if (tab == NULL || tab[msgindex].calcit == NULL)
tab = rtnl_msg_handlers[PF_UNSPEC];
- return tab ? tab[msgindex].calcit : NULL;
+ return tab[msgindex].calcit;
}
/**
@@ -1316,6 +1316,10 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm,
err = PTR_ERR(net);
goto errout;
}
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) {
+ err = -EPERM;
+ goto errout;
+ }
err = dev_change_net_namespace(dev, net, ifname);
put_net(net);
if (err)
@@ -1638,7 +1642,7 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm)
}
EXPORT_SYMBOL(rtnl_configure_link);
-struct net_device *rtnl_create_link(struct net *src_net, struct net *net,
+struct net_device *rtnl_create_link(struct net *net,
char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[])
{
int err;
@@ -1836,7 +1840,7 @@ replay:
if (IS_ERR(dest_net))
return PTR_ERR(dest_net);
- dev = rtnl_create_link(net, dest_net, ifname, ops, tb);
+ dev = rtnl_create_link(dest_net, ifname, ops, tb);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out;
@@ -2057,6 +2061,9 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
u8 *addr;
int err;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
if (err < 0)
return err;
@@ -2123,6 +2130,9 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
int err = -EINVAL;
__u8 *addr;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (nlmsg_len(nlh) < sizeof(*ndm))
return -EINVAL;
@@ -2192,7 +2202,8 @@ static int nlmsg_populate_fdb(struct sk_buff *skb,
goto skip;
err = nlmsg_populate_fdb_fill(skb, dev, ha->addr,
- portid, seq, 0, NTF_SELF);
+ portid, seq,
+ RTM_NEWNEIGH, NTF_SELF);
if (err < 0)
return err;
skip:
@@ -2252,6 +2263,211 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
+int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
+ struct net_device *dev, u16 mode)
+{
+ struct nlmsghdr *nlh;
+ struct ifinfomsg *ifm;
+ struct nlattr *br_afspec;
+ u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
+
+ nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ifm = nlmsg_data(nlh);
+ ifm->ifi_family = AF_BRIDGE;
+ ifm->__ifi_pad = 0;
+ ifm->ifi_type = dev->type;
+ ifm->ifi_index = dev->ifindex;
+ ifm->ifi_flags = dev_get_flags(dev);
+ ifm->ifi_change = 0;
+
+
+ if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
+ nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
+ nla_put_u8(skb, IFLA_OPERSTATE, operstate) ||
+ (dev->master &&
+ nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) ||
+ (dev->addr_len &&
+ nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
+ (dev->ifindex != dev->iflink &&
+ nla_put_u32(skb, IFLA_LINK, dev->iflink)))
+ goto nla_put_failure;
+
+ br_afspec = nla_nest_start(skb, IFLA_AF_SPEC);
+ if (!br_afspec)
+ goto nla_put_failure;
+
+ if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) ||
+ nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
+ nla_nest_cancel(skb, br_afspec);
+ goto nla_put_failure;
+ }
+ nla_nest_end(skb, br_afspec);
+
+ return nlmsg_end(skb, nlh);
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+EXPORT_SYMBOL(ndo_dflt_bridge_getlink);
+
+static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ int idx = 0;
+ u32 portid = NETLINK_CB(cb->skb).portid;
+ u32 seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ const struct net_device_ops *ops = dev->netdev_ops;
+ struct net_device *master = dev->master;
+
+ if (master && master->netdev_ops->ndo_bridge_getlink) {
+ if (idx >= cb->args[0] &&
+ master->netdev_ops->ndo_bridge_getlink(
+ skb, portid, seq, dev) < 0)
+ break;
+ idx++;
+ }
+
+ if (ops->ndo_bridge_getlink) {
+ if (idx >= cb->args[0] &&
+ ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0)
+ break;
+ idx++;
+ }
+ }
+ rcu_read_unlock();
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
+static inline size_t bridge_nlmsg_size(void)
+{
+ return NLMSG_ALIGN(sizeof(struct ifinfomsg))
+ + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */
+ + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */
+ + nla_total_size(sizeof(u32)) /* IFLA_MASTER */
+ + nla_total_size(sizeof(u32)) /* IFLA_MTU */
+ + nla_total_size(sizeof(u32)) /* IFLA_LINK */
+ + nla_total_size(sizeof(u32)) /* IFLA_OPERSTATE */
+ + nla_total_size(sizeof(u8)) /* IFLA_PROTINFO */
+ + nla_total_size(sizeof(struct nlattr)) /* IFLA_AF_SPEC */
+ + nla_total_size(sizeof(u16)) /* IFLA_BRIDGE_FLAGS */
+ + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */
+}
+
+static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
+{
+ struct net *net = dev_net(dev);
+ struct net_device *master = dev->master;
+ struct sk_buff *skb;
+ int err = -EOPNOTSUPP;
+
+ skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC);
+ if (!skb) {
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
+ master && master->netdev_ops->ndo_bridge_getlink) {
+ err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+ if (err < 0)
+ goto errout;
+ }
+
+ if ((flags & BRIDGE_FLAGS_SELF) &&
+ dev->netdev_ops->ndo_bridge_getlink) {
+ err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
+ if (err < 0)
+ goto errout;
+ }
+
+ rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC);
+ return 0;
+errout:
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ rtnl_set_sk_err(net, RTNLGRP_LINK, err);
+ return err;
+}
+
+static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct net *net = sock_net(skb->sk);
+ struct ifinfomsg *ifm;
+ struct net_device *dev;
+ struct nlattr *br_spec, *attr = NULL;
+ int rem, err = -EOPNOTSUPP;
+ u16 oflags, flags = 0;
+ bool have_flags = false;
+
+ if (nlmsg_len(nlh) < sizeof(*ifm))
+ return -EINVAL;
+
+ ifm = nlmsg_data(nlh);
+ if (ifm->ifi_family != AF_BRIDGE)
+ return -EPFNOSUPPORT;
+
+ dev = __dev_get_by_index(net, ifm->ifi_index);
+ if (!dev) {
+ pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n");
+ return -ENODEV;
+ }
+
+ br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
+ if (br_spec) {
+ nla_for_each_nested(attr, br_spec, rem) {
+ if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ have_flags = true;
+ flags = nla_get_u16(attr);
+ break;
+ }
+ }
+ }
+
+ oflags = flags;
+
+ if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
+ if (!dev->master ||
+ !dev->master->netdev_ops->ndo_bridge_setlink) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh);
+ if (err)
+ goto out;
+
+ flags &= ~BRIDGE_FLAGS_MASTER;
+ }
+
+ if ((flags & BRIDGE_FLAGS_SELF)) {
+ if (!dev->netdev_ops->ndo_bridge_setlink)
+ err = -EOPNOTSUPP;
+ else
+ err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh);
+
+ if (!err)
+ flags &= ~BRIDGE_FLAGS_SELF;
+ }
+
+ if (have_flags)
+ memcpy(nla_data(attr), &flags, sizeof(flags));
+ /* Generate event to notify upper layer of bridge change */
+ if (!err)
+ err = rtnl_bridge_notify(dev, oflags);
+out:
+ return err;
+}
+
/* Protected by RTNL sempahore. */
static struct rtattr **rta_buf;
static int rtattr_max;
@@ -2282,7 +2498,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
sz_idx = type>>2;
kind = type&3;
- if (kind != 2 && !capable(CAP_NET_ADMIN))
+ if (kind != 2 && !ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) {
@@ -2433,5 +2649,8 @@ void __init rtnetlink_init(void)
rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL);
rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL);
+
+ rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL);
+ rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL);
}
diff --git a/net/core/scm.c b/net/core/scm.c
index ab570841a53..905dcc6ad1e 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -35,6 +35,7 @@
#include <net/sock.h>
#include <net/compat.h>
#include <net/scm.h>
+#include <net/cls_cgroup.h>
/*
@@ -51,11 +52,11 @@ static __inline__ int scm_check_creds(struct ucred *creds)
if (!uid_valid(uid) || !gid_valid(gid))
return -EINVAL;
- if ((creds->pid == task_tgid_vnr(current) || capable(CAP_SYS_ADMIN)) &&
+ if ((creds->pid == task_tgid_vnr(current) || nsown_capable(CAP_SYS_ADMIN)) &&
((uid_eq(uid, cred->uid) || uid_eq(uid, cred->euid) ||
- uid_eq(uid, cred->suid)) || capable(CAP_SETUID)) &&
+ uid_eq(uid, cred->suid)) || nsown_capable(CAP_SETUID)) &&
((gid_eq(gid, cred->gid) || gid_eq(gid, cred->egid) ||
- gid_eq(gid, cred->sgid)) || capable(CAP_SETGID))) {
+ gid_eq(gid, cred->sgid)) || nsown_capable(CAP_SETGID))) {
return 0;
}
return -EPERM;
@@ -302,8 +303,10 @@ void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
}
/* Bump the usage count and install the file. */
sock = sock_from_file(fp[i], &err);
- if (sock)
+ if (sock) {
sock_update_netprioidx(sock->sk, current);
+ sock_update_classid(sock->sk, current);
+ }
fd_install(new_fd, get_file(fp[i]));
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 4007c1437fd..a9a2ae3e221 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -519,7 +519,7 @@ static void skb_release_data(struct sk_buff *skb)
uarg = skb_shinfo(skb)->destructor_arg;
if (uarg->callback)
- uarg->callback(uarg);
+ uarg->callback(uarg, true);
}
if (skb_has_frag_list(skb))
@@ -635,6 +635,26 @@ void kfree_skb(struct sk_buff *skb)
EXPORT_SYMBOL(kfree_skb);
/**
+ * skb_tx_error - report an sk_buff xmit error
+ * @skb: buffer that triggered an error
+ *
+ * Report xmit error if a device callback is tracking this skb.
+ * skb must be freed afterwards.
+ */
+void skb_tx_error(struct sk_buff *skb)
+{
+ if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) {
+ struct ubuf_info *uarg;
+
+ uarg = skb_shinfo(skb)->destructor_arg;
+ if (uarg->callback)
+ uarg->callback(uarg, false);
+ skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY;
+ }
+}
+EXPORT_SYMBOL(skb_tx_error);
+
+/**
* consume_skb - free an skbuff
* @skb: buffer to free
*
@@ -662,11 +682,14 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->transport_header = old->transport_header;
new->network_header = old->network_header;
new->mac_header = old->mac_header;
+ new->inner_transport_header = old->inner_transport_header;
+ new->inner_network_header = old->inner_transport_header;
skb_dst_copy(new, old);
new->rxhash = old->rxhash;
new->ooo_okay = old->ooo_okay;
new->l4_rxhash = old->l4_rxhash;
new->no_fcs = old->no_fcs;
+ new->encapsulation = old->encapsulation;
#ifdef CONFIG_XFRM
new->sp = secpath_get(old->sp);
#endif
@@ -797,7 +820,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
for (i = 0; i < num_frags; i++)
skb_frag_unref(skb, i);
- uarg->callback(uarg);
+ uarg->callback(uarg, false);
/* skb frags point to kernel buffers */
for (i = num_frags - 1; i >= 0; i--) {
@@ -872,6 +895,8 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->network_header += offset;
if (skb_mac_header_was_set(new))
new->mac_header += offset;
+ new->inner_transport_header += offset;
+ new->inner_network_header += offset;
#endif
skb_shinfo(new)->gso_size = skb_shinfo(old)->gso_size;
skb_shinfo(new)->gso_segs = skb_shinfo(old)->gso_segs;
@@ -1069,6 +1094,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
skb->network_header += off;
if (skb_mac_header_was_set(skb))
skb->mac_header += off;
+ skb->inner_transport_header += off;
+ skb->inner_network_header += off;
/* Only adjust this if it actually is csum_start rather than csum */
if (skb->ip_summed == CHECKSUM_PARTIAL)
skb->csum_start += nhead;
@@ -1168,6 +1195,8 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb,
n->network_header += off;
if (skb_mac_header_was_set(skb))
n->mac_header += off;
+ n->inner_transport_header += off;
+ n->inner_network_header += off;
#endif
return n;
@@ -1620,7 +1649,7 @@ static void sock_spd_release(struct splice_pipe_desc *spd, unsigned int i)
static struct page *linear_to_page(struct page *page, unsigned int *len,
unsigned int *offset,
- struct sk_buff *skb, struct sock *sk)
+ struct sock *sk)
{
struct page_frag *pfrag = sk_page_frag(sk);
@@ -1653,14 +1682,14 @@ static bool spd_can_coalesce(const struct splice_pipe_desc *spd,
static bool spd_fill_page(struct splice_pipe_desc *spd,
struct pipe_inode_info *pipe, struct page *page,
unsigned int *len, unsigned int offset,
- struct sk_buff *skb, bool linear,
+ bool linear,
struct sock *sk)
{
if (unlikely(spd->nr_pages == MAX_SKB_FRAGS))
return true;
if (linear) {
- page = linear_to_page(page, len, &offset, skb, sk);
+ page = linear_to_page(page, len, &offset, sk);
if (!page)
return true;
}
@@ -1677,23 +1706,9 @@ static bool spd_fill_page(struct splice_pipe_desc *spd,
return false;
}
-static inline void __segment_seek(struct page **page, unsigned int *poff,
- unsigned int *plen, unsigned int off)
-{
- unsigned long n;
-
- *poff += off;
- n = *poff / PAGE_SIZE;
- if (n)
- *page = nth_page(*page, n);
-
- *poff = *poff % PAGE_SIZE;
- *plen -= off;
-}
-
static bool __splice_segment(struct page *page, unsigned int poff,
unsigned int plen, unsigned int *off,
- unsigned int *len, struct sk_buff *skb,
+ unsigned int *len,
struct splice_pipe_desc *spd, bool linear,
struct sock *sk,
struct pipe_inode_info *pipe)
@@ -1708,23 +1723,19 @@ static bool __splice_segment(struct page *page, unsigned int poff,
}
/* ignore any bits we already processed */
- if (*off) {
- __segment_seek(&page, &poff, &plen, *off);
- *off = 0;
- }
+ poff += *off;
+ plen -= *off;
+ *off = 0;
do {
unsigned int flen = min(*len, plen);
- /* the linear region may spread across several pages */
- flen = min_t(unsigned int, flen, PAGE_SIZE - poff);
-
- if (spd_fill_page(spd, pipe, page, &flen, poff, skb, linear, sk))
+ if (spd_fill_page(spd, pipe, page, &flen, poff,
+ linear, sk))
return true;
-
- __segment_seek(&page, &poff, &plen, flen);
+ poff += flen;
+ plen -= flen;
*len -= flen;
-
} while (*len && plen);
return false;
@@ -1748,7 +1759,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
if (__splice_segment(virt_to_page(skb->data),
(unsigned long) skb->data & (PAGE_SIZE - 1),
skb_headlen(skb),
- offset, len, skb, spd,
+ offset, len, spd,
skb_head_is_locked(skb),
sk, pipe))
return true;
@@ -1761,7 +1772,7 @@ static bool __skb_splice_bits(struct sk_buff *skb, struct pipe_inode_info *pipe,
if (__splice_segment(skb_frag_page(f),
f->page_offset, skb_frag_size(f),
- offset, len, skb, spd, false, sk, pipe))
+ offset, len, spd, false, sk, pipe))
return true;
}
@@ -2999,12 +3010,11 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
memcpy(skb_mac_header(nskb), skb_mac_header(p),
p->data - skb_mac_header(p));
- *NAPI_GRO_CB(nskb) = *NAPI_GRO_CB(p);
skb_shinfo(nskb)->frag_list = p;
skb_shinfo(nskb)->gso_size = pinfo->gso_size;
pinfo->gso_size = 0;
skb_header_release(p);
- nskb->prev = p;
+ NAPI_GRO_CB(nskb)->last = p;
nskb->data_len += p->len;
nskb->truesize += p->truesize;
@@ -3030,8 +3040,8 @@ merge:
__skb_pull(skb, offset);
- p->prev->next = skb;
- p->prev = skb;
+ NAPI_GRO_CB(p)->last->next = skb;
+ NAPI_GRO_CB(p)->last = skb;
skb_header_release(skb);
done:
diff --git a/net/core/sock.c b/net/core/sock.c
index 8a146cfcc36..bc131d41968 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -505,7 +505,8 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie)
}
EXPORT_SYMBOL(sk_dst_check);
-static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
+static int sock_setbindtodevice(struct sock *sk, char __user *optval,
+ int optlen)
{
int ret = -ENOPROTOOPT;
#ifdef CONFIG_NETDEVICES
@@ -515,7 +516,7 @@ static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen)
/* Sorry... */
ret = -EPERM;
- if (!capable(CAP_NET_RAW))
+ if (!ns_capable(net->user_ns, CAP_NET_RAW))
goto out;
ret = -EINVAL;
@@ -562,6 +563,59 @@ out:
return ret;
}
+static int sock_getbindtodevice(struct sock *sk, char __user *optval,
+ int __user *optlen, int len)
+{
+ int ret = -ENOPROTOOPT;
+#ifdef CONFIG_NETDEVICES
+ struct net *net = sock_net(sk);
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
+ unsigned seq;
+
+ if (sk->sk_bound_dev_if == 0) {
+ len = 0;
+ goto zero;
+ }
+
+ ret = -EINVAL;
+ if (len < IFNAMSIZ)
+ goto out;
+
+retry:
+ seq = read_seqcount_begin(&devnet_rename_seq);
+ rcu_read_lock();
+ dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if);
+ ret = -ENODEV;
+ if (!dev) {
+ rcu_read_unlock();
+ goto out;
+ }
+
+ strcpy(devname, dev->name);
+ rcu_read_unlock();
+ if (read_seqcount_retry(&devnet_rename_seq, seq))
+ goto retry;
+
+ len = strlen(devname) + 1;
+
+ ret = -EFAULT;
+ if (copy_to_user(optval, devname, len))
+ goto out;
+
+zero:
+ ret = -EFAULT;
+ if (put_user(len, optlen))
+ goto out;
+
+ ret = 0;
+
+out:
+#endif
+
+ return ret;
+}
+
static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool)
{
if (valbool)
@@ -589,7 +643,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
*/
if (optname == SO_BINDTODEVICE)
- return sock_bindtodevice(sk, optval, optlen);
+ return sock_setbindtodevice(sk, optval, optlen);
if (optlen < sizeof(int))
return -EINVAL;
@@ -696,7 +750,8 @@ set_rcvbuf:
break;
case SO_PRIORITY:
- if ((val >= 0 && val <= 6) || capable(CAP_NET_ADMIN))
+ if ((val >= 0 && val <= 6) ||
+ ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
sk->sk_priority = val;
else
ret = -EPERM;
@@ -813,7 +868,7 @@ set_rcvbuf:
clear_bit(SOCK_PASSSEC, &sock->flags);
break;
case SO_MARK:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
ret = -EPERM;
else
sk->sk_mark = val;
@@ -1074,6 +1129,17 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
case SO_NOFCS:
v.val = sock_flag(sk, SOCK_NOFCS);
break;
+
+ case SO_BINDTODEVICE:
+ return sock_getbindtodevice(sk, optval, optlen, len);
+
+ case SO_GET_FILTER:
+ len = sk_get_filter(sk, (struct sock_filter __user *)optval, len);
+ if (len < 0)
+ return len;
+
+ goto lenout;
+
default:
return -ENOPROTOOPT;
}
@@ -1214,13 +1280,11 @@ static void sk_prot_free(struct proto *prot, struct sock *sk)
#ifdef CONFIG_CGROUPS
#if IS_ENABLED(CONFIG_NET_CLS_CGROUP)
-void sock_update_classid(struct sock *sk)
+void sock_update_classid(struct sock *sk, struct task_struct *task)
{
u32 classid;
- rcu_read_lock(); /* doing current task, which cannot vanish. */
- classid = task_cls_classid(current);
- rcu_read_unlock();
+ classid = task_cls_classid(task);
if (classid != sk->sk_classid)
sk->sk_classid = classid;
}
@@ -1263,7 +1327,7 @@ struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
sock_net_set(sk, get_net(net));
atomic_set(&sk->sk_wmem_alloc, 1);
- sock_update_classid(sk);
+ sock_update_classid(sk, current);
sock_update_netprioidx(sk, current);
}
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index a7c36845b12..d1b08045a9d 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -216,6 +216,11 @@ static __net_init int sysctl_core_net_init(struct net *net)
goto err_dup;
tbl[0].data = &net->core.sysctl_somaxconn;
+
+ /* Don't export any sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns) {
+ tbl[0].procname = NULL;
+ }
}
net->core.sysctl_hdr = register_net_sysctl(net, "net/core", tbl);
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index 70989e67230..1b588e23cf8 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -1662,8 +1662,8 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct nlmsghdr *reply_nlh = NULL;
const struct reply_func *fn;
- if (!net_eq(net, &init_net))
- return -EINVAL;
+ if ((nlh->nlmsg_type == RTM_SETDCB) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
ret = nlmsg_parse(nlh, sizeof(*dcb), tb, DCB_ATTR_MAX,
dcbnl_rtnl_policy);
@@ -1681,7 +1681,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (!tb[DCB_ATTR_IFNAME])
return -EINVAL;
- netdev = dev_get_by_name(&init_net, nla_data(tb[DCB_ATTR_IFNAME]));
+ netdev = dev_get_by_name(net, nla_data(tb[DCB_ATTR_IFNAME]));
if (!netdev)
return -ENODEV;
@@ -1705,7 +1705,7 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
nlmsg_end(reply_skb, reply_nlh);
- ret = rtnl_unicast(reply_skb, &init_net, portid);
+ ret = rtnl_unicast(reply_skb, net, portid);
out:
dev_put(netdev);
return ret;
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 176ecdba4a2..4f9f5eb478f 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -439,8 +439,8 @@ exit:
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
return NULL;
put_and_exit:
- bh_unlock_sock(newsk);
- sock_put(newsk);
+ inet_csk_prepare_forced_close(newsk);
+ dccp_done(newsk);
goto exit;
}
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 56840b249f3..6e05981f271 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -585,7 +585,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
if (__inet_inherit_port(sk, newsk) < 0) {
- sock_put(newsk);
+ inet_csk_prepare_forced_close(newsk);
+ dccp_done(newsk);
goto out;
}
__inet6_hash(newsk, NULL);
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index ea850ce35d4..662071b249c 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -174,8 +174,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
* To protect against Request floods, increment retrans
* counter (backoff, monitored by dccp_response_timer).
*/
- req->retrans++;
- req->rsk_ops->rtx_syn_ack(sk, req, NULL);
+ inet_rtx_syn_ack(sk, req);
}
/* Network Duplicate, discard packet */
return NULL;
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index 7b7e561412d..e47ba9fc4a0 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -573,6 +573,9 @@ static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct dn_ifaddr __rcu **ifap;
int err = -EINVAL;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!net_eq(net, &init_net))
goto errout;
@@ -614,6 +617,9 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
struct dn_ifaddr *ifa;
int err;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!net_eq(net, &init_net))
return -EINVAL;
diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c
index 102d6106a94..e36614eccc0 100644
--- a/net/decnet/dn_fib.c
+++ b/net/decnet/dn_fib.c
@@ -520,6 +520,9 @@ static int dn_fib_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!net_eq(net, &init_net))
return -EINVAL;
@@ -540,6 +543,9 @@ static int dn_fib_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *
struct rtattr **rta = arg;
struct rtmsg *r = NLMSG_DATA(nlh);
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!net_eq(net, &init_net))
return -EINVAL;
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index 8aa4b111538..0a69d075779 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -259,20 +259,16 @@ static int __init init_dns_resolver(void)
if (!cred)
return -ENOMEM;
- keyring = key_alloc(&key_type_keyring, ".dns_resolver",
- GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
- (KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ,
- KEY_ALLOC_NOT_IN_QUOTA);
+ keyring = keyring_alloc(".dns_resolver",
+ GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+ KEY_USR_VIEW | KEY_USR_READ,
+ KEY_ALLOC_NOT_IN_QUOTA, NULL);
if (IS_ERR(keyring)) {
ret = PTR_ERR(keyring);
goto failed_put_cred;
}
- ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
- if (ret < 0)
- goto failed_put_key;
-
ret = register_key_type(&key_type_dns_resolver);
if (ret < 0)
goto failed_put_key;
@@ -304,3 +300,4 @@ static void __exit exit_dns_resolver(void)
module_init(init_dns_resolver)
module_exit(exit_dns_resolver)
MODULE_LICENSE("GPL");
+
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 274791cd7a3..f5eede1d6cb 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -1,26 +1,24 @@
+config HAVE_NET_DSA
+ def_bool y
+ depends on NETDEVICES && !S390
+
+# Drivers must select NET_DSA and the appropriate tagging format
+
config NET_DSA
- tristate "Distributed Switch Architecture support"
- default n
- depends on EXPERIMENTAL && NETDEVICES && !S390
+ tristate
+ depends on HAVE_NET_DSA
select PHYLIB
- ---help---
- This allows you to use hardware switch chips that use
- the Distributed Switch Architecture.
-
if NET_DSA
# tagging formats
config NET_DSA_TAG_DSA
bool
- default n
config NET_DSA_TAG_EDSA
bool
- default n
config NET_DSA_TAG_TRAILER
bool
- default n
endif
diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c
index 6d42c17af96..f651da60f16 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan.c
@@ -1047,7 +1047,8 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
goto error;
}
- if (skb->len <= IEEE802154_MTU) {
+ /* Send directly if less than the MTU minus the 2 checksum bytes. */
+ if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) {
err = dev_queue_xmit(skb);
goto out;
}
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 766c5965856..24b384b7903 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -346,7 +346,8 @@ lookup_protocol:
}
err = -EPERM;
- if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
+ if (sock->type == SOCK_RAW && !kern &&
+ !ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock;
err = -EAFNOSUPPORT;
@@ -473,6 +474,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
struct sock *sk = sock->sk;
struct inet_sock *inet = inet_sk(sk);
+ struct net *net = sock_net(sk);
unsigned short snum;
int chk_addr_ret;
int err;
@@ -496,7 +498,7 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out;
}
- chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);
+ chk_addr_ret = inet_addr_type(net, addr->sin_addr.s_addr);
/* Not specified by any standard per-se, however it breaks too
* many applications when removed. It is unfortunate since
@@ -516,7 +518,8 @@ int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
snum = ntohs(addr->sin_port);
err = -EACCES;
- if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ if (snum && snum < PROT_SOCK &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
goto out;
/* We keep a pair of addresses. rcv_saddr is the one
@@ -1251,7 +1254,7 @@ EXPORT_SYMBOL(inet_sk_rebuild_header);
static int inet_gso_send_check(struct sk_buff *skb)
{
- const struct net_protocol *ops;
+ const struct net_offload *ops;
const struct iphdr *iph;
int proto;
int ihl;
@@ -1275,9 +1278,9 @@ static int inet_gso_send_check(struct sk_buff *skb)
err = -EPROTONOSUPPORT;
rcu_read_lock();
- ops = rcu_dereference(inet_protos[proto]);
- if (likely(ops && ops->gso_send_check))
- err = ops->gso_send_check(skb);
+ ops = rcu_dereference(inet_offloads[proto]);
+ if (likely(ops && ops->callbacks.gso_send_check))
+ err = ops->callbacks.gso_send_check(skb);
rcu_read_unlock();
out:
@@ -1288,7 +1291,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
struct sk_buff *segs = ERR_PTR(-EINVAL);
- const struct net_protocol *ops;
+ const struct net_offload *ops;
struct iphdr *iph;
int proto;
int ihl;
@@ -1325,9 +1328,9 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
segs = ERR_PTR(-EPROTONOSUPPORT);
rcu_read_lock();
- ops = rcu_dereference(inet_protos[proto]);
- if (likely(ops && ops->gso_segment))
- segs = ops->gso_segment(skb, features);
+ ops = rcu_dereference(inet_offloads[proto]);
+ if (likely(ops && ops->callbacks.gso_segment))
+ segs = ops->callbacks.gso_segment(skb, features);
rcu_read_unlock();
if (!segs || IS_ERR(segs))
@@ -1356,7 +1359,7 @@ out:
static struct sk_buff **inet_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
- const struct net_protocol *ops;
+ const struct net_offload *ops;
struct sk_buff **pp = NULL;
struct sk_buff *p;
const struct iphdr *iph;
@@ -1378,8 +1381,8 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
proto = iph->protocol;
rcu_read_lock();
- ops = rcu_dereference(inet_protos[proto]);
- if (!ops || !ops->gro_receive)
+ ops = rcu_dereference(inet_offloads[proto]);
+ if (!ops || !ops->callbacks.gro_receive)
goto out_unlock;
if (*(u8 *)iph != 0x45)
@@ -1420,7 +1423,7 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
- pp = ops->gro_receive(head, skb);
+ pp = ops->callbacks.gro_receive(head, skb);
out_unlock:
rcu_read_unlock();
@@ -1435,7 +1438,7 @@ static int inet_gro_complete(struct sk_buff *skb)
{
__be16 newlen = htons(skb->len - skb_network_offset(skb));
struct iphdr *iph = ip_hdr(skb);
- const struct net_protocol *ops;
+ const struct net_offload *ops;
int proto = iph->protocol;
int err = -ENOSYS;
@@ -1443,11 +1446,11 @@ static int inet_gro_complete(struct sk_buff *skb)
iph->tot_len = newlen;
rcu_read_lock();
- ops = rcu_dereference(inet_protos[proto]);
- if (WARN_ON(!ops || !ops->gro_complete))
+ ops = rcu_dereference(inet_offloads[proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;
- err = ops->gro_complete(skb);
+ err = ops->callbacks.gro_complete(skb);
out_unlock:
rcu_read_unlock();
@@ -1558,23 +1561,33 @@ static const struct net_protocol tcp_protocol = {
.early_demux = tcp_v4_early_demux,
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
- .gso_send_check = tcp_v4_gso_send_check,
- .gso_segment = tcp_tso_segment,
- .gro_receive = tcp4_gro_receive,
- .gro_complete = tcp4_gro_complete,
.no_policy = 1,
.netns_ok = 1,
};
+static const struct net_offload tcp_offload = {
+ .callbacks = {
+ .gso_send_check = tcp_v4_gso_send_check,
+ .gso_segment = tcp_tso_segment,
+ .gro_receive = tcp4_gro_receive,
+ .gro_complete = tcp4_gro_complete,
+ },
+};
+
static const struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
- .gso_send_check = udp4_ufo_send_check,
- .gso_segment = udp4_ufo_fragment,
.no_policy = 1,
.netns_ok = 1,
};
+static const struct net_offload udp_offload = {
+ .callbacks = {
+ .gso_send_check = udp4_ufo_send_check,
+ .gso_segment = udp4_ufo_fragment,
+ },
+};
+
static const struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
.err_handler = ping_err,
@@ -1659,13 +1672,35 @@ static int ipv4_proc_init(void);
* IP protocol layer initialiser
*/
+static struct packet_offload ip_packet_offload __read_mostly = {
+ .type = cpu_to_be16(ETH_P_IP),
+ .callbacks = {
+ .gso_send_check = inet_gso_send_check,
+ .gso_segment = inet_gso_segment,
+ .gro_receive = inet_gro_receive,
+ .gro_complete = inet_gro_complete,
+ },
+};
+
+static int __init ipv4_offload_init(void)
+{
+ /*
+ * Add offloads
+ */
+ if (inet_add_offload(&udp_offload, IPPROTO_UDP) < 0)
+ pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
+ if (inet_add_offload(&tcp_offload, IPPROTO_TCP) < 0)
+ pr_crit("%s: Cannot add TCP protocol offlaod\n", __func__);
+
+ dev_add_offload(&ip_packet_offload);
+ return 0;
+}
+
+fs_initcall(ipv4_offload_init);
+
static struct packet_type ip_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IP),
.func = ip_rcv,
- .gso_send_check = inet_gso_send_check,
- .gso_segment = inet_gso_segment,
- .gro_receive = inet_gro_receive,
- .gro_complete = inet_gro_complete,
};
static int __init inet_init(void)
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index a0d8392491c..a69b4e4a02b 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -269,7 +269,11 @@ static void ah_input_done(struct crypto_async_request *base, int err)
skb->network_header += ah_hlen;
memcpy(skb_network_header(skb), work_iph, ihl);
__skb_pull(skb, ah_hlen + ihl);
- skb_set_transport_header(skb, -ihl);
+
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -ihl);
out:
kfree(AH_SKB_CB(skb)->tmp);
xfrm_input_resume(skb, err);
@@ -381,7 +385,10 @@ static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
skb->network_header += ah_hlen;
memcpy(skb_network_header(skb), work_iph, ihl);
__skb_pull(skb, ah_hlen + ihl);
- skb_set_transport_header(skb, -ihl);
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -ihl);
err = nexthdr;
@@ -413,9 +420,12 @@ static void ah4_err(struct sk_buff *skb, u32 info)
if (!x)
return;
- if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+ if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+ atomic_inc(&flow_cache_genid);
+ rt_genid_bump(net);
+
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_AH, 0);
- else
+ } else
ipv4_redirect(skb, net, 0, 0, IPPROTO_AH, 0);
xfrm_state_put(x);
}
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 47800459e4c..9547a273b9e 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -321,7 +321,7 @@ static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb)
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{
__be32 saddr = 0;
- u8 *dst_ha = NULL;
+ u8 dst_ha[MAX_ADDR_LEN], *dst_hw = NULL;
struct net_device *dev = neigh->dev;
__be32 target = *(__be32 *)neigh->primary_key;
int probes = atomic_read(&neigh->probes);
@@ -363,8 +363,8 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
if (probes < 0) {
if (!(neigh->nud_state & NUD_VALID))
pr_debug("trying to ucast probe in NUD_INVALID\n");
- dst_ha = neigh->ha;
- read_lock_bh(&neigh->lock);
+ neigh_ha_snapshot(dst_ha, neigh, dev);
+ dst_hw = dst_ha;
} else {
probes -= neigh->parms->app_probes;
if (probes < 0) {
@@ -376,9 +376,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
}
arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,
- dst_ha, dev->dev_addr, NULL);
- if (dst_ha)
- read_unlock_bh(&neigh->lock);
+ dst_hw, dev->dev_addr, NULL);
}
static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
@@ -1161,7 +1159,7 @@ int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg)
switch (cmd) {
case SIOCDARP:
case SIOCSARP:
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
case SIOCGARP:
err = copy_from_user(&r, arg, sizeof(struct arpreq));
diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c
index 424fafbc8cb..b28e863fe0a 100644
--- a/net/ipv4/datagram.c
+++ b/net/ipv4/datagram.c
@@ -85,3 +85,28 @@ out:
return err;
}
EXPORT_SYMBOL(ip4_datagram_connect);
+
+void ip4_datagram_release_cb(struct sock *sk)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ip_options_rcu *inet_opt;
+ __be32 daddr = inet->inet_daddr;
+ struct flowi4 fl4;
+ struct rtable *rt;
+
+ if (! __sk_dst_get(sk) || __sk_dst_check(sk, 0))
+ return;
+
+ rcu_read_lock();
+ inet_opt = rcu_dereference(inet->inet_opt);
+ if (inet_opt && inet_opt->opt.srr)
+ daddr = inet_opt->opt.faddr;
+ rt = ip_route_output_ports(sock_net(sk), &fl4, sk, daddr,
+ inet->inet_saddr, inet->inet_dport,
+ inet->inet_sport, sk->sk_protocol,
+ RT_CONN_FLAGS(sk), sk->sk_bound_dev_if);
+ if (!IS_ERR(rt))
+ __sk_dst_set(sk, &rt->dst);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ip4_datagram_release_cb);
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 2a6abc163ed..a8e4f2665d5 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -55,6 +55,7 @@
#include <linux/sysctl.h>
#endif
#include <linux/kmod.h>
+#include <linux/netconf.h>
#include <net/arp.h>
#include <net/ip.h>
@@ -723,7 +724,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCSIFFLAGS:
ret = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto out;
break;
case SIOCSIFADDR: /* Set interface address (and family) */
@@ -731,7 +732,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCSIFDSTADDR: /* Set the destination address */
case SIOCSIFNETMASK: /* Set the netmask for the interface */
ret = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto out;
ret = -EINVAL;
if (sin->sin_family != AF_INET)
@@ -822,9 +823,9 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
if (!ifa) {
ret = -ENOBUFS;
ifa = inet_alloc_ifa();
- INIT_HLIST_NODE(&ifa->hash);
if (!ifa)
break;
+ INIT_HLIST_NODE(&ifa->hash);
if (colon)
memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
else
@@ -1442,6 +1443,155 @@ static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
return 0;
}
+static int inet_netconf_msgsize_devconf(int type)
+{
+ int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
+ + nla_total_size(4); /* NETCONFA_IFINDEX */
+
+ /* type -1 is used for ALL */
+ if (type == -1 || type == NETCONFA_FORWARDING)
+ size += nla_total_size(4);
+ if (type == -1 || type == NETCONFA_RP_FILTER)
+ size += nla_total_size(4);
+ if (type == -1 || type == NETCONFA_MC_FORWARDING)
+ size += nla_total_size(4);
+
+ return size;
+}
+
+static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
+ struct ipv4_devconf *devconf, u32 portid,
+ u32 seq, int event, unsigned int flags,
+ int type)
+{
+ struct nlmsghdr *nlh;
+ struct netconfmsg *ncm;
+
+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
+ flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ncm = nlmsg_data(nlh);
+ ncm->ncm_family = AF_INET;
+
+ if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
+ goto nla_put_failure;
+
+ /* type -1 is used for ALL */
+ if ((type == -1 || type == NETCONFA_FORWARDING) &&
+ nla_put_s32(skb, NETCONFA_FORWARDING,
+ IPV4_DEVCONF(*devconf, FORWARDING)) < 0)
+ goto nla_put_failure;
+ if ((type == -1 || type == NETCONFA_RP_FILTER) &&
+ nla_put_s32(skb, NETCONFA_RP_FILTER,
+ IPV4_DEVCONF(*devconf, RP_FILTER)) < 0)
+ goto nla_put_failure;
+ if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
+ nla_put_s32(skb, NETCONFA_MC_FORWARDING,
+ IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
+ goto nla_put_failure;
+
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+void inet_netconf_notify_devconf(struct net *net, int type, int ifindex,
+ struct ipv4_devconf *devconf)
+{
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
+ RTM_NEWNETCONF, 0, type);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC);
+ return;
+errout:
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err);
+}
+
+static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
+ [NETCONFA_IFINDEX] = { .len = sizeof(int) },
+ [NETCONFA_FORWARDING] = { .len = sizeof(int) },
+ [NETCONFA_RP_FILTER] = { .len = sizeof(int) },
+};
+
+static int inet_netconf_get_devconf(struct sk_buff *in_skb,
+ struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct nlattr *tb[NETCONFA_MAX+1];
+ struct netconfmsg *ncm;
+ struct sk_buff *skb;
+ struct ipv4_devconf *devconf;
+ struct in_device *in_dev;
+ struct net_device *dev;
+ int ifindex;
+ int err;
+
+ err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+ devconf_ipv4_policy);
+ if (err < 0)
+ goto errout;
+
+ err = EINVAL;
+ if (!tb[NETCONFA_IFINDEX])
+ goto errout;
+
+ ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
+ switch (ifindex) {
+ case NETCONFA_IFINDEX_ALL:
+ devconf = net->ipv4.devconf_all;
+ break;
+ case NETCONFA_IFINDEX_DEFAULT:
+ devconf = net->ipv4.devconf_dflt;
+ break;
+ default:
+ dev = __dev_get_by_index(net, ifindex);
+ if (dev == NULL)
+ goto errout;
+ in_dev = __in_dev_get_rtnl(dev);
+ if (in_dev == NULL)
+ goto errout;
+ devconf = &in_dev->cnf;
+ break;
+ }
+
+ err = -ENOBUFS;
+ skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = inet_netconf_fill_devconf(skb, ifindex, devconf,
+ NETLINK_CB(in_skb).portid,
+ nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
+ -1);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+errout:
+ return err;
+}
+
#ifdef CONFIG_SYSCTL
static void devinet_copy_dflt_conf(struct net *net, int i)
@@ -1467,6 +1617,12 @@ static void inet_forward_change(struct net *net)
IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
+ inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv4.devconf_all);
+ inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ NETCONFA_IFINDEX_DEFAULT,
+ net->ipv4.devconf_dflt);
for_each_netdev(net, dev) {
struct in_device *in_dev;
@@ -1474,8 +1630,11 @@ static void inet_forward_change(struct net *net)
dev_disable_lro(dev);
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev);
- if (in_dev)
+ if (in_dev) {
IN_DEV_CONF_SET(in_dev, FORWARDING, on);
+ inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ dev->ifindex, &in_dev->cnf);
+ }
rcu_read_unlock();
}
}
@@ -1501,6 +1660,23 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
if ((new_value == 0) && (old_value != 0))
rt_cache_flush(net);
+ if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
+ new_value != old_value) {
+ int ifindex;
+
+ if (cnf == net->ipv4.devconf_dflt)
+ ifindex = NETCONFA_IFINDEX_DEFAULT;
+ else if (cnf == net->ipv4.devconf_all)
+ ifindex = NETCONFA_IFINDEX_ALL;
+ else {
+ struct in_device *idev =
+ container_of(cnf, struct in_device,
+ cnf);
+ ifindex = idev->dev->ifindex;
+ }
+ inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
+ ifindex, cnf);
+ }
}
return ret;
@@ -1527,15 +1703,23 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
}
if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
inet_forward_change(net);
- } else if (*valp) {
+ } else {
struct ipv4_devconf *cnf = ctl->extra1;
struct in_device *idev =
container_of(cnf, struct in_device, cnf);
- dev_disable_lro(idev->dev);
+ if (*valp)
+ dev_disable_lro(idev->dev);
+ inet_netconf_notify_devconf(net,
+ NETCONFA_FORWARDING,
+ idev->dev->ifindex,
+ cnf);
}
rtnl_unlock();
rt_cache_flush(net);
- }
+ } else
+ inet_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ NETCONFA_IFINDEX_DEFAULT,
+ net->ipv4.devconf_dflt);
}
return ret;
@@ -1809,5 +1993,7 @@ void __init devinet_init(void)
rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
+ rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf,
+ NULL, NULL);
}
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index b61e9deb7c7..3b4f0cd2e63 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -346,7 +346,10 @@ static int esp_input_done2(struct sk_buff *skb, int err)
pskb_trim(skb, skb->len - alen - padlen - 2);
__skb_pull(skb, hlen);
- skb_set_transport_header(skb, -ihl);
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -ihl);
err = nexthdr[1];
@@ -499,9 +502,12 @@ static void esp4_err(struct sk_buff *skb, u32 info)
if (!x)
return;
- if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+ if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+ atomic_inc(&flow_cache_genid);
+ rt_genid_bump(net);
+
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_ESP, 0);
- else
+ } else
ipv4_redirect(skb, net, 0, 0, IPPROTO_ESP, 0);
xfrm_state_put(x);
}
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 825c608826d..5cd75e2dab2 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -488,7 +488,7 @@ int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
switch (cmd) {
case SIOCADDRT: /* Add a route */
case SIOCDELRT: /* Delete a route */
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&rt, arg, sizeof(rt)))
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 71b125cd5db..4797a800faf 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -803,7 +803,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg)
unsigned int bytes;
if (!new_size)
- new_size = 1;
+ new_size = 16;
bytes = new_size * sizeof(struct hlist_head *);
new_info_hash = fib_info_hash_alloc(bytes);
new_laddrhash = fib_info_hash_alloc(bytes);
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index f2eccd53174..17ff9fd7cdd 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -257,7 +257,8 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
struct inet_peer *peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1);
rc = inet_peer_xrlim_allow(peer,
net->ipv4.sysctl_icmp_ratelimit);
- inet_putpeer(peer);
+ if (peer)
+ inet_putpeer(peer);
}
out:
return rc;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index d34ce2972c8..d0670f00d52 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -521,21 +521,31 @@ static inline void syn_ack_recalc(struct request_sock *req, const int thresh,
int *expire, int *resend)
{
if (!rskq_defer_accept) {
- *expire = req->retrans >= thresh;
+ *expire = req->num_timeout >= thresh;
*resend = 1;
return;
}
- *expire = req->retrans >= thresh &&
- (!inet_rsk(req)->acked || req->retrans >= max_retries);
+ *expire = req->num_timeout >= thresh &&
+ (!inet_rsk(req)->acked || req->num_timeout >= max_retries);
/*
* Do not resend while waiting for data after ACK,
* start to resend on end of deferring period to give
* last chance for data or ACK to create established socket.
*/
*resend = !inet_rsk(req)->acked ||
- req->retrans >= rskq_defer_accept - 1;
+ req->num_timeout >= rskq_defer_accept - 1;
}
+int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req)
+{
+ int err = req->rsk_ops->rtx_syn_ack(parent, req, NULL);
+
+ if (!err)
+ req->num_retrans++;
+ return err;
+}
+EXPORT_SYMBOL(inet_rtx_syn_ack);
+
void inet_csk_reqsk_queue_prune(struct sock *parent,
const unsigned long interval,
const unsigned long timeout,
@@ -599,13 +609,14 @@ void inet_csk_reqsk_queue_prune(struct sock *parent,
req->rsk_ops->syn_ack_timeout(parent, req);
if (!expire &&
(!resend ||
- !req->rsk_ops->rtx_syn_ack(parent, req, NULL) ||
+ !inet_rtx_syn_ack(parent, req) ||
inet_rsk(req)->acked)) {
unsigned long timeo;
- if (req->retrans++ == 0)
+ if (req->num_timeout++ == 0)
lopt->qlen_young--;
- timeo = min((timeout << req->retrans), max_rto);
+ timeo = min(timeout << req->num_timeout,
+ max_rto);
req->expires = now + timeo;
reqp = &req->dl_next;
continue;
@@ -699,6 +710,22 @@ void inet_csk_destroy_sock(struct sock *sk)
}
EXPORT_SYMBOL(inet_csk_destroy_sock);
+/* This function allows to force a closure of a socket after the call to
+ * tcp/dccp_create_openreq_child().
+ */
+void inet_csk_prepare_forced_close(struct sock *sk)
+{
+ /* sk_clone_lock locked the socket and set refcnt to 2 */
+ bh_unlock_sock(sk);
+ sock_put(sk);
+
+ /* The below has to be done to allow calling inet_csk_destroy_sock */
+ sock_set_flag(sk, SOCK_DEAD);
+ percpu_counter_inc(sk->sk_prot->orphan_count);
+ inet_sk(sk)->inet_num = 0;
+}
+EXPORT_SYMBOL(inet_csk_prepare_forced_close);
+
int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
{
struct inet_sock *inet = inet_sk(sk);
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 535584c00f9..7afa2c3c788 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -44,6 +44,10 @@ struct inet_diag_entry {
u16 dport;
u16 family;
u16 userlocks;
+#if IS_ENABLED(CONFIG_IPV6)
+ struct in6_addr saddr_storage; /* for IPv4-mapped-IPv6 addresses */
+ struct in6_addr daddr_storage; /* for IPv4-mapped-IPv6 addresses */
+#endif
};
static DEFINE_MUTEX(inet_diag_table_mutex);
@@ -105,6 +109,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk,
r->id.idiag_src[0] = inet->inet_rcv_saddr;
r->id.idiag_dst[0] = inet->inet_daddr;
+ if (nla_put_u8(skb, INET_DIAG_SHUTDOWN, sk->sk_shutdown))
+ goto errout;
+
/* IPv6 dual-stack sockets use inet->tos for IPv4 connections,
* hence this needs to be included regardless of socket family.
*/
@@ -428,25 +435,31 @@ static int inet_diag_bc_run(const struct nlattr *_bc,
break;
}
- if (cond->prefix_len == 0)
- break;
-
if (op->code == INET_DIAG_BC_S_COND)
addr = entry->saddr;
else
addr = entry->daddr;
+ if (cond->family != AF_UNSPEC &&
+ cond->family != entry->family) {
+ if (entry->family == AF_INET6 &&
+ cond->family == AF_INET) {
+ if (addr[0] == 0 && addr[1] == 0 &&
+ addr[2] == htonl(0xffff) &&
+ bitstring_match(addr + 3,
+ cond->addr,
+ cond->prefix_len))
+ break;
+ }
+ yes = 0;
+ break;
+ }
+
+ if (cond->prefix_len == 0)
+ break;
if (bitstring_match(addr, cond->addr,
cond->prefix_len))
break;
- if (entry->family == AF_INET6 &&
- cond->family == AF_INET) {
- if (addr[0] == 0 && addr[1] == 0 &&
- addr[2] == htonl(0xffff) &&
- bitstring_match(addr + 3, cond->addr,
- cond->prefix_len))
- break;
- }
yes = 0;
break;
}
@@ -509,6 +522,55 @@ static int valid_cc(const void *bc, int len, int cc)
return 0;
}
+/* Validate an inet_diag_hostcond. */
+static bool valid_hostcond(const struct inet_diag_bc_op *op, int len,
+ int *min_len)
+{
+ int addr_len;
+ struct inet_diag_hostcond *cond;
+
+ /* Check hostcond space. */
+ *min_len += sizeof(struct inet_diag_hostcond);
+ if (len < *min_len)
+ return false;
+ cond = (struct inet_diag_hostcond *)(op + 1);
+
+ /* Check address family and address length. */
+ switch (cond->family) {
+ case AF_UNSPEC:
+ addr_len = 0;
+ break;
+ case AF_INET:
+ addr_len = sizeof(struct in_addr);
+ break;
+ case AF_INET6:
+ addr_len = sizeof(struct in6_addr);
+ break;
+ default:
+ return false;
+ }
+ *min_len += addr_len;
+ if (len < *min_len)
+ return false;
+
+ /* Check prefix length (in bits) vs address length (in bytes). */
+ if (cond->prefix_len > 8 * addr_len)
+ return false;
+
+ return true;
+}
+
+/* Validate a port comparison operator. */
+static inline bool valid_port_comparison(const struct inet_diag_bc_op *op,
+ int len, int *min_len)
+{
+ /* Port comparisons put the port in a follow-on inet_diag_bc_op. */
+ *min_len += sizeof(struct inet_diag_bc_op);
+ if (len < *min_len)
+ return false;
+ return true;
+}
+
static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
{
const void *bc = bytecode;
@@ -516,29 +578,39 @@ static int inet_diag_bc_audit(const void *bytecode, int bytecode_len)
while (len > 0) {
const struct inet_diag_bc_op *op = bc;
+ int min_len = sizeof(struct inet_diag_bc_op);
//printk("BC: %d %d %d {%d} / %d\n", op->code, op->yes, op->no, op[1].no, len);
switch (op->code) {
- case INET_DIAG_BC_AUTO:
case INET_DIAG_BC_S_COND:
case INET_DIAG_BC_D_COND:
+ if (!valid_hostcond(bc, len, &min_len))
+ return -EINVAL;
+ break;
case INET_DIAG_BC_S_GE:
case INET_DIAG_BC_S_LE:
case INET_DIAG_BC_D_GE:
case INET_DIAG_BC_D_LE:
- case INET_DIAG_BC_JMP:
- if (op->no < 4 || op->no > len + 4 || op->no & 3)
- return -EINVAL;
- if (op->no < len &&
- !valid_cc(bytecode, bytecode_len, len - op->no))
+ if (!valid_port_comparison(bc, len, &min_len))
return -EINVAL;
break;
+ case INET_DIAG_BC_AUTO:
+ case INET_DIAG_BC_JMP:
case INET_DIAG_BC_NOP:
break;
default:
return -EINVAL;
}
- if (op->yes < 4 || op->yes > len + 4 || op->yes & 3)
+
+ if (op->code != INET_DIAG_BC_NOP) {
+ if (op->no < min_len || op->no > len + 4 || op->no & 3)
+ return -EINVAL;
+ if (op->no < len &&
+ !valid_cc(bytecode, bytecode_len, len - op->no))
+ return -EINVAL;
+ }
+
+ if (op->yes < min_len || op->yes > len + 4 || op->yes & 3)
return -EINVAL;
bc += op->yes;
len -= op->yes;
@@ -596,6 +668,36 @@ static int inet_twsk_diag_dump(struct inet_timewait_sock *tw,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh);
}
+/* Get the IPv4, IPv6, or IPv4-mapped-IPv6 local and remote addresses
+ * from a request_sock. For IPv4-mapped-IPv6 we must map IPv4 to IPv6.
+ */
+static inline void inet_diag_req_addrs(const struct sock *sk,
+ const struct request_sock *req,
+ struct inet_diag_entry *entry)
+{
+ struct inet_request_sock *ireq = inet_rsk(req);
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6) {
+ if (req->rsk_ops->family == AF_INET6) {
+ entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32;
+ entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32;
+ } else if (req->rsk_ops->family == AF_INET) {
+ ipv6_addr_set_v4mapped(ireq->loc_addr,
+ &entry->saddr_storage);
+ ipv6_addr_set_v4mapped(ireq->rmt_addr,
+ &entry->daddr_storage);
+ entry->saddr = entry->saddr_storage.s6_addr32;
+ entry->daddr = entry->daddr_storage.s6_addr32;
+ }
+ } else
+#endif
+ {
+ entry->saddr = &ireq->loc_addr;
+ entry->daddr = &ireq->rmt_addr;
+ }
+}
+
static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
struct request_sock *req,
struct user_namespace *user_ns,
@@ -617,7 +719,7 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
r->idiag_family = sk->sk_family;
r->idiag_state = TCP_SYN_RECV;
r->idiag_timer = 1;
- r->idiag_retrans = req->retrans;
+ r->idiag_retrans = req->num_retrans;
r->id.idiag_if = sk->sk_bound_dev_if;
sock_diag_save_cookie(req, r->id.idiag_cookie);
@@ -637,8 +739,10 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk,
r->idiag_inode = 0;
#if IS_ENABLED(CONFIG_IPV6)
if (r->idiag_family == AF_INET6) {
- *(struct in6_addr *)r->id.idiag_src = inet6_rsk(req)->loc_addr;
- *(struct in6_addr *)r->id.idiag_dst = inet6_rsk(req)->rmt_addr;
+ struct inet_diag_entry entry;
+ inet_diag_req_addrs(sk, req, &entry);
+ memcpy(r->id.idiag_src, entry.saddr, sizeof(struct in6_addr));
+ memcpy(r->id.idiag_dst, entry.daddr, sizeof(struct in6_addr));
}
#endif
@@ -691,18 +795,7 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk,
continue;
if (bc) {
- entry.saddr =
-#if IS_ENABLED(CONFIG_IPV6)
- (entry.family == AF_INET6) ?
- inet6_rsk(req)->loc_addr.s6_addr32 :
-#endif
- &ireq->loc_addr;
- entry.daddr =
-#if IS_ENABLED(CONFIG_IPV6)
- (entry.family == AF_INET6) ?
- inet6_rsk(req)->rmt_addr.s6_addr32 :
-#endif
- &ireq->rmt_addr;
+ inet_diag_req_addrs(sk, req, &entry);
entry.dport = ntohs(ireq->rmt_port);
if (!inet_diag_bc_run(bc, &entry))
@@ -892,13 +985,16 @@ static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct inet_diag_req_v2 *r, struct nlattr *bc)
{
const struct inet_diag_handler *handler;
+ int err = 0;
handler = inet_diag_lock_handler(r->sdiag_protocol);
if (!IS_ERR(handler))
handler->dump(skb, cb, r, bc);
+ else
+ err = PTR_ERR(handler);
inet_diag_unlock_handler(handler);
- return skb->len;
+ return err ? : skb->len;
}
static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 7880af97020..fa3ae814871 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -237,12 +237,14 @@ struct sock *__inet_lookup_established(struct net *net,
rcu_read_lock();
begin:
sk_nulls_for_each_rcu(sk, node, &head->chain) {
- if (INET_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif)) {
+ if (sk->sk_hash != hash)
+ continue;
+ if (likely(INET_MATCH(sk, net, acookie,
+ saddr, daddr, ports, dif))) {
if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
goto begintw;
- if (unlikely(!INET_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif))) {
+ if (unlikely(!INET_MATCH(sk, net, acookie,
+ saddr, daddr, ports, dif))) {
sock_put(sk);
goto begin;
}
@@ -260,14 +262,18 @@ begin:
begintw:
/* Must check for a TIME_WAIT'er before going to listener hash. */
sk_nulls_for_each_rcu(sk, node, &head->twchain) {
- if (INET_TW_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif)) {
+ if (sk->sk_hash != hash)
+ continue;
+ if (likely(INET_TW_MATCH(sk, net, acookie,
+ saddr, daddr, ports,
+ dif))) {
if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
sk = NULL;
goto out;
}
- if (unlikely(!INET_TW_MATCH(sk, net, hash, acookie,
- saddr, daddr, ports, dif))) {
+ if (unlikely(!INET_TW_MATCH(sk, net, acookie,
+ saddr, daddr, ports,
+ dif))) {
sock_put(sk);
goto begintw;
}
@@ -314,10 +320,12 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
/* Check TIME-WAIT sockets first. */
sk_nulls_for_each(sk2, node, &head->twchain) {
- tw = inet_twsk(sk2);
+ if (sk2->sk_hash != hash)
+ continue;
- if (INET_TW_MATCH(sk2, net, hash, acookie,
- saddr, daddr, ports, dif)) {
+ if (likely(INET_TW_MATCH(sk2, net, acookie,
+ saddr, daddr, ports, dif))) {
+ tw = inet_twsk(sk2);
if (twsk_unique(sk, sk2, twp))
goto unique;
else
@@ -328,8 +336,10 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
/* And established part... */
sk_nulls_for_each(sk2, node, &head->chain) {
- if (INET_MATCH(sk2, net, hash, acookie,
- saddr, daddr, ports, dif))
+ if (sk2->sk_hash != hash)
+ continue;
+ if (likely(INET_MATCH(sk2, net, acookie,
+ saddr, daddr, ports, dif)))
goto not_unique;
}
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 448e6854682..eb9d63a570c 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -707,28 +707,27 @@ EXPORT_SYMBOL(ip_defrag);
struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
{
- const struct iphdr *iph;
+ struct iphdr iph;
u32 len;
if (skb->protocol != htons(ETH_P_IP))
return skb;
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ if (!skb_copy_bits(skb, 0, &iph, sizeof(iph)))
return skb;
- iph = ip_hdr(skb);
- if (iph->ihl < 5 || iph->version != 4)
+ if (iph.ihl < 5 || iph.version != 4)
return skb;
- if (!pskb_may_pull(skb, iph->ihl*4))
- return skb;
- iph = ip_hdr(skb);
- len = ntohs(iph->tot_len);
- if (skb->len < len || len < (iph->ihl * 4))
+
+ len = ntohs(iph.tot_len);
+ if (skb->len < len || len < (iph.ihl * 4))
return skb;
- if (ip_is_fragment(ip_hdr(skb))) {
+ if (ip_is_fragment(&iph)) {
skb = skb_share_check(skb, GFP_ATOMIC);
if (skb) {
+ if (!pskb_may_pull(skb, iph.ihl*4))
+ return skb;
if (pskb_trim_rcsum(skb, len))
return skb;
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
@@ -802,6 +801,10 @@ static int __net_init ip4_frags_ns_ctl_register(struct net *net)
table[0].data = &net->ipv4.frags.high_thresh;
table[1].data = &net->ipv4.frags.low_thresh;
table[2].data = &net->ipv4.frags.timeout;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
}
hdr = register_net_sysctl(net, "net/ipv4", table);
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 7240f8e2dd4..e81b1caf2ea 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -164,21 +164,6 @@ struct ipgre_net {
#define tunnels_r tunnels[2]
#define tunnels_l tunnels[1]
#define tunnels_wc tunnels[0]
-/*
- * Locking : hash tables are protected by RCU and RTNL
- */
-
-#define for_each_ip_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
-
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- u64 rx_packets;
- u64 rx_bytes;
- u64 tx_packets;
- u64 tx_bytes;
- struct u64_stats_sync syncp;
-};
static struct rtnl_link_stats64 *ipgre_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
@@ -250,7 +235,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
ARPHRD_ETHER : ARPHRD_IPGRE;
int score, cand_score = 4;
- for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
if (local != t->parms.iph.saddr ||
remote != t->parms.iph.daddr ||
!(t->dev->flags & IFF_UP))
@@ -277,7 +262,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
if (remote != t->parms.iph.daddr ||
!(t->dev->flags & IFF_UP))
continue;
@@ -303,7 +288,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
if ((local != t->parms.iph.saddr &&
(local != t->parms.iph.daddr ||
!ipv4_is_multicast(local))) ||
@@ -331,7 +316,7 @@ static struct ip_tunnel *ipgre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
if (t->parms.i_key != key ||
!(t->dev->flags & IFF_UP))
continue;
@@ -753,7 +738,6 @@ drop:
static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- struct pcpu_tstats *tstats;
const struct iphdr *old_iph = ip_hdr(skb);
const struct iphdr *tiph;
struct flowi4 fl4;
@@ -766,6 +750,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
int gre_hlen;
__be32 dst;
int mtu;
+ u8 ttl;
if (skb->ip_summed == CHECKSUM_PARTIAL &&
skb_checksum_help(skb))
@@ -776,7 +761,10 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
if (dev->header_ops && dev->type == ARPHRD_IPGRE) {
gre_hlen = 0;
- tiph = (const struct iphdr *)skb->data;
+ if (skb->protocol == htons(ETH_P_IP))
+ tiph = (const struct iphdr *)skb->data;
+ else
+ tiph = &tunnel->parms.iph;
} else {
gre_hlen = tunnel->hlen;
tiph = &tunnel->parms.iph;
@@ -828,6 +816,7 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
goto tx_error;
}
+ ttl = tiph->ttl;
tos = tiph->tos;
if (tos == 1) {
tos = 0;
@@ -920,11 +909,12 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
dev_kfree_skb(skb);
skb = new_skb;
old_iph = ip_hdr(skb);
+ /* Warning : tiph value might point to freed memory */
}
- skb_reset_transport_header(skb);
skb_push(skb, gre_hlen);
skb_reset_network_header(skb);
+ skb_set_transport_header(skb, sizeof(*iph));
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
@@ -943,8 +933,9 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
iph->tos = ipgre_ecn_encapsulate(tos, old_iph, skb);
iph->daddr = fl4.daddr;
iph->saddr = fl4.saddr;
+ iph->ttl = ttl;
- if ((iph->ttl = tiph->ttl) == 0) {
+ if (ttl == 0) {
if (skb->protocol == htons(ETH_P_IP))
iph->ttl = old_iph->ttl;
#if IS_ENABLED(CONFIG_IPV6)
@@ -972,14 +963,16 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev
ptr--;
}
if (tunnel->parms.o_flags&GRE_CSUM) {
+ int offset = skb_transport_offset(skb);
+
*ptr = 0;
- *(__sum16 *)ptr = ip_compute_csum((void *)(iph+1), skb->len - sizeof(struct iphdr));
+ *(__sum16 *)ptr = csum_fold(skb_checksum(skb, offset,
+ skb->len - offset,
+ 0));
}
}
- nf_reset(skb);
- tstats = this_cpu_ptr(dev->tstats);
- __IPTUNNEL_XMIT(tstats, &dev->stats);
+ iptunnel_xmit(skb, dev);
return NETDEV_TX_OK;
#if IS_ENABLED(CONFIG_IPV6)
@@ -1082,7 +1075,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -1157,7 +1150,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
if (dev == ign->fb_tunnel_dev) {
diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c
index 1dc01f9793d..f6289bf6f33 100644
--- a/net/ipv4/ip_options.c
+++ b/net/ipv4/ip_options.c
@@ -409,7 +409,7 @@ int ip_options_compile(struct net *net,
optptr[2] += 8;
break;
default:
- if (!skb && !capable(CAP_NET_RAW)) {
+ if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
pp_ptr = optptr + 3;
goto error;
}
@@ -445,7 +445,7 @@ int ip_options_compile(struct net *net,
opt->router_alert = optptr - iph;
break;
case IPOPT_CIPSO:
- if ((!skb && !capable(CAP_NET_RAW)) || opt->cipso) {
+ if ((!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) || opt->cipso) {
pp_ptr = optptr;
goto error;
}
@@ -458,7 +458,7 @@ int ip_options_compile(struct net *net,
case IPOPT_SEC:
case IPOPT_SID:
default:
- if (!skb && !capable(CAP_NET_RAW)) {
+ if (!skb && !ns_capable(net->user_ns, CAP_NET_RAW)) {
pp_ptr = optptr;
goto error;
}
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 6537a408a4f..3e98ed2bff5 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -595,6 +595,10 @@ slow_path_clean:
}
slow_path:
+ /* for offloaded checksums cleanup checksum before fragmentation */
+ if ((skb->ip_summed == CHECKSUM_PARTIAL) && skb_checksum_help(skb))
+ goto fail;
+
left = skb->len - hlen; /* Space per frame */
ptr = hlen; /* Where to start from */
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index 5eea4a81104..d9c4f113d70 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -457,19 +457,28 @@ static int do_ip_setsockopt(struct sock *sk, int level,
struct inet_sock *inet = inet_sk(sk);
int val = 0, err;
- if (((1<<optname) & ((1<<IP_PKTINFO) | (1<<IP_RECVTTL) |
- (1<<IP_RECVOPTS) | (1<<IP_RECVTOS) |
- (1<<IP_RETOPTS) | (1<<IP_TOS) |
- (1<<IP_TTL) | (1<<IP_HDRINCL) |
- (1<<IP_MTU_DISCOVER) | (1<<IP_RECVERR) |
- (1<<IP_ROUTER_ALERT) | (1<<IP_FREEBIND) |
- (1<<IP_PASSSEC) | (1<<IP_TRANSPARENT) |
- (1<<IP_MINTTL) | (1<<IP_NODEFRAG))) ||
- optname == IP_UNICAST_IF ||
- optname == IP_MULTICAST_TTL ||
- optname == IP_MULTICAST_ALL ||
- optname == IP_MULTICAST_LOOP ||
- optname == IP_RECVORIGDSTADDR) {
+ switch (optname) {
+ case IP_PKTINFO:
+ case IP_RECVTTL:
+ case IP_RECVOPTS:
+ case IP_RECVTOS:
+ case IP_RETOPTS:
+ case IP_TOS:
+ case IP_TTL:
+ case IP_HDRINCL:
+ case IP_MTU_DISCOVER:
+ case IP_RECVERR:
+ case IP_ROUTER_ALERT:
+ case IP_FREEBIND:
+ case IP_PASSSEC:
+ case IP_TRANSPARENT:
+ case IP_MINTTL:
+ case IP_NODEFRAG:
+ case IP_UNICAST_IF:
+ case IP_MULTICAST_TTL:
+ case IP_MULTICAST_ALL:
+ case IP_MULTICAST_LOOP:
+ case IP_RECVORIGDSTADDR:
if (optlen >= sizeof(int)) {
if (get_user(val, (int __user *) optval))
return -EFAULT;
@@ -581,7 +590,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
case IP_TTL:
if (optlen < 1)
goto e_inval;
- if (val != -1 && (val < 0 || val > 255))
+ if (val != -1 && (val < 1 || val > 255))
goto e_inval;
inet->uc_ttl = val;
break;
@@ -980,13 +989,14 @@ mc_msf_out:
case IP_IPSEC_POLICY:
case IP_XFRM_POLICY:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
break;
err = xfrm_user_policy(sk, optname, optval, optlen);
break;
case IP_TRANSPARENT:
- if (!!val && !capable(CAP_NET_RAW) && !capable(CAP_NET_ADMIN)) {
+ if (!!val && !ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
+ !ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) {
err = -EPERM;
break;
}
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 1831092f999..c3a4233c0ac 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -66,20 +66,6 @@ static void vti_tunnel_setup(struct net_device *dev);
static void vti_dev_free(struct net_device *dev);
static int vti_tunnel_bind_dev(struct net_device *dev);
-/* Locking : hash tables are protected by RCU and RTNL */
-
-#define for_each_ip_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
-
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- u64 rx_packets;
- u64 rx_bytes;
- u64 tx_packets;
- u64 tx_bytes;
- struct u64_stats_sync syncp;
-};
-
#define VTI_XMIT(stats1, stats2) do { \
int err; \
int pkt_len = skb->len; \
@@ -142,19 +128,19 @@ static struct ip_tunnel *vti_tunnel_lookup(struct net *net,
struct ip_tunnel *t;
struct vti_net *ipn = net_generic(net, vti_net_id);
- for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1])
if (local == t->parms.iph.saddr &&
remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
return t;
- for_each_ip_tunnel_rcu(ipn->tunnels_r[h0])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0])
if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
return t;
- for_each_ip_tunnel_rcu(ipn->tunnels_l[h1])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1])
if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
return t;
- for_each_ip_tunnel_rcu(ipn->tunnels_wc[0])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_wc[0])
if (t && (t->dev->flags&IFF_UP))
return t;
return NULL;
@@ -338,12 +324,17 @@ static int vti_rcv(struct sk_buff *skb)
if (tunnel != NULL) {
struct pcpu_tstats *tstats;
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
+ return -1;
+
tstats = this_cpu_ptr(tunnel->dev->tstats);
u64_stats_update_begin(&tstats->syncp);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
u64_stats_update_end(&tstats->syncp);
+ skb->mark = 0;
+ secpath_reset(skb);
skb->dev = tunnel->dev;
return 1;
}
@@ -497,7 +488,7 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -562,7 +553,7 @@ vti_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
if (dev == ipn->fb_tunnel_dev) {
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index d3ab47e19a8..9a46daed2f3 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -47,9 +47,12 @@ static void ipcomp4_err(struct sk_buff *skb, u32 info)
if (!x)
return;
- if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH)
+ if (icmp_hdr(skb)->type == ICMP_DEST_UNREACH) {
+ atomic_inc(&flow_cache_genid);
+ rt_genid_bump(net);
+
ipv4_update_pmtu(skb, net, info, 0, 0, IPPROTO_COMP, 0);
- else
+ } else
ipv4_redirect(skb, net, 0, 0, IPPROTO_COMP, 0);
xfrm_state_put(x);
}
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 798358b1071..a2e50ae80b5 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -136,6 +136,8 @@ __be32 ic_myaddr = NONE; /* My IP address */
static __be32 ic_netmask = NONE; /* Netmask for local subnet */
__be32 ic_gateway = NONE; /* Gateway IP address */
+__be32 ic_addrservaddr = NONE; /* IP Address of the IP addresses'server */
+
__be32 ic_servaddr = NONE; /* Boot server IP address */
__be32 root_server_addr = NONE; /* Address of NFS server */
@@ -558,6 +560,7 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
if (ic_myaddr == NONE)
ic_myaddr = tip;
ic_servaddr = sip;
+ ic_addrservaddr = sip;
ic_got_reply = IC_RARP;
drop_unlock:
@@ -1068,7 +1071,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
ic_servaddr = server_id;
#ifdef IPCONFIG_DEBUG
printk("DHCP: Offered address %pI4 by server %pI4\n",
- &ic_myaddr, &ic_servaddr);
+ &ic_myaddr, &b->iph.saddr);
#endif
/* The DHCP indicated server address takes
* precedence over the bootp header one if
@@ -1113,6 +1116,7 @@ static int __init ic_bootp_recv(struct sk_buff *skb, struct net_device *dev, str
ic_dev = dev;
ic_myaddr = b->your_ip;
ic_servaddr = b->server_ip;
+ ic_addrservaddr = b->iph.saddr;
if (ic_gateway == NONE && b->relay_ip)
ic_gateway = b->relay_ip;
if (ic_nameservers[0] == NONE)
@@ -1268,7 +1272,7 @@ static int __init ic_dynamic(void)
printk("IP-Config: Got %s answer from %pI4, ",
((ic_got_reply & IC_RARP) ? "RARP"
: (ic_proto_enabled & IC_USE_DHCP) ? "DHCP" : "BOOTP"),
- &ic_servaddr);
+ &ic_addrservaddr);
pr_cont("my address is %pI4\n", &ic_myaddr);
return 0;
@@ -1500,8 +1504,10 @@ static int __init ip_auto_config(void)
* Clue in the operator.
*/
pr_info("IP-Config: Complete:\n");
- pr_info(" device=%s, addr=%pI4, mask=%pI4, gw=%pI4\n",
- ic_dev->name, &ic_myaddr, &ic_netmask, &ic_gateway);
+
+ pr_info(" device=%s, hwaddr=%*phC, ipaddr=%pI4, mask=%pI4, gw=%pI4\n",
+ ic_dev->name, ic_dev->addr_len, ic_dev->dev_addr,
+ &ic_myaddr, &ic_netmask, &ic_gateway);
pr_info(" host=%s, domain=%s, nis-domain=%s\n",
utsname()->nodename, ic_domain, utsname()->domainname);
pr_info(" bootserver=%pI4, rootserver=%pI4, rootpath=%s",
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index e15b45297c0..191fc24a745 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -138,22 +138,7 @@ struct ipip_net {
static int ipip_tunnel_init(struct net_device *dev);
static void ipip_tunnel_setup(struct net_device *dev);
static void ipip_dev_free(struct net_device *dev);
-
-/*
- * Locking : hash tables are protected by RCU and RTNL
- */
-
-#define for_each_ip_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
-
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- u64 rx_packets;
- u64 rx_bytes;
- u64 tx_packets;
- u64 tx_bytes;
- struct u64_stats_sync syncp;
-};
+static struct rtnl_link_ops ipip_link_ops __read_mostly;
static struct rtnl_link_stats64 *ipip_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
@@ -197,16 +182,16 @@ static struct ip_tunnel *ipip_tunnel_lookup(struct net *net,
struct ip_tunnel *t;
struct ipip_net *ipn = net_generic(net, ipip_net_id);
- for_each_ip_tunnel_rcu(ipn->tunnels_r_l[h0 ^ h1])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_r_l[h0 ^ h1])
if (local == t->parms.iph.saddr &&
remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
return t;
- for_each_ip_tunnel_rcu(ipn->tunnels_r[h0])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_r[h0])
if (remote == t->parms.iph.daddr && (t->dev->flags&IFF_UP))
return t;
- for_each_ip_tunnel_rcu(ipn->tunnels_l[h1])
+ for_each_ip_tunnel_rcu(t, ipn->tunnels_l[h1])
if (local == t->parms.iph.saddr && (t->dev->flags&IFF_UP))
return t;
@@ -264,6 +249,32 @@ static void ipip_tunnel_link(struct ipip_net *ipn, struct ip_tunnel *t)
rcu_assign_pointer(*tp, t);
}
+static int ipip_tunnel_create(struct net_device *dev)
+{
+ struct ip_tunnel *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct ipip_net *ipn = net_generic(net, ipip_net_id);
+ int err;
+
+ err = ipip_tunnel_init(dev);
+ if (err < 0)
+ goto out;
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ goto out;
+
+ strcpy(t->parms.name, dev->name);
+ dev->rtnl_link_ops = &ipip_link_ops;
+
+ dev_hold(dev);
+ ipip_tunnel_link(ipn, t);
+ return 0;
+
+out:
+ return err;
+}
+
static struct ip_tunnel *ipip_tunnel_locate(struct net *net,
struct ip_tunnel_parm *parms, int create)
{
@@ -298,16 +309,9 @@ static struct ip_tunnel *ipip_tunnel_locate(struct net *net,
nt = netdev_priv(dev);
nt->parms = *parms;
- if (ipip_tunnel_init(dev) < 0)
+ if (ipip_tunnel_create(dev) < 0)
goto failed_free;
- if (register_netdevice(dev) < 0)
- goto failed_free;
-
- strcpy(nt->parms.name, dev->name);
-
- dev_hold(dev);
- ipip_tunnel_link(ipn, nt);
return nt;
failed_free:
@@ -463,7 +467,6 @@ drop:
static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- struct pcpu_tstats *tstats;
const struct iphdr *tiph = &tunnel->parms.iph;
u8 tos = tunnel->parms.iph.tos;
__be16 df = tiph->frag_off;
@@ -479,6 +482,10 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->protocol != htons(ETH_P_IP))
goto tx_error;
+ if (skb->ip_summed == CHECKSUM_PARTIAL &&
+ skb_checksum_help(skb))
+ goto tx_error;
+
if (tos & 1)
tos = old_iph->tos;
@@ -586,9 +593,7 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
if ((iph->ttl = tiph->ttl) == 0)
iph->ttl = old_iph->ttl;
- nf_reset(skb);
- tstats = this_cpu_ptr(dev->tstats);
- __IPTUNNEL_XMIT(tstats, &dev->stats);
+ iptunnel_xmit(skb, dev);
return NETDEV_TX_OK;
tx_error_icmp:
@@ -635,6 +640,28 @@ static void ipip_tunnel_bind_dev(struct net_device *dev)
dev->iflink = tunnel->parms.link;
}
+static void ipip_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
+{
+ struct net *net = dev_net(t->dev);
+ struct ipip_net *ipn = net_generic(net, ipip_net_id);
+
+ ipip_tunnel_unlink(ipn, t);
+ synchronize_net();
+ t->parms.iph.saddr = p->iph.saddr;
+ t->parms.iph.daddr = p->iph.daddr;
+ memcpy(t->dev->dev_addr, &p->iph.saddr, 4);
+ memcpy(t->dev->broadcast, &p->iph.daddr, 4);
+ ipip_tunnel_link(ipn, t);
+ t->parms.iph.ttl = p->iph.ttl;
+ t->parms.iph.tos = p->iph.tos;
+ t->parms.iph.frag_off = p->iph.frag_off;
+ if (t->parms.link != p->link) {
+ t->parms.link = p->link;
+ ipip_tunnel_bind_dev(t->dev);
+ }
+ netdev_state_change(t->dev);
+}
+
static int
ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
@@ -664,7 +691,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -693,29 +720,13 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
t = netdev_priv(dev);
- ipip_tunnel_unlink(ipn, t);
- synchronize_net();
- t->parms.iph.saddr = p.iph.saddr;
- t->parms.iph.daddr = p.iph.daddr;
- memcpy(dev->dev_addr, &p.iph.saddr, 4);
- memcpy(dev->broadcast, &p.iph.daddr, 4);
- ipip_tunnel_link(ipn, t);
- netdev_state_change(dev);
}
+
+ ipip_tunnel_update(t, &p);
}
if (t) {
err = 0;
- if (cmd == SIOCCHGTUNNEL) {
- t->parms.iph.ttl = p.iph.ttl;
- t->parms.iph.tos = p.iph.tos;
- t->parms.iph.frag_off = p.iph.frag_off;
- if (t->parms.link != p.link) {
- t->parms.link = p.link;
- ipip_tunnel_bind_dev(dev);
- netdev_state_change(dev);
- }
- }
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
err = -EFAULT;
} else
@@ -724,7 +735,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
if (dev == ipn->fb_tunnel_dev) {
@@ -773,6 +784,11 @@ static void ipip_dev_free(struct net_device *dev)
free_netdev(dev);
}
+#define IPIP_FEATURES (NETIF_F_SG | \
+ NETIF_F_FRAGLIST | \
+ NETIF_F_HIGHDMA | \
+ NETIF_F_HW_CSUM)
+
static void ipip_tunnel_setup(struct net_device *dev)
{
dev->netdev_ops = &ipip_netdev_ops;
@@ -787,6 +803,9 @@ static void ipip_tunnel_setup(struct net_device *dev)
dev->features |= NETIF_F_NETNS_LOCAL;
dev->features |= NETIF_F_LLTX;
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+
+ dev->features |= IPIP_FEATURES;
+ dev->hw_features |= IPIP_FEATURES;
}
static int ipip_tunnel_init(struct net_device *dev)
@@ -829,6 +848,142 @@ static int __net_init ipip_fb_tunnel_init(struct net_device *dev)
return 0;
}
+static void ipip_netlink_parms(struct nlattr *data[],
+ struct ip_tunnel_parm *parms)
+{
+ memset(parms, 0, sizeof(*parms));
+
+ parms->iph.version = 4;
+ parms->iph.protocol = IPPROTO_IPIP;
+ parms->iph.ihl = 5;
+
+ if (!data)
+ return;
+
+ if (data[IFLA_IPTUN_LINK])
+ parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
+
+ if (data[IFLA_IPTUN_LOCAL])
+ parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]);
+
+ if (data[IFLA_IPTUN_REMOTE])
+ parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]);
+
+ if (data[IFLA_IPTUN_TTL]) {
+ parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
+ if (parms->iph.ttl)
+ parms->iph.frag_off = htons(IP_DF);
+ }
+
+ if (data[IFLA_IPTUN_TOS])
+ parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);
+
+ if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
+ parms->iph.frag_off = htons(IP_DF);
+}
+
+static int ipip_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct net *net = dev_net(dev);
+ struct ip_tunnel *nt;
+
+ nt = netdev_priv(dev);
+ ipip_netlink_parms(data, &nt->parms);
+
+ if (ipip_tunnel_locate(net, &nt->parms, 0))
+ return -EEXIST;
+
+ return ipip_tunnel_create(dev);
+}
+
+static int ipip_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct ip_tunnel *t;
+ struct ip_tunnel_parm p;
+ struct net *net = dev_net(dev);
+ struct ipip_net *ipn = net_generic(net, ipip_net_id);
+
+ if (dev == ipn->fb_tunnel_dev)
+ return -EINVAL;
+
+ ipip_netlink_parms(data, &p);
+
+ if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
+ (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
+ return -EINVAL;
+
+ t = ipip_tunnel_locate(net, &p, 0);
+
+ if (t) {
+ if (t->dev != dev)
+ return -EEXIST;
+ } else
+ t = netdev_priv(dev);
+
+ ipip_tunnel_update(t, &p);
+ return 0;
+}
+
+static size_t ipip_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_IPTUN_LINK */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_LOCAL */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_REMOTE */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_TTL */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_TOS */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_PMTUDISC */
+ nla_total_size(1) +
+ 0;
+}
+
+static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+ struct ip_tunnel_parm *parm = &tunnel->parms;
+
+ if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
+ nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
+ nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
+ nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
+ nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
+ nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
+ !!(parm->iph.frag_off & htons(IP_DF))))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static const struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
+ [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
+ [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
+};
+
+static struct rtnl_link_ops ipip_link_ops __read_mostly = {
+ .kind = "ipip",
+ .maxtype = IFLA_IPTUN_MAX,
+ .policy = ipip_policy,
+ .priv_size = sizeof(struct ip_tunnel),
+ .setup = ipip_tunnel_setup,
+ .newlink = ipip_newlink,
+ .changelink = ipip_changelink,
+ .get_size = ipip_get_size,
+ .fill_info = ipip_fill_info,
+};
+
static struct xfrm_tunnel ipip_handler __read_mostly = {
.handler = ipip_rcv,
.err_handler = ipip_err,
@@ -925,14 +1080,26 @@ static int __init ipip_init(void)
return err;
err = xfrm4_tunnel_register(&ipip_handler, AF_INET);
if (err < 0) {
- unregister_pernet_device(&ipip_net_ops);
pr_info("%s: can't register tunnel\n", __func__);
+ goto xfrm_tunnel_failed;
}
+ err = rtnl_link_register(&ipip_link_ops);
+ if (err < 0)
+ goto rtnl_link_failed;
+
+out:
return err;
+
+rtnl_link_failed:
+ xfrm4_tunnel_deregister(&ipip_handler, AF_INET);
+xfrm_tunnel_failed:
+ unregister_pernet_device(&ipip_net_ops);
+ goto out;
}
static void __exit ipip_fini(void)
{
+ rtnl_link_unregister(&ipip_link_ops);
if (xfrm4_tunnel_deregister(&ipip_handler, AF_INET))
pr_info("%s: can't deregister tunnel\n", __func__);
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 6168c4dc58b..a9454cbd953 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -65,6 +65,7 @@
#include <net/checksum.h>
#include <net/netlink.h>
#include <net/fib_rules.h>
+#include <linux/netconf.h>
#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
#define CONFIG_IP_PIMSM 1
@@ -83,8 +84,8 @@ struct mr_table {
struct vif_device vif_table[MAXVIFS];
int maxvif;
atomic_t cache_resolve_queue_len;
- int mroute_do_assert;
- int mroute_do_pim;
+ bool mroute_do_assert;
+ bool mroute_do_pim;
#if defined(CONFIG_IP_PIMSM_V1) || defined(CONFIG_IP_PIMSM_V2)
int mroute_reg_vif_num;
#endif
@@ -133,6 +134,8 @@ static int ipmr_cache_report(struct mr_table *mrt,
struct sk_buff *pkt, vifi_t vifi, int assert);
static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
struct mfc_cache *c, struct rtmsg *rtm);
+static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
+ int cmd);
static void mroute_clean_tables(struct mr_table *mrt);
static void ipmr_expire_process(unsigned long arg);
@@ -582,6 +585,9 @@ static int vif_delete(struct mr_table *mrt, int vifi, int notify,
in_dev = __in_dev_get_rtnl(dev);
if (in_dev) {
IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)--;
+ inet_netconf_notify_devconf(dev_net(dev),
+ NETCONFA_MC_FORWARDING,
+ dev->ifindex, &in_dev->cnf);
ip_rt_multicast_event(in_dev);
}
@@ -665,6 +671,7 @@ static void ipmr_expire_process(unsigned long arg)
}
list_del(&c->list);
+ mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_destroy_unres(mrt, c);
}
@@ -772,6 +779,8 @@ static int vif_add(struct net *net, struct mr_table *mrt,
return -EADDRNOTAVAIL;
}
IPV4_DEVCONF(in_dev->cnf, MC_FORWARDING)++;
+ inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING, dev->ifindex,
+ &in_dev->cnf);
ip_rt_multicast_event(in_dev);
/* Fill in the VIF structures */
@@ -1020,6 +1029,7 @@ ipmr_cache_unresolved(struct mr_table *mrt, vifi_t vifi, struct sk_buff *skb)
atomic_inc(&mrt->cache_resolve_queue_len);
list_add(&c->list, &mrt->mfc_unres_queue);
+ mroute_netlink_event(mrt, c, RTM_NEWROUTE);
if (atomic_read(&mrt->cache_resolve_queue_len) == 1)
mod_timer(&mrt->ipmr_expire_timer, c->mfc_un.unres.expires);
@@ -1054,7 +1064,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc)
if (c->mfc_origin == mfc->mfcc_origin.s_addr &&
c->mfc_mcastgrp == mfc->mfcc_mcastgrp.s_addr) {
list_del_rcu(&c->list);
-
+ mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_cache_free(c);
return 0;
}
@@ -1089,6 +1099,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
write_unlock_bh(&mrt_lock);
+ mroute_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
@@ -1131,6 +1142,7 @@ static int ipmr_mfc_add(struct net *net, struct mr_table *mrt,
ipmr_cache_resolve(net, mrt, uc, c);
ipmr_cache_free(uc);
}
+ mroute_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
@@ -1159,6 +1171,7 @@ static void mroute_clean_tables(struct mr_table *mrt)
if (c->mfc_flags & MFC_STATIC)
continue;
list_del_rcu(&c->list);
+ mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_cache_free(c);
}
}
@@ -1167,6 +1180,7 @@ static void mroute_clean_tables(struct mr_table *mrt)
spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, next, &mrt->mfc_unres_queue, list) {
list_del(&c->list);
+ mroute_netlink_event(mrt, c, RTM_DELROUTE);
ipmr_destroy_unres(mrt, c);
}
spin_unlock_bh(&mfc_unres_lock);
@@ -1185,6 +1199,9 @@ static void mrtsock_destruct(struct sock *sk)
ipmr_for_each_table(mrt, net) {
if (sk == rtnl_dereference(mrt->mroute_sk)) {
IPV4_DEVCONF_ALL(net, MC_FORWARDING)--;
+ inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv4.devconf_all);
RCU_INIT_POINTER(mrt->mroute_sk, NULL);
mroute_clean_tables(mrt);
}
@@ -1207,23 +1224,24 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
struct net *net = sock_net(sk);
struct mr_table *mrt;
+ if (sk->sk_type != SOCK_RAW ||
+ inet_sk(sk)->inet_num != IPPROTO_IGMP)
+ return -EOPNOTSUPP;
+
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
if (mrt == NULL)
return -ENOENT;
if (optname != MRT_INIT) {
if (sk != rcu_access_pointer(mrt->mroute_sk) &&
- !capable(CAP_NET_ADMIN))
+ !ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EACCES;
}
switch (optname) {
case MRT_INIT:
- if (sk->sk_type != SOCK_RAW ||
- inet_sk(sk)->inet_num != IPPROTO_IGMP)
- return -EOPNOTSUPP;
if (optlen != sizeof(int))
- return -ENOPROTOOPT;
+ return -EINVAL;
rtnl_lock();
if (rtnl_dereference(mrt->mroute_sk)) {
@@ -1235,6 +1253,9 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
if (ret == 0) {
rcu_assign_pointer(mrt->mroute_sk, sk);
IPV4_DEVCONF_ALL(net, MC_FORWARDING)++;
+ inet_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv4.devconf_all);
}
rtnl_unlock();
return ret;
@@ -1284,9 +1305,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
case MRT_ASSERT:
{
int v;
+ if (optlen != sizeof(v))
+ return -EINVAL;
if (get_user(v, (int __user *)optval))
return -EFAULT;
- mrt->mroute_do_assert = (v) ? 1 : 0;
+ mrt->mroute_do_assert = v;
return 0;
}
#ifdef CONFIG_IP_PIMSM
@@ -1294,9 +1317,11 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
{
int v;
+ if (optlen != sizeof(v))
+ return -EINVAL;
if (get_user(v, (int __user *)optval))
return -EFAULT;
- v = (v) ? 1 : 0;
+ v = !!v;
rtnl_lock();
ret = 0;
@@ -1318,6 +1343,10 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
if (get_user(v, (u32 __user *)optval))
return -EFAULT;
+ /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */
+ if (v != RT_TABLE_DEFAULT && v >= 1000000000)
+ return -EINVAL;
+
rtnl_lock();
ret = 0;
if (sk == rtnl_dereference(mrt->mroute_sk)) {
@@ -1325,7 +1354,8 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi
} else {
if (!ipmr_new_table(net, v))
ret = -ENOMEM;
- raw_sk(sk)->ipmr_table = v;
+ else
+ raw_sk(sk)->ipmr_table = v;
}
rtnl_unlock();
return ret;
@@ -1351,6 +1381,10 @@ int ip_mroute_getsockopt(struct sock *sk, int optname, char __user *optval, int
struct net *net = sock_net(sk);
struct mr_table *mrt;
+ if (sk->sk_type != SOCK_RAW ||
+ inet_sk(sk)->inet_num != IPPROTO_IGMP)
+ return -EOPNOTSUPP;
+
mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
if (mrt == NULL)
return -ENOENT;
@@ -2020,6 +2054,7 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
int ct;
struct rtnexthop *nhp;
struct nlattr *mp_attr;
+ struct rta_mfc_stats mfcs;
/* If cache is unresolved, don't try to parse IIF and OIF */
if (c->mfc_parent >= MAXVIFS)
@@ -2048,6 +2083,12 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
nla_nest_end(skb, mp_attr);
+ mfcs.mfcs_packets = c->mfc_un.res.pkt;
+ mfcs.mfcs_bytes = c->mfc_un.res.bytes;
+ mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if;
+ if (nla_put(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs) < 0)
+ return -EMSGSIZE;
+
rtm->rtm_type = RTN_MULTICAST;
return 1;
}
@@ -2117,12 +2158,13 @@ int ipmr_get_route(struct net *net, struct sk_buff *skb,
}
static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
- u32 portid, u32 seq, struct mfc_cache *c)
+ u32 portid, u32 seq, struct mfc_cache *c, int cmd)
{
struct nlmsghdr *nlh;
struct rtmsg *rtm;
+ int err;
- nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
+ nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), NLM_F_MULTI);
if (nlh == NULL)
return -EMSGSIZE;
@@ -2136,13 +2178,18 @@ static int ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb,
goto nla_put_failure;
rtm->rtm_type = RTN_MULTICAST;
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
- rtm->rtm_protocol = RTPROT_UNSPEC;
+ if (c->mfc_flags & MFC_STATIC)
+ rtm->rtm_protocol = RTPROT_STATIC;
+ else
+ rtm->rtm_protocol = RTPROT_MROUTED;
rtm->rtm_flags = 0;
if (nla_put_be32(skb, RTA_SRC, c->mfc_origin) ||
nla_put_be32(skb, RTA_DST, c->mfc_mcastgrp))
goto nla_put_failure;
- if (__ipmr_fill_mroute(mrt, skb, c, rtm) < 0)
+ err = __ipmr_fill_mroute(mrt, skb, c, rtm);
+ /* do not break the dump if cache is unresolved */
+ if (err < 0 && err != -ENOENT)
goto nla_put_failure;
return nlmsg_end(skb, nlh);
@@ -2152,6 +2199,52 @@ nla_put_failure:
return -EMSGSIZE;
}
+static size_t mroute_msgsize(bool unresolved, int maxvif)
+{
+ size_t len =
+ NLMSG_ALIGN(sizeof(struct rtmsg))
+ + nla_total_size(4) /* RTA_TABLE */
+ + nla_total_size(4) /* RTA_SRC */
+ + nla_total_size(4) /* RTA_DST */
+ ;
+
+ if (!unresolved)
+ len = len
+ + nla_total_size(4) /* RTA_IIF */
+ + nla_total_size(0) /* RTA_MULTIPATH */
+ + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
+ /* RTA_MFC_STATS */
+ + nla_total_size(sizeof(struct rta_mfc_stats))
+ ;
+
+ return len;
+}
+
+static void mroute_netlink_event(struct mr_table *mrt, struct mfc_cache *mfc,
+ int cmd)
+{
+ struct net *net = read_pnet(&mrt->net);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(mroute_msgsize(mfc->mfc_parent >= MAXVIFS, mrt->maxvif),
+ GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = ipmr_fill_mroute(mrt, skb, 0, 0, mfc, cmd);
+ if (err < 0)
+ goto errout;
+
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV4_MROUTE, NULL, GFP_ATOMIC);
+ return;
+
+errout:
+ kfree_skb(skb);
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_IPV4_MROUTE, err);
+}
+
static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
@@ -2178,13 +2271,29 @@ static int ipmr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
if (ipmr_fill_mroute(mrt, skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
- mfc) < 0)
+ mfc, RTM_NEWROUTE) < 0)
goto done;
next_entry:
e++;
}
e = s_e = 0;
}
+ spin_lock_bh(&mfc_unres_lock);
+ list_for_each_entry(mfc, &mrt->mfc_unres_queue, list) {
+ if (e < s_e)
+ goto next_entry2;
+ if (ipmr_fill_mroute(mrt, skb,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ mfc, RTM_NEWROUTE) < 0) {
+ spin_unlock_bh(&mfc_unres_lock);
+ goto done;
+ }
+next_entry2:
+ e++;
+ }
+ spin_unlock_bh(&mfc_unres_lock);
+ e = s_e = 0;
s_h = 0;
next_table:
t++;
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 97e61eadf58..3ea4127404d 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -1533,7 +1533,7 @@ static int compat_do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user,
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1677,7 +1677,7 @@ static int compat_do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user,
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1698,7 +1698,7 @@ static int do_arpt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1722,7 +1722,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 170b1fdd6b7..17c5e06da66 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -1846,7 +1846,7 @@ compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1961,7 +1961,7 @@ compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1983,7 +1983,7 @@ do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -2008,7 +2008,7 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c
index fe5daea5214..75e33a7048f 100644
--- a/net/ipv4/netfilter/ipt_CLUSTERIP.c
+++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c
@@ -661,6 +661,7 @@ static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
#define PROC_WRITELEN 10
char buffer[PROC_WRITELEN+1];
unsigned long nodenum;
+ int rc;
if (size > PROC_WRITELEN)
return -EIO;
@@ -669,11 +670,15 @@ static ssize_t clusterip_proc_write(struct file *file, const char __user *input,
buffer[size] = 0;
if (*buffer == '+') {
- nodenum = simple_strtoul(buffer+1, NULL, 10);
+ rc = kstrtoul(buffer+1, 10, &nodenum);
+ if (rc)
+ return rc;
if (clusterip_add_node(c, nodenum))
return -ENOMEM;
} else if (*buffer == '-') {
- nodenum = simple_strtoul(buffer+1, NULL,10);
+ rc = kstrtoul(buffer+1, 10, &nodenum);
+ if (rc)
+ return rc;
if (clusterip_del_node(c, nodenum))
return -ENOENT;
} else
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index 51f13f8ec72..04b18c1ac34 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -81,6 +81,7 @@ static void send_reset(struct sk_buff *oldskb, int hook)
niph->saddr = oiph->daddr;
niph->daddr = oiph->saddr;
+ skb_reset_transport_header(nskb);
tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
memset(tcph, 0, sizeof(*tcph));
tcph->source = oth->dest;
diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c
index 9e0ffaf1d94..eeaff7e4acb 100644
--- a/net/ipv4/netfilter/iptable_nat.c
+++ b/net/ipv4/netfilter/iptable_nat.c
@@ -124,19 +124,28 @@ nf_nat_ipv4_fn(unsigned int hooknum,
ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
if (ret != NF_ACCEPT)
return ret;
- } else
+ } else {
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
+ if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+ goto oif_changed;
+ }
break;
default:
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
+ if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+ goto oif_changed;
}
return nf_nat_packet(ct, ctinfo, hooknum, skb);
+
+oif_changed:
+ nf_ct_kill_acct(ct, ctinfo, skb);
+ return NF_DROP;
}
static unsigned int
@@ -184,7 +193,8 @@ nf_nat_ipv4_out(unsigned int hooknum,
if ((ct->tuplehash[dir].tuple.src.u3.ip !=
ct->tuplehash[!dir].tuple.dst.u3.ip) ||
- (ct->tuplehash[dir].tuple.src.u.all !=
+ (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
+ ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all))
if (nf_xfrm_me_harder(skb, AF_INET) < 0)
ret = NF_DROP;
@@ -221,6 +231,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum,
}
#ifdef CONFIG_XFRM
else if (!(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) &&
+ ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMP &&
ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all)
if (nf_xfrm_me_harder(skb, AF_INET) < 0)
@@ -274,9 +285,7 @@ static int __net_init iptable_nat_net_init(struct net *net)
return -ENOMEM;
net->ipv4.nat_table = ipt_register_table(net, &nf_nat_ipv4_table, repl);
kfree(repl);
- if (IS_ERR(net->ipv4.nat_table))
- return PTR_ERR(net->ipv4.nat_table);
- return 0;
+ return PTR_RET(net->ipv4.nat_table);
}
static void __net_exit iptable_nat_net_exit(struct net *net)
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 8f3d05424a3..6f9c07268cf 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -738,6 +738,7 @@ struct proto ping_prot = {
.recvmsg = ping_recvmsg,
.bind = ping_bind,
.backlog_rcv = ping_queue_rcv_skb,
+ .release_cb = ip4_datagram_release_cb,
.hash = ping_v4_hash,
.unhash = ping_v4_unhash,
.get_port = ping_v4_get_port,
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index 8918eff1426..0f9d09f54bd 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -29,6 +29,7 @@
#include <net/protocol.h>
const struct net_protocol __rcu *inet_protos[MAX_INET_PROTOS] __read_mostly;
+const struct net_offload __rcu *inet_offloads[MAX_INET_PROTOS] __read_mostly;
/*
* Add a protocol handler to the hash tables
@@ -41,6 +42,13 @@ int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
}
EXPORT_SYMBOL(inet_add_protocol);
+int inet_add_offload(const struct net_offload *prot, unsigned char protocol)
+{
+ return !cmpxchg((const struct net_offload **)&inet_offloads[protocol],
+ NULL, prot) ? 0 : -1;
+}
+EXPORT_SYMBOL(inet_add_offload);
+
/*
* Remove a protocol from the hash tables.
*/
@@ -57,3 +65,16 @@ int inet_del_protocol(const struct net_protocol *prot, unsigned char protocol)
return ret;
}
EXPORT_SYMBOL(inet_del_protocol);
+
+int inet_del_offload(const struct net_offload *prot, unsigned char protocol)
+{
+ int ret;
+
+ ret = (cmpxchg((const struct net_offload **)&inet_offloads[protocol],
+ prot, NULL) == prot) ? 0 : -1;
+
+ synchronize_net();
+
+ return ret;
+}
+EXPORT_SYMBOL(inet_del_offload);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 73d1e4df4bf..6f08991409c 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -894,6 +894,7 @@ struct proto raw_prot = {
.recvmsg = raw_recvmsg,
.bind = raw_bind,
.backlog_rcv = raw_rcv_skb,
+ .release_cb = ip4_datagram_release_cb,
.hash = raw_hash_sk,
.unhash = raw_unhash_sk,
.obj_size = sizeof(struct raw_sock),
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index a8c651216fa..a0fcc47fee7 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -912,6 +912,9 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
struct dst_entry *dst = &rt->dst;
struct fib_result res;
+ if (dst_metric_locked(dst, RTAX_MTU))
+ return;
+
if (dst->dev->mtu < mtu)
return;
@@ -962,7 +965,7 @@ void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu,
}
EXPORT_SYMBOL_GPL(ipv4_update_pmtu);
-void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+static void __ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
{
const struct iphdr *iph = (const struct iphdr *) skb->data;
struct flowi4 fl4;
@@ -975,6 +978,53 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
ip_rt_put(rt);
}
}
+
+void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu)
+{
+ const struct iphdr *iph = (const struct iphdr *) skb->data;
+ struct flowi4 fl4;
+ struct rtable *rt;
+ struct dst_entry *dst;
+ bool new = false;
+
+ bh_lock_sock(sk);
+ rt = (struct rtable *) __sk_dst_get(sk);
+
+ if (sock_owned_by_user(sk) || !rt) {
+ __ipv4_sk_update_pmtu(skb, sk, mtu);
+ goto out;
+ }
+
+ __build_flow_key(&fl4, sk, iph, 0, 0, 0, 0, 0);
+
+ if (!__sk_dst_check(sk, 0)) {
+ rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
+ if (IS_ERR(rt))
+ goto out;
+
+ new = true;
+ }
+
+ __ip_rt_update_pmtu((struct rtable *) rt->dst.path, &fl4, mtu);
+
+ dst = dst_check(&rt->dst, 0);
+ if (!dst) {
+ if (new)
+ dst_release(&rt->dst);
+
+ rt = ip_route_output_flow(sock_net(sk), &fl4, sk);
+ if (IS_ERR(rt))
+ goto out;
+
+ new = true;
+ }
+
+ if (new)
+ __sk_dst_set(sk, &rt->dst);
+
+out:
+ bh_unlock_sock(sk);
+}
EXPORT_SYMBOL_GPL(ipv4_sk_update_pmtu);
void ipv4_redirect(struct sk_buff *skb, struct net *net,
@@ -1120,7 +1170,7 @@ static unsigned int ipv4_mtu(const struct dst_entry *dst)
if (!mtu || time_after_eq(jiffies, rt->dst.expires))
mtu = dst_metric_raw(dst, RTAX_MTU);
- if (mtu && rt_is_output_route(rt))
+ if (mtu)
return mtu;
mtu = dst->dev->mtu;
@@ -1785,6 +1835,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
if (dev_out->flags & IFF_LOOPBACK)
flags |= RTCF_LOCAL;
+ do_cache = true;
if (type == RTN_BROADCAST) {
flags |= RTCF_BROADCAST | RTCF_LOCAL;
fi = NULL;
@@ -1793,6 +1844,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
if (!ip_check_mc_rcu(in_dev, fl4->daddr, fl4->saddr,
fl4->flowi4_proto))
flags &= ~RTCF_LOCAL;
+ else
+ do_cache = false;
/* If multicast route do not exist use
* default one, but do not gateway in this case.
* Yes, it is hack.
@@ -1802,8 +1855,8 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
}
fnhe = NULL;
- do_cache = fi != NULL;
- if (fi) {
+ do_cache &= fi != NULL;
+ if (do_cache) {
struct rtable __rcu **prth;
struct fib_nh *nh = &FIB_RES_NH(*res);
@@ -2229,8 +2282,27 @@ static int rt_fill_info(struct net *net, __be32 dst, __be32 src,
error = rt->dst.error;
if (rt_is_input_route(rt)) {
- if (nla_put_u32(skb, RTA_IIF, rt->rt_iif))
- goto nla_put_failure;
+#ifdef CONFIG_IP_MROUTE
+ if (ipv4_is_multicast(dst) && !ipv4_is_local_multicast(dst) &&
+ IPV4_DEVCONF_ALL(net, MC_FORWARDING)) {
+ int err = ipmr_get_route(net, skb,
+ fl4->saddr, fl4->daddr,
+ r, nowait);
+ if (err <= 0) {
+ if (!nowait) {
+ if (err == 0)
+ return 0;
+ goto nla_put_failure;
+ } else {
+ if (err == -EMSGSIZE)
+ goto nla_put_failure;
+ error = err;
+ }
+ }
+ } else
+#endif
+ if (nla_put_u32(skb, RTA_IIF, rt->rt_iif))
+ goto nla_put_failure;
}
if (rtnl_put_cacheinfo(skb, &rt->dst, 0, expires, error) < 0)
@@ -2493,6 +2565,10 @@ static __net_init int sysctl_route_net_init(struct net *net)
tbl = kmemdup(tbl, sizeof(ipv4_route_flush_table), GFP_KERNEL);
if (tbl == NULL)
goto err_dup;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ tbl[0].procname = NULL;
}
tbl[0].extra1 = net;
@@ -2597,7 +2673,7 @@ int __init ip_rt_init(void)
pr_err("Unable to create route proc files\n");
#ifdef CONFIG_XFRM
xfrm_init();
- xfrm4_init(ip_rt_max_size);
+ xfrm4_init();
#endif
rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index ba48e799b03..b236ef04914 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -340,7 +340,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
}
req->expires = 0UL;
- req->retrans = 0;
+ req->num_retrans = 0;
/*
* We need to lookup the route here to get at the correct
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 63d4eccc674..d84400b6504 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -883,6 +883,9 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
table[6].data =
&net->ipv4.sysctl_ping_group_range;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
}
/*
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 197c0008503..2aa69c8ae60 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -536,13 +536,14 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
struct tcp_sock *tp = tcp_sk(sk);
int answ;
+ bool slow;
switch (cmd) {
case SIOCINQ:
if (sk->sk_state == TCP_LISTEN)
return -EINVAL;
- lock_sock(sk);
+ slow = lock_sock_fast(sk);
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))
answ = 0;
else if (sock_flag(sk, SOCK_URGINLINE) ||
@@ -557,7 +558,7 @@ int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg)
answ--;
} else
answ = tp->urg_seq - tp->copied_seq;
- release_sock(sk);
+ unlock_sock_fast(sk, slow);
break;
case SIOCATMARK:
answ = tp->urg_data && tp->urg_seq == tp->copied_seq;
@@ -830,8 +831,8 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
return mss_now;
}
-static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset,
- size_t psize, int flags)
+static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
+ size_t size, int flags)
{
struct tcp_sock *tp = tcp_sk(sk);
int mss_now, size_goal;
@@ -858,12 +859,9 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
goto out_err;
- while (psize > 0) {
+ while (size > 0) {
struct sk_buff *skb = tcp_write_queue_tail(sk);
- struct page *page = pages[poffset / PAGE_SIZE];
int copy, i;
- int offset = poffset % PAGE_SIZE;
- int size = min_t(size_t, psize, PAGE_SIZE - offset);
bool can_coalesce;
if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0) {
@@ -912,8 +910,8 @@ new_segment:
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;
copied += copy;
- poffset += copy;
- if (!(psize -= copy))
+ offset += copy;
+ if (!(size -= copy))
goto out;
if (skb->len < size_goal || (flags & MSG_OOB))
@@ -960,7 +958,7 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,
flags);
lock_sock(sk);
- res = do_tcp_sendpages(sk, &page, offset, size, flags);
+ res = do_tcp_sendpages(sk, page, offset, size, flags);
release_sock(sk);
return res;
}
@@ -1212,7 +1210,7 @@ new_segment:
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
- if (copied && likely(!tp->repair))
+ if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
@@ -1223,7 +1221,7 @@ wait_for_memory:
}
out:
- if (copied && likely(!tp->repair))
+ if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle);
release_sock(sk);
return copied + copied_syn;
@@ -1430,12 +1428,12 @@ static void tcp_service_net_dma(struct sock *sk, bool wait)
}
#endif
-static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
+static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
{
struct sk_buff *skb;
u32 offset;
- skb_queue_walk(&sk->sk_receive_queue, skb) {
+ while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
offset = seq - TCP_SKB_CB(skb)->seq;
if (tcp_hdr(skb)->syn)
offset--;
@@ -1443,6 +1441,11 @@ static inline struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off)
*off = offset;
return skb;
}
+ /* This looks weird, but this can happen if TCP collapsing
+ * splitted a fat GRO packet, while we released socket lock
+ * in skb_splice_bits()
+ */
+ sk_eat_skb(sk, skb, false);
}
return NULL;
}
@@ -1484,7 +1487,7 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
break;
}
used = recv_actor(desc, skb, offset, len);
- if (used < 0) {
+ if (used <= 0) {
if (!copied)
copied = used;
break;
@@ -1493,15 +1496,19 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
copied += used;
offset += used;
}
- /*
- * If recv_actor drops the lock (e.g. TCP splice
+ /* If recv_actor drops the lock (e.g. TCP splice
* receive) the skb pointer might be invalid when
* getting here: tcp_collapse might have deleted it
* while aggregating skbs from the socket queue.
*/
- skb = tcp_recv_skb(sk, seq-1, &offset);
- if (!skb || (offset+1 != skb->len))
+ skb = tcp_recv_skb(sk, seq - 1, &offset);
+ if (!skb)
break;
+ /* TCP coalescing might have appended data to the skb.
+ * Try to splice more frags
+ */
+ if (offset + 1 != skb->len)
+ continue;
}
if (tcp_hdr(skb)->fin) {
sk_eat_skb(sk, skb, false);
@@ -1518,8 +1525,10 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
tcp_rcv_space_adjust(sk);
/* Clean up data we have read: This will do ACK frames. */
- if (copied > 0)
+ if (copied > 0) {
+ tcp_recv_skb(sk, seq, &offset);
tcp_cleanup_rbuf(sk, copied);
+ }
return copied;
}
EXPORT_SYMBOL(tcp_read_sock);
@@ -2303,7 +2312,7 @@ void tcp_sock_destruct(struct sock *sk)
static inline bool tcp_can_repair_sock(const struct sock *sk)
{
- return capable(CAP_NET_ADMIN) &&
+ return ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN) &&
((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_ESTABLISHED));
}
@@ -3589,8 +3598,7 @@ void __init tcp_init(void)
alloc_large_system_hash("TCP established",
sizeof(struct inet_ehash_bucket),
thash_entries,
- (totalram_pages >= 128 * 1024) ?
- 13 : 15,
+ 17, /* one slot per 128 KB of memory */
0,
NULL,
&tcp_hashinfo.ehash_mask,
@@ -3606,8 +3614,7 @@ void __init tcp_init(void)
alloc_large_system_hash("TCP bind",
sizeof(struct inet_bind_hashbucket),
tcp_hashinfo.ehash_mask + 1,
- (totalram_pages >= 128 * 1024) ?
- 13 : 15,
+ 17, /* one slot per 128 KB of memory */
0,
&tcp_hashinfo.bhash_size,
NULL,
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 701a7f6fb9b..291f2ed7cc3 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -259,7 +259,8 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
if (!ca)
err = -ENOENT;
- else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) || capable(CAP_NET_ADMIN)))
+ else if (!((ca->flags & TCP_CONG_NON_RESTRICTED) ||
+ ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)))
err = -EPERM;
else if (!try_module_get(ca->owner))
diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c
index 813b43a76fe..834857f3c87 100644
--- a/net/ipv4/tcp_illinois.c
+++ b/net/ipv4/tcp_illinois.c
@@ -313,11 +313,13 @@ static void tcp_illinois_info(struct sock *sk, u32 ext,
.tcpv_rttcnt = ca->cnt_rtt,
.tcpv_minrtt = ca->base_rtt,
};
- u64 t = ca->sum_rtt;
- do_div(t, ca->cnt_rtt);
- info.tcpv_rtt = t;
+ if (info.tcpv_rttcnt > 0) {
+ u64 t = ca->sum_rtt;
+ do_div(t, info.tcpv_rttcnt);
+ info.tcpv_rtt = t;
+ }
nla_put(skb, INET_DIAG_VEGASINFO, sizeof(info), &info);
}
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 1db66398358..18f97ca76b0 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3552,6 +3552,24 @@ static bool tcp_process_frto(struct sock *sk, int flag)
return false;
}
+/* RFC 5961 7 [ACK Throttling] */
+static void tcp_send_challenge_ack(struct sock *sk)
+{
+ /* unprotected vars, we dont care of overwrites */
+ static u32 challenge_timestamp;
+ static unsigned int challenge_count;
+ u32 now = jiffies / HZ;
+
+ if (now != challenge_timestamp) {
+ challenge_timestamp = now;
+ challenge_count = 0;
+ }
+ if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
+ NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
+ tcp_send_ack(sk);
+ }
+}
+
/* This routine deals with incoming acks, but not outgoing ones. */
static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
{
@@ -3571,8 +3589,14 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
/* If the ack is older than previous acks
* then we can probably ignore it.
*/
- if (before(ack, prior_snd_una))
+ if (before(ack, prior_snd_una)) {
+ /* RFC 5961 5.2 [Blind Data Injection Attack].[Mitigation] */
+ if (before(ack, prior_snd_una - tp->max_window)) {
+ tcp_send_challenge_ack(sk);
+ return -1;
+ }
goto old_ack;
+ }
/* If the ack includes data we haven't sent yet, discard
* this segment (RFC793 Section 3.9).
@@ -4529,6 +4553,9 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
struct tcphdr *th;
bool fragstolen;
+ if (size == 0)
+ return 0;
+
skb = alloc_skb(size + sizeof(*th), sk->sk_allocation);
if (!skb)
goto err;
@@ -5241,23 +5268,6 @@ out:
}
#endif /* CONFIG_NET_DMA */
-static void tcp_send_challenge_ack(struct sock *sk)
-{
- /* unprotected vars, we dont care of overwrites */
- static u32 challenge_timestamp;
- static unsigned int challenge_count;
- u32 now = jiffies / HZ;
-
- if (now != challenge_timestamp) {
- challenge_timestamp = now;
- challenge_count = 0;
- }
- if (++challenge_count <= sysctl_tcp_challenge_ack_limit) {
- NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK);
- tcp_send_ack(sk);
- }
-}
-
/* Does PAWS and seqno based validation of an incoming segment, flags will
* play significant role here.
*/
@@ -5310,11 +5320,6 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
goto discard;
}
- /* ts_recent update must be made after we are sure that the packet
- * is in window.
- */
- tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
-
/* step 3: check security and precedence [ignored] */
/* step 4: Check for a SYN
@@ -5538,6 +5543,9 @@ slow_path:
if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
goto csum_error;
+ if (!th->ack && !th->rst)
+ goto discard;
+
/*
* Standard slow path.
*/
@@ -5546,9 +5554,14 @@ slow_path:
return 0;
step5:
- if (th->ack && tcp_ack(sk, skb, FLAG_SLOWPATH) < 0)
+ if (tcp_ack(sk, skb, FLAG_SLOWPATH) < 0)
goto discard;
+ /* ts_recent update must be made after we are sure that the packet
+ * is in window.
+ */
+ tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
+
tcp_rcv_rtt_measure_ts(sk, skb);
/* Process urgent data. */
@@ -5642,7 +5655,11 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack,
tcp_fastopen_cache_set(sk, mss, cookie, syn_drop);
if (data) { /* Retransmit unacked data in SYN */
- tcp_retransmit_skb(sk, data);
+ tcp_for_write_queue_from(data, sk) {
+ if (data == tcp_send_head(sk) ||
+ __tcp_retransmit_skb(sk, data))
+ break;
+ }
tcp_rearm_rto(sk);
return true;
}
@@ -5970,11 +5987,15 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
if (tcp_check_req(sk, skb, req, NULL, true) == NULL)
goto discard;
}
+
+ if (!th->ack && !th->rst)
+ goto discard;
+
if (!tcp_validate_incoming(sk, skb, th, 0))
return 0;
/* step 5: check the ACK field */
- if (th->ack) {
+ if (true) {
int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH) > 0;
switch (sk->sk_state) {
@@ -5985,7 +6006,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
*/
if (req) {
tcp_synack_rtt_meas(sk, req);
- tp->total_retrans = req->retrans;
+ tp->total_retrans = req->num_retrans;
reqsk_fastopen_remove(sk, req, false);
} else {
@@ -6124,8 +6145,12 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
}
break;
}
- } else
- goto discard;
+ }
+
+ /* ts_recent update must be made after we are sure that the packet
+ * is in window.
+ */
+ tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq);
/* step 6: check the URG bit */
tcp_urg(sk, skb, th);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 0c4a6435560..70b09ef2463 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -138,14 +138,6 @@ int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
}
EXPORT_SYMBOL_GPL(tcp_twsk_unique);
-static int tcp_repair_connect(struct sock *sk)
-{
- tcp_connect_init(sk);
- tcp_finish_connect(sk, NULL);
-
- return 0;
-}
-
/* This will initiate an outgoing connection. */
int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
@@ -250,10 +242,7 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
inet->inet_id = tp->write_seq ^ jiffies;
- if (likely(!tp->repair))
- err = tcp_connect(sk);
- else
- err = tcp_repair_connect(sk);
+ err = tcp_connect(sk);
rt = NULL;
if (err)
@@ -380,11 +369,10 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
* We do take care of PMTU discovery (RFC1191) special case :
* we can receive locally generated ICMP messages while socket is held.
*/
- if (sock_owned_by_user(sk) &&
- type != ICMP_DEST_UNREACH &&
- code != ICMP_FRAG_NEEDED)
- NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
-
+ if (sock_owned_by_user(sk)) {
+ if (!(type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED))
+ NET_INC_STATS_BH(net, LINUX_MIB_LOCKDROPPEDICMPS);
+ }
if (sk->sk_state == TCP_CLOSE)
goto out;
@@ -877,10 +865,13 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst,
}
static int tcp_v4_rtx_synack(struct sock *sk, struct request_sock *req,
- struct request_values *rvp)
+ struct request_values *rvp)
{
- TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
- return tcp_v4_send_synack(sk, NULL, req, rvp, 0, false);
+ int res = tcp_v4_send_synack(sk, NULL, req, rvp, 0, false);
+
+ if (!res)
+ TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+ return res;
}
/*
@@ -1070,7 +1061,7 @@ int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, int family)
}
EXPORT_SYMBOL(tcp_md5_do_del);
-void tcp_clear_md5_list(struct sock *sk)
+static void tcp_clear_md5_list(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_md5sig_key *key;
@@ -1386,7 +1377,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk,
struct sock *child;
int err;
- req->retrans = 0;
+ req->num_retrans = 0;
+ req->num_timeout = 0;
req->sk = NULL;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
@@ -1741,7 +1733,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
tcp_initialize_rcv_mss(newsk);
tcp_synack_rtt_meas(newsk, req);
- newtp->total_retrans = req->retrans;
+ newtp->total_retrans = req->num_retrans;
#ifdef CONFIG_TCP_MD5SIG
/* Copy over the MD5 key from the original socket */
@@ -1774,10 +1766,8 @@ exit:
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
return NULL;
put_and_exit:
- tcp_clear_xmit_timers(newsk);
- tcp_cleanup_congestion_control(newsk);
- bh_unlock_sock(newsk);
- sock_put(newsk);
+ inet_csk_prepare_forced_close(newsk);
+ tcp_done(newsk);
goto exit;
}
EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
@@ -1919,7 +1909,6 @@ EXPORT_SYMBOL(tcp_v4_do_rcv);
void tcp_v4_early_demux(struct sk_buff *skb)
{
- struct net *net = dev_net(skb->dev);
const struct iphdr *iph;
const struct tcphdr *th;
struct sock *sk;
@@ -1927,16 +1916,16 @@ void tcp_v4_early_demux(struct sk_buff *skb)
if (skb->pkt_type != PACKET_HOST)
return;
- if (!pskb_may_pull(skb, ip_hdrlen(skb) + sizeof(struct tcphdr)))
+ if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct tcphdr)))
return;
iph = ip_hdr(skb);
- th = (struct tcphdr *) ((char *)iph + ip_hdrlen(skb));
+ th = tcp_hdr(skb);
if (th->doff < sizeof(struct tcphdr) / 4)
return;
- sk = __inet_lookup_established(net, &tcp_hashinfo,
+ sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
skb->skb_iif);
@@ -2640,7 +2629,7 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req,
0, 0, /* could print option size, but that is af dependent. */
1, /* timers active (only the expire timer) */
jiffies_delta_to_clock_t(delta),
- req->retrans,
+ req->num_timeout,
from_kuid_munged(seq_user_ns(f), uid),
0, /* non standard timer */
0, /* open_requests have no inode */
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index 4c752a6e0bc..f696d7c2e9f 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -1,7 +1,6 @@
#include <linux/rcupdate.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
-#include <linux/bootmem.h>
#include <linux/module.h>
#include <linux/cache.h>
#include <linux/slab.h>
@@ -9,6 +8,7 @@
#include <linux/tcp.h>
#include <linux/hash.h>
#include <linux/tcp_metrics.h>
+#include <linux/vmalloc.h>
#include <net/inet_connection_sock.h>
#include <net/net_namespace.h>
@@ -864,7 +864,7 @@ static int parse_nl_addr(struct genl_info *info, struct inetpeer_addr *addr,
}
a = info->attrs[TCP_METRICS_ATTR_ADDR_IPV6];
if (a) {
- if (nla_len(a) != sizeof(sizeof(struct in6_addr)))
+ if (nla_len(a) != sizeof(struct in6_addr))
return -EINVAL;
addr->family = AF_INET6;
memcpy(addr->addr.a6, nla_data(a), sizeof(addr->addr.a6));
@@ -1034,7 +1034,10 @@ static int __net_init tcp_net_metrics_init(struct net *net)
net->ipv4.tcp_metrics_hash_log = order_base_2(slots);
size = sizeof(struct tcpm_hash_bucket) << net->ipv4.tcp_metrics_hash_log;
- net->ipv4.tcp_metrics_hash = kzalloc(size, GFP_KERNEL);
+ net->ipv4.tcp_metrics_hash = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
+ if (!net->ipv4.tcp_metrics_hash)
+ net->ipv4.tcp_metrics_hash = vzalloc(size);
+
if (!net->ipv4.tcp_metrics_hash)
return -ENOMEM;
@@ -1055,7 +1058,10 @@ static void __net_exit tcp_net_metrics_exit(struct net *net)
tm = next;
}
}
- kfree(net->ipv4.tcp_metrics_hash);
+ if (is_vmalloc_addr(net->ipv4.tcp_metrics_hash))
+ vfree(net->ipv4.tcp_metrics_hash);
+ else
+ kfree(net->ipv4.tcp_metrics_hash);
}
static __net_initdata struct pernet_operations tcp_net_metrics_ops = {
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index a7302d974f3..f35f2dfb640 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -553,7 +553,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* it can be estimated (approximately)
* from another data.
*/
- tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<<req->retrans);
+ tmp_opt.ts_recent_stamp = get_seconds() - ((TCP_TIMEOUT_INIT/HZ)<<req->num_timeout);
paws_reject = tcp_paws_reject(&tmp_opt, th->rst);
}
}
@@ -582,7 +582,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
* Note that even if there is new data in the SYN packet
* they will be thrown away too.
*/
- req->rsk_ops->rtx_syn_ack(sk, req, NULL);
+ inet_rtx_syn_ack(sk, req);
return NULL;
}
@@ -696,7 +696,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
/* Got ACK for our SYNACK, so update baseline for SYNACK RTT sample. */
if (tmp_opt.saw_tstamp && tmp_opt.rcv_tsecr)
tcp_rsk(req)->snt_synack = tmp_opt.rcv_tsecr;
- else if (req->retrans) /* don't take RTT sample if retrans && ~TS */
+ else if (req->num_retrans) /* don't take RTT sample if retrans && ~TS */
tcp_rsk(req)->snt_synack = 0;
/* For Fast Open no more processing is needed (sk is the
@@ -706,7 +706,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
return sk;
/* While TCP_DEFER_ACCEPT is active, drop bare ACK. */
- if (req->retrans < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept &&
+ if (req->num_timeout < inet_csk(sk)->icsk_accept_queue.rskq_defer_accept &&
TCP_SKB_CB(skb)->end_seq == tcp_rsk(req)->rcv_isn + 1) {
inet_rsk(req)->acked = 1;
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDEFERACCEPTDROP);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index cfe6ffe1c17..5d451593ef1 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -1986,6 +1986,9 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
BUG_ON(!tso_segs);
+ if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE)
+ goto repair; /* Skip network transmission */
+
cwnd_quota = tcp_cwnd_test(tp, skb);
if (!cwnd_quota)
break;
@@ -2026,6 +2029,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
break;
+repair:
/* Advance the send_head. This one is sent out.
* This call will increment packets_out.
*/
@@ -2305,12 +2309,11 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
* state updates are done by the caller. Returns non-zero if an
* error occurred which prevented the send.
*/
-int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
+int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
unsigned int cur_mss;
- int err;
/* Inconslusive MTU probe */
if (icsk->icsk_mtup.probe_size) {
@@ -2383,11 +2386,17 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
if (unlikely(NET_IP_ALIGN && ((unsigned long)skb->data & 3))) {
struct sk_buff *nskb = __pskb_copy(skb, MAX_TCP_HEADER,
GFP_ATOMIC);
- err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
- -ENOBUFS;
+ return nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
+ -ENOBUFS;
} else {
- err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
+ return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
}
+}
+
+int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ int err = __tcp_retransmit_skb(sk, skb);
if (err == 0) {
/* Update global TCP statistics. */
@@ -2983,6 +2992,11 @@ int tcp_connect(struct sock *sk)
tcp_connect_init(sk);
+ if (unlikely(tp->repair)) {
+ tcp_finish_connect(sk, NULL);
+ return 0;
+ }
+
buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
if (unlikely(buff == NULL))
return -ENOBUFS;
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index d47c1b4421a..b78aac30c49 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -318,7 +318,7 @@ static void tcp_fastopen_synack_timer(struct sock *sk)
req = tcp_sk(sk)->fastopen_rsk;
req->rsk_ops->syn_ack_timeout(sk, req);
- if (req->retrans >= max_retries) {
+ if (req->num_timeout >= max_retries) {
tcp_write_err(sk);
return;
}
@@ -327,10 +327,10 @@ static void tcp_fastopen_synack_timer(struct sock *sk)
* regular retransmit because if the child socket has been accepted
* it's not good to give up too easily.
*/
- req->rsk_ops->rtx_syn_ack(sk, req, NULL);
- req->retrans++;
+ inet_rtx_syn_ack(sk, req);
+ req->num_timeout++;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
- TCP_TIMEOUT_INIT << req->retrans, TCP_RTO_MAX);
+ TCP_TIMEOUT_INIT << req->num_timeout, TCP_RTO_MAX);
}
/*
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 79c8dbe59b5..1f4d405eafb 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -1952,6 +1952,7 @@ struct proto udp_prot = {
.recvmsg = udp_recvmsg,
.sendpage = udp_sendpage,
.backlog_rcv = __udp_queue_rcv_skb,
+ .release_cb = ip4_datagram_release_cb,
.hash = udp_lib_hash,
.unhash = udp_lib_unhash,
.rehash = udp_v4_rehash,
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 05c5ab8d983..3be0ac2c192 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -279,19 +279,8 @@ static void __exit xfrm4_policy_fini(void)
xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
}
-void __init xfrm4_init(int rt_max_size)
+void __init xfrm4_init(void)
{
- /*
- * Select a default value for the gc_thresh based on the main route
- * table hash size. It seems to me the worst case scenario is when
- * we have ipsec operating in transport mode, in which we create a
- * dst_entry per socket. The xfrm gc algorithm starts trying to remove
- * entries at gc_thresh, and prevents new allocations as 2*gc_thresh
- * so lets set an initial xfrm gc_thresh value at the rt_max_size/2.
- * That will let us store an ipsec connection per route table entry,
- * and start cleaning when were 1/2 full
- */
- xfrm4_dst_ops.gc_thresh = rt_max_size/2;
dst_entries_init(&xfrm4_dst_ops);
xfrm4_state_init();
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index b6d3f79151e..4ea244891b5 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -7,9 +7,11 @@ obj-$(CONFIG_IPV6) += ipv6.o
ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \
addrlabel.o \
route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \
- raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
+ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o \
exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o
+ipv6-offload := ip6_offload.o tcpv6_offload.o udp_offload.o exthdrs_offload.o
+
ipv6-$(CONFIG_SYSCTL) = sysctl_net_ipv6.o
ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
@@ -39,5 +41,6 @@ obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o
obj-$(CONFIG_IPV6_GRE) += ip6_gre.o
obj-y += addrconf_core.o exthdrs_core.o
+obj-$(CONFIG_INET) += output_core.o protocol.o $(ipv6-offload)
obj-$(subst m,y,$(CONFIG_IPV6)) += inet6_hashtables.o
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0424e4e2741..420e5632638 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -81,6 +81,7 @@
#include <net/pkt_sched.h>
#include <linux/if_tunnel.h>
#include <linux/rtnetlink.h>
+#include <linux/netconf.h>
#ifdef CONFIG_IPV6_PRIVACY
#include <linux/random.h>
@@ -153,6 +154,11 @@ static void addrconf_type_change(struct net_device *dev,
unsigned long event);
static int addrconf_ifdown(struct net_device *dev, int how);
+static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
+ int plen,
+ const struct net_device *dev,
+ u32 flags, u32 noflags);
+
static void addrconf_dad_start(struct inet6_ifaddr *ifp);
static void addrconf_dad_timer(unsigned long data);
static void addrconf_dad_completed(struct inet6_ifaddr *ifp);
@@ -249,12 +255,6 @@ static inline bool addrconf_qdisc_ok(const struct net_device *dev)
return !qdisc_tx_is_noop(dev);
}
-/* Check if a route is valid prefix route */
-static inline int addrconf_is_prefix_route(const struct rt6_info *rt)
-{
- return (rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0;
-}
-
static void addrconf_del_timer(struct inet6_ifaddr *ifp)
{
if (del_timer(&ifp->timer))
@@ -401,7 +401,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
ndev->cnf.accept_dad = -1;
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
pr_info("%s: Disabled Multicast RS\n", dev->name);
ndev->cnf.rtr_solicits = 0;
@@ -460,6 +460,149 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
return idev;
}
+static int inet6_netconf_msgsize_devconf(int type)
+{
+ int size = NLMSG_ALIGN(sizeof(struct netconfmsg))
+ + nla_total_size(4); /* NETCONFA_IFINDEX */
+
+ /* type -1 is used for ALL */
+ if (type == -1 || type == NETCONFA_FORWARDING)
+ size += nla_total_size(4);
+#ifdef CONFIG_IPV6_MROUTE
+ if (type == -1 || type == NETCONFA_MC_FORWARDING)
+ size += nla_total_size(4);
+#endif
+
+ return size;
+}
+
+static int inet6_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
+ struct ipv6_devconf *devconf, u32 portid,
+ u32 seq, int event, unsigned int flags,
+ int type)
+{
+ struct nlmsghdr *nlh;
+ struct netconfmsg *ncm;
+
+ nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg),
+ flags);
+ if (nlh == NULL)
+ return -EMSGSIZE;
+
+ ncm = nlmsg_data(nlh);
+ ncm->ncm_family = AF_INET6;
+
+ if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0)
+ goto nla_put_failure;
+
+ /* type -1 is used for ALL */
+ if ((type == -1 || type == NETCONFA_FORWARDING) &&
+ nla_put_s32(skb, NETCONFA_FORWARDING, devconf->forwarding) < 0)
+ goto nla_put_failure;
+#ifdef CONFIG_IPV6_MROUTE
+ if ((type == -1 || type == NETCONFA_MC_FORWARDING) &&
+ nla_put_s32(skb, NETCONFA_MC_FORWARDING,
+ devconf->mc_forwarding) < 0)
+ goto nla_put_failure;
+#endif
+ return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+ return -EMSGSIZE;
+}
+
+void inet6_netconf_notify_devconf(struct net *net, int type, int ifindex,
+ struct ipv6_devconf *devconf)
+{
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(inet6_netconf_msgsize_devconf(type), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = inet6_netconf_fill_devconf(skb, ifindex, devconf, 0, 0,
+ RTM_NEWNETCONF, 0, type);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV6_NETCONF, NULL, GFP_ATOMIC);
+ return;
+errout:
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_NETCONF, err);
+}
+
+static const struct nla_policy devconf_ipv6_policy[NETCONFA_MAX+1] = {
+ [NETCONFA_IFINDEX] = { .len = sizeof(int) },
+ [NETCONFA_FORWARDING] = { .len = sizeof(int) },
+};
+
+static int inet6_netconf_get_devconf(struct sk_buff *in_skb,
+ struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct net *net = sock_net(in_skb->sk);
+ struct nlattr *tb[NETCONFA_MAX+1];
+ struct netconfmsg *ncm;
+ struct sk_buff *skb;
+ struct ipv6_devconf *devconf;
+ struct inet6_dev *in6_dev;
+ struct net_device *dev;
+ int ifindex;
+ int err;
+
+ err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX,
+ devconf_ipv6_policy);
+ if (err < 0)
+ goto errout;
+
+ err = EINVAL;
+ if (!tb[NETCONFA_IFINDEX])
+ goto errout;
+
+ ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]);
+ switch (ifindex) {
+ case NETCONFA_IFINDEX_ALL:
+ devconf = net->ipv6.devconf_all;
+ break;
+ case NETCONFA_IFINDEX_DEFAULT:
+ devconf = net->ipv6.devconf_dflt;
+ break;
+ default:
+ dev = __dev_get_by_index(net, ifindex);
+ if (dev == NULL)
+ goto errout;
+ in6_dev = __in6_dev_get(dev);
+ if (in6_dev == NULL)
+ goto errout;
+ devconf = &in6_dev->cnf;
+ break;
+ }
+
+ err = -ENOBUFS;
+ skb = nlmsg_new(inet6_netconf_msgsize_devconf(-1), GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = inet6_netconf_fill_devconf(skb, ifindex, devconf,
+ NETLINK_CB(in_skb).portid,
+ nlh->nlmsg_seq, RTM_NEWNETCONF, 0,
+ -1);
+ if (err < 0) {
+ /* -EMSGSIZE implies BUG in inet6_netconf_msgsize_devconf() */
+ WARN_ON(err == -EMSGSIZE);
+ kfree_skb(skb);
+ goto errout;
+ }
+ err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid);
+errout:
+ return err;
+}
+
#ifdef CONFIG_SYSCTL
static void dev_forward_change(struct inet6_dev *idev)
{
@@ -471,7 +614,7 @@ static void dev_forward_change(struct inet6_dev *idev)
dev = idev->dev;
if (idev->cnf.forwarding)
dev_disable_lro(dev);
- if (dev && (dev->flags & IFF_MULTICAST)) {
+ if (dev->flags & IFF_MULTICAST) {
if (idev->cnf.forwarding)
ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
else
@@ -486,6 +629,8 @@ static void dev_forward_change(struct inet6_dev *idev)
else
addrconf_leave_anycast(ifa);
}
+ inet6_netconf_notify_devconf(dev_net(dev), NETCONFA_FORWARDING,
+ dev->ifindex, &idev->cnf);
}
@@ -518,6 +663,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
*p = newf;
if (p == &net->ipv6.devconf_dflt->forwarding) {
+ if ((!newf) ^ (!old))
+ inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ NETCONFA_IFINDEX_DEFAULT,
+ net->ipv6.devconf_dflt);
rtnl_unlock();
return 0;
}
@@ -525,6 +674,10 @@ static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
if (p == &net->ipv6.devconf_all->forwarding) {
net->ipv6.devconf_dflt->forwarding = newf;
addrconf_forward_change(net, newf);
+ if ((!newf) ^ (!old))
+ inet6_netconf_notify_devconf(net, NETCONFA_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv6.devconf_all);
} else if ((!newf) ^ (!old))
dev_forward_change((struct inet6_dev *)table->extra1);
rtnl_unlock();
@@ -553,7 +706,7 @@ void inet6_ifa_finish_destroy(struct inet6_ifaddr *ifp)
pr_warn("Freeing alive inet6 address %p\n", ifp);
return;
}
- dst_release(&ifp->rt->dst);
+ ip6_rt_put(ifp->rt);
kfree_rcu(ifp, rcu);
}
@@ -787,17 +940,15 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
struct in6_addr prefix;
struct rt6_info *rt;
- struct net *net = dev_net(ifp->idev->dev);
- struct flowi6 fl6 = {};
ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
- fl6.flowi6_oif = ifp->idev->dev->ifindex;
- fl6.daddr = prefix;
- rt = (struct rt6_info *)ip6_route_lookup(net, &fl6,
- RT6_LOOKUP_F_IFACE);
- if (rt != net->ipv6.ip6_null_entry &&
- addrconf_is_prefix_route(rt)) {
+ rt = addrconf_get_prefix_route(&prefix,
+ ifp->prefix_len,
+ ifp->idev->dev,
+ 0, RTF_GATEWAY | RTF_DEFAULT);
+
+ if (rt) {
if (onlink == 0) {
ip6_del_rt(rt);
rt = NULL;
@@ -805,7 +956,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp)
rt6_set_expires(rt, expires);
}
}
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
/* clean up prefsrc entries */
@@ -1692,7 +1843,7 @@ addrconf_prefix_route(struct in6_addr *pfx, int plen, struct net_device *dev,
This thing is done here expecting that the whole
class of non-broadcast devices need not cloning.
*/
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
if (dev->type == ARPHRD_SIT && (dev->flags & IFF_POINTOPOINT))
cfg.fc_flags |= RTF_NONEXTHOP;
#endif
@@ -1723,7 +1874,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx,
continue;
if ((rt->rt6i_flags & flags) != flags)
continue;
- if ((noflags != 0) && ((rt->rt6i_flags & flags) != 0))
+ if ((rt->rt6i_flags & noflags) != 0)
continue;
dst_hold(&rt->dst);
break;
@@ -1752,7 +1903,7 @@ static void addrconf_add_mroute(struct net_device *dev)
ip6_route_add(&cfg);
}
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
static void sit_route_add(struct net_device *dev)
{
struct fib6_config cfg = {
@@ -1881,8 +2032,7 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
addrconf_prefix_route(&pinfo->prefix, pinfo->prefix_len,
dev, expires, flags);
}
- if (rt)
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
/* Try to figure out our local address for this prefix */
@@ -2104,7 +2254,7 @@ int addrconf_set_dstaddr(struct net *net, void __user *arg)
if (dev == NULL)
goto err_exit;
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
if (dev->type == ARPHRD_SIT) {
const struct net_device_ops *ops = dev->netdev_ops;
struct ifreq ifr;
@@ -2268,7 +2418,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg)
struct in6_ifreq ireq;
int err;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
@@ -2287,7 +2437,7 @@ int addrconf_del_ifaddr(struct net *net, void __user *arg)
struct in6_ifreq ireq;
int err;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
@@ -2315,7 +2465,7 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
}
}
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
static void sit_add_v4_addrs(struct inet6_dev *idev)
{
struct in6_addr addr;
@@ -2434,7 +2584,7 @@ static void addrconf_dev_config(struct net_device *dev)
addrconf_add_linklocal(idev, &addr);
}
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
static void addrconf_sit_config(struct net_device *dev)
{
struct inet6_dev *idev;
@@ -2471,7 +2621,7 @@ static void addrconf_sit_config(struct net_device *dev)
}
#endif
-#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE)
+#if IS_ENABLED(CONFIG_NET_IPGRE)
static void addrconf_gre_config(struct net_device *dev)
{
struct inet6_dev *idev;
@@ -2601,12 +2751,12 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
}
switch (dev->type) {
-#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_SIT)
case ARPHRD_SIT:
addrconf_sit_config(dev);
break;
#endif
-#if defined(CONFIG_NET_IPGRE) || defined(CONFIG_NET_IPGRE_MODULE)
+#if IS_ENABLED(CONFIG_NET_IPGRE)
case ARPHRD_IPGRE:
addrconf_gre_config(dev);
break;
@@ -2843,7 +2993,7 @@ static void addrconf_rs_timer(unsigned long data)
if (idev->dead || !(idev->if_flags & IF_READY))
goto out;
- if (idev->cnf.forwarding)
+ if (!ipv6_accept_ra(idev))
goto out;
/* Announcement received after solicitation was sent */
@@ -3005,8 +3155,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp)
router advertisements, start sending router solicitations.
*/
- if (((ifp->idev->cnf.accept_ra == 1 && !ifp->idev->cnf.forwarding) ||
- ifp->idev->cnf.accept_ra == 2) &&
+ if (ipv6_accept_ra(ifp->idev) &&
ifp->idev->cnf.rtr_solicits > 0 &&
(dev->flags&IFF_LOOPBACK) == 0 &&
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
@@ -3194,7 +3343,7 @@ void if6_proc_exit(void)
}
#endif /* CONFIG_PROC_FS */
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
/* Check if address is a home address configured on any interface. */
int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr)
{
@@ -3892,6 +4041,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
array[DEVCONF_FORCE_TLLAO] = cnf->force_tllao;
+ array[DEVCONF_NDISC_NOTIFY] = cnf->ndisc_notify;
}
static inline size_t inet6_ifla6_size(void)
@@ -4560,6 +4710,13 @@ static struct addrconf_sysctl_table
.proc_handler = proc_dointvec
},
{
+ .procname = "ndisc_notify",
+ .data = &ipv6_devconf.ndisc_notify,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
/* sentinel */
}
},
@@ -4784,6 +4941,8 @@ int __init addrconf_init(void)
inet6_dump_ifmcaddr, NULL);
__rtnl_register(PF_INET6, RTM_GETANYCAST, NULL,
inet6_dump_ifacaddr, NULL);
+ __rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf,
+ NULL, NULL);
ipv6_addr_label_rtnl_register();
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index a974247a9ae..b043c60429b 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -160,7 +160,8 @@ lookup_protocol:
}
err = -EPERM;
- if (sock->type == SOCK_RAW && !kern && !capable(CAP_NET_RAW))
+ if (sock->type == SOCK_RAW && !kern &&
+ !ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock;
sock->ops = answer->ops;
@@ -282,7 +283,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return -EINVAL;
snum = ntohs(addr->sin6_port);
- if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ if (snum && snum < PROT_SOCK && !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
lock_sock(sk);
@@ -699,249 +700,9 @@ bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(ipv6_opt_accepted);
-static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
-{
- const struct inet6_protocol *ops = NULL;
-
- for (;;) {
- struct ipv6_opt_hdr *opth;
- int len;
-
- if (proto != NEXTHDR_HOP) {
- ops = rcu_dereference(inet6_protos[proto]);
-
- if (unlikely(!ops))
- break;
-
- if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
- break;
- }
-
- if (unlikely(!pskb_may_pull(skb, 8)))
- break;
-
- opth = (void *)skb->data;
- len = ipv6_optlen(opth);
-
- if (unlikely(!pskb_may_pull(skb, len)))
- break;
-
- proto = opth->nexthdr;
- __skb_pull(skb, len);
- }
-
- return proto;
-}
-
-static int ipv6_gso_send_check(struct sk_buff *skb)
-{
- const struct ipv6hdr *ipv6h;
- const struct inet6_protocol *ops;
- int err = -EINVAL;
-
- if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
- goto out;
-
- ipv6h = ipv6_hdr(skb);
- __skb_pull(skb, sizeof(*ipv6h));
- err = -EPROTONOSUPPORT;
-
- rcu_read_lock();
- ops = rcu_dereference(inet6_protos[
- ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
-
- if (likely(ops && ops->gso_send_check)) {
- skb_reset_transport_header(skb);
- err = ops->gso_send_check(skb);
- }
- rcu_read_unlock();
-
-out:
- return err;
-}
-
-static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
- netdev_features_t features)
-{
- struct sk_buff *segs = ERR_PTR(-EINVAL);
- struct ipv6hdr *ipv6h;
- const struct inet6_protocol *ops;
- int proto;
- struct frag_hdr *fptr;
- unsigned int unfrag_ip6hlen;
- u8 *prevhdr;
- int offset = 0;
-
- if (!(features & NETIF_F_V6_CSUM))
- features &= ~NETIF_F_SG;
-
- if (unlikely(skb_shinfo(skb)->gso_type &
- ~(SKB_GSO_UDP |
- SKB_GSO_DODGY |
- SKB_GSO_TCP_ECN |
- SKB_GSO_TCPV6 |
- 0)))
- goto out;
-
- if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
- goto out;
-
- ipv6h = ipv6_hdr(skb);
- __skb_pull(skb, sizeof(*ipv6h));
- segs = ERR_PTR(-EPROTONOSUPPORT);
-
- proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
- rcu_read_lock();
- ops = rcu_dereference(inet6_protos[proto]);
- if (likely(ops && ops->gso_segment)) {
- skb_reset_transport_header(skb);
- segs = ops->gso_segment(skb, features);
- }
- rcu_read_unlock();
-
- if (IS_ERR(segs))
- goto out;
-
- for (skb = segs; skb; skb = skb->next) {
- ipv6h = ipv6_hdr(skb);
- ipv6h->payload_len = htons(skb->len - skb->mac_len -
- sizeof(*ipv6h));
- if (proto == IPPROTO_UDP) {
- unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
- fptr = (struct frag_hdr *)(skb_network_header(skb) +
- unfrag_ip6hlen);
- fptr->frag_off = htons(offset);
- if (skb->next != NULL)
- fptr->frag_off |= htons(IP6_MF);
- offset += (ntohs(ipv6h->payload_len) -
- sizeof(struct frag_hdr));
- }
- }
-
-out:
- return segs;
-}
-
-static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
- struct sk_buff *skb)
-{
- const struct inet6_protocol *ops;
- struct sk_buff **pp = NULL;
- struct sk_buff *p;
- struct ipv6hdr *iph;
- unsigned int nlen;
- unsigned int hlen;
- unsigned int off;
- int flush = 1;
- int proto;
- __wsum csum;
-
- off = skb_gro_offset(skb);
- hlen = off + sizeof(*iph);
- iph = skb_gro_header_fast(skb, off);
- if (skb_gro_header_hard(skb, hlen)) {
- iph = skb_gro_header_slow(skb, hlen, off);
- if (unlikely(!iph))
- goto out;
- }
-
- skb_gro_pull(skb, sizeof(*iph));
- skb_set_transport_header(skb, skb_gro_offset(skb));
-
- flush += ntohs(iph->payload_len) != skb_gro_len(skb);
-
- rcu_read_lock();
- proto = iph->nexthdr;
- ops = rcu_dereference(inet6_protos[proto]);
- if (!ops || !ops->gro_receive) {
- __pskb_pull(skb, skb_gro_offset(skb));
- proto = ipv6_gso_pull_exthdrs(skb, proto);
- skb_gro_pull(skb, -skb_transport_offset(skb));
- skb_reset_transport_header(skb);
- __skb_push(skb, skb_gro_offset(skb));
-
- ops = rcu_dereference(inet6_protos[proto]);
- if (!ops || !ops->gro_receive)
- goto out_unlock;
-
- iph = ipv6_hdr(skb);
- }
-
- NAPI_GRO_CB(skb)->proto = proto;
-
- flush--;
- nlen = skb_network_header_len(skb);
-
- for (p = *head; p; p = p->next) {
- const struct ipv6hdr *iph2;
- __be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
-
- if (!NAPI_GRO_CB(p)->same_flow)
- continue;
-
- iph2 = ipv6_hdr(p);
- first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
-
- /* All fields must match except length and Traffic Class. */
- if (nlen != skb_network_header_len(p) ||
- (first_word & htonl(0xF00FFFFF)) ||
- memcmp(&iph->nexthdr, &iph2->nexthdr,
- nlen - offsetof(struct ipv6hdr, nexthdr))) {
- NAPI_GRO_CB(p)->same_flow = 0;
- continue;
- }
- /* flush if Traffic Class fields are different */
- NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
- NAPI_GRO_CB(p)->flush |= flush;
- }
-
- NAPI_GRO_CB(skb)->flush |= flush;
-
- csum = skb->csum;
- skb_postpull_rcsum(skb, iph, skb_network_header_len(skb));
-
- pp = ops->gro_receive(head, skb);
-
- skb->csum = csum;
-
-out_unlock:
- rcu_read_unlock();
-
-out:
- NAPI_GRO_CB(skb)->flush |= flush;
-
- return pp;
-}
-
-static int ipv6_gro_complete(struct sk_buff *skb)
-{
- const struct inet6_protocol *ops;
- struct ipv6hdr *iph = ipv6_hdr(skb);
- int err = -ENOSYS;
-
- iph->payload_len = htons(skb->len - skb_network_offset(skb) -
- sizeof(*iph));
-
- rcu_read_lock();
- ops = rcu_dereference(inet6_protos[NAPI_GRO_CB(skb)->proto]);
- if (WARN_ON(!ops || !ops->gro_complete))
- goto out_unlock;
-
- err = ops->gro_complete(skb);
-
-out_unlock:
- rcu_read_unlock();
-
- return err;
-}
-
static struct packet_type ipv6_packet_type __read_mostly = {
.type = cpu_to_be16(ETH_P_IPV6),
.func = ipv6_rcv,
- .gso_send_check = ipv6_gso_send_check,
- .gso_segment = ipv6_gso_segment,
- .gro_receive = ipv6_gro_receive,
- .gro_complete = ipv6_gro_complete,
};
static int __init ipv6_packet_init(void)
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 7e6139508ee..384233188ac 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -44,7 +44,7 @@
#define IPV6HDR_BASELEN 8
struct tmp_ext {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
struct in6_addr saddr;
#endif
struct in6_addr daddr;
@@ -152,7 +152,7 @@ bad:
return false;
}
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
/**
* ipv6_rearrange_destopt - rearrange IPv6 destination options header
* @iph: IPv6 header
@@ -320,7 +320,7 @@ static void ah6_output_done(struct crypto_async_request *base, int err)
memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
if (extlen) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
memcpy(&top_iph->saddr, iph_ext, extlen);
#else
memcpy(&top_iph->daddr, iph_ext, extlen);
@@ -385,7 +385,7 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
memcpy(iph_base, top_iph, IPV6HDR_BASELEN);
if (extlen) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
memcpy(iph_ext, &top_iph->saddr, extlen);
#else
memcpy(iph_ext, &top_iph->daddr, extlen);
@@ -434,7 +434,7 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
if (extlen) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
memcpy(&top_iph->saddr, iph_ext, extlen);
#else
memcpy(&top_iph->daddr, iph_ext, extlen);
@@ -472,7 +472,10 @@ static void ah6_input_done(struct crypto_async_request *base, int err)
skb->network_header += ah_hlen;
memcpy(skb_network_header(skb), work_iph, hdr_len);
__skb_pull(skb, ah_hlen + hdr_len);
- skb_set_transport_header(skb, -hdr_len);
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -hdr_len);
out:
kfree(AH_SKB_CB(skb)->tmp);
xfrm_input_resume(skb, err);
@@ -593,9 +596,13 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
skb->network_header += ah_hlen;
memcpy(skb_network_header(skb), work_iph, hdr_len);
- skb->transport_header = skb->network_header;
__skb_pull(skb, ah_hlen + hdr_len);
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -hdr_len);
+
err = nexthdr;
out_free:
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index cdf02be5f19..757a810d8f1 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -64,7 +64,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
int ishost = !net->ipv6.devconf_all->forwarding;
int err = 0;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (ipv6_addr_is_multicast(addr))
return -EINVAL;
@@ -84,7 +84,7 @@ int ipv6_sock_ac_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) {
dev = rt->dst.dev;
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
} else if (ishost) {
err = -EADDRNOTAVAIL;
goto error;
@@ -189,6 +189,9 @@ void ipv6_sock_ac_close(struct sock *sk)
struct net *net = sock_net(sk);
int prev_index;
+ if (!np->ipv6_ac_list)
+ return;
+
write_lock_bh(&ipv6_sk_ac_lock);
pac = np->ipv6_ac_list;
np->ipv6_ac_list = NULL;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index be2b67d631e..8edf2601065 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -701,7 +701,7 @@ int datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!capable(CAP_NET_RAW)) {
+ if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
err = -EPERM;
goto exit_f;
}
@@ -721,7 +721,7 @@ int datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!capable(CAP_NET_RAW)) {
+ if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
err = -EPERM;
goto exit_f;
}
@@ -746,7 +746,7 @@ int datagram_send_ctl(struct net *net, struct sock *sk,
err = -EINVAL;
goto exit_f;
}
- if (!capable(CAP_NET_RAW)) {
+ if (!ns_capable(net->user_ns, CAP_NET_RAW)) {
err = -EPERM;
goto exit_f;
}
@@ -769,7 +769,7 @@ int datagram_send_ctl(struct net *net, struct sock *sk,
rthdr = (struct ipv6_rt_hdr *)CMSG_DATA(cmsg);
switch (rthdr->type) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_SRCRT_TYPE_2:
if (rthdr->hdrlen != 2 ||
rthdr->segments_left != 1) {
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 282f3723ee1..40ffd72243a 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -300,7 +300,10 @@ static int esp_input_done2(struct sk_buff *skb, int err)
pskb_trim(skb, skb->len - alen - padlen - 2);
__skb_pull(skb, hlen);
- skb_set_transport_header(skb, -hdr_len);
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ skb_reset_transport_header(skb);
+ else
+ skb_set_transport_header(skb, -hdr_len);
err = nexthdr[1];
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index fa3d9c32809..473f628f9f2 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -43,56 +43,12 @@
#include <net/ndisc.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/xfrm.h>
#endif
#include <asm/uaccess.h>
-int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
-{
- const unsigned char *nh = skb_network_header(skb);
- int packet_len = skb->tail - skb->network_header;
- struct ipv6_opt_hdr *hdr;
- int len;
-
- if (offset + 2 > packet_len)
- goto bad;
- hdr = (struct ipv6_opt_hdr *)(nh + offset);
- len = ((hdr->hdrlen + 1) << 3);
-
- if (offset + len > packet_len)
- goto bad;
-
- offset += 2;
- len -= 2;
-
- while (len > 0) {
- int opttype = nh[offset];
- int optlen;
-
- if (opttype == type)
- return offset;
-
- switch (opttype) {
- case IPV6_TLV_PAD1:
- optlen = 1;
- break;
- default:
- optlen = nh[offset + 1] + 2;
- if (optlen > len)
- goto bad;
- break;
- }
- offset += optlen;
- len -= optlen;
- }
- /* not_found */
- bad:
- return -1;
-}
-EXPORT_SYMBOL_GPL(ipv6_find_tlv);
-
/*
* Parsing tlv encoded headers.
*
@@ -224,7 +180,7 @@ bad:
Destination options header.
*****************************/
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
{
struct ipv6_destopt_hao *hao;
@@ -288,7 +244,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
#endif
static const struct tlvtype_proc tlvprocdestopt_lst[] = {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
{
.type = IPV6_TLV_HAO,
.func = ipv6_dest_hao,
@@ -300,7 +256,7 @@ static const struct tlvtype_proc tlvprocdestopt_lst[] = {
static int ipv6_destopt_rcv(struct sk_buff *skb)
{
struct inet6_skb_parm *opt = IP6CB(skb);
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
__u16 dstbuf;
#endif
struct dst_entry *dst = skb_dst(skb);
@@ -315,14 +271,14 @@ static int ipv6_destopt_rcv(struct sk_buff *skb)
}
opt->lastopt = opt->dst1 = skb_network_header_len(skb);
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
dstbuf = opt->dst1;
#endif
if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) {
skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3;
opt = IP6CB(skb);
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
opt->nhoff = dstbuf;
#else
opt->nhoff = opt->dst1;
@@ -378,7 +334,7 @@ static int ipv6_rthdr_rcv(struct sk_buff *skb)
looped_back:
if (hdr->segments_left == 0) {
switch (hdr->type) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_SRCRT_TYPE_2:
/* Silently discard type 2 header unless it was
* processed by own
@@ -404,7 +360,7 @@ looped_back:
}
switch (hdr->type) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_SRCRT_TYPE_2:
if (accept_source_route < 0)
goto unknown_rh;
@@ -461,7 +417,7 @@ looped_back:
addr += i - 1;
switch (hdr->type) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_SRCRT_TYPE_2:
if (xfrm6_input_addr(skb, (xfrm_address_t *)addr,
(xfrm_address_t *)&ipv6_hdr(skb)->saddr,
@@ -528,12 +484,12 @@ unknown_rh:
static const struct inet6_protocol rthdr_protocol = {
.handler = ipv6_rthdr_rcv,
- .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
+ .flags = INET6_PROTO_NOPOLICY,
};
static const struct inet6_protocol destopt_protocol = {
.handler = ipv6_destopt_rcv,
- .flags = INET6_PROTO_NOPOLICY | INET6_PROTO_GSO_EXTHDR,
+ .flags = INET6_PROTO_NOPOLICY,
};
static const struct inet6_protocol nodata_protocol = {
@@ -559,10 +515,10 @@ int __init ipv6_exthdrs_init(void)
out:
return ret;
-out_rthdr:
- inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
out_destopt:
inet6_del_protocol(&destopt_protocol, IPPROTO_DSTOPTS);
+out_rthdr:
+ inet6_del_protocol(&rthdr_protocol, IPPROTO_ROUTING);
goto out;
};
diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c
index f73d59a1413..c5e83fae4df 100644
--- a/net/ipv6/exthdrs_core.c
+++ b/net/ipv6/exthdrs_core.c
@@ -111,3 +111,171 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp,
return start;
}
EXPORT_SYMBOL(ipv6_skip_exthdr);
+
+int ipv6_find_tlv(struct sk_buff *skb, int offset, int type)
+{
+ const unsigned char *nh = skb_network_header(skb);
+ int packet_len = skb->tail - skb->network_header;
+ struct ipv6_opt_hdr *hdr;
+ int len;
+
+ if (offset + 2 > packet_len)
+ goto bad;
+ hdr = (struct ipv6_opt_hdr *)(nh + offset);
+ len = ((hdr->hdrlen + 1) << 3);
+
+ if (offset + len > packet_len)
+ goto bad;
+
+ offset += 2;
+ len -= 2;
+
+ while (len > 0) {
+ int opttype = nh[offset];
+ int optlen;
+
+ if (opttype == type)
+ return offset;
+
+ switch (opttype) {
+ case IPV6_TLV_PAD1:
+ optlen = 1;
+ break;
+ default:
+ optlen = nh[offset + 1] + 2;
+ if (optlen > len)
+ goto bad;
+ break;
+ }
+ offset += optlen;
+ len -= optlen;
+ }
+ /* not_found */
+ bad:
+ return -1;
+}
+EXPORT_SYMBOL_GPL(ipv6_find_tlv);
+
+/*
+ * find the offset to specified header or the protocol number of last header
+ * if target < 0. "last header" is transport protocol header, ESP, or
+ * "No next header".
+ *
+ * Note that *offset is used as input/output parameter. an if it is not zero,
+ * then it must be a valid offset to an inner IPv6 header. This can be used
+ * to explore inner IPv6 header, eg. ICMPv6 error messages.
+ *
+ * If target header is found, its offset is set in *offset and return protocol
+ * number. Otherwise, return -1.
+ *
+ * If the first fragment doesn't contain the final protocol header or
+ * NEXTHDR_NONE it is considered invalid.
+ *
+ * Note that non-1st fragment is special case that "the protocol number
+ * of last header" is "next header" field in Fragment header. In this case,
+ * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
+ * isn't NULL.
+ *
+ * if flags is not NULL and it's a fragment, then the frag flag
+ * IP6_FH_F_FRAG will be set. If it's an AH header, the
+ * IP6_FH_F_AUTH flag is set and target < 0, then this function will
+ * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this
+ * function will skip all those routing headers, where segements_left was 0.
+ */
+int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
+ int target, unsigned short *fragoff, int *flags)
+{
+ unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
+ u8 nexthdr = ipv6_hdr(skb)->nexthdr;
+ unsigned int len;
+ bool found;
+
+ if (fragoff)
+ *fragoff = 0;
+
+ if (*offset) {
+ struct ipv6hdr _ip6, *ip6;
+
+ ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
+ if (!ip6 || (ip6->version != 6)) {
+ printk(KERN_ERR "IPv6 header not found\n");
+ return -EBADMSG;
+ }
+ start = *offset + sizeof(struct ipv6hdr);
+ nexthdr = ip6->nexthdr;
+ }
+ len = skb->len - start;
+
+ do {
+ struct ipv6_opt_hdr _hdr, *hp;
+ unsigned int hdrlen;
+ found = (nexthdr == target);
+
+ if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
+ if (target < 0)
+ break;
+ return -ENOENT;
+ }
+
+ hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
+ if (hp == NULL)
+ return -EBADMSG;
+
+ if (nexthdr == NEXTHDR_ROUTING) {
+ struct ipv6_rt_hdr _rh, *rh;
+
+ rh = skb_header_pointer(skb, start, sizeof(_rh),
+ &_rh);
+ if (rh == NULL)
+ return -EBADMSG;
+
+ if (flags && (*flags & IP6_FH_F_SKIP_RH) &&
+ rh->segments_left == 0)
+ found = false;
+ }
+
+ if (nexthdr == NEXTHDR_FRAGMENT) {
+ unsigned short _frag_off;
+ __be16 *fp;
+
+ if (flags) /* Indicate that this is a fragment */
+ *flags |= IP6_FH_F_FRAG;
+ fp = skb_header_pointer(skb,
+ start+offsetof(struct frag_hdr,
+ frag_off),
+ sizeof(_frag_off),
+ &_frag_off);
+ if (fp == NULL)
+ return -EBADMSG;
+
+ _frag_off = ntohs(*fp) & ~0x7;
+ if (_frag_off) {
+ if (target < 0 &&
+ ((!ipv6_ext_hdr(hp->nexthdr)) ||
+ hp->nexthdr == NEXTHDR_NONE)) {
+ if (fragoff)
+ *fragoff = _frag_off;
+ return hp->nexthdr;
+ }
+ return -ENOENT;
+ }
+ hdrlen = 8;
+ } else if (nexthdr == NEXTHDR_AUTH) {
+ if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0))
+ break;
+ hdrlen = (hp->hdrlen + 2) << 2;
+ } else
+ hdrlen = ipv6_optlen(hp);
+
+ if (!found) {
+ nexthdr = hp->nexthdr;
+ len -= hdrlen;
+ start += hdrlen;
+ }
+ } while (!found);
+
+ *offset = start;
+ return nexthdr;
+}
+EXPORT_SYMBOL(ipv6_find_hdr);
+
diff --git a/net/ipv6/exthdrs_offload.c b/net/ipv6/exthdrs_offload.c
new file mode 100644
index 00000000000..cf77f3abfd0
--- /dev/null
+++ b/net/ipv6/exthdrs_offload.c
@@ -0,0 +1,41 @@
+/*
+ * IPV6 GSO/GRO offload support
+ * Linux INET6 implementation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * IPV6 Extension Header GSO/GRO support
+ */
+#include <net/protocol.h>
+#include "ip6_offload.h"
+
+static const struct net_offload rthdr_offload = {
+ .flags = INET6_PROTO_GSO_EXTHDR,
+};
+
+static const struct net_offload dstopt_offload = {
+ .flags = INET6_PROTO_GSO_EXTHDR,
+};
+
+int __init ipv6_exthdrs_offload_init(void)
+{
+ int ret;
+
+ ret = inet6_add_offload(&rthdr_offload, IPPROTO_ROUTING);
+ if (!ret)
+ goto out;
+
+ ret = inet6_add_offload(&dstopt_offload, IPPROTO_DSTOPTS);
+ if (!ret)
+ goto out_rt;
+
+out:
+ return ret;
+
+out_rt:
+ inet_del_offload(&rthdr_offload, IPPROTO_ROUTING);
+ goto out;
+}
diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c
index d9fb9110f60..2e1a432867c 100644
--- a/net/ipv6/fib6_rules.c
+++ b/net/ipv6/fib6_rules.c
@@ -100,7 +100,7 @@ static int fib6_rule_action(struct fib_rule *rule, struct flowi *flp,
goto out;
}
again:
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
rt = NULL;
goto out;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 24d69dbca4d..fff5bdd8b68 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -81,10 +81,22 @@ static inline struct sock *icmpv6_sk(struct net *net)
return net->ipv6.icmp_sk[smp_processor_id()];
}
+static void icmpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ u8 type, u8 code, int offset, __be32 info)
+{
+ struct net *net = dev_net(skb->dev);
+
+ if (type == ICMPV6_PKT_TOOBIG)
+ ip6_update_pmtu(skb, net, info, 0, 0);
+ else if (type == NDISC_REDIRECT)
+ ip6_redirect(skb, net, 0, 0);
+}
+
static int icmpv6_rcv(struct sk_buff *skb);
static const struct inet6_protocol icmpv6_protocol = {
.handler = icmpv6_rcv,
+ .err_handler = icmpv6_err,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
@@ -280,7 +292,7 @@ static int icmpv6_getfrag(void *from, char *to, int offset, int len, int odd, st
return 0;
}
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
static void mip6_addr_swap(struct sk_buff *skb)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index c4f934176ca..30647857a37 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -252,6 +252,7 @@ struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu)
return NULL;
dst->ops->update_pmtu(dst, sk, NULL, mtu);
- return inet6_csk_route_socket(sk, &fl6);
+ dst = inet6_csk_route_socket(sk, &fl6);
+ return IS_ERR(dst) ? NULL : dst;
}
EXPORT_SYMBOL_GPL(inet6_csk_update_pmtu);
diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c
index 73f1a00a96a..dea17fd28e5 100644
--- a/net/ipv6/inet6_hashtables.c
+++ b/net/ipv6/inet6_hashtables.c
@@ -87,11 +87,13 @@ struct sock *__inet6_lookup_established(struct net *net,
rcu_read_lock();
begin:
sk_nulls_for_each_rcu(sk, node, &head->chain) {
- /* For IPV6 do the cheaper port and family tests first. */
- if (INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (sk->sk_hash != hash)
+ continue;
+ if (likely(INET6_MATCH(sk, net, saddr, daddr, ports, dif))) {
if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt)))
goto begintw;
- if (!INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (unlikely(!INET6_MATCH(sk, net, saddr, daddr,
+ ports, dif))) {
sock_put(sk);
goto begin;
}
@@ -104,12 +106,16 @@ begin:
begintw:
/* Must check for a TIME_WAIT'er before going to listener hash. */
sk_nulls_for_each_rcu(sk, node, &head->twchain) {
- if (INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (sk->sk_hash != hash)
+ continue;
+ if (likely(INET6_TW_MATCH(sk, net, saddr, daddr,
+ ports, dif))) {
if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) {
sk = NULL;
goto out;
}
- if (!INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) {
+ if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr,
+ ports, dif))) {
sock_put(sk);
goto begintw;
}
@@ -236,9 +242,12 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
/* Check TIME-WAIT sockets first. */
sk_nulls_for_each(sk2, node, &head->twchain) {
- tw = inet_twsk(sk2);
+ if (sk2->sk_hash != hash)
+ continue;
- if (INET6_TW_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) {
+ if (likely(INET6_TW_MATCH(sk2, net, saddr, daddr,
+ ports, dif))) {
+ tw = inet_twsk(sk2);
if (twsk_unique(sk, sk2, twp))
goto unique;
else
@@ -249,7 +258,9 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row,
/* And established part... */
sk_nulls_for_each(sk2, node, &head->chain) {
- if (INET6_MATCH(sk2, net, hash, saddr, daddr, ports, dif))
+ if (sk2->sk_hash != hash)
+ continue;
+ if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif)))
goto not_unique;
}
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 24995a93ef8..710cafd2e1a 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -672,6 +672,8 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
iter->rt6i_idev == rt->rt6i_idev &&
ipv6_addr_equal(&iter->rt6i_gateway,
&rt->rt6i_gateway)) {
+ if (rt->rt6i_nsiblings)
+ rt->rt6i_nsiblings = 0;
if (!(iter->rt6i_flags & RTF_EXPIRES))
return -EEXIST;
if (!(rt->rt6i_flags & RTF_EXPIRES))
@@ -680,6 +682,21 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
rt6_set_expires(iter, rt->dst.expires);
return -EEXIST;
}
+ /* If we have the same destination and the same metric,
+ * but not the same gateway, then the route we try to
+ * add is sibling to this route, increment our counter
+ * of siblings, and later we will add our route to the
+ * list.
+ * Only static routes (which don't have flag
+ * RTF_EXPIRES) are used for ECMPv6.
+ *
+ * To avoid long list, we only had siblings if the
+ * route have a gateway.
+ */
+ if (rt->rt6i_flags & RTF_GATEWAY &&
+ !(rt->rt6i_flags & RTF_EXPIRES) &&
+ !(iter->rt6i_flags & RTF_EXPIRES))
+ rt->rt6i_nsiblings++;
}
if (iter->rt6i_metric > rt->rt6i_metric)
@@ -692,6 +709,35 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt,
if (ins == &fn->leaf)
fn->rr_ptr = NULL;
+ /* Link this route to others same route. */
+ if (rt->rt6i_nsiblings) {
+ unsigned int rt6i_nsiblings;
+ struct rt6_info *sibling, *temp_sibling;
+
+ /* Find the first route that have the same metric */
+ sibling = fn->leaf;
+ while (sibling) {
+ if (sibling->rt6i_metric == rt->rt6i_metric) {
+ list_add_tail(&rt->rt6i_siblings,
+ &sibling->rt6i_siblings);
+ break;
+ }
+ sibling = sibling->dst.rt6_next;
+ }
+ /* For each sibling in the list, increment the counter of
+ * siblings. BUG() if counters does not match, list of siblings
+ * is broken!
+ */
+ rt6i_nsiblings = 0;
+ list_for_each_entry_safe(sibling, temp_sibling,
+ &rt->rt6i_siblings, rt6i_siblings) {
+ sibling->rt6i_nsiblings++;
+ BUG_ON(sibling->rt6i_nsiblings != rt->rt6i_nsiblings);
+ rt6i_nsiblings++;
+ }
+ BUG_ON(rt6i_nsiblings != rt->rt6i_nsiblings);
+ }
+
/*
* insert node
*/
@@ -1193,6 +1239,17 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp,
if (fn->rr_ptr == rt)
fn->rr_ptr = NULL;
+ /* Remove this entry from other siblings */
+ if (rt->rt6i_nsiblings) {
+ struct rt6_info *sibling, *next_sibling;
+
+ list_for_each_entry_safe(sibling, next_sibling,
+ &rt->rt6i_siblings, rt6i_siblings)
+ sibling->rt6i_nsiblings--;
+ rt->rt6i_nsiblings = 0;
+ list_del_init(&rt->rt6i_siblings);
+ }
+
/* Adjust walkers */
read_lock(&fib6_walker_lock);
FOR_WALKERS(w) {
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 90bbefb5794..29124b7a04c 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -519,7 +519,8 @@ int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen)
}
read_unlock_bh(&ip6_sk_fl_lock);
- if (freq.flr_share == IPV6_FL_S_NONE && capable(CAP_NET_ADMIN)) {
+ if (freq.flr_share == IPV6_FL_S_NONE &&
+ ns_capable(net->user_ns, CAP_NET_ADMIN)) {
fl = fl_lookup(net, freq.flr_label);
if (fl) {
err = fl6_renew(fl, freq.flr_linger, freq.flr_expires);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 0185679c5f5..c727e471275 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -109,21 +109,6 @@ static u32 HASH_ADDR(const struct in6_addr *addr)
#define tunnels_r tunnels[2]
#define tunnels_l tunnels[1]
#define tunnels_wc tunnels[0]
-/*
- * Locking : hash tables are protected by RCU and RTNL
- */
-
-#define for_each_ip_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
-
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- u64 rx_packets;
- u64 rx_bytes;
- u64 tx_packets;
- u64 tx_bytes;
- struct u64_stats_sync syncp;
-};
static struct rtnl_link_stats64 *ip6gre_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
@@ -181,7 +166,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
ARPHRD_ETHER : ARPHRD_IP6GRE;
int score, cand_score = 4;
- for_each_ip_tunnel_rcu(ign->tunnels_r_l[h0 ^ h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_r_l[h0 ^ h1]) {
if (!ipv6_addr_equal(local, &t->parms.laddr) ||
!ipv6_addr_equal(remote, &t->parms.raddr) ||
key != t->parms.i_key ||
@@ -206,7 +191,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_r[h0 ^ h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_r[h0 ^ h1]) {
if (!ipv6_addr_equal(remote, &t->parms.raddr) ||
key != t->parms.i_key ||
!(t->dev->flags & IFF_UP))
@@ -230,7 +215,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_l[h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_l[h1]) {
if ((!ipv6_addr_equal(local, &t->parms.laddr) &&
(!ipv6_addr_equal(local, &t->parms.raddr) ||
!ipv6_addr_is_multicast(local))) ||
@@ -256,7 +241,7 @@ static struct ip6_tnl *ip6gre_tunnel_lookup(struct net_device *dev,
}
}
- for_each_ip_tunnel_rcu(ign->tunnels_wc[h1]) {
+ for_each_ip_tunnel_rcu(t, ign->tunnels_wc[h1]) {
if (t->parms.i_key != key ||
!(t->dev->flags & IFF_UP))
continue;
@@ -773,8 +758,6 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
skb_dst_set_noref(skb, dst);
}
- skb->transport_header = skb->network_header;
-
proto = NEXTHDR_GRE;
if (encap_limit >= 0) {
init_tel_txopt(&opt, encap_limit);
@@ -783,6 +766,7 @@ static netdev_tx_t ip6gre_xmit2(struct sk_buff *skb,
skb_push(skb, gre_hlen);
skb_reset_network_header(skb);
+ skb_set_transport_header(skb, sizeof(*ipv6h));
/*
* Push down and install the IP header.
@@ -1069,7 +1053,7 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
dev->mtu = IPV6_MIN_MTU;
}
}
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
t->hlen = addend;
@@ -1161,7 +1145,7 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -1209,7 +1193,7 @@ static int ip6gre_tunnel_ioctl(struct net_device *dev,
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
if (dev == ign->fb_tunnel_dev) {
@@ -1633,9 +1617,9 @@ static size_t ip6gre_get_size(const struct net_device *dev)
/* IFLA_GRE_OKEY */
nla_total_size(4) +
/* IFLA_GRE_LOCAL */
- nla_total_size(4) +
+ nla_total_size(sizeof(struct in6_addr)) +
/* IFLA_GRE_REMOTE */
- nla_total_size(4) +
+ nla_total_size(sizeof(struct in6_addr)) +
/* IFLA_GRE_TTL */
nla_total_size(1) +
/* IFLA_GRE_TOS */
@@ -1659,8 +1643,8 @@ static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_be16(skb, IFLA_GRE_OFLAGS, p->o_flags) ||
nla_put_be32(skb, IFLA_GRE_IKEY, p->i_key) ||
nla_put_be32(skb, IFLA_GRE_OKEY, p->o_key) ||
- nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->raddr) ||
- nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->laddr) ||
+ nla_put(skb, IFLA_GRE_LOCAL, sizeof(struct in6_addr), &p->laddr) ||
+ nla_put(skb, IFLA_GRE_REMOTE, sizeof(struct in6_addr), &p->raddr) ||
nla_put_u8(skb, IFLA_GRE_TTL, p->hop_limit) ||
/*nla_put_u8(skb, IFLA_GRE_TOS, t->priority) ||*/
nla_put_u8(skb, IFLA_GRE_ENCAP_LIMIT, p->encap_limit) ||
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
new file mode 100644
index 00000000000..f26f0da7f09
--- /dev/null
+++ b/net/ipv6/ip6_offload.c
@@ -0,0 +1,282 @@
+/*
+ * IPV6 GSO/GRO offload support
+ * Linux INET6 implementation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/printk.h>
+
+#include <net/protocol.h>
+#include <net/ipv6.h>
+
+#include "ip6_offload.h"
+
+static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
+{
+ const struct net_offload *ops = NULL;
+
+ for (;;) {
+ struct ipv6_opt_hdr *opth;
+ int len;
+
+ if (proto != NEXTHDR_HOP) {
+ ops = rcu_dereference(inet6_offloads[proto]);
+
+ if (unlikely(!ops))
+ break;
+
+ if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
+ break;
+ }
+
+ if (unlikely(!pskb_may_pull(skb, 8)))
+ break;
+
+ opth = (void *)skb->data;
+ len = ipv6_optlen(opth);
+
+ if (unlikely(!pskb_may_pull(skb, len)))
+ break;
+
+ proto = opth->nexthdr;
+ __skb_pull(skb, len);
+ }
+
+ return proto;
+}
+
+static int ipv6_gso_send_check(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ipv6h;
+ const struct net_offload *ops;
+ int err = -EINVAL;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+ goto out;
+
+ ipv6h = ipv6_hdr(skb);
+ __skb_pull(skb, sizeof(*ipv6h));
+ err = -EPROTONOSUPPORT;
+
+ rcu_read_lock();
+ ops = rcu_dereference(inet6_offloads[
+ ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
+
+ if (likely(ops && ops->callbacks.gso_send_check)) {
+ skb_reset_transport_header(skb);
+ err = ops->callbacks.gso_send_check(skb);
+ }
+ rcu_read_unlock();
+
+out:
+ return err;
+}
+
+static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ struct ipv6hdr *ipv6h;
+ const struct net_offload *ops;
+ int proto;
+ struct frag_hdr *fptr;
+ unsigned int unfrag_ip6hlen;
+ u8 *prevhdr;
+ int offset = 0;
+
+ if (!(features & NETIF_F_V6_CSUM))
+ features &= ~NETIF_F_SG;
+
+ if (unlikely(skb_shinfo(skb)->gso_type &
+ ~(SKB_GSO_UDP |
+ SKB_GSO_DODGY |
+ SKB_GSO_TCP_ECN |
+ SKB_GSO_TCPV6 |
+ 0)))
+ goto out;
+
+ if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))
+ goto out;
+
+ ipv6h = ipv6_hdr(skb);
+ __skb_pull(skb, sizeof(*ipv6h));
+ segs = ERR_PTR(-EPROTONOSUPPORT);
+
+ proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
+ rcu_read_lock();
+ ops = rcu_dereference(inet6_offloads[proto]);
+ if (likely(ops && ops->callbacks.gso_segment)) {
+ skb_reset_transport_header(skb);
+ segs = ops->callbacks.gso_segment(skb, features);
+ }
+ rcu_read_unlock();
+
+ if (IS_ERR(segs))
+ goto out;
+
+ for (skb = segs; skb; skb = skb->next) {
+ ipv6h = ipv6_hdr(skb);
+ ipv6h->payload_len = htons(skb->len - skb->mac_len -
+ sizeof(*ipv6h));
+ if (proto == IPPROTO_UDP) {
+ unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+ fptr = (struct frag_hdr *)(skb_network_header(skb) +
+ unfrag_ip6hlen);
+ fptr->frag_off = htons(offset);
+ if (skb->next != NULL)
+ fptr->frag_off |= htons(IP6_MF);
+ offset += (ntohs(ipv6h->payload_len) -
+ sizeof(struct frag_hdr));
+ }
+ }
+
+out:
+ return segs;
+}
+
+static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ const struct net_offload *ops;
+ struct sk_buff **pp = NULL;
+ struct sk_buff *p;
+ struct ipv6hdr *iph;
+ unsigned int nlen;
+ unsigned int hlen;
+ unsigned int off;
+ int flush = 1;
+ int proto;
+ __wsum csum;
+
+ off = skb_gro_offset(skb);
+ hlen = off + sizeof(*iph);
+ iph = skb_gro_header_fast(skb, off);
+ if (skb_gro_header_hard(skb, hlen)) {
+ iph = skb_gro_header_slow(skb, hlen, off);
+ if (unlikely(!iph))
+ goto out;
+ }
+
+ skb_gro_pull(skb, sizeof(*iph));
+ skb_set_transport_header(skb, skb_gro_offset(skb));
+
+ flush += ntohs(iph->payload_len) != skb_gro_len(skb);
+
+ rcu_read_lock();
+ proto = iph->nexthdr;
+ ops = rcu_dereference(inet6_offloads[proto]);
+ if (!ops || !ops->callbacks.gro_receive) {
+ __pskb_pull(skb, skb_gro_offset(skb));
+ proto = ipv6_gso_pull_exthdrs(skb, proto);
+ skb_gro_pull(skb, -skb_transport_offset(skb));
+ skb_reset_transport_header(skb);
+ __skb_push(skb, skb_gro_offset(skb));
+
+ ops = rcu_dereference(inet6_offloads[proto]);
+ if (!ops || !ops->callbacks.gro_receive)
+ goto out_unlock;
+
+ iph = ipv6_hdr(skb);
+ }
+
+ NAPI_GRO_CB(skb)->proto = proto;
+
+ flush--;
+ nlen = skb_network_header_len(skb);
+
+ for (p = *head; p; p = p->next) {
+ const struct ipv6hdr *iph2;
+ __be32 first_word; /* <Version:4><Traffic_Class:8><Flow_Label:20> */
+
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ iph2 = ipv6_hdr(p);
+ first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
+
+ /* All fields must match except length and Traffic Class. */
+ if (nlen != skb_network_header_len(p) ||
+ (first_word & htonl(0xF00FFFFF)) ||
+ memcmp(&iph->nexthdr, &iph2->nexthdr,
+ nlen - offsetof(struct ipv6hdr, nexthdr))) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+ /* flush if Traffic Class fields are different */
+ NAPI_GRO_CB(p)->flush |= !!(first_word & htonl(0x0FF00000));
+ NAPI_GRO_CB(p)->flush |= flush;
+ }
+
+ NAPI_GRO_CB(skb)->flush |= flush;
+
+ csum = skb->csum;
+ skb_postpull_rcsum(skb, iph, skb_network_header_len(skb));
+
+ pp = ops->callbacks.gro_receive(head, skb);
+
+ skb->csum = csum;
+
+out_unlock:
+ rcu_read_unlock();
+
+out:
+ NAPI_GRO_CB(skb)->flush |= flush;
+
+ return pp;
+}
+
+static int ipv6_gro_complete(struct sk_buff *skb)
+{
+ const struct net_offload *ops;
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+ int err = -ENOSYS;
+
+ iph->payload_len = htons(skb->len - skb_network_offset(skb) -
+ sizeof(*iph));
+
+ rcu_read_lock();
+ ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_complete))
+ goto out_unlock;
+
+ err = ops->callbacks.gro_complete(skb);
+
+out_unlock:
+ rcu_read_unlock();
+
+ return err;
+}
+
+static struct packet_offload ipv6_packet_offload __read_mostly = {
+ .type = cpu_to_be16(ETH_P_IPV6),
+ .callbacks = {
+ .gso_send_check = ipv6_gso_send_check,
+ .gso_segment = ipv6_gso_segment,
+ .gro_receive = ipv6_gro_receive,
+ .gro_complete = ipv6_gro_complete,
+ },
+};
+
+static int __init ipv6_offload_init(void)
+{
+
+ if (tcpv6_offload_init() < 0)
+ pr_crit("%s: Cannot add TCP protocol offload\n", __func__);
+ if (udp_offload_init() < 0)
+ pr_crit("%s: Cannot add UDP protocol offload\n", __func__);
+ if (ipv6_exthdrs_offload_init() < 0)
+ pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__);
+
+ dev_add_offload(&ipv6_packet_offload);
+ return 0;
+}
+
+fs_initcall(ipv6_offload_init);
diff --git a/net/ipv6/ip6_offload.h b/net/ipv6/ip6_offload.h
new file mode 100644
index 00000000000..2e155c651b3
--- /dev/null
+++ b/net/ipv6/ip6_offload.h
@@ -0,0 +1,18 @@
+/*
+ * IPV6 GSO/GRO offload support
+ * Linux INET6 implementation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef __ip6_offload_h
+#define __ip6_offload_h
+
+int ipv6_exthdrs_offload_init(void);
+int udp_offload_init(void);
+int tcpv6_offload_init(void);
+
+#endif
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index aece3e792f8..0c7c03d50dc 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -538,78 +538,12 @@ static void ip6_copy_metadata(struct sk_buff *to, struct sk_buff *from)
to->tc_index = from->tc_index;
#endif
nf_copy(to, from);
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
- defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
to->nf_trace = from->nf_trace;
#endif
skb_copy_secmark(to, from);
}
-int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
-{
- u16 offset = sizeof(struct ipv6hdr);
- struct ipv6_opt_hdr *exthdr =
- (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
- unsigned int packet_len = skb->tail - skb->network_header;
- int found_rhdr = 0;
- *nexthdr = &ipv6_hdr(skb)->nexthdr;
-
- while (offset + 1 <= packet_len) {
-
- switch (**nexthdr) {
-
- case NEXTHDR_HOP:
- break;
- case NEXTHDR_ROUTING:
- found_rhdr = 1;
- break;
- case NEXTHDR_DEST:
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
- if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
- break;
-#endif
- if (found_rhdr)
- return offset;
- break;
- default :
- return offset;
- }
-
- offset += ipv6_optlen(exthdr);
- *nexthdr = &exthdr->nexthdr;
- exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
- offset);
- }
-
- return offset;
-}
-
-void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
-{
- static atomic_t ipv6_fragmentation_id;
- int old, new;
-
- if (rt && !(rt->dst.flags & DST_NOPEER)) {
- struct inet_peer *peer;
- struct net *net;
-
- net = dev_net(rt->dst.dev);
- peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
- if (peer) {
- fhdr->identification = htonl(inet_getid(peer, 0));
- inet_putpeer(peer);
- return;
- }
- }
- do {
- old = atomic_read(&ipv6_fragmentation_id);
- new = old + 1;
- if (!new)
- new = 1;
- } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
- fhdr->identification = htonl(new);
-}
-
int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
{
struct sk_buff *frag;
@@ -756,7 +690,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
if (err == 0) {
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
IPSTATS_MIB_FRAGOKS);
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return 0;
}
@@ -768,7 +702,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
IP6_INC_STATS(net, ip6_dst_idev(&rt->dst),
IPSTATS_MIB_FRAGFAILS);
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return err;
slow_path_clean:
@@ -1279,10 +1213,10 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
if (dst_allfrag(rt->dst.path))
cork->flags |= IPCORK_ALLFRAG;
cork->length = 0;
- exthdrlen = (opt ? opt->opt_flen : 0) - rt->rt6i_nfheader_len;
+ exthdrlen = (opt ? opt->opt_flen : 0);
length += exthdrlen;
transhdrlen += exthdrlen;
- dst_exthdrlen = rt->dst.header_len;
+ dst_exthdrlen = rt->dst.header_len - rt->rt6i_nfheader_len;
} else {
rt = (struct rt6_info *)cork->dst;
fl6 = &inet->cork.fl.u.ip6;
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index cb7e2ded6f0..a14f28b280f 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -74,6 +74,10 @@ MODULE_ALIAS_NETDEV("ip6tnl0");
#define HASH_SIZE_SHIFT 5
#define HASH_SIZE (1 << HASH_SIZE_SHIFT)
+static bool log_ecn_error = true;
+module_param(log_ecn_error, bool, 0644);
+MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
+
static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
{
u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2);
@@ -83,6 +87,7 @@ static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2)
static int ip6_tnl_dev_init(struct net_device *dev);
static void ip6_tnl_dev_setup(struct net_device *dev);
+static struct rtnl_link_ops ip6_link_ops __read_mostly;
static int ip6_tnl_net_id __read_mostly;
struct ip6_tnl_net {
@@ -94,14 +99,6 @@ struct ip6_tnl_net {
struct ip6_tnl __rcu **tnls[2];
};
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- unsigned long rx_packets;
- unsigned long rx_bytes;
- unsigned long tx_packets;
- unsigned long tx_bytes;
-} __attribute__((aligned(4*sizeof(unsigned long))));
-
static struct net_device_stats *ip6_get_stats(struct net_device *dev)
{
struct pcpu_tstats sum = { 0 };
@@ -258,6 +255,33 @@ static void ip6_dev_free(struct net_device *dev)
free_netdev(dev);
}
+static int ip6_tnl_create2(struct net_device *dev)
+{
+ struct ip6_tnl *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ int err;
+
+ t = netdev_priv(dev);
+ err = ip6_tnl_dev_init(dev);
+ if (err < 0)
+ goto out;
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ goto out;
+
+ strcpy(t->parms.name, dev->name);
+ dev->rtnl_link_ops = &ip6_link_ops;
+
+ dev_hold(dev);
+ ip6_tnl_link(ip6n, t);
+ return 0;
+
+out:
+ return err;
+}
+
/**
* ip6_tnl_create - create a new tunnel
* @p: tunnel parameters
@@ -276,7 +300,6 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
struct ip6_tnl *t;
char name[IFNAMSIZ];
int err;
- struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
if (p->name[0])
strlcpy(name, p->name, IFNAMSIZ);
@@ -291,17 +314,10 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p)
t = netdev_priv(dev);
t->parms = *p;
- err = ip6_tnl_dev_init(dev);
+ err = ip6_tnl_create2(dev);
if (err < 0)
goto failed_free;
- if ((err = register_netdevice(dev)) < 0)
- goto failed_free;
-
- strcpy(t->parms.name, dev->name);
-
- dev_hold(dev);
- ip6_tnl_link(ip6n, t);
return t;
failed_free:
@@ -663,8 +679,7 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
icmpv6_send(skb2, rel_type, rel_code, rel_info);
- if (rt)
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
kfree_skb(skb2);
}
@@ -672,28 +687,26 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
return 0;
}
-static void ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
- const struct ipv6hdr *ipv6h,
- struct sk_buff *skb)
+static int ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
+ const struct ipv6hdr *ipv6h,
+ struct sk_buff *skb)
{
__u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK;
if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield);
- if (INET_ECN_is_ce(dsfield))
- IP_ECN_set_ce(ip_hdr(skb));
+ return IP6_ECN_decapsulate(ipv6h, skb);
}
-static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
- const struct ipv6hdr *ipv6h,
- struct sk_buff *skb)
+static int ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t,
+ const struct ipv6hdr *ipv6h,
+ struct sk_buff *skb)
{
if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY)
ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb));
- if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h)))
- IP6_ECN_set_ce(ipv6_hdr(skb));
+ return IP6_ECN_decapsulate(ipv6h, skb);
}
__u32 ip6_tnl_get_cap(struct ip6_tnl *t,
@@ -757,12 +770,13 @@ EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl);
static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
__u8 ipproto,
- void (*dscp_ecn_decapsulate)(const struct ip6_tnl *t,
- const struct ipv6hdr *ipv6h,
- struct sk_buff *skb))
+ int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t,
+ const struct ipv6hdr *ipv6h,
+ struct sk_buff *skb))
{
struct ip6_tnl *t;
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ int err;
rcu_read_lock();
@@ -792,14 +806,26 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
skb->pkt_type = PACKET_HOST;
memset(skb->cb, 0, sizeof(struct inet6_skb_parm));
+ __skb_tunnel_rx(skb, t->dev);
+
+ err = dscp_ecn_decapsulate(t, ipv6h, skb);
+ if (unlikely(err)) {
+ if (log_ecn_error)
+ net_info_ratelimited("non-ECT from %pI6 with dsfield=%#x\n",
+ &ipv6h->saddr,
+ ipv6_get_dsfield(ipv6h));
+ if (err > 1) {
+ ++t->dev->stats.rx_frame_errors;
+ ++t->dev->stats.rx_errors;
+ rcu_read_unlock();
+ goto discard;
+ }
+ }
+
tstats = this_cpu_ptr(t->dev->tstats);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
- __skb_tunnel_rx(skb, t->dev);
-
- dscp_ecn_decapsulate(t, ipv6h, skb);
-
netif_rx(skb);
rcu_read_unlock();
@@ -1208,7 +1234,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t)
if (dev->mtu < IPV6_MIN_MTU)
dev->mtu = IPV6_MIN_MTU;
}
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
}
@@ -1237,6 +1263,20 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
return 0;
}
+static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
+{
+ struct net *net = dev_net(t->dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ int err;
+
+ ip6_tnl_unlink(ip6n, t);
+ synchronize_net();
+ err = ip6_tnl_change(t, p);
+ ip6_tnl_link(ip6n, t);
+ netdev_state_change(t->dev);
+ return err;
+}
+
static void
ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u)
{
@@ -1325,7 +1365,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
err = -EFAULT;
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
@@ -1345,11 +1385,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
} else
t = netdev_priv(dev);
- ip6_tnl_unlink(ip6n, t);
- synchronize_net();
- err = ip6_tnl_change(t, &p1);
- ip6_tnl_link(ip6n, t);
- netdev_state_change(dev);
+ err = ip6_tnl_update(t, &p1);
}
if (t) {
err = 0;
@@ -1362,7 +1398,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
if (dev == ip6n->fb_tnl_dev) {
@@ -1505,6 +1541,164 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
return 0;
}
+static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[])
+{
+ u8 proto;
+
+ if (!data)
+ return 0;
+
+ proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
+ if (proto != IPPROTO_IPV6 &&
+ proto != IPPROTO_IPIP &&
+ proto != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void ip6_tnl_netlink_parms(struct nlattr *data[],
+ struct __ip6_tnl_parm *parms)
+{
+ memset(parms, 0, sizeof(*parms));
+
+ if (!data)
+ return;
+
+ if (data[IFLA_IPTUN_LINK])
+ parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
+
+ if (data[IFLA_IPTUN_LOCAL])
+ nla_memcpy(&parms->laddr, data[IFLA_IPTUN_LOCAL],
+ sizeof(struct in6_addr));
+
+ if (data[IFLA_IPTUN_REMOTE])
+ nla_memcpy(&parms->raddr, data[IFLA_IPTUN_REMOTE],
+ sizeof(struct in6_addr));
+
+ if (data[IFLA_IPTUN_TTL])
+ parms->hop_limit = nla_get_u8(data[IFLA_IPTUN_TTL]);
+
+ if (data[IFLA_IPTUN_ENCAP_LIMIT])
+ parms->encap_limit = nla_get_u8(data[IFLA_IPTUN_ENCAP_LIMIT]);
+
+ if (data[IFLA_IPTUN_FLOWINFO])
+ parms->flowinfo = nla_get_be32(data[IFLA_IPTUN_FLOWINFO]);
+
+ if (data[IFLA_IPTUN_FLAGS])
+ parms->flags = nla_get_u32(data[IFLA_IPTUN_FLAGS]);
+
+ if (data[IFLA_IPTUN_PROTO])
+ parms->proto = nla_get_u8(data[IFLA_IPTUN_PROTO]);
+}
+
+static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct net *net = dev_net(dev);
+ struct ip6_tnl *nt;
+
+ nt = netdev_priv(dev);
+ ip6_tnl_netlink_parms(data, &nt->parms);
+
+ if (ip6_tnl_locate(net, &nt->parms, 0))
+ return -EEXIST;
+
+ return ip6_tnl_create2(dev);
+}
+
+static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct ip6_tnl *t;
+ struct __ip6_tnl_parm p;
+ struct net *net = dev_net(dev);
+ struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+
+ if (dev == ip6n->fb_tnl_dev)
+ return -EINVAL;
+
+ ip6_tnl_netlink_parms(data, &p);
+
+ t = ip6_tnl_locate(net, &p, 0);
+
+ if (t) {
+ if (t->dev != dev)
+ return -EEXIST;
+ } else
+ t = netdev_priv(dev);
+
+ return ip6_tnl_update(t, &p);
+}
+
+static size_t ip6_tnl_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_IPTUN_LINK */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_LOCAL */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_REMOTE */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_TTL */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_ENCAP_LIMIT */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_FLOWINFO */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_FLAGS */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_PROTO */
+ nla_total_size(1) +
+ 0;
+}
+
+static int ip6_tnl_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct ip6_tnl *tunnel = netdev_priv(dev);
+ struct __ip6_tnl_parm *parm = &tunnel->parms;
+
+ if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
+ nla_put(skb, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr),
+ &parm->raddr) ||
+ nla_put(skb, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr),
+ &parm->laddr) ||
+ nla_put_u8(skb, IFLA_IPTUN_TTL, parm->hop_limit) ||
+ nla_put_u8(skb, IFLA_IPTUN_ENCAP_LIMIT, parm->encap_limit) ||
+ nla_put_be32(skb, IFLA_IPTUN_FLOWINFO, parm->flowinfo) ||
+ nla_put_u32(skb, IFLA_IPTUN_FLAGS, parm->flags) ||
+ nla_put_u8(skb, IFLA_IPTUN_PROTO, parm->proto))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_REMOTE] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 },
+ [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 },
+ [IFLA_IPTUN_PROTO] = { .type = NLA_U8 },
+};
+
+static struct rtnl_link_ops ip6_link_ops __read_mostly = {
+ .kind = "ip6tnl",
+ .maxtype = IFLA_IPTUN_MAX,
+ .policy = ip6_tnl_policy,
+ .priv_size = sizeof(struct ip6_tnl),
+ .setup = ip6_tnl_dev_setup,
+ .validate = ip6_tnl_validate,
+ .newlink = ip6_tnl_newlink,
+ .changelink = ip6_tnl_changelink,
+ .get_size = ip6_tnl_get_size,
+ .fill_info = ip6_tnl_fill_info,
+};
+
static struct xfrm6_tunnel ip4ip6_handler __read_mostly = {
.handler = ip4ip6_rcv,
.err_handler = ip4ip6_err,
@@ -1613,9 +1807,14 @@ static int __init ip6_tunnel_init(void)
pr_err("%s: can't register ip6ip6\n", __func__);
goto out_ip6ip6;
}
+ err = rtnl_link_register(&ip6_link_ops);
+ if (err < 0)
+ goto rtnl_link_failed;
return 0;
+rtnl_link_failed:
+ xfrm6_tunnel_deregister(&ip6ip6_handler, AF_INET6);
out_ip6ip6:
xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET);
out_ip4ip6:
@@ -1630,6 +1829,7 @@ out_pernet:
static void __exit ip6_tunnel_cleanup(void)
{
+ rtnl_link_unregister(&ip6_link_ops);
if (xfrm6_tunnel_deregister(&ip4ip6_handler, AF_INET))
pr_info("%s: can't deregister ip4ip6\n", __func__);
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index f7c7c631972..8fd154e5f07 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -52,6 +52,7 @@
#include <linux/netfilter_ipv6.h>
#include <linux/export.h>
#include <net/ip6_checksum.h>
+#include <linux/netconf.h>
struct mr6_table {
struct list_head list;
@@ -66,8 +67,8 @@ struct mr6_table {
struct mif_device vif6_table[MAXMIFS];
int maxvif;
atomic_t cache_resolve_queue_len;
- int mroute_do_assert;
- int mroute_do_pim;
+ bool mroute_do_assert;
+ bool mroute_do_pim;
#ifdef CONFIG_IPV6_PIMSM_V2
int mroute_reg_vif_num;
#endif
@@ -115,6 +116,8 @@ static int ip6mr_cache_report(struct mr6_table *mrt, struct sk_buff *pkt,
mifi_t mifi, int assert);
static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
struct mfc6_cache *c, struct rtmsg *rtm);
+static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
+ int cmd);
static int ip6mr_rtm_dumproute(struct sk_buff *skb,
struct netlink_callback *cb);
static void mroute_clean_tables(struct mr6_table *mrt);
@@ -805,8 +808,12 @@ static int mif6_delete(struct mr6_table *mrt, int vifi, struct list_head *head)
dev_set_allmulti(dev, -1);
in6_dev = __in6_dev_get(dev);
- if (in6_dev)
+ if (in6_dev) {
in6_dev->cnf.mc_forwarding--;
+ inet6_netconf_notify_devconf(dev_net(dev),
+ NETCONFA_MC_FORWARDING,
+ dev->ifindex, &in6_dev->cnf);
+ }
if (v->flags & MIFF_REGISTER)
unregister_netdevice_queue(dev, head);
@@ -865,6 +872,7 @@ static void ipmr_do_expire_process(struct mr6_table *mrt)
}
list_del(&c->list);
+ mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_destroy_unres(mrt, c);
}
@@ -958,8 +966,12 @@ static int mif6_add(struct net *net, struct mr6_table *mrt,
}
in6_dev = __in6_dev_get(dev);
- if (in6_dev)
+ if (in6_dev) {
in6_dev->cnf.mc_forwarding++;
+ inet6_netconf_notify_devconf(dev_net(dev),
+ NETCONFA_MC_FORWARDING,
+ dev->ifindex, &in6_dev->cnf);
+ }
/*
* Fill in the VIF structures
@@ -1211,6 +1223,7 @@ ip6mr_cache_unresolved(struct mr6_table *mrt, mifi_t mifi, struct sk_buff *skb)
atomic_inc(&mrt->cache_resolve_queue_len);
list_add(&c->list, &mrt->mfc6_unres_queue);
+ mr6_netlink_event(mrt, c, RTM_NEWROUTE);
ipmr_do_expire_process(mrt);
}
@@ -1248,6 +1261,7 @@ static int ip6mr_mfc_delete(struct mr6_table *mrt, struct mf6cctl *mfc)
list_del(&c->list);
write_unlock_bh(&mrt_lock);
+ mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_cache_free(c);
return 0;
}
@@ -1412,6 +1426,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
if (!mrtsock)
c->mfc_flags |= MFC_STATIC;
write_unlock_bh(&mrt_lock);
+ mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
@@ -1456,6 +1471,7 @@ static int ip6mr_mfc_add(struct net *net, struct mr6_table *mrt,
ip6mr_cache_resolve(net, mrt, uc, c);
ip6mr_cache_free(uc);
}
+ mr6_netlink_event(mrt, c, RTM_NEWROUTE);
return 0;
}
@@ -1489,6 +1505,7 @@ static void mroute_clean_tables(struct mr6_table *mrt)
list_del(&c->list);
write_unlock_bh(&mrt_lock);
+ mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_cache_free(c);
}
}
@@ -1497,6 +1514,7 @@ static void mroute_clean_tables(struct mr6_table *mrt)
spin_lock_bh(&mfc_unres_lock);
list_for_each_entry_safe(c, next, &mrt->mfc6_unres_queue, list) {
list_del(&c->list);
+ mr6_netlink_event(mrt, c, RTM_DELROUTE);
ip6mr_destroy_unres(mrt, c);
}
spin_unlock_bh(&mfc_unres_lock);
@@ -1513,6 +1531,9 @@ static int ip6mr_sk_init(struct mr6_table *mrt, struct sock *sk)
if (likely(mrt->mroute6_sk == NULL)) {
mrt->mroute6_sk = sk;
net->ipv6.devconf_all->mc_forwarding++;
+ inet6_netconf_notify_devconf(net, NETCONFA_MC_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv6.devconf_all);
}
else
err = -EADDRINUSE;
@@ -1535,6 +1556,10 @@ int ip6mr_sk_done(struct sock *sk)
write_lock_bh(&mrt_lock);
mrt->mroute6_sk = NULL;
net->ipv6.devconf_all->mc_forwarding--;
+ inet6_netconf_notify_devconf(net,
+ NETCONFA_MC_FORWARDING,
+ NETCONFA_IFINDEX_ALL,
+ net->ipv6.devconf_all);
write_unlock_bh(&mrt_lock);
mroute_clean_tables(mrt);
@@ -1583,7 +1608,7 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
return -ENOENT;
if (optname != MRT6_INIT) {
- if (sk != mrt->mroute6_sk && !capable(CAP_NET_ADMIN))
+ if (sk != mrt->mroute6_sk && !ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EACCES;
}
@@ -1646,9 +1671,12 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
case MRT6_ASSERT:
{
int v;
+
+ if (optlen != sizeof(v))
+ return -EINVAL;
if (get_user(v, (int __user *)optval))
return -EFAULT;
- mrt->mroute_do_assert = !!v;
+ mrt->mroute_do_assert = v;
return 0;
}
@@ -1656,6 +1684,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
case MRT6_PIM:
{
int v;
+
+ if (optlen != sizeof(v))
+ return -EINVAL;
if (get_user(v, (int __user *)optval))
return -EFAULT;
v = !!v;
@@ -1679,6 +1710,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns
return -EINVAL;
if (get_user(v, (u32 __user *)optval))
return -EFAULT;
+ /* "pim6reg%u" should not exceed 16 bytes (IFNAMSIZ) */
+ if (v != RT_TABLE_DEFAULT && v >= 100000000)
+ return -EINVAL;
if (sk == mrt->mroute6_sk)
return -EBUSY;
@@ -2097,8 +2131,8 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
{
int ct;
struct rtnexthop *nhp;
- u8 *b = skb_tail_pointer(skb);
- struct rtattr *mp_head;
+ struct nlattr *mp_attr;
+ struct rta_mfc_stats mfcs;
/* If cache is unresolved, don't try to parse IIF and OIF */
if (c->mf6c_parent >= MAXMIFS)
@@ -2107,28 +2141,35 @@ static int __ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
if (MIF_EXISTS(mrt, c->mf6c_parent) &&
nla_put_u32(skb, RTA_IIF, mrt->vif6_table[c->mf6c_parent].dev->ifindex) < 0)
return -EMSGSIZE;
-
- mp_head = (struct rtattr *)skb_put(skb, RTA_LENGTH(0));
+ mp_attr = nla_nest_start(skb, RTA_MULTIPATH);
+ if (mp_attr == NULL)
+ return -EMSGSIZE;
for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
if (MIF_EXISTS(mrt, ct) && c->mfc_un.res.ttls[ct] < 255) {
- if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
- goto rtattr_failure;
- nhp = (struct rtnexthop *)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
+ nhp = nla_reserve_nohdr(skb, sizeof(*nhp));
+ if (nhp == NULL) {
+ nla_nest_cancel(skb, mp_attr);
+ return -EMSGSIZE;
+ }
+
nhp->rtnh_flags = 0;
nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
nhp->rtnh_ifindex = mrt->vif6_table[ct].dev->ifindex;
nhp->rtnh_len = sizeof(*nhp);
}
}
- mp_head->rta_type = RTA_MULTIPATH;
- mp_head->rta_len = skb_tail_pointer(skb) - (u8 *)mp_head;
+
+ nla_nest_end(skb, mp_attr);
+
+ mfcs.mfcs_packets = c->mfc_un.res.pkt;
+ mfcs.mfcs_bytes = c->mfc_un.res.bytes;
+ mfcs.mfcs_wrong_if = c->mfc_un.res.wrong_if;
+ if (nla_put(skb, RTA_MFC_STATS, sizeof(mfcs), &mfcs) < 0)
+ return -EMSGSIZE;
+
rtm->rtm_type = RTN_MULTICAST;
return 1;
-
-rtattr_failure:
- nlmsg_trim(skb, b);
- return -EMSGSIZE;
}
int ip6mr_get_route(struct net *net,
@@ -2202,31 +2243,38 @@ int ip6mr_get_route(struct net *net,
}
static int ip6mr_fill_mroute(struct mr6_table *mrt, struct sk_buff *skb,
- u32 portid, u32 seq, struct mfc6_cache *c)
+ u32 portid, u32 seq, struct mfc6_cache *c, int cmd)
{
struct nlmsghdr *nlh;
struct rtmsg *rtm;
+ int err;
- nlh = nlmsg_put(skb, portid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
+ nlh = nlmsg_put(skb, portid, seq, cmd, sizeof(*rtm), NLM_F_MULTI);
if (nlh == NULL)
return -EMSGSIZE;
rtm = nlmsg_data(nlh);
- rtm->rtm_family = RTNL_FAMILY_IPMR;
+ rtm->rtm_family = RTNL_FAMILY_IP6MR;
rtm->rtm_dst_len = 128;
rtm->rtm_src_len = 128;
rtm->rtm_tos = 0;
rtm->rtm_table = mrt->id;
if (nla_put_u32(skb, RTA_TABLE, mrt->id))
goto nla_put_failure;
+ rtm->rtm_type = RTN_MULTICAST;
rtm->rtm_scope = RT_SCOPE_UNIVERSE;
- rtm->rtm_protocol = RTPROT_UNSPEC;
+ if (c->mfc_flags & MFC_STATIC)
+ rtm->rtm_protocol = RTPROT_STATIC;
+ else
+ rtm->rtm_protocol = RTPROT_MROUTED;
rtm->rtm_flags = 0;
if (nla_put(skb, RTA_SRC, 16, &c->mf6c_origin) ||
nla_put(skb, RTA_DST, 16, &c->mf6c_mcastgrp))
goto nla_put_failure;
- if (__ip6mr_fill_mroute(mrt, skb, c, rtm) < 0)
+ err = __ip6mr_fill_mroute(mrt, skb, c, rtm);
+ /* do not break the dump if cache is unresolved */
+ if (err < 0 && err != -ENOENT)
goto nla_put_failure;
return nlmsg_end(skb, nlh);
@@ -2236,6 +2284,52 @@ nla_put_failure:
return -EMSGSIZE;
}
+static int mr6_msgsize(bool unresolved, int maxvif)
+{
+ size_t len =
+ NLMSG_ALIGN(sizeof(struct rtmsg))
+ + nla_total_size(4) /* RTA_TABLE */
+ + nla_total_size(sizeof(struct in6_addr)) /* RTA_SRC */
+ + nla_total_size(sizeof(struct in6_addr)) /* RTA_DST */
+ ;
+
+ if (!unresolved)
+ len = len
+ + nla_total_size(4) /* RTA_IIF */
+ + nla_total_size(0) /* RTA_MULTIPATH */
+ + maxvif * NLA_ALIGN(sizeof(struct rtnexthop))
+ /* RTA_MFC_STATS */
+ + nla_total_size(sizeof(struct rta_mfc_stats))
+ ;
+
+ return len;
+}
+
+static void mr6_netlink_event(struct mr6_table *mrt, struct mfc6_cache *mfc,
+ int cmd)
+{
+ struct net *net = read_pnet(&mrt->net);
+ struct sk_buff *skb;
+ int err = -ENOBUFS;
+
+ skb = nlmsg_new(mr6_msgsize(mfc->mf6c_parent >= MAXMIFS, mrt->maxvif),
+ GFP_ATOMIC);
+ if (skb == NULL)
+ goto errout;
+
+ err = ip6mr_fill_mroute(mrt, skb, 0, 0, mfc, cmd);
+ if (err < 0)
+ goto errout;
+
+ rtnl_notify(skb, net, 0, RTNLGRP_IPV6_MROUTE, NULL, GFP_ATOMIC);
+ return;
+
+errout:
+ kfree_skb(skb);
+ if (err < 0)
+ rtnl_set_sk_err(net, RTNLGRP_IPV6_MROUTE, err);
+}
+
static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
{
struct net *net = sock_net(skb->sk);
@@ -2262,13 +2356,29 @@ static int ip6mr_rtm_dumproute(struct sk_buff *skb, struct netlink_callback *cb)
if (ip6mr_fill_mroute(mrt, skb,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq,
- mfc) < 0)
+ mfc, RTM_NEWROUTE) < 0)
goto done;
next_entry:
e++;
}
e = s_e = 0;
}
+ spin_lock_bh(&mfc_unres_lock);
+ list_for_each_entry(mfc, &mrt->mfc6_unres_queue, list) {
+ if (e < s_e)
+ goto next_entry2;
+ if (ip6mr_fill_mroute(mrt, skb,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ mfc, RTM_NEWROUTE) < 0) {
+ spin_unlock_bh(&mfc_unres_lock);
+ goto done;
+ }
+next_entry2:
+ e++;
+ }
+ spin_unlock_bh(&mfc_unres_lock);
+ e = s_e = 0;
s_h = 0;
next_table:
t++;
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index ba6d13d1f1e..ee94d31c9d4 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -343,7 +343,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
break;
case IPV6_TRANSPARENT:
- if (valbool && !capable(CAP_NET_ADMIN) && !capable(CAP_NET_RAW)) {
+ if (valbool && !ns_capable(net->user_ns, CAP_NET_ADMIN) &&
+ !ns_capable(net->user_ns, CAP_NET_RAW)) {
retv = -EPERM;
break;
}
@@ -381,7 +382,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
/* hop-by-hop / destination options are privileged option */
retv = -EPERM;
- if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))
+ if (optname != IPV6_RTHDR && !ns_capable(net->user_ns, CAP_NET_RAW))
break;
opt = ipv6_renew_options(sk, np->opt, optname,
@@ -397,7 +398,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
if (optname == IPV6_RTHDR && opt && opt->srcrt) {
struct ipv6_rt_hdr *rthdr = opt->srcrt;
switch (rthdr->type) {
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPV6_SRCRT_TYPE_2:
if (rthdr->hdrlen != 2 ||
rthdr->segments_left != 1)
@@ -754,7 +755,7 @@ done:
case IPV6_IPSEC_POLICY:
case IPV6_XFRM_POLICY:
retv = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
break;
retv = xfrm_user_policy(sk, optname, optval, optlen);
break;
@@ -827,6 +828,7 @@ pref_skip_coa:
if (val < 0 || val > 255)
goto e_inval;
np->min_hopcount = val;
+ retv = 0;
break;
case IPV6_DONTFRAG:
np->dontfrag = valbool;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 92f8e48e4ba..28dfa5f3801 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -163,7 +163,7 @@ int ipv6_sock_mc_join(struct sock *sk, int ifindex, const struct in6_addr *addr)
rt = rt6_lookup(net, addr, NULL, 0, 0);
if (rt) {
dev = rt->dst.dev;
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
} else
dev = dev_get_by_index_rcu(net, ifindex);
@@ -260,7 +260,7 @@ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net,
if (rt) {
dev = rt->dst.dev;
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
} else
dev = dev_get_by_index_rcu(net, ifindex);
@@ -284,6 +284,9 @@ void ipv6_sock_mc_close(struct sock *sk)
struct ipv6_mc_socklist *mc_lst;
struct net *net = sock_net(sk);
+ if (!rcu_access_pointer(np->ipv6_mc_list))
+ return;
+
spin_lock(&ipv6_sk_mc_lock);
while ((mc_lst = rcu_dereference_protected(np->ipv6_mc_list,
lockdep_is_held(&ipv6_sk_mc_lock))) != NULL) {
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index ff36194a71a..6574175795d 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -151,8 +151,8 @@ static inline int ndisc_opt_addr_space(struct net_device *dev)
static u8 *ndisc_fill_addr_option(u8 *opt, int type, void *data, int data_len,
unsigned short addr_type)
{
- int space = NDISC_OPT_SPACE(data_len);
int pad = ndisc_addr_option_pad(addr_type);
+ int space = NDISC_OPT_SPACE(data_len + pad);
opt[0] = type;
opt[1] = space>>3;
@@ -370,12 +370,12 @@ static void pndisc_destructor(struct pneigh_entry *n)
ipv6_dev_mc_dec(dev, &maddr);
}
-struct sk_buff *ndisc_build_skb(struct net_device *dev,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr,
- struct icmp6hdr *icmp6h,
- const struct in6_addr *target,
- int llinfo)
+static struct sk_buff *ndisc_build_skb(struct net_device *dev,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h,
+ const struct in6_addr *target,
+ int llinfo)
{
struct net *net = dev_net(dev);
struct sock *sk = net->ipv6.ndisc_sk;
@@ -431,14 +431,11 @@ struct sk_buff *ndisc_build_skb(struct net_device *dev,
return skb;
}
-EXPORT_SYMBOL(ndisc_build_skb);
-
-void ndisc_send_skb(struct sk_buff *skb,
- struct net_device *dev,
- struct neighbour *neigh,
- const struct in6_addr *daddr,
- const struct in6_addr *saddr,
- struct icmp6hdr *icmp6h)
+static void ndisc_send_skb(struct sk_buff *skb, struct net_device *dev,
+ struct neighbour *neigh,
+ const struct in6_addr *daddr,
+ const struct in6_addr *saddr,
+ struct icmp6hdr *icmp6h)
{
struct flowi6 fl6;
struct dst_entry *dst;
@@ -473,8 +470,6 @@ void ndisc_send_skb(struct sk_buff *skb,
rcu_read_unlock();
}
-EXPORT_SYMBOL(ndisc_send_skb);
-
/*
* Send a Neighbour Discover packet
*/
@@ -535,7 +530,6 @@ static void ndisc_send_unsol_na(struct net_device *dev)
{
struct inet6_dev *idev;
struct inet6_ifaddr *ifa;
- struct in6_addr mcaddr;
idev = in6_dev_get(dev);
if (!idev)
@@ -543,8 +537,7 @@ static void ndisc_send_unsol_na(struct net_device *dev)
read_lock_bh(&idev->lock);
list_for_each_entry(ifa, &idev->addr_list, if_list) {
- addrconf_addr_solict_mult(&ifa->addr, &mcaddr);
- ndisc_send_na(dev, NULL, &mcaddr, &ifa->addr,
+ ndisc_send_na(dev, NULL, &in6addr_linklocal_allnodes, &ifa->addr,
/*router=*/ !!idev->cnf.forwarding,
/*solicited=*/ false, /*override=*/ true,
/*inc_opt=*/ true);
@@ -906,7 +899,7 @@ static void ndisc_recv_na(struct sk_buff *skb)
if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&
net->ipv6.devconf_all->forwarding && net->ipv6.devconf_all->proxy_ndp &&
pneigh_lookup(&nd_tbl, net, &msg->target, dev, 0)) {
- /* XXX: idev->cnf.prixy_ndp */
+ /* XXX: idev->cnf.proxy_ndp */
goto out;
}
@@ -1034,18 +1027,6 @@ errout:
rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err);
}
-static inline int accept_ra(struct inet6_dev *in6_dev)
-{
- /*
- * If forwarding is enabled, RA are not accepted unless the special
- * hybrid mode (accept_ra=2) is enabled.
- */
- if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2)
- return 0;
-
- return in6_dev->cnf.accept_ra;
-}
-
static void ndisc_router_discovery(struct sk_buff *skb)
{
struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);
@@ -1093,7 +1074,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
return;
}
- if (!accept_ra(in6_dev))
+ if (!ipv6_accept_ra(in6_dev))
goto skip_linkparms;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
@@ -1145,7 +1126,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK(0, err,
"RA: %s got default router without neighbour\n",
__func__);
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return;
}
}
@@ -1170,7 +1151,7 @@ static void ndisc_router_discovery(struct sk_buff *skb)
ND_PRINTK(0, err,
"RA: %s got default router without neighbour\n",
__func__);
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return;
}
neigh->flags |= NTF_ROUTER;
@@ -1249,7 +1230,7 @@ skip_linkparms:
NEIGH_UPDATE_F_ISROUTER);
}
- if (!accept_ra(in6_dev))
+ if (!ipv6_accept_ra(in6_dev))
goto out;
#ifdef CONFIG_IPV6_ROUTE_INFO
@@ -1326,14 +1307,19 @@ skip_routeinfo:
ND_PRINTK(2, warn, "RA: invalid RA options\n");
}
out:
- if (rt)
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
if (neigh)
neigh_release(neigh);
}
static void ndisc_redirect_rcv(struct sk_buff *skb)
{
+ u8 *hdr;
+ struct ndisc_options ndopts;
+ struct rd_msg *msg = (struct rd_msg *)skb_transport_header(skb);
+ u32 ndoptlen = skb->tail - (skb->transport_header +
+ offsetof(struct rd_msg, opt));
+
#ifdef CONFIG_IPV6_NDISC_NODETYPE
switch (skb->ndisc_nodetype) {
case NDISC_NODETYPE_HOST:
@@ -1350,6 +1336,17 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
return;
}
+ if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
+ return;
+
+ if (!ndopts.nd_opts_rh)
+ return;
+
+ hdr = (u8 *)ndopts.nd_opts_rh;
+ hdr += 8;
+ if (!pskb_pull(skb, hdr - skb_transport_header(skb)))
+ return;
+
icmpv6_notify(skb, NDISC_REDIRECT, 0, 0);
}
@@ -1575,11 +1572,18 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event,
{
struct net_device *dev = ptr;
struct net *net = dev_net(dev);
+ struct inet6_dev *idev;
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&nd_tbl, dev);
fib6_run_gc(~0UL, net);
+ idev = in6_dev_get(dev);
+ if (!idev)
+ break;
+ if (idev->cnf.ndisc_notify)
+ ndisc_send_unsol_na(dev);
+ in6_dev_put(idev);
break;
case NETDEV_DOWN:
neigh_ifdown(&nd_tbl, dev);
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index d7cb04506c3..125a90d6a79 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -207,8 +207,7 @@ ip6t_get_target_c(const struct ip6t_entry *e)
return ip6t_get_target((struct ip6t_entry *)e);
}
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
- defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* This cries for unification! */
static const char *const hooknames[] = {
[NF_INET_PRE_ROUTING] = "PREROUTING",
@@ -381,8 +380,7 @@ ip6t_do_table(struct sk_buff *skb,
t = ip6t_get_target_c(e);
IP_NF_ASSERT(t->u.kernel.target);
-#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
- defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
+#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
/* The packet is traced: log it */
if (unlikely(skb->nf_trace))
trace_packet(skb, hook, in, out,
@@ -1856,7 +1854,7 @@ compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1971,7 +1969,7 @@ compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -1993,7 +1991,7 @@ do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -2018,7 +2016,7 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
{
int ret;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
@@ -2273,112 +2271,9 @@ static void __exit ip6_tables_fini(void)
unregister_pernet_subsys(&ip6_tables_net_ops);
}
-/*
- * find the offset to specified header or the protocol number of last header
- * if target < 0. "last header" is transport protocol header, ESP, or
- * "No next header".
- *
- * Note that *offset is used as input/output parameter. an if it is not zero,
- * then it must be a valid offset to an inner IPv6 header. This can be used
- * to explore inner IPv6 header, eg. ICMPv6 error messages.
- *
- * If target header is found, its offset is set in *offset and return protocol
- * number. Otherwise, return -1.
- *
- * If the first fragment doesn't contain the final protocol header or
- * NEXTHDR_NONE it is considered invalid.
- *
- * Note that non-1st fragment is special case that "the protocol number
- * of last header" is "next header" field in Fragment header. In this case,
- * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
- * isn't NULL.
- *
- * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG
- * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and
- * target < 0, then this function will stop at the AH header.
- */
-int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
- int target, unsigned short *fragoff, int *flags)
-{
- unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
- u8 nexthdr = ipv6_hdr(skb)->nexthdr;
- unsigned int len;
-
- if (fragoff)
- *fragoff = 0;
-
- if (*offset) {
- struct ipv6hdr _ip6, *ip6;
-
- ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6);
- if (!ip6 || (ip6->version != 6)) {
- printk(KERN_ERR "IPv6 header not found\n");
- return -EBADMSG;
- }
- start = *offset + sizeof(struct ipv6hdr);
- nexthdr = ip6->nexthdr;
- }
- len = skb->len - start;
-
- while (nexthdr != target) {
- struct ipv6_opt_hdr _hdr, *hp;
- unsigned int hdrlen;
-
- if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
- if (target < 0)
- break;
- return -ENOENT;
- }
-
- hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
- if (hp == NULL)
- return -EBADMSG;
- if (nexthdr == NEXTHDR_FRAGMENT) {
- unsigned short _frag_off;
- __be16 *fp;
-
- if (flags) /* Indicate that this is a fragment */
- *flags |= IP6T_FH_F_FRAG;
- fp = skb_header_pointer(skb,
- start+offsetof(struct frag_hdr,
- frag_off),
- sizeof(_frag_off),
- &_frag_off);
- if (fp == NULL)
- return -EBADMSG;
-
- _frag_off = ntohs(*fp) & ~0x7;
- if (_frag_off) {
- if (target < 0 &&
- ((!ipv6_ext_hdr(hp->nexthdr)) ||
- hp->nexthdr == NEXTHDR_NONE)) {
- if (fragoff)
- *fragoff = _frag_off;
- return hp->nexthdr;
- }
- return -ENOENT;
- }
- hdrlen = 8;
- } else if (nexthdr == NEXTHDR_AUTH) {
- if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0))
- break;
- hdrlen = (hp->hdrlen + 2) << 2;
- } else
- hdrlen = ipv6_optlen(hp);
-
- nexthdr = hp->nexthdr;
- len -= hdrlen;
- start += hdrlen;
- }
-
- *offset = start;
- return nexthdr;
-}
-
EXPORT_SYMBOL(ip6t_register_table);
EXPORT_SYMBOL(ip6t_unregister_table);
EXPORT_SYMBOL(ip6t_do_table);
-EXPORT_SYMBOL(ipv6_find_hdr);
module_init(ip6_tables_init);
module_exit(ip6_tables_fini);
diff --git a/net/ipv6/netfilter/ip6t_NPT.c b/net/ipv6/netfilter/ip6t_NPT.c
index e9486915eff..7302b0b7b64 100644
--- a/net/ipv6/netfilter/ip6t_NPT.c
+++ b/net/ipv6/netfilter/ip6t_NPT.c
@@ -14,42 +14,23 @@
#include <linux/netfilter_ipv6/ip6t_NPT.h>
#include <linux/netfilter/x_tables.h>
-static __sum16 csum16_complement(__sum16 a)
-{
- return (__force __sum16)(0xffff - (__force u16)a);
-}
-
-static __sum16 csum16_add(__sum16 a, __sum16 b)
-{
- u16 sum;
-
- sum = (__force u16)a + (__force u16)b;
- sum += (__force u16)a < (__force u16)b;
- return (__force __sum16)sum;
-}
-
-static __sum16 csum16_sub(__sum16 a, __sum16 b)
-{
- return csum16_add(a, csum16_complement(b));
-}
-
static int ip6t_npt_checkentry(const struct xt_tgchk_param *par)
{
struct ip6t_npt_tginfo *npt = par->targinfo;
- __sum16 src_sum = 0, dst_sum = 0;
+ __wsum src_sum = 0, dst_sum = 0;
unsigned int i;
if (npt->src_pfx_len > 64 || npt->dst_pfx_len > 64)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(npt->src_pfx.in6.s6_addr16); i++) {
- src_sum = csum16_add(src_sum,
- (__force __sum16)npt->src_pfx.in6.s6_addr16[i]);
- dst_sum = csum16_add(dst_sum,
- (__force __sum16)npt->dst_pfx.in6.s6_addr16[i]);
+ src_sum = csum_add(src_sum,
+ (__force __wsum)npt->src_pfx.in6.s6_addr16[i]);
+ dst_sum = csum_add(dst_sum,
+ (__force __wsum)npt->dst_pfx.in6.s6_addr16[i]);
}
- npt->adjustment = csum16_sub(src_sum, dst_sum);
+ npt->adjustment = (__force __sum16) csum_sub(src_sum, dst_sum);
return 0;
}
@@ -85,7 +66,7 @@ static bool ip6t_npt_map_pfx(const struct ip6t_npt_tginfo *npt,
return false;
}
- sum = csum16_add((__force __sum16)addr->s6_addr16[idx],
+ sum = (__force __sum16) csum_add((__force __wsum)addr->s6_addr16[idx],
npt->adjustment);
if (sum == CSUM_MANGLED_0)
sum = 0;
diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c
index fd4fb34c51c..029623dbd41 100644
--- a/net/ipv6/netfilter/ip6t_REJECT.c
+++ b/net/ipv6/netfilter/ip6t_REJECT.c
@@ -132,6 +132,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb)
ip6h->saddr = oip6h->daddr;
ip6h->daddr = oip6h->saddr;
+ skb_reset_transport_header(nskb);
tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
/* Truncate to length (no data) */
tcph->doff = sizeof(struct tcphdr)/4;
diff --git a/net/ipv6/netfilter/ip6t_rpfilter.c b/net/ipv6/netfilter/ip6t_rpfilter.c
index 5d1d8b04d69..5060d54199a 100644
--- a/net/ipv6/netfilter/ip6t_rpfilter.c
+++ b/net/ipv6/netfilter/ip6t_rpfilter.c
@@ -67,7 +67,7 @@ static bool rpfilter_lookup_reverse6(const struct sk_buff *skb,
if (rt->rt6i_idev->dev == dev || (flags & XT_RPFILTER_LOOSE))
ret = true;
out:
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return ret;
}
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index e418bd6350a..e0e788d25b1 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -127,19 +127,28 @@ nf_nat_ipv6_fn(unsigned int hooknum,
ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
if (ret != NF_ACCEPT)
return ret;
- } else
+ } else {
pr_debug("Already setup manip %s for ct %p\n",
maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
+ if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+ goto oif_changed;
+ }
break;
default:
/* ESTABLISHED */
NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
ctinfo == IP_CT_ESTABLISHED_REPLY);
+ if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
+ goto oif_changed;
}
return nf_nat_packet(ct, ctinfo, hooknum, skb);
+
+oif_changed:
+ nf_ct_kill_acct(ct, ctinfo, skb);
+ return NF_DROP;
}
static unsigned int
@@ -186,7 +195,8 @@ nf_nat_ipv6_out(unsigned int hooknum,
if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
&ct->tuplehash[!dir].tuple.dst.u3) ||
- (ct->tuplehash[dir].tuple.src.u.all !=
+ (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
+ ct->tuplehash[dir].tuple.src.u.all !=
ct->tuplehash[!dir].tuple.dst.u.all))
if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
ret = NF_DROP;
@@ -222,6 +232,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum,
}
#ifdef CONFIG_XFRM
else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
+ ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
ct->tuplehash[dir].tuple.dst.u.all !=
ct->tuplehash[!dir].tuple.src.u.all)
if (nf_xfrm_me_harder(skb, AF_INET6))
@@ -275,9 +286,7 @@ static int __net_init ip6table_nat_net_init(struct net *net)
return -ENOMEM;
net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl);
kfree(repl);
- if (IS_ERR(net->ipv6.ip6table_nat))
- return PTR_ERR(net->ipv6.ip6table_nat);
- return 0;
+ return PTR_RET(net->ipv6.ip6table_nat);
}
static void __net_exit ip6table_nat_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 8860d23e61c..137e245860a 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -21,6 +21,7 @@
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_l4proto.h>
@@ -80,8 +81,8 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
}
protoff = ipv6_skip_exthdr(skb, extoff, &nexthdr, &frag_off);
/*
- * (protoff == skb->len) mean that the packet doesn't have no data
- * except of IPv6 & ext headers. but it's tracked anyway. - YK
+ * (protoff == skb->len) means the packet has not data, just
+ * IPv6 and possibly extensions headers, but it is tracked anyway
*/
if (protoff < 0 || (frag_off & htons(~0x7)) != 0) {
pr_debug("ip6_conntrack_core: can't find proto in pkt\n");
@@ -295,7 +296,56 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
},
};
-#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
+static int
+ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len)
+{
+ const struct inet_sock *inet = inet_sk(sk);
+ const struct ipv6_pinfo *inet6 = inet6_sk(sk);
+ const struct nf_conntrack_tuple_hash *h;
+ struct sockaddr_in6 sin6;
+ struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 };
+ struct nf_conn *ct;
+
+ tuple.src.u3.in6 = inet6->rcv_saddr;
+ tuple.src.u.tcp.port = inet->inet_sport;
+ tuple.dst.u3.in6 = inet6->daddr;
+ tuple.dst.u.tcp.port = inet->inet_dport;
+ tuple.dst.protonum = sk->sk_protocol;
+
+ if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP)
+ return -ENOPROTOOPT;
+
+ if (*len < 0 || (unsigned int) *len < sizeof(sin6))
+ return -EINVAL;
+
+ h = nf_conntrack_find_get(sock_net(sk), NF_CT_DEFAULT_ZONE, &tuple);
+ if (!h) {
+ pr_debug("IP6T_SO_ORIGINAL_DST: Can't find %pI6c/%u-%pI6c/%u.\n",
+ &tuple.src.u3.ip6, ntohs(tuple.src.u.tcp.port),
+ &tuple.dst.u3.ip6, ntohs(tuple.dst.u.tcp.port));
+ return -ENOENT;
+ }
+
+ ct = nf_ct_tuplehash_to_ctrack(h);
+
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u.tcp.port;
+ sin6.sin6_flowinfo = inet6->flow_label & IPV6_FLOWINFO_MASK;
+ memcpy(&sin6.sin6_addr,
+ &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.in6,
+ sizeof(sin6.sin6_addr));
+
+ nf_ct_put(ct);
+
+ if (ipv6_addr_type(&sin6.sin6_addr) & IPV6_ADDR_LINKLOCAL)
+ sin6.sin6_scope_id = sk->sk_bound_dev_if;
+ else
+ sin6.sin6_scope_id = 0;
+
+ return copy_to_user(user, &sin6, sizeof(sin6)) ? -EFAULT : 0;
+}
+
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
@@ -346,7 +396,7 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = {
.invert_tuple = ipv6_invert_tuple,
.print_tuple = ipv6_print_tuple,
.get_l4proto = ipv6_get_l4proto,
-#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
.tuple_to_nlattr = ipv6_tuple_to_nlattr,
.nlattr_tuple_size = ipv6_nlattr_tuple_size,
.nlattr_to_tuple = ipv6_nlattr_to_tuple,
@@ -359,6 +409,14 @@ MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
+static struct nf_sockopt_ops so_getorigdst6 = {
+ .pf = NFPROTO_IPV6,
+ .get_optmin = IP6T_SO_ORIGINAL_DST,
+ .get_optmax = IP6T_SO_ORIGINAL_DST + 1,
+ .get = ipv6_getorigdst,
+ .owner = THIS_MODULE,
+};
+
static int ipv6_net_init(struct net *net)
{
int ret = 0;
@@ -425,6 +483,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
need_conntrack();
nf_defrag_ipv6_enable();
+ ret = nf_register_sockopt(&so_getorigdst6);
+ if (ret < 0) {
+ pr_err("Unable to register netfilter socket option\n");
+ return ret;
+ }
+
ret = register_pernet_subsys(&ipv6_net_ops);
if (ret < 0)
goto cleanup_pernet;
@@ -440,6 +504,7 @@ static int __init nf_conntrack_l3proto_ipv6_init(void)
cleanup_ipv6:
unregister_pernet_subsys(&ipv6_net_ops);
cleanup_pernet:
+ nf_unregister_sockopt(&so_getorigdst6);
return ret;
}
@@ -448,6 +513,7 @@ static void __exit nf_conntrack_l3proto_ipv6_fini(void)
synchronize_net();
nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
unregister_pernet_subsys(&ipv6_net_ops);
+ nf_unregister_sockopt(&so_getorigdst6);
}
module_init(nf_conntrack_l3proto_ipv6_init);
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index 2d54b2061d6..24df3dde007 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -232,7 +232,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl,
return icmpv6_error_message(net, tmpl, skb, dataoff, ctinfo, hooknum);
}
-#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
@@ -375,7 +375,7 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly =
.get_timeouts = icmpv6_get_timeouts,
.new = icmpv6_new,
.error = icmpv6_error,
-#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
.tuple_to_nlattr = icmpv6_tuple_to_nlattr,
.nlattr_tuple_size = icmpv6_nlattr_tuple_size,
.nlattr_to_tuple = icmpv6_nlattr_to_tuple,
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 18bd9bbbd1c..3dacecc9906 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -85,7 +85,7 @@ static struct ctl_table nf_ct_frag6_sysctl_table[] = {
{ }
};
-static int __net_init nf_ct_frag6_sysctl_register(struct net *net)
+static int nf_ct_frag6_sysctl_register(struct net *net)
{
struct ctl_table *table;
struct ctl_table_header *hdr;
@@ -127,7 +127,7 @@ static void __net_exit nf_ct_frags6_sysctl_unregister(struct net *net)
}
#else
-static int __net_init nf_ct_frag6_sysctl_register(struct net *net)
+static int nf_ct_frag6_sysctl_register(struct net *net)
{
return 0;
}
@@ -311,7 +311,10 @@ found:
else
fq->q.fragments = skb;
- skb->dev = NULL;
+ if (skb->dev) {
+ fq->iif = skb->dev->ifindex;
+ skb->dev = NULL;
+ }
fq->q.stamp = skb->tstamp;
fq->q.meat += skb->len;
if (payload_len > fq->q.max_size)
diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
index cdd6d045e42..aacd121fe8c 100644
--- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
+++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c
@@ -19,7 +19,7 @@
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_bridge.h>
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_l4proto.h>
@@ -35,7 +35,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum,
{
u16 zone = NF_CT_DEFAULT_ZONE;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
if (skb->nfct)
zone = nf_ct_zone((struct nf_conn *)skb->nfct);
#endif
@@ -60,7 +60,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum,
{
struct sk_buff *reasm;
-#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CONNTRACK)
/* Previously seen (loopback)? */
if (skb->nfct && !nf_ct_is_template((struct nf_conn *)skb->nfct))
return NF_ACCEPT;
diff --git a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c
index 5d6da784305..61aaf70f376 100644
--- a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c
@@ -84,7 +84,7 @@ const struct nf_nat_l4proto nf_nat_l4proto_icmpv6 = {
.manip_pkt = icmpv6_manip_pkt,
.in_range = icmpv6_in_range,
.unique_tuple = icmpv6_unique_tuple,
-#if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE)
+#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
.nlattr_to_range = nf_nat_l4proto_nlattr_to_range,
#endif
};
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
new file mode 100644
index 00000000000..c2e73e647e4
--- /dev/null
+++ b/net/ipv6/output_core.c
@@ -0,0 +1,76 @@
+/*
+ * IPv6 library code, needed by static components when full IPv6 support is
+ * not configured or static. These functions are needed by GSO/GRO implementation.
+ */
+#include <linux/export.h>
+#include <net/ipv6.h>
+#include <net/ip6_fib.h>
+
+void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
+{
+ static atomic_t ipv6_fragmentation_id;
+ int old, new;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (rt && !(rt->dst.flags & DST_NOPEER)) {
+ struct inet_peer *peer;
+ struct net *net;
+
+ net = dev_net(rt->dst.dev);
+ peer = inet_getpeer_v6(net->ipv6.peers, &rt->rt6i_dst.addr, 1);
+ if (peer) {
+ fhdr->identification = htonl(inet_getid(peer, 0));
+ inet_putpeer(peer);
+ return;
+ }
+ }
+#endif
+ do {
+ old = atomic_read(&ipv6_fragmentation_id);
+ new = old + 1;
+ if (!new)
+ new = 1;
+ } while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
+ fhdr->identification = htonl(new);
+}
+EXPORT_SYMBOL(ipv6_select_ident);
+
+int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
+{
+ u16 offset = sizeof(struct ipv6hdr);
+ struct ipv6_opt_hdr *exthdr =
+ (struct ipv6_opt_hdr *)(ipv6_hdr(skb) + 1);
+ unsigned int packet_len = skb->tail - skb->network_header;
+ int found_rhdr = 0;
+ *nexthdr = &ipv6_hdr(skb)->nexthdr;
+
+ while (offset + 1 <= packet_len) {
+
+ switch (**nexthdr) {
+
+ case NEXTHDR_HOP:
+ break;
+ case NEXTHDR_ROUTING:
+ found_rhdr = 1;
+ break;
+ case NEXTHDR_DEST:
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+ if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0)
+ break;
+#endif
+ if (found_rhdr)
+ return offset;
+ break;
+ default :
+ return offset;
+ }
+
+ offset += ipv6_optlen(exthdr);
+ *nexthdr = &exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
+ offset);
+ }
+
+ return offset;
+}
+EXPORT_SYMBOL(ip6_find_1stfragopt);
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 053082dfc93..22d1bd4670d 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -25,7 +25,9 @@
#include <linux/spinlock.h>
#include <net/protocol.h>
+#if IS_ENABLED(CONFIG_IPV6)
const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS] __read_mostly;
+EXPORT_SYMBOL(inet6_protos);
int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char protocol)
{
@@ -50,3 +52,26 @@ int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char protocol
return ret;
}
EXPORT_SYMBOL(inet6_del_protocol);
+#endif
+
+const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS] __read_mostly;
+
+int inet6_add_offload(const struct net_offload *prot, unsigned char protocol)
+{
+ return !cmpxchg((const struct net_offload **)&inet6_offloads[protocol],
+ NULL, prot) ? 0 : -1;
+}
+EXPORT_SYMBOL(inet6_add_offload);
+
+int inet6_del_offload(const struct net_offload *prot, unsigned char protocol)
+{
+ int ret;
+
+ ret = (cmpxchg((const struct net_offload **)&inet6_offloads[protocol],
+ prot, NULL) == prot) ? 0 : -1;
+
+ synchronize_net();
+
+ return ret;
+}
+EXPORT_SYMBOL(inet6_del_offload);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index d8e95c77db9..6cd29b1e8b9 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -50,7 +50,7 @@
#include <net/udp.h>
#include <net/inet_common.h>
#include <net/tcp_states.h>
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/mip6.h>
#endif
#include <linux/mroute6.h>
@@ -123,7 +123,7 @@ static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb)
return 1;
}
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
typedef int mh_filter_t(struct sock *sock, struct sk_buff *skb);
static mh_filter_t __rcu *mh_filter __read_mostly;
@@ -184,7 +184,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr)
filtered = icmpv6_filter(sk, skb);
break;
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPPROTO_MH:
{
/* XXX: To validate MH only once for each packet,
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index da8a4e301b1..e5253ec9e0f 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -616,6 +616,10 @@ static int __net_init ip6_frags_ns_sysctl_register(struct net *net)
table[0].data = &net->ipv6.frags.high_thresh;
table[1].data = &net->ipv6.frags.low_thresh;
table[2].data = &net->ipv6.frags.timeout;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
}
hdr = register_net_sysctl(net, "net/ipv6", table);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index b1e6cf0b95f..e229a3bc345 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -57,6 +57,7 @@
#include <net/xfrm.h>
#include <net/netevent.h>
#include <net/netlink.h>
+#include <net/nexthop.h>
#include <asm/uaccess.h>
@@ -289,6 +290,8 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
rt->rt6i_genid = rt_genid(net);
+ INIT_LIST_HEAD(&rt->rt6i_siblings);
+ rt->rt6i_nsiblings = 0;
}
return rt;
}
@@ -318,13 +321,6 @@ static void ip6_dst_destroy(struct dst_entry *dst)
}
}
-static atomic_t __rt6_peer_genid = ATOMIC_INIT(0);
-
-static u32 rt6_peer_genid(void)
-{
- return atomic_read(&__rt6_peer_genid);
-}
-
void rt6_bind_peer(struct rt6_info *rt, int create)
{
struct inet_peer_base *base;
@@ -338,8 +334,6 @@ void rt6_bind_peer(struct rt6_info *rt, int create)
if (peer) {
if (!rt6_set_peer(rt, peer))
inet_putpeer(peer);
- else
- rt->rt6i_peer_genid = rt6_peer_genid();
}
}
@@ -385,6 +379,69 @@ static bool rt6_need_strict(const struct in6_addr *daddr)
(IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL | IPV6_ADDR_LOOPBACK);
}
+/* Multipath route selection:
+ * Hash based function using packet header and flowlabel.
+ * Adapted from fib_info_hashfn()
+ */
+static int rt6_info_hash_nhsfn(unsigned int candidate_count,
+ const struct flowi6 *fl6)
+{
+ unsigned int val = fl6->flowi6_proto;
+
+ val ^= (__force u32)fl6->daddr.s6_addr32[0];
+ val ^= (__force u32)fl6->daddr.s6_addr32[1];
+ val ^= (__force u32)fl6->daddr.s6_addr32[2];
+ val ^= (__force u32)fl6->daddr.s6_addr32[3];
+
+ val ^= (__force u32)fl6->saddr.s6_addr32[0];
+ val ^= (__force u32)fl6->saddr.s6_addr32[1];
+ val ^= (__force u32)fl6->saddr.s6_addr32[2];
+ val ^= (__force u32)fl6->saddr.s6_addr32[3];
+
+ /* Work only if this not encapsulated */
+ switch (fl6->flowi6_proto) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ case IPPROTO_SCTP:
+ val ^= (__force u16)fl6->fl6_sport;
+ val ^= (__force u16)fl6->fl6_dport;
+ break;
+
+ case IPPROTO_ICMPV6:
+ val ^= (__force u16)fl6->fl6_icmp_type;
+ val ^= (__force u16)fl6->fl6_icmp_code;
+ break;
+ }
+ /* RFC6438 recommands to use flowlabel */
+ val ^= (__force u32)fl6->flowlabel;
+
+ /* Perhaps, we need to tune, this function? */
+ val = val ^ (val >> 7) ^ (val >> 12);
+ return val % candidate_count;
+}
+
+static struct rt6_info *rt6_multipath_select(struct rt6_info *match,
+ struct flowi6 *fl6)
+{
+ struct rt6_info *sibling, *next_sibling;
+ int route_choosen;
+
+ route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6);
+ /* Don't change the route, if route_choosen == 0
+ * (siblings does not include ourself)
+ */
+ if (route_choosen)
+ list_for_each_entry_safe(sibling, next_sibling,
+ &match->rt6i_siblings, rt6i_siblings) {
+ route_choosen--;
+ if (route_choosen == 0) {
+ match = sibling;
+ break;
+ }
+ }
+ return match;
+}
+
/*
* Route lookup. Any table->tb6_lock is implied.
*/
@@ -487,35 +544,32 @@ static inline int rt6_check_dev(struct rt6_info *rt, int oif)
return 0;
}
-static inline int rt6_check_neigh(struct rt6_info *rt)
+static inline bool rt6_check_neigh(struct rt6_info *rt)
{
struct neighbour *neigh;
- int m;
+ bool ret = false;
neigh = rt->n;
if (rt->rt6i_flags & RTF_NONEXTHOP ||
!(rt->rt6i_flags & RTF_GATEWAY))
- m = 1;
+ ret = true;
else if (neigh) {
read_lock_bh(&neigh->lock);
if (neigh->nud_state & NUD_VALID)
- m = 2;
+ ret = true;
#ifdef CONFIG_IPV6_ROUTER_PREF
- else if (neigh->nud_state & NUD_FAILED)
- m = 0;
+ else if (!(neigh->nud_state & NUD_FAILED))
+ ret = true;
#endif
- else
- m = 1;
read_unlock_bh(&neigh->lock);
- } else
- m = 0;
- return m;
+ }
+ return ret;
}
static int rt6_score_route(struct rt6_info *rt, int oif,
int strict)
{
- int m, n;
+ int m;
m = rt6_check_dev(rt, oif);
if (!m && (strict & RT6_LOOKUP_F_IFACE))
@@ -523,8 +577,7 @@ static int rt6_score_route(struct rt6_info *rt, int oif,
#ifdef CONFIG_IPV6_ROUTER_PREF
m |= IPV6_DECODE_PREF(IPV6_EXTRACT_PREF(rt->rt6i_flags)) << 2;
#endif
- n = rt6_check_neigh(rt);
- if (!n && (strict & RT6_LOOKUP_F_REACHABLE))
+ if (!rt6_check_neigh(rt) && (strict & RT6_LOOKUP_F_REACHABLE))
return -1;
return m;
}
@@ -666,7 +719,7 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
else
rt6_set_expires(rt, jiffies + HZ * lifetime);
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
}
return 0;
}
@@ -702,6 +755,8 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
restart:
rt = fn->leaf;
rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
+ if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
+ rt = rt6_multipath_select(rt, fl6);
BACKTRACK(net, &fl6->saddr);
out:
dst_use(&rt->dst, jiffies);
@@ -863,7 +918,8 @@ restart_2:
restart:
rt = rt6_select(fn, oif, strict | reachable);
-
+ if (rt->rt6i_nsiblings && oif == 0)
+ rt = rt6_multipath_select(rt, fl6);
BACKTRACK(net, &fl6->saddr);
if (rt == net->ipv6.ip6_null_entry ||
rt->rt6i_flags & RTF_CACHE)
@@ -879,7 +935,7 @@ restart:
else
goto out2;
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
rt = nrt ? : net->ipv6.ip6_null_entry;
dst_hold(&rt->dst);
@@ -896,7 +952,7 @@ restart:
* Race condition! In the gap, when table->tb6_lock was
* released someone could insert this route. Relookup.
*/
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
goto relookup;
out:
@@ -1030,14 +1086,9 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
return NULL;
- if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
- if (rt->rt6i_peer_genid != rt6_peer_genid()) {
- if (!rt6_has_peer(rt))
- rt6_bind_peer(rt, 0);
- rt->rt6i_peer_genid = rt6_peer_genid();
- }
+ if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie))
return dst;
- }
+
return NULL;
}
@@ -1316,12 +1367,6 @@ out:
return entries > rt_max_size;
}
-/* Clean host part of a prefix. Not necessary in radix tree,
- but results in cleaner routing tables.
-
- Remove it only when all the things will work!
- */
-
int ip6_dst_hoplimit(struct dst_entry *dst)
{
int hoplimit = dst_metric_raw(dst, RTAX_HOPLIMIT);
@@ -1507,7 +1552,7 @@ int ip6_route_add(struct fib6_config *cfg)
goto out;
if (dev) {
if (dev != grt->dst.dev) {
- dst_release(&grt->dst);
+ ip6_rt_put(grt);
goto out;
}
} else {
@@ -1518,7 +1563,7 @@ int ip6_route_add(struct fib6_config *cfg)
}
if (!(grt->rt6i_flags & RTF_GATEWAY))
err = 0;
- dst_release(&grt->dst);
+ ip6_rt_put(grt);
if (err)
goto out;
@@ -1604,7 +1649,7 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
write_unlock_bh(&table->tb6_lock);
out:
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
return err;
}
@@ -1987,7 +2032,7 @@ int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg)
switch(cmd) {
case SIOCADDRT: /* Add a route */
case SIOCDELRT: /* Delete a route */
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
err = copy_from_user(&rtmsg, arg,
sizeof(struct in6_rtmsg));
@@ -2249,6 +2294,7 @@ static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = {
[RTA_IIF] = { .type = NLA_U32 },
[RTA_PRIORITY] = { .type = NLA_U32 },
[RTA_METRICS] = { .type = NLA_NESTED },
+ [RTA_MULTIPATH] = { .len = sizeof(struct rtnexthop) },
};
static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
@@ -2326,11 +2372,71 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh,
if (tb[RTA_TABLE])
cfg->fc_table = nla_get_u32(tb[RTA_TABLE]);
+ if (tb[RTA_MULTIPATH]) {
+ cfg->fc_mp = nla_data(tb[RTA_MULTIPATH]);
+ cfg->fc_mp_len = nla_len(tb[RTA_MULTIPATH]);
+ }
+
err = 0;
errout:
return err;
}
+static int ip6_route_multipath(struct fib6_config *cfg, int add)
+{
+ struct fib6_config r_cfg;
+ struct rtnexthop *rtnh;
+ int remaining;
+ int attrlen;
+ int err = 0, last_err = 0;
+
+beginning:
+ rtnh = (struct rtnexthop *)cfg->fc_mp;
+ remaining = cfg->fc_mp_len;
+
+ /* Parse a Multipath Entry */
+ while (rtnh_ok(rtnh, remaining)) {
+ memcpy(&r_cfg, cfg, sizeof(*cfg));
+ if (rtnh->rtnh_ifindex)
+ r_cfg.fc_ifindex = rtnh->rtnh_ifindex;
+
+ attrlen = rtnh_attrlen(rtnh);
+ if (attrlen > 0) {
+ struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
+
+ nla = nla_find(attrs, attrlen, RTA_GATEWAY);
+ if (nla) {
+ nla_memcpy(&r_cfg.fc_gateway, nla, 16);
+ r_cfg.fc_flags |= RTF_GATEWAY;
+ }
+ }
+ err = add ? ip6_route_add(&r_cfg) : ip6_route_del(&r_cfg);
+ if (err) {
+ last_err = err;
+ /* If we are trying to remove a route, do not stop the
+ * loop when ip6_route_del() fails (because next hop is
+ * already gone), we should try to remove all next hops.
+ */
+ if (add) {
+ /* If add fails, we should try to delete all
+ * next hops that have been already added.
+ */
+ add = 0;
+ goto beginning;
+ }
+ }
+ /* Because each route is added like a single route we remove
+ * this flag after the first nexthop (if there is a collision,
+ * we have already fail to add the first nexthop:
+ * fib6_add_rt2node() has reject it).
+ */
+ cfg->fc_nlinfo.nlh->nlmsg_flags &= ~NLM_F_EXCL;
+ rtnh = rtnh_next(rtnh, &remaining);
+ }
+
+ return last_err;
+}
+
static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
{
struct fib6_config cfg;
@@ -2340,7 +2446,10 @@ static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *a
if (err < 0)
return err;
- return ip6_route_del(&cfg);
+ if (cfg.fc_mp)
+ return ip6_route_multipath(&cfg, 0);
+ else
+ return ip6_route_del(&cfg);
}
static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
@@ -2352,7 +2461,10 @@ static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *a
if (err < 0)
return err;
- return ip6_route_add(&cfg);
+ if (cfg.fc_mp)
+ return ip6_route_multipath(&cfg, 1);
+ else
+ return ip6_route_add(&cfg);
}
static inline size_t rt6_nlmsg_size(void)
@@ -2596,7 +2708,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
- dst_release(&rt->dst);
+ ip6_rt_put(rt);
err = -ENOBUFS;
goto errout;
}
@@ -2873,6 +2985,10 @@ struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net)
table[7].data = &net->ipv6.sysctl.ip6_rt_mtu_expires;
table[8].data = &net->ipv6.sysctl.ip6_rt_min_advmss;
table[9].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
}
return table;
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 3ed54ffd8d5..cfba99b2c2a 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -65,9 +65,14 @@
#define HASH_SIZE 16
#define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF)
+static bool log_ecn_error = true;
+module_param(log_ecn_error, bool, 0644);
+MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
+
static int ipip6_tunnel_init(struct net_device *dev);
static void ipip6_tunnel_setup(struct net_device *dev);
static void ipip6_dev_free(struct net_device *dev);
+static struct rtnl_link_ops sit_link_ops __read_mostly;
static int sit_net_id __read_mostly;
struct sit_net {
@@ -80,22 +85,6 @@ struct sit_net {
struct net_device *fb_tunnel_dev;
};
-/*
- * Locking : hash tables are protected by RCU and RTNL
- */
-
-#define for_each_ip_tunnel_rcu(start) \
- for (t = rcu_dereference(start); t; t = rcu_dereference(t->next))
-
-/* often modified stats are per cpu, other are shared (netdev->stats) */
-struct pcpu_tstats {
- u64 rx_packets;
- u64 rx_bytes;
- u64 tx_packets;
- u64 tx_bytes;
- struct u64_stats_sync syncp;
-};
-
static struct rtnl_link_stats64 *ipip6_get_stats64(struct net_device *dev,
struct rtnl_link_stats64 *tot)
{
@@ -121,6 +110,7 @@ static struct rtnl_link_stats64 *ipip6_get_stats64(struct net_device *dev,
}
tot->rx_errors = dev->stats.rx_errors;
+ tot->rx_frame_errors = dev->stats.rx_frame_errors;
tot->tx_fifo_errors = dev->stats.tx_fifo_errors;
tot->tx_carrier_errors = dev->stats.tx_carrier_errors;
tot->tx_dropped = dev->stats.tx_dropped;
@@ -141,20 +131,20 @@ static struct ip_tunnel *ipip6_tunnel_lookup(struct net *net,
struct ip_tunnel *t;
struct sit_net *sitn = net_generic(net, sit_net_id);
- for_each_ip_tunnel_rcu(sitn->tunnels_r_l[h0 ^ h1]) {
+ for_each_ip_tunnel_rcu(t, sitn->tunnels_r_l[h0 ^ h1]) {
if (local == t->parms.iph.saddr &&
remote == t->parms.iph.daddr &&
(!dev || !t->parms.link || dev->iflink == t->parms.link) &&
(t->dev->flags & IFF_UP))
return t;
}
- for_each_ip_tunnel_rcu(sitn->tunnels_r[h0]) {
+ for_each_ip_tunnel_rcu(t, sitn->tunnels_r[h0]) {
if (remote == t->parms.iph.daddr &&
(!dev || !t->parms.link || dev->iflink == t->parms.link) &&
(t->dev->flags & IFF_UP))
return t;
}
- for_each_ip_tunnel_rcu(sitn->tunnels_l[h1]) {
+ for_each_ip_tunnel_rcu(t, sitn->tunnels_l[h1]) {
if (local == t->parms.iph.saddr &&
(!dev || !t->parms.link || dev->iflink == t->parms.link) &&
(t->dev->flags & IFF_UP))
@@ -231,6 +221,37 @@ static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn)
#endif
}
+static int ipip6_tunnel_create(struct net_device *dev)
+{
+ struct ip_tunnel *t = netdev_priv(dev);
+ struct net *net = dev_net(dev);
+ struct sit_net *sitn = net_generic(net, sit_net_id);
+ int err;
+
+ err = ipip6_tunnel_init(dev);
+ if (err < 0)
+ goto out;
+ ipip6_tunnel_clone_6rd(dev, sitn);
+
+ if ((__force u16)t->parms.i_flags & SIT_ISATAP)
+ dev->priv_flags |= IFF_ISATAP;
+
+ err = register_netdevice(dev);
+ if (err < 0)
+ goto out;
+
+ strcpy(t->parms.name, dev->name);
+ dev->rtnl_link_ops = &sit_link_ops;
+
+ dev_hold(dev);
+
+ ipip6_tunnel_link(sitn, t);
+ return 0;
+
+out:
+ return err;
+}
+
static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,
struct ip_tunnel_parm *parms, int create)
{
@@ -271,21 +292,9 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,
nt = netdev_priv(dev);
nt->parms = *parms;
- if (ipip6_tunnel_init(dev) < 0)
+ if (ipip6_tunnel_create(dev) < 0)
goto failed_free;
- ipip6_tunnel_clone_6rd(dev, sitn);
- if (parms->i_flags & SIT_ISATAP)
- dev->priv_flags |= IFF_ISATAP;
-
- if (register_netdevice(dev) < 0)
- goto failed_free;
-
- strcpy(nt->parms.name, dev->name);
-
- dev_hold(dev);
-
- ipip6_tunnel_link(sitn, nt);
return nt;
failed_free:
@@ -581,16 +590,11 @@ out:
return err;
}
-static inline void ipip6_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb)
-{
- if (INET_ECN_is_ce(iph->tos))
- IP6_ECN_set_ce(ipv6_hdr(skb));
-}
-
static int ipip6_rcv(struct sk_buff *skb)
{
const struct iphdr *iph;
struct ip_tunnel *tunnel;
+ int err;
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
goto out;
@@ -612,18 +616,27 @@ static int ipip6_rcv(struct sk_buff *skb)
if ((tunnel->dev->priv_flags & IFF_ISATAP) &&
!isatap_chksrc(skb, iph, tunnel)) {
tunnel->dev->stats.rx_errors++;
- kfree_skb(skb);
- return 0;
+ goto out;
+ }
+
+ __skb_tunnel_rx(skb, tunnel->dev);
+
+ err = IP_ECN_decapsulate(iph, skb);
+ if (unlikely(err)) {
+ if (log_ecn_error)
+ net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n",
+ &iph->saddr, iph->tos);
+ if (err > 1) {
+ ++tunnel->dev->stats.rx_frame_errors;
+ ++tunnel->dev->stats.rx_errors;
+ goto out;
+ }
}
tstats = this_cpu_ptr(tunnel->dev->tstats);
tstats->rx_packets++;
tstats->rx_bytes += skb->len;
- __skb_tunnel_rx(skb, tunnel->dev);
-
- ipip6_ecn_decapsulate(iph, skb);
-
netif_rx(skb);
return 0;
@@ -683,7 +696,6 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- struct pcpu_tstats *tstats;
const struct iphdr *tiph = &tunnel->parms.iph;
const struct ipv6hdr *iph6 = ipv6_hdr(skb);
u8 tos = tunnel->parms.iph.tos;
@@ -864,9 +876,7 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
if ((iph->ttl = tiph->ttl) == 0)
iph->ttl = iph6->hop_limit;
- nf_reset(skb);
- tstats = this_cpu_ptr(dev->tstats);
- __IPTUNNEL_XMIT(tstats, &dev->stats);
+ iptunnel_xmit(skb, dev);
return NETDEV_TX_OK;
tx_error_icmp:
@@ -914,6 +924,59 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
dev->iflink = tunnel->parms.link;
}
+static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
+{
+ struct net *net = dev_net(t->dev);
+ struct sit_net *sitn = net_generic(net, sit_net_id);
+
+ ipip6_tunnel_unlink(sitn, t);
+ synchronize_net();
+ t->parms.iph.saddr = p->iph.saddr;
+ t->parms.iph.daddr = p->iph.daddr;
+ memcpy(t->dev->dev_addr, &p->iph.saddr, 4);
+ memcpy(t->dev->broadcast, &p->iph.daddr, 4);
+ ipip6_tunnel_link(sitn, t);
+ t->parms.iph.ttl = p->iph.ttl;
+ t->parms.iph.tos = p->iph.tos;
+ if (t->parms.link != p->link) {
+ t->parms.link = p->link;
+ ipip6_tunnel_bind_dev(t->dev);
+ }
+ netdev_state_change(t->dev);
+}
+
+#ifdef CONFIG_IPV6_SIT_6RD
+static int ipip6_tunnel_update_6rd(struct ip_tunnel *t,
+ struct ip_tunnel_6rd *ip6rd)
+{
+ struct in6_addr prefix;
+ __be32 relay_prefix;
+
+ if (ip6rd->relay_prefixlen > 32 ||
+ ip6rd->prefixlen + (32 - ip6rd->relay_prefixlen) > 64)
+ return -EINVAL;
+
+ ipv6_addr_prefix(&prefix, &ip6rd->prefix, ip6rd->prefixlen);
+ if (!ipv6_addr_equal(&prefix, &ip6rd->prefix))
+ return -EINVAL;
+ if (ip6rd->relay_prefixlen)
+ relay_prefix = ip6rd->relay_prefix &
+ htonl(0xffffffffUL <<
+ (32 - ip6rd->relay_prefixlen));
+ else
+ relay_prefix = 0;
+ if (relay_prefix != ip6rd->relay_prefix)
+ return -EINVAL;
+
+ t->ip6rd.prefix = prefix;
+ t->ip6rd.relay_prefix = relay_prefix;
+ t->ip6rd.prefixlen = ip6rd->prefixlen;
+ t->ip6rd.relay_prefixlen = ip6rd->relay_prefixlen;
+ netdev_state_change(t->dev);
+ return 0;
+}
+#endif
+
static int
ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
{
@@ -966,7 +1029,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCADDTUNNEL:
case SIOCCHGTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -995,28 +1058,13 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
break;
}
t = netdev_priv(dev);
- ipip6_tunnel_unlink(sitn, t);
- synchronize_net();
- t->parms.iph.saddr = p.iph.saddr;
- t->parms.iph.daddr = p.iph.daddr;
- memcpy(dev->dev_addr, &p.iph.saddr, 4);
- memcpy(dev->broadcast, &p.iph.daddr, 4);
- ipip6_tunnel_link(sitn, t);
- netdev_state_change(dev);
}
+
+ ipip6_tunnel_update(t, &p);
}
if (t) {
err = 0;
- if (cmd == SIOCCHGTUNNEL) {
- t->parms.iph.ttl = p.iph.ttl;
- t->parms.iph.tos = p.iph.tos;
- if (t->parms.link != p.link) {
- t->parms.link = p.link;
- ipip6_tunnel_bind_dev(dev);
- netdev_state_change(dev);
- }
- }
if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof(p)))
err = -EFAULT;
} else
@@ -1025,7 +1073,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCDELTUNNEL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
if (dev == sitn->fb_tunnel_dev) {
@@ -1058,7 +1106,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCDELPRL:
case SIOCCHGPRL:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EINVAL;
if (dev == sitn->fb_tunnel_dev)
@@ -1087,7 +1135,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCCHG6RD:
case SIOCDEL6RD:
err = -EPERM;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
goto done;
err = -EFAULT;
@@ -1098,31 +1146,9 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
t = netdev_priv(dev);
if (cmd != SIOCDEL6RD) {
- struct in6_addr prefix;
- __be32 relay_prefix;
-
- err = -EINVAL;
- if (ip6rd.relay_prefixlen > 32 ||
- ip6rd.prefixlen + (32 - ip6rd.relay_prefixlen) > 64)
- goto done;
-
- ipv6_addr_prefix(&prefix, &ip6rd.prefix,
- ip6rd.prefixlen);
- if (!ipv6_addr_equal(&prefix, &ip6rd.prefix))
- goto done;
- if (ip6rd.relay_prefixlen)
- relay_prefix = ip6rd.relay_prefix &
- htonl(0xffffffffUL <<
- (32 - ip6rd.relay_prefixlen));
- else
- relay_prefix = 0;
- if (relay_prefix != ip6rd.relay_prefix)
+ err = ipip6_tunnel_update_6rd(t, &ip6rd);
+ if (err < 0)
goto done;
-
- t->ip6rd.prefix = prefix;
- t->ip6rd.relay_prefix = relay_prefix;
- t->ip6rd.prefixlen = ip6rd.prefixlen;
- t->ip6rd.relay_prefixlen = ip6rd.relay_prefixlen;
} else
ipip6_tunnel_clone_6rd(dev, sitn);
@@ -1216,6 +1242,239 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
return 0;
}
+static void ipip6_netlink_parms(struct nlattr *data[],
+ struct ip_tunnel_parm *parms)
+{
+ memset(parms, 0, sizeof(*parms));
+
+ parms->iph.version = 4;
+ parms->iph.protocol = IPPROTO_IPV6;
+ parms->iph.ihl = 5;
+ parms->iph.ttl = 64;
+
+ if (!data)
+ return;
+
+ if (data[IFLA_IPTUN_LINK])
+ parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
+
+ if (data[IFLA_IPTUN_LOCAL])
+ parms->iph.saddr = nla_get_be32(data[IFLA_IPTUN_LOCAL]);
+
+ if (data[IFLA_IPTUN_REMOTE])
+ parms->iph.daddr = nla_get_be32(data[IFLA_IPTUN_REMOTE]);
+
+ if (data[IFLA_IPTUN_TTL]) {
+ parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
+ if (parms->iph.ttl)
+ parms->iph.frag_off = htons(IP_DF);
+ }
+
+ if (data[IFLA_IPTUN_TOS])
+ parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);
+
+ if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
+ parms->iph.frag_off = htons(IP_DF);
+
+ if (data[IFLA_IPTUN_FLAGS])
+ parms->i_flags = nla_get_be16(data[IFLA_IPTUN_FLAGS]);
+}
+
+#ifdef CONFIG_IPV6_SIT_6RD
+/* This function returns true when 6RD attributes are present in the nl msg */
+static bool ipip6_netlink_6rd_parms(struct nlattr *data[],
+ struct ip_tunnel_6rd *ip6rd)
+{
+ bool ret = false;
+ memset(ip6rd, 0, sizeof(*ip6rd));
+
+ if (!data)
+ return ret;
+
+ if (data[IFLA_IPTUN_6RD_PREFIX]) {
+ ret = true;
+ nla_memcpy(&ip6rd->prefix, data[IFLA_IPTUN_6RD_PREFIX],
+ sizeof(struct in6_addr));
+ }
+
+ if (data[IFLA_IPTUN_6RD_RELAY_PREFIX]) {
+ ret = true;
+ ip6rd->relay_prefix =
+ nla_get_be32(data[IFLA_IPTUN_6RD_RELAY_PREFIX]);
+ }
+
+ if (data[IFLA_IPTUN_6RD_PREFIXLEN]) {
+ ret = true;
+ ip6rd->prefixlen = nla_get_u16(data[IFLA_IPTUN_6RD_PREFIXLEN]);
+ }
+
+ if (data[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]) {
+ ret = true;
+ ip6rd->relay_prefixlen =
+ nla_get_u16(data[IFLA_IPTUN_6RD_RELAY_PREFIXLEN]);
+ }
+
+ return ret;
+}
+#endif
+
+static int ipip6_newlink(struct net *src_net, struct net_device *dev,
+ struct nlattr *tb[], struct nlattr *data[])
+{
+ struct net *net = dev_net(dev);
+ struct ip_tunnel *nt;
+#ifdef CONFIG_IPV6_SIT_6RD
+ struct ip_tunnel_6rd ip6rd;
+#endif
+ int err;
+
+ nt = netdev_priv(dev);
+ ipip6_netlink_parms(data, &nt->parms);
+
+ if (ipip6_tunnel_locate(net, &nt->parms, 0))
+ return -EEXIST;
+
+ err = ipip6_tunnel_create(dev);
+ if (err < 0)
+ return err;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+ if (ipip6_netlink_6rd_parms(data, &ip6rd))
+ err = ipip6_tunnel_update_6rd(nt, &ip6rd);
+#endif
+
+ return err;
+}
+
+static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
+ struct nlattr *data[])
+{
+ struct ip_tunnel *t;
+ struct ip_tunnel_parm p;
+ struct net *net = dev_net(dev);
+ struct sit_net *sitn = net_generic(net, sit_net_id);
+#ifdef CONFIG_IPV6_SIT_6RD
+ struct ip_tunnel_6rd ip6rd;
+#endif
+
+ if (dev == sitn->fb_tunnel_dev)
+ return -EINVAL;
+
+ ipip6_netlink_parms(data, &p);
+
+ if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
+ (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
+ return -EINVAL;
+
+ t = ipip6_tunnel_locate(net, &p, 0);
+
+ if (t) {
+ if (t->dev != dev)
+ return -EEXIST;
+ } else
+ t = netdev_priv(dev);
+
+ ipip6_tunnel_update(t, &p);
+
+#ifdef CONFIG_IPV6_SIT_6RD
+ if (ipip6_netlink_6rd_parms(data, &ip6rd))
+ return ipip6_tunnel_update_6rd(t, &ip6rd);
+#endif
+
+ return 0;
+}
+
+static size_t ipip6_get_size(const struct net_device *dev)
+{
+ return
+ /* IFLA_IPTUN_LINK */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_LOCAL */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_REMOTE */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_TTL */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_TOS */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_PMTUDISC */
+ nla_total_size(1) +
+ /* IFLA_IPTUN_FLAGS */
+ nla_total_size(2) +
+#ifdef CONFIG_IPV6_SIT_6RD
+ /* IFLA_IPTUN_6RD_PREFIX */
+ nla_total_size(sizeof(struct in6_addr)) +
+ /* IFLA_IPTUN_6RD_RELAY_PREFIX */
+ nla_total_size(4) +
+ /* IFLA_IPTUN_6RD_PREFIXLEN */
+ nla_total_size(2) +
+ /* IFLA_IPTUN_6RD_RELAY_PREFIXLEN */
+ nla_total_size(2) +
+#endif
+ 0;
+}
+
+static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev)
+{
+ struct ip_tunnel *tunnel = netdev_priv(dev);
+ struct ip_tunnel_parm *parm = &tunnel->parms;
+
+ if (nla_put_u32(skb, IFLA_IPTUN_LINK, parm->link) ||
+ nla_put_be32(skb, IFLA_IPTUN_LOCAL, parm->iph.saddr) ||
+ nla_put_be32(skb, IFLA_IPTUN_REMOTE, parm->iph.daddr) ||
+ nla_put_u8(skb, IFLA_IPTUN_TTL, parm->iph.ttl) ||
+ nla_put_u8(skb, IFLA_IPTUN_TOS, parm->iph.tos) ||
+ nla_put_u8(skb, IFLA_IPTUN_PMTUDISC,
+ !!(parm->iph.frag_off & htons(IP_DF))) ||
+ nla_put_be16(skb, IFLA_IPTUN_FLAGS, parm->i_flags))
+ goto nla_put_failure;
+
+#ifdef CONFIG_IPV6_SIT_6RD
+ if (nla_put(skb, IFLA_IPTUN_6RD_PREFIX, sizeof(struct in6_addr),
+ &tunnel->ip6rd.prefix) ||
+ nla_put_be32(skb, IFLA_IPTUN_6RD_RELAY_PREFIX,
+ tunnel->ip6rd.relay_prefix) ||
+ nla_put_u16(skb, IFLA_IPTUN_6RD_PREFIXLEN,
+ tunnel->ip6rd.prefixlen) ||
+ nla_put_u16(skb, IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+ tunnel->ip6rd.relay_prefixlen))
+ goto nla_put_failure;
+#endif
+
+ return 0;
+
+nla_put_failure:
+ return -EMSGSIZE;
+}
+
+static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
+ [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
+ [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 },
+#ifdef CONFIG_IPV6_SIT_6RD
+ [IFLA_IPTUN_6RD_PREFIX] = { .len = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_6RD_RELAY_PREFIX] = { .type = NLA_U32 },
+ [IFLA_IPTUN_6RD_PREFIXLEN] = { .type = NLA_U16 },
+ [IFLA_IPTUN_6RD_RELAY_PREFIXLEN] = { .type = NLA_U16 },
+#endif
+};
+
+static struct rtnl_link_ops sit_link_ops __read_mostly = {
+ .kind = "sit",
+ .maxtype = IFLA_IPTUN_MAX,
+ .policy = ipip6_policy,
+ .priv_size = sizeof(struct ip_tunnel),
+ .setup = ipip6_tunnel_setup,
+ .newlink = ipip6_newlink,
+ .changelink = ipip6_changelink,
+ .get_size = ipip6_get_size,
+ .fill_info = ipip6_fill_info,
+};
+
static struct xfrm_tunnel sit_handler __read_mostly = {
.handler = ipip6_rcv,
.err_handler = ipip6_err,
@@ -1302,6 +1561,7 @@ static struct pernet_operations sit_net_ops = {
static void __exit sit_cleanup(void)
{
+ rtnl_link_unregister(&sit_link_ops);
xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
unregister_pernet_device(&sit_net_ops);
@@ -1319,10 +1579,21 @@ static int __init sit_init(void)
return err;
err = xfrm4_tunnel_register(&sit_handler, AF_INET6);
if (err < 0) {
- unregister_pernet_device(&sit_net_ops);
pr_info("%s: can't add protocol\n", __func__);
+ goto xfrm_tunnel_failed;
}
+ err = rtnl_link_register(&sit_link_ops);
+ if (err < 0)
+ goto rtnl_link_failed;
+
+out:
return err;
+
+rtnl_link_failed:
+ xfrm4_tunnel_deregister(&sit_handler, AF_INET6);
+xfrm_tunnel_failed:
+ unregister_pernet_device(&sit_net_ops);
+ goto out;
}
module_init(sit_init);
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 182ab9a85d6..40161977f7c 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -214,7 +214,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
ireq6->iif = inet6_iif(skb);
req->expires = 0UL;
- req->retrans = 0;
+ req->num_retrans = 0;
ireq->ecn_ok = ecn_ok;
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 26175bffbaa..93825dd3a7c 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -77,9 +77,6 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static void __tcp_v6_send_check(struct sk_buff *skb,
- const struct in6_addr *saddr,
- const struct in6_addr *daddr);
static const struct inet_connection_sock_af_ops ipv6_mapped;
static const struct inet_connection_sock_af_ops ipv6_specific;
@@ -119,14 +116,6 @@ static void tcp_v6_hash(struct sock *sk)
}
}
-static __inline__ __sum16 tcp_v6_check(int len,
- const struct in6_addr *saddr,
- const struct in6_addr *daddr,
- __wsum base)
-{
- return csum_ipv6_magic(saddr, daddr, len, IPPROTO_TCP, base);
-}
-
static __u32 tcp_v6_init_sequence(const struct sk_buff *skb)
{
return secure_tcpv6_sequence_number(ipv6_hdr(skb)->daddr.s6_addr32,
@@ -306,7 +295,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
if (err)
goto late_failure;
- if (!tp->write_seq)
+ if (!tp->write_seq && likely(!tp->repair))
tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32,
np->daddr.s6_addr32,
inet->inet_sport,
@@ -495,9 +484,12 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req,
struct request_values *rvp)
{
struct flowi6 fl6;
+ int res;
- TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
- return tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0);
+ res = tcp_v6_send_synack(sk, NULL, &fl6, req, rvp, 0);
+ if (!res)
+ TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_RETRANSSEGS);
+ return res;
}
static void tcp_v6_reqsk_destructor(struct request_sock *req)
@@ -719,94 +711,6 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
};
#endif
-static void __tcp_v6_send_check(struct sk_buff *skb,
- const struct in6_addr *saddr, const struct in6_addr *daddr)
-{
- struct tcphdr *th = tcp_hdr(skb);
-
- if (skb->ip_summed == CHECKSUM_PARTIAL) {
- th->check = ~tcp_v6_check(skb->len, saddr, daddr, 0);
- skb->csum_start = skb_transport_header(skb) - skb->head;
- skb->csum_offset = offsetof(struct tcphdr, check);
- } else {
- th->check = tcp_v6_check(skb->len, saddr, daddr,
- csum_partial(th, th->doff << 2,
- skb->csum));
- }
-}
-
-static void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb)
-{
- struct ipv6_pinfo *np = inet6_sk(sk);
-
- __tcp_v6_send_check(skb, &np->saddr, &np->daddr);
-}
-
-static int tcp_v6_gso_send_check(struct sk_buff *skb)
-{
- const struct ipv6hdr *ipv6h;
- struct tcphdr *th;
-
- if (!pskb_may_pull(skb, sizeof(*th)))
- return -EINVAL;
-
- ipv6h = ipv6_hdr(skb);
- th = tcp_hdr(skb);
-
- th->check = 0;
- skb->ip_summed = CHECKSUM_PARTIAL;
- __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
- return 0;
-}
-
-static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
- struct sk_buff *skb)
-{
- const struct ipv6hdr *iph = skb_gro_network_header(skb);
- __wsum wsum;
- __sum16 sum;
-
- switch (skb->ip_summed) {
- case CHECKSUM_COMPLETE:
- if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
- skb->csum)) {
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- break;
- }
-flush:
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
-
- case CHECKSUM_NONE:
- wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
- skb_gro_len(skb),
- IPPROTO_TCP, 0));
- sum = csum_fold(skb_checksum(skb,
- skb_gro_offset(skb),
- skb_gro_len(skb),
- wsum));
- if (sum)
- goto flush;
-
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- break;
- }
-
- return tcp_gro_receive(head, skb);
-}
-
-static int tcp6_gro_complete(struct sk_buff *skb)
-{
- const struct ipv6hdr *iph = ipv6_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
-
- th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
- &iph->saddr, &iph->daddr, 0);
- skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
-
- return tcp_gro_complete(skb);
-}
-
static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
u32 ts, struct tcp_md5sig_key *key, int rst, u8 tclass)
{
@@ -1364,7 +1268,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
tcp_initialize_rcv_mss(newsk);
tcp_synack_rtt_meas(newsk, req);
- newtp->total_retrans = req->retrans;
+ newtp->total_retrans = req->num_retrans;
newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
@@ -1384,7 +1288,8 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
#endif
if (__inet_inherit_port(sk, newsk) < 0) {
- sock_put(newsk);
+ inet_csk_prepare_forced_close(newsk);
+ tcp_done(newsk);
goto out;
}
__inet6_hash(newsk, NULL);
@@ -1741,11 +1646,11 @@ static void tcp_v6_early_demux(struct sk_buff *skb)
skb->destructor = sock_edemux;
if (sk->sk_state != TCP_TIME_WAIT) {
struct dst_entry *dst = sk->sk_rx_dst;
- struct inet_sock *icsk = inet_sk(sk);
+
if (dst)
dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
if (dst &&
- icsk->rx_dst_ifindex == skb->skb_iif)
+ inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
skb_dst_set_noref(skb, dst);
}
}
@@ -1866,7 +1771,7 @@ static void get_openreq6(struct seq_file *seq,
0,0, /* could print option size, but that is af dependent. */
1, /* timers active (only the expire timer) */
jiffies_to_clock_t(ttd),
- req->retrans,
+ req->num_timeout,
from_kuid_munged(seq_user_ns(seq), uid),
0, /* non standard timer */
0, /* open_requests have no inode */
@@ -2063,10 +1968,6 @@ static const struct inet6_protocol tcpv6_protocol = {
.early_demux = tcp_v6_early_demux,
.handler = tcp_v6_rcv,
.err_handler = tcp_v6_err,
- .gso_send_check = tcp_v6_gso_send_check,
- .gso_segment = tcp_tso_segment,
- .gro_receive = tcp6_gro_receive,
- .gro_complete = tcp6_gro_complete,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
@@ -2121,10 +2022,10 @@ int __init tcpv6_init(void)
out:
return ret;
-out_tcpv6_protocol:
- inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
out_tcpv6_protosw:
inet6_unregister_protosw(&tcpv6_protosw);
+out_tcpv6_protocol:
+ inet6_del_protocol(&tcpv6_protocol, IPPROTO_TCP);
goto out;
}
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
new file mode 100644
index 00000000000..2ec6bf6a0aa
--- /dev/null
+++ b/net/ipv6/tcpv6_offload.c
@@ -0,0 +1,95 @@
+/*
+ * IPV6 GSO/GRO offload support
+ * Linux INET6 implementation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * TCPv6 GSO/GRO support
+ */
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include "ip6_offload.h"
+
+static int tcp_v6_gso_send_check(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ipv6h;
+ struct tcphdr *th;
+
+ if (!pskb_may_pull(skb, sizeof(*th)))
+ return -EINVAL;
+
+ ipv6h = ipv6_hdr(skb);
+ th = tcp_hdr(skb);
+
+ th->check = 0;
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ __tcp_v6_send_check(skb, &ipv6h->saddr, &ipv6h->daddr);
+ return 0;
+}
+
+static struct sk_buff **tcp6_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb)
+{
+ const struct ipv6hdr *iph = skb_gro_network_header(skb);
+ __wsum wsum;
+ __sum16 sum;
+
+ switch (skb->ip_summed) {
+ case CHECKSUM_COMPLETE:
+ if (!tcp_v6_check(skb_gro_len(skb), &iph->saddr, &iph->daddr,
+ skb->csum)) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ }
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
+
+ case CHECKSUM_NONE:
+ wsum = ~csum_unfold(csum_ipv6_magic(&iph->saddr, &iph->daddr,
+ skb_gro_len(skb),
+ IPPROTO_TCP, 0));
+ sum = csum_fold(skb_checksum(skb,
+ skb_gro_offset(skb),
+ skb_gro_len(skb),
+ wsum));
+ if (sum)
+ goto flush;
+
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ break;
+ }
+
+ return tcp_gro_receive(head, skb);
+}
+
+static int tcp6_gro_complete(struct sk_buff *skb)
+{
+ const struct ipv6hdr *iph = ipv6_hdr(skb);
+ struct tcphdr *th = tcp_hdr(skb);
+
+ th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
+ &iph->saddr, &iph->daddr, 0);
+ skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+
+ return tcp_gro_complete(skb);
+}
+
+static const struct net_offload tcpv6_offload = {
+ .callbacks = {
+ .gso_send_check = tcp_v6_gso_send_check,
+ .gso_segment = tcp_tso_segment,
+ .gro_receive = tcp6_gro_receive,
+ .gro_complete = tcp6_gro_complete,
+ },
+};
+
+int __init tcpv6_offload_init(void)
+{
+ return inet6_add_offload(&tcpv6_offload, IPPROTO_TCP);
+}
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index fc9997260a6..dfaa29b8b29 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1343,103 +1343,9 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
}
#endif
-static int udp6_ufo_send_check(struct sk_buff *skb)
-{
- const struct ipv6hdr *ipv6h;
- struct udphdr *uh;
-
- if (!pskb_may_pull(skb, sizeof(*uh)))
- return -EINVAL;
-
- ipv6h = ipv6_hdr(skb);
- uh = udp_hdr(skb);
-
- uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
- IPPROTO_UDP, 0);
- skb->csum_start = skb_transport_header(skb) - skb->head;
- skb->csum_offset = offsetof(struct udphdr, check);
- skb->ip_summed = CHECKSUM_PARTIAL;
- return 0;
-}
-
-static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
- netdev_features_t features)
-{
- struct sk_buff *segs = ERR_PTR(-EINVAL);
- unsigned int mss;
- unsigned int unfrag_ip6hlen, unfrag_len;
- struct frag_hdr *fptr;
- u8 *mac_start, *prevhdr;
- u8 nexthdr;
- u8 frag_hdr_sz = sizeof(struct frag_hdr);
- int offset;
- __wsum csum;
-
- mss = skb_shinfo(skb)->gso_size;
- if (unlikely(skb->len <= mss))
- goto out;
-
- if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
- /* Packet is from an untrusted source, reset gso_segs. */
- int type = skb_shinfo(skb)->gso_type;
-
- if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
- !(type & (SKB_GSO_UDP))))
- goto out;
-
- skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
-
- segs = NULL;
- goto out;
- }
-
- /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
- * do checksum of UDP packets sent as multiple IP fragments.
- */
- offset = skb_checksum_start_offset(skb);
- csum = skb_checksum(skb, offset, skb->len - offset, 0);
- offset += skb->csum_offset;
- *(__sum16 *)(skb->data + offset) = csum_fold(csum);
- skb->ip_summed = CHECKSUM_NONE;
-
- /* Check if there is enough headroom to insert fragment header. */
- if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
- pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
- goto out;
-
- /* Find the unfragmentable header and shift it left by frag_hdr_sz
- * bytes to insert fragment header.
- */
- unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
- nexthdr = *prevhdr;
- *prevhdr = NEXTHDR_FRAGMENT;
- unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
- unfrag_ip6hlen;
- mac_start = skb_mac_header(skb);
- memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
-
- skb->mac_header -= frag_hdr_sz;
- skb->network_header -= frag_hdr_sz;
-
- fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
- fptr->nexthdr = nexthdr;
- fptr->reserved = 0;
- ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
-
- /* Fragment the skb. ipv6 header and the remaining fields of the
- * fragment header are updated in ipv6_gso_segment()
- */
- segs = skb_segment(skb, features);
-
-out:
- return segs;
-}
-
static const struct inet6_protocol udpv6_protocol = {
.handler = udpv6_rcv,
.err_handler = udpv6_err,
- .gso_send_check = udp6_ufo_send_check,
- .gso_segment = udp6_ufo_fragment,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
new file mode 100644
index 00000000000..0c8934a317c
--- /dev/null
+++ b/net/ipv6/udp_offload.c
@@ -0,0 +1,120 @@
+/*
+ * IPV6 GSO/GRO offload support
+ * Linux INET6 implementation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * UDPv6 GSO support
+ */
+#include <linux/skbuff.h>
+#include <net/protocol.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <net/ip6_checksum.h>
+#include "ip6_offload.h"
+
+static int udp6_ufo_send_check(struct sk_buff *skb)
+{
+ const struct ipv6hdr *ipv6h;
+ struct udphdr *uh;
+
+ if (!pskb_may_pull(skb, sizeof(*uh)))
+ return -EINVAL;
+
+ ipv6h = ipv6_hdr(skb);
+ uh = udp_hdr(skb);
+
+ uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+ IPPROTO_UDP, 0);
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ return 0;
+}
+
+static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ unsigned int mss;
+ unsigned int unfrag_ip6hlen, unfrag_len;
+ struct frag_hdr *fptr;
+ u8 *mac_start, *prevhdr;
+ u8 nexthdr;
+ u8 frag_hdr_sz = sizeof(struct frag_hdr);
+ int offset;
+ __wsum csum;
+
+ mss = skb_shinfo(skb)->gso_size;
+ if (unlikely(skb->len <= mss))
+ goto out;
+
+ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+ /* Packet is from an untrusted source, reset gso_segs. */
+ int type = skb_shinfo(skb)->gso_type;
+
+ if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
+ !(type & (SKB_GSO_UDP))))
+ goto out;
+
+ skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+ segs = NULL;
+ goto out;
+ }
+
+ /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+ * do checksum of UDP packets sent as multiple IP fragments.
+ */
+ offset = skb_checksum_start_offset(skb);
+ csum = skb_checksum(skb, offset, skb->len - offset, 0);
+ offset += skb->csum_offset;
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* Check if there is enough headroom to insert fragment header. */
+ if ((skb_mac_header(skb) < skb->head + frag_hdr_sz) &&
+ pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
+ goto out;
+
+ /* Find the unfragmentable header and shift it left by frag_hdr_sz
+ * bytes to insert fragment header.
+ */
+ unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+ nexthdr = *prevhdr;
+ *prevhdr = NEXTHDR_FRAGMENT;
+ unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
+ unfrag_ip6hlen;
+ mac_start = skb_mac_header(skb);
+ memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+
+ skb->mac_header -= frag_hdr_sz;
+ skb->network_header -= frag_hdr_sz;
+
+ fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+ fptr->nexthdr = nexthdr;
+ fptr->reserved = 0;
+ ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
+
+ /* Fragment the skb. ipv6 header and the remaining fields of the
+ * fragment header are updated in ipv6_gso_segment()
+ */
+ segs = skb_segment(skb, features);
+
+out:
+ return segs;
+}
+static const struct net_offload udpv6_offload = {
+ .callbacks = {
+ .gso_send_check = udp6_ufo_send_check,
+ .gso_segment = udp6_ufo_fragment,
+ },
+};
+
+int __init udp_offload_init(void)
+{
+ return inet6_add_offload(&udpv6_offload, IPPROTO_UDP);
+}
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index f8c4c08ffb6..c9844135c9c 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -20,7 +20,7 @@
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ip6_route.h>
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
#include <net/mip6.h>
#endif
@@ -182,7 +182,7 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse)
fl6->flowi6_proto = nexthdr;
return;
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case IPPROTO_MH:
if (!onlyproto && pskb_may_pull(skb, nh + offset + 3 - skb->data)) {
struct ip6_mh *mh;
@@ -327,21 +327,7 @@ static struct ctl_table_header *sysctl_hdr;
int __init xfrm6_init(void)
{
int ret;
- unsigned int gc_thresh;
-
- /*
- * We need a good default value for the xfrm6 gc threshold.
- * In ipv4 we set it to the route hash table size * 8, which
- * is half the size of the maximaum route cache for ipv4. It
- * would be good to do the same thing for v6, except the table is
- * constructed differently here. Here each table for a net namespace
- * can have FIB_TABLE_HASHSZ entries, so lets go with the same
- * computation that we used for ipv4 here. Also, lets keep the initial
- * gc_thresh to a minimum of 1024, since, the ipv6 route cache defaults
- * to that as a minimum as well
- */
- gc_thresh = FIB6_TABLE_HASHSZ * 8;
- xfrm6_dst_ops.gc_thresh = (gc_thresh < 1024) ? 1024 : gc_thresh;
+
dst_entries_init(&xfrm6_dst_ops);
ret = xfrm6_policy_init();
@@ -370,7 +356,6 @@ void xfrm6_fini(void)
if (sysctl_hdr)
unregister_net_sysctl_table(sysctl_hdr);
#endif
- //xfrm6_input_fini();
xfrm6_policy_fini();
xfrm6_state_fini();
dst_entries_destroy(&xfrm6_dst_ops);
diff --git a/net/ipv6/xfrm6_state.c b/net/ipv6/xfrm6_state.c
index 3f2f7c4ab72..d8c70b8efc2 100644
--- a/net/ipv6/xfrm6_state.c
+++ b/net/ipv6/xfrm6_state.c
@@ -101,7 +101,7 @@ static int __xfrm6_state_sort_cmp(void *p)
return 1;
else
return 3;
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case XFRM_MODE_ROUTEOPTIMIZATION:
case XFRM_MODE_IN_TRIGGER:
return 2;
@@ -134,7 +134,7 @@ static int __xfrm6_tmpl_sort_cmp(void *p)
switch (v->mode) {
case XFRM_MODE_TRANSPORT:
return 1;
-#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
case XFRM_MODE_ROUTEOPTIMIZATION:
case XFRM_MODE_IN_TRIGGER:
return 2;
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 496ce2cebcd..a68c88cdec6 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -183,6 +183,7 @@ static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
ircomm_tty_shutdown(self);
self->magic = 0;
+ tty_port_destroy(&self->port);
kfree(self);
}
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index 1002e3396f7..ae43c62f904 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -441,6 +441,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0);
if (lsap == NULL) {
IRDA_DEBUG(0, "%s: unable to allocate LSAP!!\n", __func__);
+ __irttp_close_tsap(self);
return NULL;
}
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index 3ad1f9db5f8..df082508362 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -1806,7 +1806,7 @@ static void iucv_external_interrupt(struct ext_code ext_code,
struct iucv_irq_data *p;
struct iucv_irq_list *work;
- kstat_cpu(smp_processor_id()).irqs[EXTINT_IUC]++;
+ inc_irq_stat(IRQEXT_IUC);
p = iucv_irq_data[smp_processor_id()];
if (p->ippathid >= iucv_max_pathid) {
WARN_ON(p->ippathid >= iucv_max_pathid);
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 08897a3c7ec..5b426a64654 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -141,7 +141,7 @@ static int pfkey_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
int err;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (sock->type != SOCK_RAW)
return -ESOCKTNOSUPPORT;
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index 37b8b8ba31f..76125c57ee6 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -291,6 +291,7 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p
out_del_dev:
free_netdev(dev);
+ spriv->dev = NULL;
out_del_session:
l2tp_session_delete(session);
out:
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index 6c4cc12c741..bbba3a19e94 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -632,7 +632,7 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq, int fl
nla_put_u16(skb, L2TP_ATTR_MRU, session->mru)))
goto nla_put_failure;
- if ((session->ifname && session->ifname[0] &&
+ if ((session->ifname[0] &&
nla_put_string(skb, L2TP_ATTR_IFNAME, session->ifname)) ||
(session->cookie_len &&
nla_put(skb, L2TP_ATTR_COOKIE, session->cookie_len,
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index c2190005a11..88709882c46 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -160,7 +160,7 @@ static int llc_ui_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
int rc = -ESOCKTNOSUPPORT;
- if (!capable(CAP_NET_RAW))
+ if (!ns_capable(net->user_ns, CAP_NET_RAW))
return -EPERM;
if (!net_eq(net, &init_net))
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 63af25458fd..b4ecf267a34 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -248,7 +248,7 @@ config MAC80211_MHWMP_DEBUG
Do not select this option.
config MAC80211_MESH_SYNC_DEBUG
- bool "Verbose mesh mesh synchronization debugging"
+ bool "Verbose mesh synchronization debugging"
depends on MAC80211_DEBUG_MENU
depends on MAC80211_MESH
---help---
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index a7dd110faaf..4911202334d 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -8,6 +8,7 @@ mac80211-y := \
wpa.o \
scan.o offchannel.o \
ht.o agg-tx.o agg-rx.o \
+ vht.o \
ibss.o \
iface.o \
rate.o \
diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c
index a04752e9102..537488cbf94 100644
--- a/net/mac80211/aes_cmac.c
+++ b/net/mac80211/aes_cmac.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/crypto.h>
+#include <linux/export.h>
#include <linux/err.h>
#include <crypto/aes.h>
@@ -126,3 +127,20 @@ void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
{
crypto_free_cipher(tfm);
}
+
+void ieee80211_aes_cmac_calculate_k1_k2(struct ieee80211_key_conf *keyconf,
+ u8 *k1, u8 *k2)
+{
+ u8 l[AES_BLOCK_SIZE] = {};
+ struct ieee80211_key *key =
+ container_of(keyconf, struct ieee80211_key, conf);
+
+ crypto_cipher_encrypt_one(key->u.aes_cmac.tfm, l, l);
+
+ memcpy(k1, l, AES_BLOCK_SIZE);
+ gf_mulx(k1);
+
+ memcpy(k2, k1, AES_BLOCK_SIZE);
+ gf_mulx(k2);
+}
+EXPORT_SYMBOL(ieee80211_aes_cmac_calculate_k1_k2);
diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c
index 186d9919b04..808338a1bce 100644
--- a/net/mac80211/agg-rx.c
+++ b/net/mac80211/agg-rx.c
@@ -118,7 +118,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
return;
}
- for (i = 0; i < STA_TID_NUM; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
if (ba_rx_bitmap & BIT(i))
set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 3195a6307f5..eb9df22418f 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -445,10 +445,10 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
trace_api_start_tx_ba_session(pubsta, tid);
- if (WARN_ON(!local->ops->ampdu_action))
+ if (WARN_ON_ONCE(!local->ops->ampdu_action))
return -EINVAL;
- if ((tid >= STA_TID_NUM) ||
+ if ((tid >= IEEE80211_NUM_TIDS) ||
!(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) ||
(local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW))
return -EINVAL;
@@ -605,9 +605,9 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid)
trace_api_start_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
@@ -687,7 +687,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
if (!local->ops->ampdu_action)
return -EINVAL;
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
spin_lock_bh(&sta->lock);
@@ -722,9 +722,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid)
trace_api_stop_tx_ba_cb(sdata, ra, tid);
- if (tid >= STA_TID_NUM) {
+ if (tid >= IEEE80211_NUM_TIDS) {
ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n",
- tid, STA_TID_NUM);
+ tid, IEEE80211_NUM_TIDS);
return;
}
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 05f3a313db8..516fbc96fef 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -164,7 +164,17 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
sta = sta_info_get(sdata, mac_addr);
else
sta = sta_info_get_bss(sdata, mac_addr);
- if (!sta) {
+ /*
+ * The ASSOC test makes sure the driver is ready to
+ * receive the key. When wpa_supplicant has roamed
+ * using FT, it attempts to set the key before
+ * association has completed, this rejects that attempt
+ * so it will set the key again after assocation.
+ *
+ * TODO: accept the key if we have a station entry and
+ * add it to the device after the station.
+ */
+ if (!sta || !test_sta_flag(sta, WLAN_STA_ASSOC)) {
ieee80211_key_free(sdata->local, key);
err = -ENOENT;
goto out_unlock;
@@ -370,29 +380,64 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy,
return 0;
}
-static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx)
-{
- if (!(rate->flags & RATE_INFO_FLAGS_MCS)) {
- struct ieee80211_supported_band *sband;
- sband = sta->local->hw.wiphy->bands[
- sta->local->oper_channel->band];
- rate->legacy = sband->bitrates[idx].bitrate;
- } else
- rate->mcs = idx;
-}
-
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo)
{
rinfo->flags = 0;
- if (rate->flags & IEEE80211_TX_RC_MCS)
+ if (rate->flags & IEEE80211_TX_RC_MCS) {
rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ rinfo->mcs = rate->idx;
+ } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
+ rinfo->nss = ieee80211_rate_get_vht_nss(rate);
+ } else {
+ struct ieee80211_supported_band *sband;
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+ }
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(rinfo, sta, rate->idx);
+}
+
+void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+{
+ rinfo->flags = 0;
+
+ if (sta->last_rx_rate_flag & RX_FLAG_HT) {
+ rinfo->flags |= RATE_INFO_FLAGS_MCS;
+ rinfo->mcs = sta->last_rx_rate_idx;
+ } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) {
+ rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
+ rinfo->nss = sta->last_rx_rate_vht_nss;
+ rinfo->mcs = sta->last_rx_rate_idx;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = sta->local->hw.wiphy->bands[
+ ieee80211_get_sdata_band(sta->sdata)];
+ rinfo->legacy =
+ sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ }
+
+ if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
+ rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
+ if (sta->last_rx_rate_flag & RX_FLAG_80MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (sta->last_rx_rate_flag & RX_FLAG_160MHZ)
+ rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
}
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@@ -441,15 +486,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
}
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
-
- sinfo->rxrate.flags = 0;
- if (sta->last_rx_rate_flag & RX_FLAG_HT)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS;
- if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
- if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI)
- sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
- rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx);
+ sta_set_rate_info_rx(sta, &sinfo->rxrate);
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
@@ -532,6 +569,8 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
u64 *data)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *channel;
struct sta_info *sta;
struct ieee80211_local *local = sdata->local;
struct station_info sinfo;
@@ -607,19 +646,26 @@ static void ieee80211_get_et_stats(struct wiphy *wiphy,
do_survey:
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
/* Get survey stats for current channel */
- q = 0;
- while (true) {
- survey.filled = 0;
- if (drv_get_survey(local, q, &survey) != 0) {
- survey.filled = 0;
- break;
- }
+ survey.filled = 0;
- if (survey.channel &&
- (local->oper_channel->center_freq ==
- survey.channel->center_freq))
- break;
- q++;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf)
+ channel = chanctx_conf->def.chan;
+ else
+ channel = NULL;
+ rcu_read_unlock();
+
+ if (channel) {
+ q = 0;
+ do {
+ survey.filled = 0;
+ if (drv_get_survey(local, q, &survey) != 0) {
+ survey.filled = 0;
+ break;
+ }
+ q++;
+ } while (channel != survey.channel);
}
if (survey.filled)
@@ -724,47 +770,37 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
return ret;
}
-static int ieee80211_set_channel(struct wiphy *wiphy,
- struct net_device *netdev,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = NULL;
-
- if (netdev)
- sdata = IEEE80211_DEV_TO_SUB_IF(netdev);
-
- switch (ieee80211_get_channel_mode(local, NULL)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel != chan ||
- (!sdata && local->_oper_channel_type != channel_type))
- return -EBUSY;
- if (!sdata && local->_oper_channel_type == channel_type)
- return 0;
- break;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
- if (!ieee80211_set_channel_type(local, sdata, channel_type))
- return -EBUSY;
+ struct ieee80211_sub_if_data *sdata;
+ int ret = 0;
- local->oper_channel = chan;
+ if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
+ return 0;
- /* auto-detects changes */
- ieee80211_hw_config(local, 0);
+ mutex_lock(&local->iflist_mtx);
+ if (local->use_chanctx) {
+ sdata = rcu_dereference_protected(
+ local->monitor_sdata,
+ lockdep_is_held(&local->iflist_mtx));
+ if (sdata) {
+ ieee80211_vif_release_channel(sdata);
+ ret = ieee80211_vif_use_channel(sdata, chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ }
+ } else if (local->open_count == local->monitors) {
+ local->_oper_channel = chandef->chan;
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
+ ieee80211_hw_config(local, 0);
+ }
- return 0;
-}
+ if (ret == 0)
+ local->monitor_chandef = *chandef;
+ mutex_unlock(&local->iflist_mtx);
-static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
-{
- return ieee80211_set_channel(wiphy, NULL, chan, channel_type);
+ return ret;
}
static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
@@ -872,15 +908,20 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
u32 changed = BSS_CHANGED_BEACON_INT |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_BEACON |
- BSS_CHANGED_SSID;
+ BSS_CHANGED_SSID |
+ BSS_CHANGED_P2P_PS;
int err;
old = rtnl_dereference(sdata->u.ap.beacon);
if (old)
return -EALREADY;
- err = ieee80211_set_channel(wiphy, dev, params->channel,
- params->channel_type);
+ /* TODO: make hostapd tell us what it wants */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ err = ieee80211_vif_use_channel(sdata, &params->chandef,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -907,11 +948,23 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
sdata->vif.bss_conf.hidden_ssid =
(params->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
+ sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+ sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+
err = ieee80211_assign_beacon(sdata, &params->beacon);
if (err < 0)
return err;
changed |= err;
+ err = drv_start_ap(sdata->local, sdata);
+ if (err) {
+ old = rtnl_dereference(sdata->u.ap.beacon);
+ if (old)
+ kfree_rcu(old, rcu_head);
+ RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ return err;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
netif_carrier_on(dev);
@@ -943,26 +996,42 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
{
- struct ieee80211_sub_if_data *sdata, *vlan;
- struct beacon_data *old;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_sub_if_data *vlan;
+ struct ieee80211_local *local = sdata->local;
+ struct beacon_data *old_beacon;
+ struct probe_resp *old_probe_resp;
- old = rtnl_dereference(sdata->u.ap.beacon);
- if (!old)
+ old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+ if (!old_beacon)
return -ENOENT;
+ old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ /* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
netif_carrier_off(dev);
+ /* remove beacon and probe response */
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
+ RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
+ kfree_rcu(old_beacon, rcu_head);
+ if (old_probe_resp)
+ kfree_rcu(old_probe_resp, rcu_head);
- kfree_rcu(old, rcu_head);
-
- sta_info_flush(sdata->local, sdata);
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ sta_info_flush(local, vlan);
+ sta_info_flush(local, sdata);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
+ drv_stop_ap(sdata->local, sdata);
+
+ /* free all potentially still buffered bcast frames */
+ local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf);
+ skb_queue_purge(&sdata->u.ap.ps.bc_buf);
+
+ ieee80211_vif_release_channel(sdata);
+
return 0;
}
@@ -1019,9 +1088,10 @@ static int sta_apply_parameters(struct ieee80211_local *local,
int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
u32 mask, set;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[band];
mask = params->sta_flags_mask;
set = params->sta_flags_set;
@@ -1136,7 +1206,7 @@ static int sta_apply_parameters(struct ieee80211_local *local,
rates |= BIT(j);
}
}
- sta->sta.supp_rates[local->oper_channel->band] = rates;
+ sta->sta.supp_rates[band] = rates;
}
if (params->ht_capa)
@@ -1144,6 +1214,11 @@ static int sta_apply_parameters(struct ieee80211_local *local,
params->ht_capa,
&sta->sta.ht_cap);
+ if (params->vht_capa)
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ params->vht_capa,
+ &sta->sta.vht_cap);
+
if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH
if (sdata->u.mesh.security & IEEE80211_MESH_SEC_SECURED)
@@ -1664,8 +1739,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
if (err)
return err;
- err = ieee80211_set_channel(wiphy, dev, setup->channel,
- setup->channel_type);
+ /* can mesh use other SMPS modes? */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+ IEEE80211_CHANCTX_SHARED);
if (err)
return err;
@@ -1679,6 +1758,7 @@ static int ieee80211_leave_mesh(struct wiphy *wiphy, struct net_device *dev)
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
ieee80211_stop_mesh(sdata);
+ ieee80211_vif_release_channel(sdata);
return 0;
}
@@ -1688,10 +1768,14 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
struct net_device *dev,
struct bss_parameters *params)
{
- struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ enum ieee80211_band band;
u32 changed = 0;
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (!rtnl_dereference(sdata->u.ap.beacon))
+ return -ENOENT;
+
+ band = ieee80211_get_sdata_band(sdata);
if (params->use_cts_prot >= 0) {
sdata->vif.bss_conf.use_cts_prot = params->use_cts_prot;
@@ -1704,7 +1788,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
}
if (!sdata->vif.bss_conf.use_short_slot &&
- sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ) {
+ band == IEEE80211_BAND_5GHZ) {
sdata->vif.bss_conf.use_short_slot = true;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -1718,9 +1802,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
if (params->basic_rates) {
int i, j;
u32 rates = 0;
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_supported_band *sband =
- wiphy->bands[local->oper_channel->band];
+ struct ieee80211_supported_band *sband = wiphy->bands[band];
for (i = 0; i < params->basic_rates_len; i++) {
int rate = (params->basic_rates[i] & 0x7f) * 5;
@@ -1746,6 +1828,16 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
changed |= BSS_CHANGED_HT;
}
+ if (params->p2p_ctwindow >= 0) {
+ sdata->vif.bss_conf.p2p_ctwindow = params->p2p_ctwindow;
+ changed |= BSS_CHANGED_P2P_PS;
+ }
+
+ if (params->p2p_opp_ps >= 0) {
+ sdata->vif.bss_conf.p2p_oppps = params->p2p_opp_ps;
+ changed |= BSS_CHANGED_P2P_PS;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
@@ -1829,7 +1921,16 @@ static int ieee80211_scan(struct wiphy *wiphy,
* beaconing hasn't been configured yet
*/
case NL80211_IFTYPE_AP:
- if (sdata->u.ap.beacon)
+ /*
+ * If the scan has been forced (and the driver supports
+ * forcing), don't care about being beaconing already.
+ * This will create problems to the attached stations (e.g. all
+ * the frames sent while scanning on other channel will be
+ * lost)
+ */
+ if (sdata->u.ap.beacon &&
+ (!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
+ !(req->flags & NL80211_SCAN_FLAG_AP)))
return -EOPNOTSUPP;
break;
default:
@@ -1872,20 +1973,6 @@ static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (local->oper_channel == req->bss->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
@@ -1904,30 +1991,22 @@ static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_ibss_params *params)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (ieee80211_get_channel_mode(local, sdata)) {
- case CHAN_MODE_HOPPING:
- return -EBUSY;
- case CHAN_MODE_FIXED:
- if (!params->channel_fixed)
- return -EBUSY;
- if (local->oper_channel == params->channel)
- break;
- return -EBUSY;
- case CHAN_MODE_UNDEFINED:
- break;
- }
-
- return ieee80211_ibss_join(sdata, params);
+ return ieee80211_ibss_join(IEEE80211_DEV_TO_SUB_IF(dev), params);
}
static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
{
+ return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
+static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
+ int rate[IEEE80211_NUM_BANDS])
+{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- return ieee80211_ibss_leave(sdata);
+ memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate));
+
+ return 0;
}
static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
@@ -1956,10 +2035,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
return err;
}
- if (changed & WIPHY_PARAM_RETRY_SHORT)
+ if (changed & WIPHY_PARAM_RETRY_SHORT) {
+ if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
- if (changed & WIPHY_PARAM_RETRY_LONG)
+ }
+ if (changed & WIPHY_PARAM_RETRY_LONG) {
+ if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY)
+ return -EINVAL;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
+ }
if (changed &
(WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG))
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS);
@@ -1968,41 +2053,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
}
static int ieee80211_set_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
enum nl80211_tx_power_setting type, int mbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
- struct ieee80211_channel *chan = local->oper_channel;
- u32 changes = 0;
+ struct ieee80211_sub_if_data *sdata;
+
+ if (wdev) {
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+
+ switch (type) {
+ case NL80211_TX_POWER_AUTOMATIC:
+ sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ break;
+ case NL80211_TX_POWER_LIMITED:
+ case NL80211_TX_POWER_FIXED:
+ if (mbm < 0 || (mbm % 100))
+ return -EOPNOTSUPP;
+ sdata->user_power_level = MBM_TO_DBM(mbm);
+ break;
+ }
+
+ ieee80211_recalc_txpower(sdata);
+
+ return 0;
+ }
switch (type) {
case NL80211_TX_POWER_AUTOMATIC:
- local->user_power_level = -1;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
break;
case NL80211_TX_POWER_LIMITED:
- if (mbm < 0 || (mbm % 100))
- return -EOPNOTSUPP;
- local->user_power_level = MBM_TO_DBM(mbm);
- break;
case NL80211_TX_POWER_FIXED:
if (mbm < 0 || (mbm % 100))
return -EOPNOTSUPP;
- /* TODO: move to cfg80211 when it knows the channel */
- if (MBM_TO_DBM(mbm) > chan->max_power)
- return -EINVAL;
local->user_power_level = MBM_TO_DBM(mbm);
break;
}
- ieee80211_hw_config(local, changes);
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list)
+ sdata->user_power_level = local->user_power_level;
+ list_for_each_entry(sdata, &local->interfaces, list)
+ ieee80211_recalc_txpower(sdata);
+ mutex_unlock(&local->iflist_mtx);
return 0;
}
-static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
+static int ieee80211_get_tx_power(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ int *dbm)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
- *dbm = local->hw.conf.power_level;
+ if (!local->use_chanctx)
+ *dbm = local->hw.conf.power_level;
+ else
+ *dbm = sdata->vif.bss_conf.txpower;
return 0;
}
@@ -2067,13 +2176,12 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
/*
* If not associated, or current association is not an HT
- * association, there's no need to send an action frame.
+ * association, there's no need to do anything, just store
+ * the new value until we associate.
*/
if (!sdata->u.mgd.associated ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) {
- ieee80211_recalc_smps(sdata->local);
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
- }
ap = sdata->u.mgd.associated->bssid;
@@ -2179,7 +2287,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
static int ieee80211_start_roc_work(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
unsigned int duration, u64 *cookie,
struct sk_buff *txskb)
{
@@ -2189,12 +2296,14 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
lockdep_assert_held(&local->mtx);
+ if (local->use_chanctx && !local->ops->remain_on_channel)
+ return -EOPNOTSUPP;
+
roc = kzalloc(sizeof(*roc), GFP_KERNEL);
if (!roc)
return -ENOMEM;
roc->chan = channel;
- roc->chan_type = channel_type;
roc->duration = duration;
roc->req_duration = duration;
roc->frame = txskb;
@@ -2227,7 +2336,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, channel, channel_type, duration);
+ ret = drv_remain_on_channel(local, sdata, channel, duration);
if (ret) {
kfree(roc);
return ret;
@@ -2238,7 +2347,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
out_check_combine:
list_for_each_entry(tmp, &local->roc_list, list) {
- if (tmp->chan != channel || tmp->chan_type != channel_type)
+ if (tmp->chan != channel || tmp->sdata != sdata)
continue;
/*
@@ -2332,13 +2441,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
list_add_tail(&roc->list, &local->roc_list);
/*
- * cookie is either the roc (for normal roc)
+ * cookie is either the roc cookie (for normal roc)
* or the SKB (for mgmt TX)
*/
- if (txskb)
+ if (!txskb) {
+ /* local->mtx protects this */
+ local->roc_cookie_counter++;
+ roc->cookie = local->roc_cookie_counter;
+ /* wow, you wrapped 64 bits ... more likely a bug */
+ if (WARN_ON(roc->cookie == 0)) {
+ roc->cookie = 1;
+ local->roc_cookie_counter++;
+ }
+ *cookie = roc->cookie;
+ } else {
*cookie = (unsigned long)txskb;
- else
- *cookie = (unsigned long)roc;
+ }
return 0;
}
@@ -2346,7 +2464,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local,
static int ieee80211_remain_on_channel(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration,
u64 *cookie)
{
@@ -2355,7 +2472,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy,
int ret;
mutex_lock(&local->mtx);
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
duration, cookie, NULL);
mutex_unlock(&local->mtx);
@@ -2373,7 +2490,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
struct ieee80211_roc_work *dep, *tmp2;
list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) {
- if (!mgmt_tx && (unsigned long)dep != cookie)
+ if (!mgmt_tx && dep->cookie != cookie)
continue;
else if (mgmt_tx && dep->mgmt_tx_cookie != cookie)
continue;
@@ -2385,7 +2502,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local,
return 0;
}
- if (!mgmt_tx && (unsigned long)roc != cookie)
+ if (!mgmt_tx && roc->cookie != cookie)
continue;
else if (mgmt_tx && roc->mgmt_tx_cookie != cookie)
continue;
@@ -2448,10 +2565,8 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy,
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
@@ -2515,10 +2630,16 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* Check if the operating channel is the requested channel */
if (!need_offchan) {
- need_offchan = chan != local->oper_channel;
- if (channel_type_valid &&
- channel_type != local->_oper_channel_type)
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf)
+ need_offchan = chan != chanctx_conf->def.chan;
+ else
need_offchan = true;
+ rcu_read_unlock();
}
if (need_offchan && !offchan) {
@@ -2552,7 +2673,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, chan, channel_type,
+ ret = ieee80211_start_roc_work(local, sdata, chan,
wait, cookie, skb);
if (ret)
kfree_skb(skb);
@@ -2594,6 +2715,9 @@ static void ieee80211_mgmt_frame_register(struct wiphy *wiphy,
else
local->probe_req_reg--;
+ if (!local->open_count)
+ break;
+
ieee80211_queue_work(&local->hw, &local->reconfig_filter);
break;
default:
@@ -2667,7 +2791,7 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
u16 capab;
capab = 0;
- if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
+ if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
return capab;
if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
@@ -2699,7 +2823,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_tdls_data *tf;
tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
@@ -2719,10 +2843,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_req.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_RESPONSE:
@@ -2735,10 +2857,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
tf->u.setup_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
case WLAN_TDLS_SETUP_CONFIRM:
@@ -2776,7 +2896,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
u16 status_code, struct sk_buff *skb)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_mgmt *mgmt;
mgmt = (void *)skb_put(skb, 24);
@@ -2799,10 +2919,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
mgmt->u.action.u.tdls_discover_resp.capability =
cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
- ieee80211_add_srates_ie(sdata, skb, false,
- local->oper_channel->band);
- ieee80211_add_ext_srates_ie(sdata, skb, false,
- local->oper_channel->band);
+ ieee80211_add_srates_ie(sdata, skb, false, band);
+ ieee80211_add_ext_srates_ie(sdata, skb, false, band);
ieee80211_tdls_add_ext_capab(skb);
break;
default:
@@ -2819,7 +2937,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct ieee80211_tx_info *info;
struct sk_buff *skb = NULL;
bool send_direct;
int ret;
@@ -2845,7 +2962,6 @@ static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
if (!skb)
return -ENOMEM;
- info = IEEE80211_SKB_CB(skb);
skb_reserve(skb, local->hw.extra_tx_headroom);
switch (action_code) {
@@ -2982,12 +3098,19 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
bool qos;
struct ieee80211_tx_info *info;
struct sta_info *sta;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_band band;
rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ band = chanctx_conf->def.chan->band;
sta = sta_info_get(sdata, peer);
if (sta) {
qos = test_sta_flag(sta, WLAN_STA_WME);
- rcu_read_unlock();
} else {
rcu_read_unlock();
return -ENOLINK;
@@ -3005,8 +3128,10 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
}
skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
- if (!skb)
+ if (!skb) {
+ rcu_read_unlock();
return -ENOMEM;
+ }
skb->dev = dev;
@@ -3031,21 +3156,31 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
+ rcu_read_unlock();
*cookie = (unsigned long) skb;
return 0;
}
-static struct ieee80211_channel *
-ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev,
- enum nl80211_channel_type *type)
+static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int ret = -ENODATA;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (chanctx_conf) {
+ *chandef = chanctx_conf->def;
+ ret = 0;
+ }
+ rcu_read_unlock();
- *type = local->_oper_channel_type;
- return local->oper_channel;
+ return ret;
}
#ifdef CONFIG_PM
@@ -3100,6 +3235,7 @@ struct cfg80211_ops mac80211_config_ops = {
.disassoc = ieee80211_disassoc,
.join_ibss = ieee80211_join_ibss,
.leave_ibss = ieee80211_leave_ibss,
+ .set_mcast_rate = ieee80211_set_mcast_rate,
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 0bfc914ddd1..80e55527504 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -3,168 +3,385 @@
*/
#include <linux/nl80211.h>
+#include <linux/export.h>
+#include <linux/rtnetlink.h>
#include <net/cfg80211.h>
#include "ieee80211_i.h"
+#include "driver-ops.h"
-static enum ieee80211_chan_mode
-__ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
+static void ieee80211_change_chandef(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ const struct cfg80211_chan_def *chandef)
{
+ if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
+ return;
+
+ WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
+
+ ctx->conf.def = *chandef;
+ drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = cfg80211_get_chandef_type(chandef);
+ ieee80211_hw_config(local, 0);
+ }
+}
+
+static struct ieee80211_chanctx *
+ieee80211_find_chanctx(struct ieee80211_local *local,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
+{
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
+ return NULL;
+
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+ const struct cfg80211_chan_def *compat;
+
+ if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
+ continue;
+
+ compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
+ if (!compat)
+ continue;
+
+ ieee80211_change_chandef(local, ctx, compat);
+
+ return ctx;
+ }
+
+ return NULL;
+}
+
+static struct ieee80211_chanctx *
+ieee80211_new_chanctx(struct ieee80211_local *local,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
+{
+ struct ieee80211_chanctx *ctx;
+ int err;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
+ if (!ctx)
+ return ERR_PTR(-ENOMEM);
+
+ ctx->conf.def = *chandef;
+ ctx->conf.rx_chains_static = 1;
+ ctx->conf.rx_chains_dynamic = 1;
+ ctx->mode = mode;
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type =
+ cfg80211_get_chandef_type(chandef);
+ local->_oper_channel = chandef->chan;
+ ieee80211_hw_config(local, 0);
+ } else {
+ err = drv_add_chanctx(local, ctx);
+ if (err) {
+ kfree(ctx);
+ return ERR_PTR(err);
+ }
+ }
+
+ list_add_rcu(&ctx->list, &local->chanctx_list);
+
+ return ctx;
+}
+
+static void ieee80211_free_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ WARN_ON_ONCE(ctx->refcount != 0);
+
+ if (!local->use_chanctx) {
+ local->_oper_channel_type = NL80211_CHAN_NO_HT;
+ ieee80211_hw_config(local, 0);
+ } else {
+ drv_remove_chanctx(local, ctx);
+ }
+
+ list_del_rcu(&ctx->list);
+ kfree_rcu(ctx, rcu_head);
+}
+
+static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ret = drv_assign_vif_chanctx(local, sdata, ctx);
+ if (ret)
+ return ret;
+
+ rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
+ ctx->refcount++;
+
+ ieee80211_recalc_txpower(sdata);
+
+ return 0;
+}
+
+static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_chanctx_conf *conf = &ctx->conf;
struct ieee80211_sub_if_data *sdata;
+ const struct cfg80211_chan_def *compat = NULL;
+
+ lockdep_assert_held(&local->chanctx_mtx);
- lockdep_assert_held(&local->iflist_mtx);
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (sdata == ignore)
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+ if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
continue;
+ if (!compat)
+ compat = &sdata->vif.bss_conf.chandef;
+
+ compat = cfg80211_chandef_compatible(
+ &sdata->vif.bss_conf.chandef, compat);
+ if (!compat)
+ break;
+ }
+ rcu_read_unlock();
+
+ if (WARN_ON_ONCE(!compat))
+ return;
+
+ ieee80211_change_chandef(local, ctx, compat);
+}
+
+static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ ctx->refcount--;
+ rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
+
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+
+ if (ctx->refcount > 0) {
+ ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ }
+}
+
+static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (!conf)
+ return;
+
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct ieee80211_sub_if_data *vlan;
+
+ /* for the VLAN list */
+ ASSERT_RTNL();
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ rcu_assign_pointer(vlan->vif.chanctx_conf, NULL);
+ }
+
+ ieee80211_unassign_vif_chanctx(sdata, ctx);
+ if (ctx->refcount == 0)
+ ieee80211_free_chanctx(local, ctx);
+}
+
+void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u8 rx_chains_static, rx_chains_dynamic;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ rx_chains_static = 1;
+ rx_chains_dynamic = 1;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ u8 needed_static, needed_dynamic;
+
if (!ieee80211_sdata_running(sdata))
continue;
+ if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
+ &chanctx->conf)
+ continue;
+
switch (sdata->vif.type) {
- case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_DEVICE:
continue;
case NL80211_IFTYPE_STATION:
if (!sdata->u.mgd.associated)
continue;
break;
- case NL80211_IFTYPE_ADHOC:
- if (!sdata->u.ibss.ssid_len)
- continue;
- if (!sdata->u.ibss.fixed_channel)
- return CHAN_MODE_HOPPING;
- break;
case NL80211_IFTYPE_AP_VLAN:
- /* will also have _AP interface */
continue;
case NL80211_IFTYPE_AP:
- if (!sdata->u.ap.beacon)
- continue;
- break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
- if (!sdata->wdev.mesh_id_len)
- continue;
break;
default:
+ WARN_ON_ONCE(1);
+ }
+
+ switch (sdata->smps_mode) {
+ default:
+ WARN_ONCE(1, "Invalid SMPS mode %d\n",
+ sdata->smps_mode);
+ /* fall through */
+ case IEEE80211_SMPS_OFF:
+ needed_static = sdata->needed_rx_chains;
+ needed_dynamic = sdata->needed_rx_chains;
+ break;
+ case IEEE80211_SMPS_DYNAMIC:
+ needed_static = 1;
+ needed_dynamic = sdata->needed_rx_chains;
+ break;
+ case IEEE80211_SMPS_STATIC:
+ needed_static = 1;
+ needed_dynamic = 1;
break;
}
- return CHAN_MODE_FIXED;
+ rx_chains_static = max(rx_chains_static, needed_static);
+ rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
}
+ rcu_read_unlock();
- return CHAN_MODE_UNDEFINED;
+ if (!local->use_chanctx) {
+ if (rx_chains_static > 1)
+ local->smps_mode = IEEE80211_SMPS_OFF;
+ else if (rx_chains_dynamic > 1)
+ local->smps_mode = IEEE80211_SMPS_DYNAMIC;
+ else
+ local->smps_mode = IEEE80211_SMPS_STATIC;
+ ieee80211_hw_config(local, 0);
+ }
+
+ if (rx_chains_static == chanctx->conf.rx_chains_static &&
+ rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
+ return;
+
+ chanctx->conf.rx_chains_static = rx_chains_static;
+ chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
+ drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
}
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore)
+int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode)
{
- enum ieee80211_chan_mode mode;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx *ctx;
+ int ret;
- mutex_lock(&local->iflist_mtx);
- mode = __ieee80211_get_channel_mode(local, ignore);
- mutex_unlock(&local->iflist_mtx);
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
- return mode;
-}
+ mutex_lock(&local->chanctx_mtx);
+ __ieee80211_vif_release_channel(sdata);
-static enum nl80211_channel_type
-ieee80211_get_superchan(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
-{
- enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
- struct ieee80211_sub_if_data *tmp;
+ ctx = ieee80211_find_chanctx(local, chandef, mode);
+ if (!ctx)
+ ctx = ieee80211_new_chanctx(local, chandef, mode);
+ if (IS_ERR(ctx)) {
+ ret = PTR_ERR(ctx);
+ goto out;
+ }
- mutex_lock(&local->iflist_mtx);
- list_for_each_entry(tmp, &local->interfaces, list) {
- if (tmp == sdata)
- continue;
+ sdata->vif.bss_conf.chandef = *chandef;
- if (!ieee80211_sdata_running(tmp))
- continue;
+ ret = ieee80211_assign_vif_chanctx(sdata, ctx);
+ if (ret) {
+ /* if assign fails refcount stays the same */
+ if (ctx->refcount == 0)
+ ieee80211_free_chanctx(local, ctx);
+ goto out;
+ }
- switch (tmp->vif.bss_conf.channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
- if (superchan > tmp->vif.bss_conf.channel_type)
- break;
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct ieee80211_sub_if_data *vlan;
- superchan = tmp->vif.bss_conf.channel_type;
- break;
- case NL80211_CHAN_HT40PLUS:
- WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
- superchan = NL80211_CHAN_HT40PLUS;
- break;
- case NL80211_CHAN_HT40MINUS:
- WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
- superchan = NL80211_CHAN_HT40MINUS;
- break;
- }
+ /* for the VLAN list */
+ ASSERT_RTNL();
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ rcu_assign_pointer(vlan->vif.chanctx_conf, &ctx->conf);
}
- mutex_unlock(&local->iflist_mtx);
- return superchan;
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ out:
+ mutex_unlock(&local->chanctx_mtx);
+ return ret;
}
-static bool
-ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
- enum nl80211_channel_type chantype2,
- enum nl80211_channel_type *compat)
+void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
{
- /*
- * start out with chantype1 being the result,
- * overwriting later if needed
- */
- if (compat)
- *compat = chantype1;
-
- switch (chantype1) {
- case NL80211_CHAN_NO_HT:
- if (compat)
- *compat = chantype2;
- break;
- case NL80211_CHAN_HT20:
- /*
- * allow any change that doesn't go to no-HT
- * (if it already is no-HT no change is needed)
- */
- if (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (compat)
- *compat = chantype2;
- break;
- case NL80211_CHAN_HT40PLUS:
- case NL80211_CHAN_HT40MINUS:
- /* allow smaller bandwidth and same */
- if (chantype2 == NL80211_CHAN_NO_HT)
- break;
- if (chantype2 == NL80211_CHAN_HT20)
- break;
- if (chantype2 == chantype1)
- break;
- return false;
- }
+ WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
- return true;
+ mutex_lock(&sdata->local->chanctx_mtx);
+ __ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&sdata->local->chanctx_mtx);
}
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype)
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
{
- enum nl80211_channel_type superchan;
- enum nl80211_channel_type compatchan;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_sub_if_data *ap;
+ struct ieee80211_chanctx_conf *conf;
- superchan = ieee80211_get_superchan(local, sdata);
- if (!ieee80211_channel_types_are_compatible(superchan, chantype,
- &compatchan))
- return false;
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
+ return;
- local->_oper_channel_type = compatchan;
+ ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
- if (sdata)
- sdata->vif.bss_conf.channel_type = chantype;
+ mutex_lock(&local->chanctx_mtx);
- return true;
+ conf = rcu_dereference_protected(ap->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
+ mutex_unlock(&local->chanctx_mtx);
+}
+
+void ieee80211_iter_chan_contexts_atomic(
+ struct ieee80211_hw *hw,
+ void (*iter)(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *chanctx_conf,
+ void *data),
+ void *iter_data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_chanctx *ctx;
+ rcu_read_lock();
+ list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
+ iter(hw, &ctx->conf, iter_data);
+ rcu_read_unlock();
}
+EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);
diff --git a/net/mac80211/debugfs.h b/net/mac80211/debugfs.h
index 9be4e6d71d0..214ed4ecd73 100644
--- a/net/mac80211/debugfs.h
+++ b/net/mac80211/debugfs.h
@@ -2,9 +2,9 @@
#define __MAC80211_DEBUGFS_H
#ifdef CONFIG_MAC80211_DEBUGFS
-extern void debugfs_hw_add(struct ieee80211_local *local);
-extern int mac80211_format_buffer(char __user *userbuf, size_t count,
- loff_t *ppos, char *fmt, ...);
+void debugfs_hw_add(struct ieee80211_local *local);
+int __printf(4, 5) mac80211_format_buffer(char __user *userbuf, size_t count,
+ loff_t *ppos, char *fmt, ...);
#else
static inline void debugfs_hw_add(struct ieee80211_local *local)
{
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 090d08ff22c..c3a3082b72e 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -116,7 +116,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct ieee80211_key *key = file->private_data;
- char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf;
+ char buf[14*IEEE80211_NUM_TIDS+1], *p = buf;
int i, len;
const u8 *rpn;
@@ -126,7 +126,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = scnprintf(buf, sizeof(buf), "\n");
break;
case WLAN_CIPHER_SUITE_TKIP:
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p,
"%08x %04x\n",
key->u.tkip.rx[i].iv32,
@@ -134,7 +134,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf,
len = p - buf;
break;
case WLAN_CIPHER_SUITE_CCMP:
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
rpn = key->u.ccmp.rx_pn[i];
p += scnprintf(p, sizeof(buf)+buf-p,
"%02x%02x%02x%02x%02x%02x\n",
@@ -199,6 +199,22 @@ static ssize_t key_icverrors_read(struct file *file, char __user *userbuf,
}
KEY_OPS(icverrors);
+static ssize_t key_mic_failures_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_key *key = file->private_data;
+ char buf[20];
+ int len;
+
+ if (key->conf.cipher != WLAN_CIPHER_SUITE_TKIP)
+ return -EINVAL;
+
+ len = scnprintf(buf, sizeof(buf), "%u\n", key->u.tkip.mic_failures);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, len);
+}
+KEY_OPS(mic_failures);
+
static ssize_t key_key_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
@@ -260,6 +276,7 @@ void ieee80211_debugfs_key_add(struct ieee80211_key *key)
DEBUGFS_ADD(rx_spec);
DEBUGFS_ADD(replays);
DEBUGFS_ADD(icverrors);
+ DEBUGFS_ADD(mic_failures);
DEBUGFS_ADD(key);
DEBUGFS_ADD(ifindex);
};
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index 6d5aec9418e..cbde5cc49a4 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/if.h>
+#include <linux/if_ether.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
@@ -167,7 +168,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz,
IEEE80211_IF_FILE(flags, flags, HEX);
IEEE80211_IF_FILE(state, state, LHEX);
-IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC);
+IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC);
+IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC);
+IEEE80211_IF_FILE(user_power_level, user_power_level, DEC);
+
+static ssize_t
+ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata,
+ char *buf, int buflen)
+{
+ int len;
+
+ len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n",
+ sdata->vif.hw_queue[IEEE80211_AC_VO],
+ sdata->vif.hw_queue[IEEE80211_AC_VI],
+ sdata->vif.hw_queue[IEEE80211_AC_BE],
+ sdata->vif.hw_queue[IEEE80211_AC_BK]);
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ len += scnprintf(buf + len, buflen - len, "cab queue: %d\n",
+ sdata->vif.cab_queue);
+
+ return len;
+}
+__IEEE80211_IF_FILE(hw_queues, NULL);
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
@@ -217,7 +240,7 @@ static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata,
return snprintf(buf, buflen, "request: %s\nused: %s\n",
smps_modes[sdata->u.mgd.req_smps],
- smps_modes[sdata->u.mgd.ap_smps]);
+ smps_modes[sdata->smps_mode]);
}
static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata,
@@ -245,27 +268,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test(
return -EOPNOTSUPP;
}
-static int hwaddr_aton(const char *txt, u8 *addr)
-{
- int i;
-
- for (i = 0; i < ETH_ALEN; i++) {
- int a, b;
-
- a = hex_to_bin(*txt++);
- if (a < 0)
- return -1;
- b = hex_to_bin(*txt++);
- if (b < 0)
- return -1;
- *addr++ = (a << 4) | b;
- if (i < 5 && *txt++ != ':')
- return -1;
- }
-
- return 0;
-}
-
static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_sub_if_data *sdata, const char *buf, int buflen)
{
@@ -275,13 +277,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
struct ieee80211_hdr *hdr;
__le16 fc;
- /*
- * Assume colon-delimited MAC address with possible white space
- * following.
- */
- if (buflen < 3 * ETH_ALEN - 1)
- return -EINVAL;
- if (hwaddr_aton(buf, addr) < 0)
+ if (!mac_pton(buf, addr))
return -EINVAL;
if (!ieee80211_sdata_running(sdata))
@@ -307,13 +303,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test(
case NL80211_IFTYPE_STATION:
fc |= cpu_to_le16(IEEE80211_FCTL_TODS);
/* BSSID SA DA */
- if (sdata->vif.bss_conf.bssid == NULL) {
+ mutex_lock(&sdata->u.mgd.mtx);
+ if (!sdata->u.mgd.associated) {
+ mutex_unlock(&sdata->u.mgd.mtx);
dev_kfree_skb(skb);
return -ENOTCONN;
}
- memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN);
+ memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN);
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, addr, ETH_ALEN);
+ mutex_unlock(&sdata->u.mgd.mtx);
break;
default:
dev_kfree_skb(skb);
@@ -395,14 +394,14 @@ __IEEE80211_IF_FILE_W(uapsd_max_sp_len);
/* AP attributes */
IEEE80211_IF_FILE(num_mcast_sta, u.ap.num_mcast_sta, ATOMIC);
-IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
-IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC);
+IEEE80211_IF_FILE(num_sta_ps, u.ap.ps.num_sta_ps, ATOMIC);
+IEEE80211_IF_FILE(dtim_count, u.ap.ps.dtim_count, DEC);
static ssize_t ieee80211_if_fmt_num_buffered_multicast(
const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
{
return scnprintf(buf, buflen, "%u\n",
- skb_queue_len(&sdata->u.ap.ps_bc_buf));
+ skb_queue_len(&sdata->u.ap.ps.bc_buf));
}
__IEEE80211_IF_FILE(num_buffered_multicast, NULL);
@@ -443,7 +442,7 @@ static ssize_t ieee80211_if_parse_tsf(
}
ret = kstrtoull(buf, 10, &tsf);
if (ret < 0)
- return -EINVAL;
+ return ret;
if (tsf_is_delta)
tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf;
if (local->ops->set_tsf) {
@@ -471,7 +470,7 @@ IEEE80211_IF_FILE(dropped_frames_congestion,
u.mesh.mshstats.dropped_frames_congestion, DEC);
IEEE80211_IF_FILE(dropped_frames_no_route,
u.mesh.mshstats.dropped_frames_no_route, DEC);
-IEEE80211_IF_FILE(estab_plinks, u.mesh.mshstats.estab_plinks, ATOMIC);
+IEEE80211_IF_FILE(estab_plinks, u.mesh.estab_plinks, ATOMIC);
/* Mesh parameters */
IEEE80211_IF_FILE(dot11MeshMaxRetries,
@@ -531,6 +530,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(rc_rateidx_mask_5ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz);
DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz);
+ DEBUGFS_ADD(hw_queues);
}
static void add_sta_files(struct ieee80211_sub_if_data *sdata)
@@ -631,7 +631,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(flags);
DEBUGFS_ADD(state);
- DEBUGFS_ADD(channel_type);
+ DEBUGFS_ADD(txpower);
+ DEBUGFS_ADD(user_power_level);
+ DEBUGFS_ADD(ap_power_level);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
add_common_files(sdata);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 5ccec2c1e9f..6fb1168b9f1 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -14,6 +14,7 @@
#include "debugfs.h"
#include "debugfs_sta.h"
#include "sta_info.h"
+#include "driver-ops.h"
/* sta attributtes */
@@ -52,6 +53,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_FILE(aid, sta.aid, D);
STA_FILE(dev, sdata->name, S);
STA_FILE(last_signal, last_signal, D);
+STA_FILE(last_ack_signal, last_ack_signal, D);
static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
@@ -131,10 +133,10 @@ STA_OPS(connected_time);
static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[15*NUM_RX_DATA_QUEUES], *p = buf;
+ char buf[15*IEEE80211_NUM_TIDS], *p = buf;
int i;
struct sta_info *sta = file->private_data;
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
p += scnprintf(p, sizeof(buf)+buf-p, "%x ",
le16_to_cpu(sta->last_seq_ctrl[i]));
p += scnprintf(p, sizeof(buf)+buf-p, "\n");
@@ -145,7 +147,7 @@ STA_OPS(last_seq_ctrl);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[71 + STA_TID_NUM * 40], *p = buf;
+ char buf[71 + IEEE80211_NUM_TIDS * 40], *p = buf;
int i;
struct sta_info *sta = file->private_data;
struct tid_ampdu_rx *tid_rx;
@@ -158,7 +160,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
p += scnprintf(p, sizeof(buf) + buf - p,
"TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n");
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]);
tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]);
@@ -218,9 +220,11 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu
} else
return -EINVAL;
- tid = simple_strtoul(buf, NULL, 0);
+ ret = kstrtoul(buf, 0, &tid);
+ if (ret)
+ return ret;
- if (tid >= STA_TID_NUM)
+ if (tid >= IEEE80211_NUM_TIDS)
return -EINVAL;
if (tx) {
@@ -320,6 +324,38 @@ static ssize_t sta_ht_capa_read(struct file *file, char __user *userbuf,
}
STA_OPS(ht_capa);
+static ssize_t sta_current_tx_rate_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct rate_info rinfo;
+ u16 rate;
+ sta_set_rate_info_tx(sta, &sta->last_tx_rate, &rinfo);
+ rate = cfg80211_calculate_bitrate(&rinfo);
+
+ return mac80211_format_buffer(userbuf, count, ppos,
+ "%d.%d MBit/s\n",
+ rate/10, rate%10);
+}
+STA_OPS(current_tx_rate);
+
+static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct rate_info rinfo;
+ u16 rate;
+
+ sta_set_rate_info_rx(sta, &rinfo);
+
+ rate = cfg80211_calculate_bitrate(&rinfo);
+
+ return mac80211_format_buffer(userbuf, count, ppos,
+ "%d.%d MBit/s\n",
+ rate/10, rate%10);
+}
+STA_OPS(last_rx_rate);
+
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -334,6 +370,8 @@ STA_OPS(ht_capa);
void ieee80211_sta_debugfs_add(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations;
u8 mac[3*ETH_ALEN];
@@ -366,6 +404,9 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(dev);
DEBUGFS_ADD(last_signal);
DEBUGFS_ADD(ht_capa);
+ DEBUGFS_ADD(last_ack_signal);
+ DEBUGFS_ADD(current_tx_rate);
+ DEBUGFS_ADD(last_rx_rate);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
@@ -379,10 +420,16 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed);
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
+
+ drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
{
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
debugfs_remove_recursive(sta->debugfs.dir);
sta->debugfs.dir = NULL;
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index f28772443bd..698dc7e6f30 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -490,6 +490,38 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+#ifdef CONFIG_MAC80211_DEBUGFS
+static inline void drv_sta_add_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_add_debugfs)
+ local->ops->sta_add_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+
+static inline void drv_sta_remove_debugfs(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta,
+ struct dentry *dir)
+{
+ might_sleep();
+
+ sdata = get_bss_sdata(sdata);
+ check_sdata_in_driver(sdata);
+
+ if (local->ops->sta_remove_debugfs)
+ local->ops->sta_remove_debugfs(&local->hw, &sdata->vif,
+ sta, dir);
+}
+#endif
+
static inline __must_check
int drv_sta_state(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -704,17 +736,17 @@ static inline int drv_get_antenna(struct ieee80211_local *local,
}
static inline int drv_remain_on_channel(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype,
unsigned int duration)
{
int ret;
might_sleep();
- trace_drv_remain_on_channel(local, chan, chantype, duration);
- ret = local->ops->remain_on_channel(&local->hw, chan, chantype,
- duration);
+ trace_drv_remain_on_channel(local, sdata, chan, duration);
+ ret = local->ops->remain_on_channel(&local->hw, &sdata->vif,
+ chan, duration);
trace_drv_return_int(local, ret);
return ret;
@@ -871,4 +903,104 @@ static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
local->ops->mgd_prepare_tx(&local->hw, &sdata->vif);
trace_drv_return_void(local);
}
+
+static inline int drv_add_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ int ret = -EOPNOTSUPP;
+
+ trace_drv_add_chanctx(local, ctx);
+ if (local->ops->add_chanctx)
+ ret = local->ops->add_chanctx(&local->hw, &ctx->conf);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline void drv_remove_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ trace_drv_remove_chanctx(local, ctx);
+ if (local->ops->remove_chanctx)
+ local->ops->remove_chanctx(&local->hw, &ctx->conf);
+ trace_drv_return_void(local);
+}
+
+static inline void drv_change_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ u32 changed)
+{
+ trace_drv_change_chanctx(local, ctx, changed);
+ if (local->ops->change_chanctx)
+ local->ops->change_chanctx(&local->hw, &ctx->conf, changed);
+ trace_drv_return_void(local);
+}
+
+static inline int drv_assign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ int ret = 0;
+
+ check_sdata_in_driver(sdata);
+
+ trace_drv_assign_vif_chanctx(local, sdata, ctx);
+ if (local->ops->assign_vif_chanctx)
+ ret = local->ops->assign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ &ctx->conf);
+ trace_drv_return_int(local, ret);
+
+ return ret;
+}
+
+static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx)
+{
+ check_sdata_in_driver(sdata);
+
+ trace_drv_unassign_vif_chanctx(local, sdata, ctx);
+ if (local->ops->unassign_vif_chanctx)
+ local->ops->unassign_vif_chanctx(&local->hw,
+ &sdata->vif,
+ &ctx->conf);
+ trace_drv_return_void(local);
+}
+
+static inline int drv_start_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ int ret = 0;
+
+ check_sdata_in_driver(sdata);
+
+ trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf);
+ if (local->ops->start_ap)
+ ret = local->ops->start_ap(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void drv_stop_ap(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ check_sdata_in_driver(sdata);
+
+ trace_drv_stop_ap(local, sdata);
+ if (local->ops->stop_ap)
+ local->ops->stop_ap(&local->hw, &sdata->vif);
+ trace_drv_return_void(local);
+}
+
+static inline void drv_restart_complete(struct ieee80211_local *local)
+{
+ might_sleep();
+
+ trace_drv_restart_complete(local);
+ if (local->ops->restart_complete)
+ local->ops->restart_complete(&local->hw);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 4b4538d6392..a71d891794a 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -185,7 +185,7 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx)
cancel_work_sync(&sta->ampdu_mlme.work);
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
__ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx);
__ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS, tx);
@@ -209,7 +209,7 @@ void ieee80211_ba_session_work(struct work_struct *work)
return;
mutex_lock(&sta->ampdu_mlme.mtx);
- for (tid = 0; tid < STA_TID_NUM; tid++) {
+ for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired))
___ieee80211_stop_rx_ba_session(
sta, tid, WLAN_BACK_RECIPIENT,
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 5f3620f0bc0..6b7644e818d 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -26,7 +26,6 @@
#include "rate.h"
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
-#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
@@ -39,7 +38,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, const int beacon_int,
struct ieee80211_channel *chan,
const u32 basic_rates,
- const u16 capability, u64 tsf)
+ const u16 capability, u64 tsf,
+ bool creator)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
@@ -51,7 +51,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *bss;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
lockdep_assert_held(&ifibss->mtx);
@@ -72,25 +72,29 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
/* if merging, indicate to driver that we leave the old IBSS */
if (sdata->vif.bss_conf.ibss_joined) {
sdata->vif.bss_conf.ibss_joined = false;
+ sdata->vif.bss_conf.ibss_creator = false;
netif_carrier_off(sdata->dev);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS);
}
- memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- local->oper_channel = chan;
- channel_type = ifibss->channel_type;
- if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type))
- channel_type = NL80211_CHAN_HT20;
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- NL80211_CHAN_HT20));
+ cfg80211_chandef_create(&chandef, chan, ifibss->channel_type);
+ if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+ chandef.width = NL80211_CHAN_WIDTH_20;
+ chandef.center_freq1 = chan->center_freq;
+ }
+
+ ieee80211_vif_release_channel(sdata);
+ if (ieee80211_vif_use_channel(sdata, &chandef,
+ ifibss->fixed_channel ?
+ IEEE80211_CHANCTX_SHARED :
+ IEEE80211_CHANCTX_EXCLUSIVE)) {
+ sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+ return;
}
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
sband = local->hw.wiphy->bands[chan->band];
@@ -156,7 +160,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ifibss->ie, ifibss->ie_len);
/* add HT capability and information IEs */
- if (channel_type && sband->ht_cap.ht_supported) {
+ if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ sband->ht_cap.ht_supported) {
pos = skb_put(skb, 4 +
sizeof(struct ieee80211_ht_cap) +
sizeof(struct ieee80211_ht_operation));
@@ -168,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- chan, channel_type, 0);
+ &chandef, 0);
}
if (local->hw.queues >= IEEE80211_NUM_ACS) {
@@ -196,7 +201,22 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
bss_change |= BSS_CHANGED_BASIC_RATES;
bss_change |= BSS_CHANGED_HT;
bss_change |= BSS_CHANGED_IBSS;
+
+ /*
+ * In 5 GHz/802.11a, we can always use short slot time.
+ * (IEEE 802.11-2012 18.3.8.7)
+ *
+ * In 2.4GHz, we must always use long slots in IBSS for compatibility
+ * reasons.
+ * (IEEE 802.11-2012 19.4.5)
+ *
+ * HT follows these specifications (IEEE 802.11-2012 20.3.18)
+ */
+ sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
+ bss_change |= BSS_CHANGED_ERP_SLOT;
+
sdata->vif.bss_conf.ibss_joined = true;
+ sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change);
ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
@@ -249,7 +269,8 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
cbss->channel,
basic_rates,
cbss->capability,
- cbss->tsf);
+ cbss->tsf,
+ false);
}
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
@@ -279,7 +300,7 @@ static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta,
ibss_dbg(sdata,
"TX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=1)\n",
sdata->vif.addr, addr, sdata->u.ibss.bssid);
- ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, NULL, 0,
+ ieee80211_send_auth(sdata, 1, WLAN_AUTH_OPEN, 0, NULL, 0,
addr, sdata->u.ibss.bssid, NULL, 0, 0);
}
return sta;
@@ -294,7 +315,8 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -317,6 +339,13 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
return NULL;
}
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf))
+ return NULL;
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
if (!sta) {
rcu_read_lock();
@@ -362,11 +391,13 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
- if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
- return;
ibss_dbg(sdata,
"RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n",
mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction);
+
+ if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1)
+ return;
+
sta_info_destroy_addr(sdata, mgmt->sa);
sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false);
rcu_read_unlock();
@@ -389,7 +420,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
* However, try to reply to authentication attempts if someone
* has actually implemented this.
*/
- ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
+ ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, 0, NULL, 0,
mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0);
}
@@ -461,9 +492,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) {
/* we both use HT */
struct ieee80211_sta_ht_cap sta_ht_cap_new;
- enum nl80211_channel_type channel_type =
- ieee80211_ht_oper_to_channel_type(
- elems->ht_operation);
+ struct cfg80211_chan_def chandef;
+
+ ieee80211_ht_oper_to_chandef(channel,
+ elems->ht_operation,
+ &chandef);
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
@@ -473,9 +506,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
* fall back to HT20 if we don't use or use
* the other extension channel
*/
- if (!(channel_type == NL80211_CHAN_HT40MINUS ||
- channel_type == NL80211_CHAN_HT40PLUS) ||
- channel_type != sdata->u.ibss.channel_type)
+ if (chandef.width != NL80211_CHAN_WIDTH_40 ||
+ cfg80211_get_chandef_type(&chandef) !=
+ sdata->u.ibss.channel_type)
sta_ht_cap_new.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
@@ -517,7 +550,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
goto put_bss;
/* different channel */
- if (cbss->channel != local->oper_channel)
+ if (sdata->u.ibss.fixed_channel &&
+ sdata->u.ibss.channel != cbss->channel)
goto put_bss;
/* different SSID */
@@ -530,30 +564,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
goto put_bss;
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU) {
- /*
- * For correct IBSS merging we need mactime; since mactime is
- * defined as the time the first data symbol of the frame hits
- * the PHY, and the timestamp of the beacon is defined as "the
- * time that the data symbol containing the first bit of the
- * timestamp is transmitted to the PHY plus the transmitting
- * STA's delays through its local PHY from the MAC-PHY
- * interface to its interface with the WM" (802.11 11.1.2)
- * - equals the time this bit arrives at the receiver - we have
- * to take into account the offset between the two.
- *
- * E.g. at 1 MBit that means mactime is 192 usec earlier
- * (=24 bytes * 8 usecs/byte) than the beacon timestamp.
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT)
- rate = 65; /* TODO: HT rates */
- else
- rate = local->hw.wiphy->bands[band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate);
+ if (ieee80211_have_rx_timestamp(rx_status)) {
+ /* time when timestamp field was received */
+ rx_timestamp =
+ ieee80211_calculate_rx_timestamp(local, rx_status,
+ len + FCS_LEN, 24);
} else {
/*
* second best option: get current TSF
@@ -592,7 +607,8 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
- int band = local->oper_channel->band;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int band;
/*
* XXX: Consider removing the least recently used entry and
@@ -610,6 +626,15 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
if (!ether_addr_equal(bssid, sdata->u.ibss.bssid))
return;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
if (!sta)
return;
@@ -678,8 +703,8 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
sdata_info(sdata,
"No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
- ieee80211_request_internal_scan(sdata,
- ifibss->ssid, ifibss->ssid_len, NULL);
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
+ NULL);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
@@ -715,7 +740,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
__ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int,
ifibss->channel, ifibss->basic_rates,
- capability, 0);
+ capability, 0, true);
}
/*
@@ -777,25 +802,14 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
IEEE80211_SCAN_INTERVAL)) {
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
- ieee80211_request_internal_scan(sdata,
- ifibss->ssid, ifibss->ssid_len,
- ifibss->fixed_channel ? ifibss->channel : NULL);
+ ieee80211_request_ibss_scan(sdata, ifibss->ssid,
+ ifibss->ssid_len, chan);
} else {
int interval = IEEE80211_SCAN_INTERVAL;
if (time_after(jiffies, ifibss->ibss_join_req +
- IEEE80211_IBSS_JOIN_TIMEOUT)) {
- if (!(local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS)) {
- ieee80211_sta_create_ibss(sdata);
- return;
- }
- sdata_info(sdata, "IBSS not allowed on %d MHz\n",
- local->oper_channel->center_freq);
-
- /* No IBSS found - decrease scan interval and continue
- * scanning. */
- interval = IEEE80211_SCAN_INTERVAL_SLOW;
- }
+ IEEE80211_IBSS_JOIN_TIMEOUT))
+ ieee80211_sta_create_ibss(sdata);
mod_timer(&ifibss->timer,
round_jiffies(jiffies + interval));
@@ -1082,21 +1096,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
- sdata->u.ibss.channel = params->channel;
- sdata->u.ibss.channel_type = params->channel_type;
+ sdata->u.ibss.channel = params->chandef.chan;
+ sdata->u.ibss.channel_type =
+ cfg80211_get_chandef_type(&params->chandef);
sdata->u.ibss.fixed_channel = params->channel_fixed;
- /* fix ourselves to that channel now already */
- if (params->channel_fixed) {
- sdata->local->oper_channel = params->channel;
- if (!ieee80211_set_channel_type(sdata->local, sdata,
- params->channel_type)) {
- mutex_unlock(&sdata->u.ibss.mtx);
- kfree_skb(skb);
- return -EINVAL;
- }
- }
-
if (params->ie) {
sdata->u.ibss.ie = kmemdup(params->ie, params->ie_len,
GFP_KERNEL);
@@ -1108,7 +1112,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
sdata->u.ibss.ibss_join_req = jiffies;
- memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
+ memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
sdata->u.ibss.ssid_len = params->ssid_len;
mutex_unlock(&sdata->u.ibss.mtx);
@@ -1134,6 +1138,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
return 0;
@@ -1151,10 +1158,6 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
mutex_lock(&sdata->u.ibss.mtx);
- sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
- memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
- sdata->u.ibss.ssid_len = 0;
-
active_ibss = ieee80211_sta_active_ibss(sdata);
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
@@ -1175,6 +1178,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
}
}
+ ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
+ memset(ifibss->bssid, 0, ETH_ALEN);
+ ifibss->ssid_len = 0;
+
sta_info_flush(sdata->local, sdata);
spin_lock_bh(&ifibss->incomplete_lock);
@@ -1197,6 +1204,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
lockdep_is_held(&sdata->u.ibss.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
sdata->vif.bss_conf.ibss_joined = false;
+ sdata->vif.bss_conf.ibss_creator = false;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
synchronize_rcu();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 8c804550465..2ed065c0956 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -56,6 +56,9 @@ struct ieee80211_local;
#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+/* power level hasn't been configured (or set to automatic) */
+#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
+
/*
* Some APs experience problems when working with U-APSD. Decrease the
* probability of that happening by using legacy mode for all ACs but VO.
@@ -89,8 +92,6 @@ struct ieee80211_bss {
u32 device_ts;
- u8 dtim_period;
-
bool wmm_used;
bool uapsd_supported;
@@ -137,7 +138,6 @@ enum ieee80211_bss_corrupt_data_flags {
/**
* enum ieee80211_valid_data_flags - BSS valid data flags
- * @IEEE80211_BSS_VALID_DTIM: DTIM data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_WMM: WMM/UAPSD data was gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_RATES: Supported rates were gathered from non-corrupt IE
* @IEEE80211_BSS_VALID_ERP: ERP flag was gathered from non-corrupt IE
@@ -148,7 +148,6 @@ enum ieee80211_bss_corrupt_data_flags {
* beacon/probe response.
*/
enum ieee80211_bss_valid_data_flags {
- IEEE80211_BSS_VALID_DTIM = BIT(0),
IEEE80211_BSS_VALID_WMM = BIT(1),
IEEE80211_BSS_VALID_RATES = BIT(2),
IEEE80211_BSS_VALID_ERP = BIT(3)
@@ -280,23 +279,27 @@ struct probe_resp {
u8 data[0];
};
-struct ieee80211_if_ap {
- struct beacon_data __rcu *beacon;
- struct probe_resp __rcu *probe_resp;
-
- struct list_head vlans;
-
+struct ps_data {
/* yes, this looks ugly, but guarantees that we can later use
* bitmap_empty :)
* NB: don't touch this bitmap, use sta_info_{set,clear}_tim_bit */
u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)];
- struct sk_buff_head ps_bc_buf;
+ struct sk_buff_head bc_buf;
atomic_t num_sta_ps; /* number of stations in PS mode */
- atomic_t num_mcast_sta; /* number of stations receiving multicast */
int dtim_count;
bool dtim_bc_mc;
};
+struct ieee80211_if_ap {
+ struct beacon_data __rcu *beacon;
+ struct probe_resp __rcu *probe_resp;
+
+ struct list_head vlans;
+
+ struct ps_data ps;
+ atomic_t num_mcast_sta; /* number of stations receiving multicast */
+};
+
struct ieee80211_if_wds {
struct sta_info *sta;
u8 remote_addr[ETH_ALEN];
@@ -316,7 +319,6 @@ struct mesh_stats {
__u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
__u32 dropped_frames_no_route; /* Not transmitted, no route found */
__u32 dropped_frames_congestion;/* Not forwarded due to congestion */
- atomic_t estab_plinks;
};
#define PREQ_Q_F_START 0x1
@@ -342,7 +344,6 @@ struct ieee80211_roc_work {
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
- enum nl80211_channel_type chan_type;
bool started, abort, hw_begun, notified;
@@ -350,7 +351,7 @@ struct ieee80211_roc_work {
u32 duration, req_duration;
struct sk_buff *frame;
- u64 mgmt_tx_cookie;
+ u64 cookie, mgmt_tx_cookie;
};
/* flags used in struct ieee80211_if_managed.flags */
@@ -358,7 +359,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_BEACON_POLL = BIT(0),
IEEE80211_STA_CONNECTION_POLL = BIT(1),
IEEE80211_STA_CONTROL_PORT = BIT(2),
- IEEE80211_STA_DISABLE_11N = BIT(4),
+ IEEE80211_STA_DISABLE_HT = BIT(4),
IEEE80211_STA_CSA_RECEIVED = BIT(5),
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
@@ -366,6 +367,8 @@ enum ieee80211_sta_flags {
IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
IEEE80211_STA_DISABLE_40MHZ = BIT(10),
IEEE80211_STA_DISABLE_VHT = BIT(11),
+ IEEE80211_STA_DISABLE_80P80MHZ = BIT(12),
+ IEEE80211_STA_DISABLE_160MHZ = BIT(13),
};
struct ieee80211_mgd_auth_data {
@@ -378,8 +381,9 @@ struct ieee80211_mgd_auth_data {
u8 key_len, key_idx;
bool done;
- size_t ie_len;
- u8 ie[];
+ u16 sae_trans, sae_status;
+ size_t data_len;
+ u8 data[];
};
struct ieee80211_mgd_assoc_data {
@@ -432,8 +436,8 @@ struct ieee80211_if_managed {
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
bool broken_ap; /* AP is broken -- turn off powersave */
+ u8 dtim_period;
enum ieee80211_smps_mode req_smps, /* requested smps mode */
- ap_smps, /* smps mode AP thinks we're in */
driver_smps_mode; /* smps mode request */
struct work_struct request_smps_work;
@@ -467,6 +471,8 @@ struct ieee80211_if_managed {
u8 use_4addr;
+ u8 p2p_noa_index;
+
/* Signal strength from the last Beacon frame in the current BSS. */
int last_beacon_signal;
@@ -599,6 +605,7 @@ struct ieee80211_if_mesh {
int preq_queue_len;
struct mesh_stats mshstats;
struct mesh_config mshcfg;
+ atomic_t estab_plinks;
u32 mesh_seqnum;
bool accepting_plinks;
int num_gates;
@@ -610,7 +617,7 @@ struct ieee80211_if_mesh {
IEEE80211_MESH_SEC_SECURED = 0x2,
} security;
/* Extensible Synchronization Framework */
- struct ieee80211_mesh_sync_ops *sync_ops;
+ const struct ieee80211_mesh_sync_ops *sync_ops;
s64 sync_offset_clockdrift_max;
spinlock_t sync_offset_lock;
bool adjusting_tbtt;
@@ -658,6 +665,30 @@ enum ieee80211_sdata_state_bits {
SDATA_STATE_OFFCHANNEL,
};
+/**
+ * enum ieee80211_chanctx_mode - channel context configuration mode
+ *
+ * @IEEE80211_CHANCTX_SHARED: channel context may be used by
+ * multiple interfaces
+ * @IEEE80211_CHANCTX_EXCLUSIVE: channel context can be used
+ * only by a single interface. This can be used for example for
+ * non-fixed channel IBSS.
+ */
+enum ieee80211_chanctx_mode {
+ IEEE80211_CHANCTX_SHARED,
+ IEEE80211_CHANCTX_EXCLUSIVE
+};
+
+struct ieee80211_chanctx {
+ struct list_head list;
+ struct rcu_head rcu_head;
+
+ enum ieee80211_chanctx_mode mode;
+ int refcount;
+
+ struct ieee80211_chanctx_conf conf;
+};
+
struct ieee80211_sub_if_data {
struct list_head list;
@@ -704,11 +735,20 @@ struct ieee80211_sub_if_data {
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+ /* used to reconfigure hardware SM PS */
+ struct work_struct recalc_smps;
+
struct work_struct work;
struct sk_buff_head skb_queue;
bool arp_filter_state;
+ u8 needed_rx_chains;
+ enum ieee80211_smps_mode smps_mode;
+
+ int user_power_level; /* in dBm */
+ int ap_power_level; /* in dBm */
+
/*
* AP this belongs to: self in AP mode and
* corresponding AP in VLAN mode, NULL for
@@ -730,6 +770,10 @@ struct ieee80211_sub_if_data {
u32 mntr_flags;
} u;
+ spinlock_t cleanup_stations_lock;
+ struct list_head cleanup_stations;
+ struct work_struct cleanup_stations_wk;
+
#ifdef CONFIG_MAC80211_DEBUGFS
struct {
struct dentry *dir;
@@ -749,6 +793,21 @@ struct ieee80211_sub_if_data *vif_to_sdata(struct ieee80211_vif *p)
return container_of(p, struct ieee80211_sub_if_data, vif);
}
+static inline enum ieee80211_band
+ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata)
+{
+ enum ieee80211_band band = IEEE80211_BAND_2GHZ;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!WARN_ON(!chanctx_conf))
+ band = chanctx_conf->def.chan->band;
+ rcu_read_unlock();
+
+ return band;
+}
+
enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1,
@@ -821,6 +880,7 @@ enum {
* @SCAN_SUSPEND: Suspend the scan and go back to operating channel to
* send out data
* @SCAN_RESUME: Resume the scan and scan the next channel
+ * @SCAN_ABORT: Abort the scan and go back to operating channel
*/
enum mac80211_scan_state {
SCAN_DECISION,
@@ -828,6 +888,7 @@ enum mac80211_scan_state {
SCAN_SEND_PROBE,
SCAN_SUSPEND,
SCAN_RESUME,
+ SCAN_ABORT,
};
struct ieee80211_local {
@@ -858,15 +919,14 @@ struct ieee80211_local {
bool wiphy_ciphers_allocated;
+ bool use_chanctx;
+
/* protects the aggregated multicast list and filter calls */
spinlock_t filter_lock;
/* used for uploading changed mc list */
struct work_struct reconfig_filter;
- /* used to reconfigure hardware SM PS */
- struct work_struct recalc_smps;
-
/* aggregated multicast list */
struct netdev_hw_addr_list mc_list;
@@ -903,6 +963,9 @@ struct ieee80211_local {
/* wowlan is enabled -- don't reconfig on resume */
bool wowlan;
+ /* number of RX chains the hardware has */
+ u8 rx_chains;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -972,6 +1035,7 @@ struct ieee80211_local {
enum ieee80211_band hw_scan_band;
int scan_channel_idx;
int scan_ies_len;
+ int hw_scan_ies_bufsize;
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
@@ -980,12 +1044,17 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data __rcu *scan_sdata;
+ struct ieee80211_channel *csa_channel;
+ /* For backward compatibility only -- do not use */
+ struct ieee80211_channel *_oper_channel;
enum nl80211_channel_type _oper_channel_type;
- struct ieee80211_channel *oper_channel, *csa_channel;
/* Temporary remain-on-channel for off-channel operations */
struct ieee80211_channel *tmp_channel;
- enum nl80211_channel_type tmp_channel_type;
+
+ /* channel contexts */
+ struct list_head chanctx_list;
+ struct mutex chanctx_mtx;
/* SNMP counters */
/* dot11CountersTable */
@@ -1058,8 +1127,7 @@ struct ieee80211_local {
int dynamic_ps_user_timeout;
bool disable_dynamic_ps;
- int user_power_level; /* in dBm */
- int ap_power_level; /* in dBm */
+ int user_power_level; /* in dBm, for all interfaces */
enum ieee80211_smps_mode smps_mode;
@@ -1078,6 +1146,7 @@ struct ieee80211_local {
struct list_head roc_list;
struct work_struct hw_roc_start, hw_roc_done;
unsigned long hw_roc_start_time;
+ u64 roc_cookie_counter;
struct idr ack_status_frames;
spinlock_t ack_status_lock;
@@ -1091,6 +1160,7 @@ struct ieee80211_local {
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
+ struct cfg80211_chan_def monitor_chandef;
};
static inline struct ieee80211_sub_if_data *
@@ -1133,6 +1203,8 @@ struct ieee802_11_elems {
u8 *wmm_param;
struct ieee80211_ht_cap *ht_cap_elem;
struct ieee80211_ht_operation *ht_operation;
+ struct ieee80211_vht_cap *vht_cap_elem;
+ struct ieee80211_vht_operation *vht_operation;
struct ieee80211_meshconf_ie *mesh_config;
u8 *mesh_id;
u8 *peering;
@@ -1188,7 +1260,18 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr)
is_broadcast_ether_addr(raddr);
}
+static inline bool
+ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status)
+{
+ WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START &&
+ status->flag & RX_FLAG_MACTIME_END);
+ return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END);
+}
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset);
int ieee80211_hw_config(struct ieee80211_local *local, u32 changed);
void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx);
void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
@@ -1247,9 +1330,9 @@ void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
/* scan/BSS handling */
void ieee80211_scan_work(struct work_struct *work);
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
- const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan);
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+ const u8 *ssid, u8 ssid_len,
+ struct ieee80211_channel *chan);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
void ieee80211_scan_cancel(struct ieee80211_local *local);
@@ -1275,10 +1358,8 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata);
void ieee80211_sched_scan_stopped_work(struct work_struct *work);
/* off-channel helpers */
-void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
- bool offchannel_ps_enable);
-void ieee80211_offchannel_return(struct ieee80211_local *local,
- bool offchannel_ps_disable);
+void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local);
+void ieee80211_offchannel_return(struct ieee80211_local *local);
void ieee80211_roc_setup(struct ieee80211_local *local);
void ieee80211_start_next_roc(struct ieee80211_local *local);
void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata);
@@ -1302,6 +1383,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up);
void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata);
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
return test_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -1314,6 +1398,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev);
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
struct net_device *dev);
+void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+ struct sk_buff_head *skbs);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
@@ -1359,6 +1445,13 @@ void ieee80211_ba_session_work(struct work_struct *work);
void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid);
void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid);
+u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
+
+/* VHT */
+void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_vht_cap *vht_cap_ie,
+ struct ieee80211_sta_vht_cap *vht_cap);
/* Spectrum management */
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
@@ -1393,11 +1486,42 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band);
+
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band);
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid);
-static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
+static inline void
+ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
+{
+ rcu_read_lock();
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid, band);
+ rcu_read_unlock();
+}
+
+static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ __ieee80211_tx_skb_tid_band(sdata, skb, tid,
+ chanctx_conf->def.chan->band);
+ rcu_read_unlock();
+}
+
+static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
@@ -1444,14 +1568,14 @@ static inline void ieee80211_add_pending_skbs(struct ieee80211_local *local,
}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
- u16 transaction, u16 auth_alg,
+ u16 transaction, u16 auth_alg, u16 status,
u8 *extra, size_t extra_len, const u8 *bssid,
const u8 *da, const u8 *key, u8 key_len, u8 key_idx);
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason,
bool send_frame, u8 *frame_buf);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len,
+ size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
@@ -1464,7 +1588,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, bool no_cck,
- struct ieee80211_channel *channel);
+ struct ieee80211_channel *channel, bool scan);
void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
@@ -1474,7 +1598,7 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
enum ieee80211_band band, u32 *basic_rates);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode);
-void ieee80211_recalc_smps(struct ieee80211_local *local);
+void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
const u8 *ids, int n_ids, size_t offset);
@@ -1482,8 +1606,7 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
@@ -1495,20 +1618,19 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
enum ieee80211_band band);
/* channel management */
-enum ieee80211_chan_mode {
- CHAN_MODE_UNDEFINED,
- CHAN_MODE_HOPPING,
- CHAN_MODE_FIXED,
-};
-
-enum ieee80211_chan_mode
-ieee80211_get_channel_mode(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *ignore);
-bool ieee80211_set_channel_type(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata,
- enum nl80211_channel_type chantype);
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper);
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef);
+
+int __must_check
+ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ enum ieee80211_chanctx_mode mode);
+void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
+void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
+
+void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
+ struct ieee80211_chanctx *chanctx);
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 7de7717ad67..8be854e86cd 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -42,6 +42,41 @@
* by either the RTNL, the iflist_mtx or RCU.
*/
+bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int power;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return false;
+ }
+
+ power = chanctx_conf->def.chan->max_power;
+ rcu_read_unlock();
+
+ if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->user_power_level);
+
+ if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL)
+ power = min(power, sdata->ap_power_level);
+
+ if (power != sdata->vif.bss_conf.txpower) {
+ sdata->vif.bss_conf.txpower = power;
+ ieee80211_hw_config(sdata->local, 0);
+ return true;
+ }
+
+ return false;
+}
+
+void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_recalc_txpower(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER);
+}
static u32 ieee80211_idle_off(struct ieee80211_local *local,
const char *reason)
@@ -172,22 +207,54 @@ void ieee80211_recalc_idle(struct ieee80211_local *local)
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
- int meshhdrlen;
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- meshhdrlen = (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) ? 5 : 0;
-
- /* FIX: what would be proper limits for MTU?
- * This interface uses 802.3 frames. */
- if (new_mtu < 256 ||
- new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
+ if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN)
return -EINVAL;
- }
dev->mtu = new_mtu;
return 0;
}
+static int ieee80211_verify_mac(struct ieee80211_local *local, u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u64 new, mask, tmp;
+ u8 *m;
+ int ret = 0;
+
+ if (is_zero_ether_addr(local->hw.wiphy->addr_mask))
+ return 0;
+
+ m = addr;
+ new = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+ m = local->hw.wiphy->addr_mask;
+ mask = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (sdata->vif.type == NL80211_IFTYPE_MONITOR)
+ continue;
+
+ m = sdata->vif.addr;
+ tmp = ((u64)m[0] << 5*8) | ((u64)m[1] << 4*8) |
+ ((u64)m[2] << 3*8) | ((u64)m[3] << 2*8) |
+ ((u64)m[4] << 1*8) | ((u64)m[5] << 0*8);
+
+ if ((new & ~mask) != (tmp & ~mask)) {
+ ret = -EINVAL;
+ break;
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ return ret;
+}
+
static int ieee80211_change_mac(struct net_device *dev, void *addr)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -197,6 +264,10 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
if (ieee80211_sdata_running(sdata))
return -EBUSY;
+ ret = ieee80211_verify_mac(sdata->local, sa->sa_data);
+ if (ret)
+ return ret;
+
ret = eth_mac_addr(dev, sa);
if (ret == 0)
@@ -380,6 +451,14 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
goto out_unlock;
}
+ ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef,
+ IEEE80211_CHANCTX_EXCLUSIVE);
+ if (ret) {
+ drv_remove_interface(local, sdata);
+ kfree(sdata);
+ goto out_unlock;
+ }
+
rcu_assign_pointer(local->monitor_sdata, sdata);
out_unlock:
mutex_unlock(&local->iflist_mtx);
@@ -403,6 +482,8 @@ static void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
rcu_assign_pointer(local->monitor_sdata, NULL);
synchronize_net();
+ ieee80211_vif_release_channel(sdata);
+
drv_remove_interface(local, sdata);
kfree(sdata);
@@ -496,11 +577,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- /* no need to tell driver, but set carrier */
- if (rtnl_dereference(sdata->bss->beacon))
+ /* no need to tell driver, but set carrier and chanctx */
+ if (rtnl_dereference(sdata->bss->beacon)) {
+ ieee80211_vif_vlan_copy_chanctx(sdata);
netif_carrier_on(dev);
- else
+ } else {
netif_carrier_off(dev);
+ }
break;
case NL80211_IFTYPE_MONITOR:
if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
@@ -665,7 +748,6 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
int i;
- enum nl80211_channel_type orig_ct;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -729,34 +811,17 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
+ cancel_work_sync(&sdata->recalc_smps);
+
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
struct ieee80211_sub_if_data *vlan, *tmpsdata;
- struct beacon_data *old_beacon =
- rtnl_dereference(sdata->u.ap.beacon);
- struct probe_resp *old_probe_resp =
- rtnl_dereference(sdata->u.ap.probe_resp);
-
- /* sdata_running will return false, so this will disable */
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_BEACON_ENABLED);
-
- /* remove beacon and probe response */
- RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
- RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL);
- synchronize_rcu();
- kfree(old_beacon);
- kfree(old_probe_resp);
/* down all dependent devices, that is VLANs */
list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
u.vlan.list)
dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans));
-
- /* free all potentially still buffered bcast frames */
- local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf);
- skb_queue_purge(&sdata->u.ap.ps_bc_buf);
} else if (sdata->vif.type == NL80211_IFTYPE_STATION) {
ieee80211_mgd_stop(sdata);
}
@@ -767,6 +832,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
list_del(&sdata->u.vlan.list);
+ rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
@@ -790,23 +856,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
rcu_assign_pointer(local->p2p_sdata, NULL);
/* fall through */
default:
- flush_work(&sdata->work);
+ cancel_work_sync(&sdata->work);
/*
* When we get here, the interface is marked down.
- * Call rcu_barrier() to wait both for the RX path
+ * Call synchronize_rcu() to wait for the RX path
* should it be using the interface and enqueuing
- * frames at this very time on another CPU, and
- * for the sta free call_rcu callbacks.
- */
- rcu_barrier();
-
- /*
- * free_sta_rcu() enqueues a work for the actual
- * sta cleanup, so we need to flush it while
- * sdata is still valid.
+ * frames at this very time on another CPU.
*/
- flush_workqueue(local->workqueue);
-
+ synchronize_rcu();
skb_queue_purge(&sdata->skb_queue);
/*
@@ -837,14 +894,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
hw_reconf_flags = 0;
}
- /* Re-calculate channel-type, in case there are multiple vifs
- * on different channel types.
- */
- orig_ct = local->_oper_channel_type;
- ieee80211_set_channel_type(local, NULL, NL80211_CHAN_NO_HT);
-
/* do after stop to avoid reconfiguring when we stop anyway */
- if (hw_reconf_flags || (orig_ct != local->_oper_channel_type))
+ if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_flags);
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
@@ -1121,6 +1172,13 @@ static void ieee80211_iface_work(struct work_struct *work)
}
}
+static void ieee80211_recalc_smps_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, recalc_smps);
+
+ ieee80211_recalc_smps(sdata);
+}
/*
* Helper function to initialise an interface to a specific type.
@@ -1149,6 +1207,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
+ INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
@@ -1157,7 +1216,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.p2p = true;
/* fall through */
case NL80211_IFTYPE_AP:
- skb_queue_head_init(&sdata->u.ap.ps_bc_buf);
+ skb_queue_head_init(&sdata->u.ap.ps.bc_buf);
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
case NL80211_IFTYPE_P2P_CLIENT:
@@ -1282,11 +1341,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
if (type == ieee80211_vif_type_p2p(&sdata->vif))
return 0;
- /* Setting ad-hoc mode on non-IBSS channel is not supported. */
- if (sdata->local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS &&
- type == NL80211_IFTYPE_ADHOC)
- return -EOPNOTSUPP;
-
if (ieee80211_sdata_running(sdata)) {
ret = ieee80211_runtime_change_iftype(sdata, type);
if (ret)
@@ -1298,9 +1352,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
}
/* reset some values that shouldn't be kept across type changes */
- sdata->vif.bss_conf.basic_rates =
- ieee80211_mandatory_rates(sdata->local,
- sdata->local->oper_channel->band);
sdata->drop_unencrypted = 0;
if (type == NL80211_IFTYPE_STATION)
sdata->u.mgd.use_4addr = false;
@@ -1432,6 +1483,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
}
+static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
+
+ ieee80211_cleanup_sdata_stas(sdata);
+}
+
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params)
@@ -1507,6 +1567,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
INIT_LIST_HEAD(&sdata->key_list);
+ spin_lock_init(&sdata->cleanup_stations_lock);
+ INIT_LIST_HEAD(&sdata->cleanup_stations);
+ INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
+
for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
struct ieee80211_supported_band *sband;
sband = local->hw.wiphy->bands[i];
@@ -1523,6 +1587,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ieee80211_set_default_queues(sdata);
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
+ sdata->user_power_level = local->user_power_level;
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index d27e61aaa71..619c5d69799 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -339,7 +339,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = TKIP_IV_LEN;
key->conf.icv_len = TKIP_ICV_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
key->u.tkip.rx[i].iv32 =
get_unaligned_le32(&seq[2]);
key->u.tkip.rx[i].iv16 =
@@ -352,7 +352,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = CCMP_HDR_LEN;
key->conf.icv_len = CCMP_MIC_LEN;
if (seq) {
- for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
for (j = 0; j < CCMP_PN_LEN; j++)
key->u.ccmp.rx_pn[i][j] =
seq[CCMP_PN_LEN - j - 1];
@@ -372,8 +372,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
key->conf.iv_len = 0;
key->conf.icv_len = sizeof(struct ieee80211_mmie);
if (seq)
- for (j = 0; j < 6; j++)
- key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1];
+ for (j = 0; j < CMAC_PN_LEN; j++)
+ key->u.aes_cmac.rx_pn[j] =
+ seq[CMAC_PN_LEN - j - 1];
/*
* Initialize AES key state here as an optimization so that
* it does not need to be initialized for every packet.
@@ -654,16 +655,16 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
switch (key->conf.cipher) {
case WLAN_CIPHER_SUITE_TKIP:
- if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
return;
seq->tkip.iv32 = key->u.tkip.rx[tid].iv32;
seq->tkip.iv16 = key->u.tkip.rx[tid].iv16;
break;
case WLAN_CIPHER_SUITE_CCMP:
- if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES))
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
return;
if (tid < 0)
- pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES];
+ pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
else
pn = key->u.ccmp.rx_pn[tid];
memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 7d4e31f037d..382dc44ed33 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -30,8 +30,6 @@
#define TKIP_ICV_LEN 4
#define CMAC_PN_LEN 6
-#define NUM_RX_DATA_QUEUES 16
-
struct ieee80211_local;
struct ieee80211_sub_if_data;
struct sta_info;
@@ -82,17 +80,20 @@ struct ieee80211_key {
struct tkip_ctx tx;
/* last received RSC */
- struct tkip_ctx rx[NUM_RX_DATA_QUEUES];
+ struct tkip_ctx rx[IEEE80211_NUM_TIDS];
+
+ /* number of mic failures */
+ u32 mic_failures;
} tkip;
struct {
atomic64_t tx_pn;
/*
* Last received packet number. The first
- * NUM_RX_DATA_QUEUES counters are used with Data
+ * IEEE80211_NUM_TIDS counters are used with Data
* frames and the last counter is used with Robust
* Management frames.
*/
- u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN];
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN];
struct crypto_cipher *tfm;
u32 replays; /* dot11RSNAStatsCCMPReplays */
} ccmp;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index c80c4490351..1b087fff93e 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -93,15 +93,15 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
ieee80211_configure_filter(local);
}
-int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local)
{
+ struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *chan;
- int ret = 0;
+ u32 changed = 0;
int power;
enum nl80211_channel_type channel_type;
u32 offchannel_flag;
-
- might_sleep();
+ bool scanning = false;
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
if (local->scan_channel) {
@@ -109,19 +109,19 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
/* If scanning on oper channel, use whatever channel-type
* is currently in use.
*/
- if (chan == local->oper_channel)
+ if (chan == local->_oper_channel)
channel_type = local->_oper_channel_type;
else
channel_type = NL80211_CHAN_NO_HT;
} else if (local->tmp_channel) {
chan = local->tmp_channel;
- channel_type = local->tmp_channel_type;
+ channel_type = NL80211_CHAN_NO_HT;
} else {
- chan = local->oper_channel;
+ chan = local->_oper_channel;
channel_type = local->_oper_channel_type;
}
- if (chan != local->oper_channel ||
+ if (chan != local->_oper_channel ||
channel_type != local->_oper_channel_type)
local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
else
@@ -148,22 +148,39 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
- if (test_bit(SCAN_SW_SCANNING, &local->scanning) ||
- test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
- test_bit(SCAN_HW_SCANNING, &local->scanning) ||
- !local->ap_power_level)
- power = chan->max_power;
- else
- power = min(chan->max_power, local->ap_power_level);
+ scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) ||
+ test_bit(SCAN_HW_SCANNING, &local->scanning);
+ power = chan->max_power;
- if (local->user_power_level >= 0)
- power = min(power, local->user_power_level);
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!rcu_access_pointer(sdata->vif.chanctx_conf))
+ continue;
+ power = min(power, sdata->vif.bss_conf.txpower);
+ }
+ rcu_read_unlock();
if (local->hw.conf.power_level != power) {
changed |= IEEE80211_CONF_CHANGE_POWER;
local->hw.conf.power_level = power;
}
+ return changed;
+}
+
+int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
+{
+ int ret = 0;
+
+ might_sleep();
+
+ if (!local->use_chanctx)
+ changed |= ieee80211_hw_conf_chan(local);
+ else
+ changed &= ~(IEEE80211_CONF_CHANGE_CHANNEL |
+ IEEE80211_CONF_CHANGE_POWER);
+
if (changed && local->open_count) {
ret = drv_config(local, changed);
/*
@@ -359,14 +376,6 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
}
EXPORT_SYMBOL(ieee80211_restart_hw);
-static void ieee80211_recalc_smps_work(struct work_struct *work)
-{
- struct ieee80211_local *local =
- container_of(work, struct ieee80211_local, recalc_smps);
-
- ieee80211_recalc_smps(local);
-}
-
#ifdef CONFIG_INET
static int ieee80211_ifa_changed(struct notifier_block *nb,
unsigned long data, void *arg)
@@ -465,7 +474,8 @@ ieee80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = {
.tx = 0xffff,
.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
BIT(IEEE80211_STYPE_AUTH >> 4) |
- BIT(IEEE80211_STYPE_DEAUTH >> 4),
+ BIT(IEEE80211_STYPE_DEAUTH >> 4) |
+ BIT(IEEE80211_STYPE_PROBE_REQ >> 4),
},
[NL80211_IFTYPE_STATION] = {
.tx = 0xffff,
@@ -540,6 +550,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
struct ieee80211_local *local;
int priv_size, i;
struct wiphy *wiphy;
+ bool use_chanctx;
if (WARN_ON(!ops->tx || !ops->start || !ops->stop || !ops->config ||
!ops->add_interface || !ops->remove_interface ||
@@ -549,6 +560,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
return NULL;
+ /* check all or no channel context operations exist */
+ i = !!ops->add_chanctx + !!ops->remove_chanctx +
+ !!ops->change_chanctx + !!ops->assign_vif_chanctx +
+ !!ops->unassign_vif_chanctx;
+ if (WARN_ON(i != 0 && i != 5))
+ return NULL;
+ use_chanctx = i == 5;
+
/* Ensure 32-byte alignment of our private data and hw private data.
* We use the wiphy priv data for both our ieee80211_local and for
* the driver's private data
@@ -584,8 +603,15 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (ops->remain_on_channel)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
- wiphy->features = NL80211_FEATURE_SK_TX_STATUS |
- NL80211_FEATURE_HT_IBSS;
+ wiphy->features |= NL80211_FEATURE_SK_TX_STATUS |
+ NL80211_FEATURE_SAE |
+ NL80211_FEATURE_HT_IBSS |
+ NL80211_FEATURE_VIF_TXPOWER;
+
+ if (!ops->hw_scan)
+ wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN |
+ NL80211_FEATURE_AP_SCAN;
+
if (!ops->set_key)
wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
@@ -599,6 +625,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
local->ops = ops;
+ local->use_chanctx = use_chanctx;
/* set up some defaults */
local->hw.queues = 1;
@@ -612,7 +639,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
IEEE80211_RADIOTAP_MCS_HAVE_BW;
- local->user_power_level = -1;
+ local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
INIT_LIST_HEAD(&local->interfaces);
@@ -626,6 +655,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
spin_lock_init(&local->filter_lock);
spin_lock_init(&local->queue_stop_reason_lock);
+ INIT_LIST_HEAD(&local->chanctx_list);
+ mutex_init(&local->chanctx_mtx);
+
/*
* The rx_skb_queue is only accessed from tasklets,
* but other SKB queues are used from within IRQ
@@ -641,7 +673,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
INIT_WORK(&local->restart_work, ieee80211_restart_work);
INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
- INIT_WORK(&local->recalc_smps, ieee80211_recalc_smps_work);
local->smps_mode = IEEE80211_SMPS_OFF;
INIT_WORK(&local->dynamic_ps_enable_work,
@@ -719,6 +750,25 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if ((hw->flags & IEEE80211_HW_SCAN_WHILE_IDLE) && !local->ops->hw_scan)
return -EINVAL;
+ if (!local->use_chanctx) {
+ for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
+ const struct ieee80211_iface_combination *comb;
+
+ comb = &local->hw.wiphy->iface_combinations[i];
+
+ if (comb->num_different_channels > 1)
+ return -EINVAL;
+ }
+ } else {
+ /*
+ * WDS is currently prohibited when channel contexts are used
+ * because there's no clear definition of which channel WDS
+ * type interfaces use
+ */
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
+ return -EINVAL;
+ }
+
/* Only HW csum features are currently compatible with mac80211 */
feature_whitelist = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
NETIF_F_HW_CSUM;
@@ -728,6 +778,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (hw->max_report_rates == 0)
hw->max_report_rates = hw->max_rates;
+ local->rx_chains = 1;
+
/*
* generic code guarantees at least one band,
* set this very early because much code assumes
@@ -743,18 +795,28 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
sband = local->hw.wiphy->bands[band];
if (!sband)
continue;
- if (!local->oper_channel) {
+ if (!local->use_chanctx && !local->_oper_channel) {
/* init channel we're on */
local->hw.conf.channel =
- local->oper_channel = &sband->channels[0];
+ local->_oper_channel = &sband->channels[0];
local->hw.conf.channel_type = NL80211_CHAN_NO_HT;
}
+ cfg80211_chandef_create(&local->monitor_chandef,
+ &sband->channels[0],
+ NL80211_CHAN_NO_HT);
channels += sband->n_channels;
if (max_bitrates < sband->n_bitrates)
max_bitrates = sband->n_bitrates;
supp_ht = supp_ht || sband->ht_cap.ht_supported;
supp_vht = supp_vht || sband->vht_cap.vht_supported;
+
+ if (sband->ht_cap.ht_supported)
+ local->rx_chains =
+ max(ieee80211_mcs_to_chains(&sband->ht_cap.mcs),
+ local->rx_chains);
+
+ /* TODO: consider VHT for RX chains, hopefully it's the same */
}
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
@@ -778,19 +840,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
hw->wiphy->software_iftypes |= BIT(NL80211_IFTYPE_MONITOR);
- /*
- * mac80211 doesn't support more than 1 channel, and also not more
- * than one IBSS interface
- */
+ /* mac80211 doesn't support more than one IBSS interface right now */
for (i = 0; i < hw->wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *c;
int j;
c = &hw->wiphy->iface_combinations[i];
- if (c->num_different_channels > 1)
- return -EINVAL;
-
for (j = 0; j < c->n_limits; j++)
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
c->limits[j].max > 1)
@@ -830,9 +886,21 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (supp_ht)
local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap);
- if (supp_vht)
+ if (supp_vht) {
local->scan_ies_len +=
- 2 + sizeof(struct ieee80211_vht_capabilities);
+ 2 + sizeof(struct ieee80211_vht_cap);
+
+ /*
+ * (for now at least), drivers wanting to use VHT must
+ * support channel contexts, as they contain all the
+ * necessary VHT information and the global hw config
+ * doesn't (yet)
+ */
+ if (WARN_ON(!local->use_chanctx)) {
+ result = -EINVAL;
+ goto fail_wiphy_register;
+ }
+ }
if (!local->ops->hw_scan) {
/* For hw_scan, driver needs to set these up. */
@@ -871,8 +939,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->hw.wiphy->cipher_suites,
sizeof(u32) * local->hw.wiphy->n_cipher_suites,
GFP_KERNEL);
- if (!suites)
- return -ENOMEM;
+ if (!suites) {
+ result = -ENOMEM;
+ goto fail_wiphy_register;
+ }
for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
u32 suite = local->hw.wiphy->cipher_suites[r];
if (suite == WLAN_CIPHER_SUITE_WEP40 ||
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index ff0296c7bab..649ad513547 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -76,7 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
u32 basic_rates = 0;
- enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT;
+ struct cfg80211_chan_def sta_chan_def;
/*
* As support for each feature is added, check for matching
@@ -97,23 +97,17 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
goto mismatch;
- ieee80211_sta_get_rates(local, ie, local->oper_channel->band,
+ ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
goto mismatch;
- if (ie->ht_operation)
- sta_channel_type =
- ieee80211_ht_oper_to_channel_type(ie->ht_operation);
-
- /* Disallow HT40+/- mismatch */
- if (ie->ht_operation &&
- (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) &&
- (sta_channel_type == NL80211_CHAN_HT40MINUS ||
- sta_channel_type == NL80211_CHAN_HT40PLUS) &&
- sdata->vif.bss_conf.channel_type != sta_channel_type)
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ ie->ht_operation, &sta_chan_def);
+
+ if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef,
+ &sta_chan_def))
goto mismatch;
return true;
@@ -129,7 +123,7 @@ mismatch:
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0;
}
/**
@@ -169,7 +163,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
return -ENOMEM;
sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
for (i = 0; i < RMC_BUCKETS; i++)
- INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i].list);
+ INIT_LIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
return 0;
}
@@ -183,7 +177,7 @@ void mesh_rmc_free(struct ieee80211_sub_if_data *sdata)
return;
for (i = 0; i < RMC_BUCKETS; i++)
- list_for_each_entry_safe(p, n, &rmc->bucket[i].list, list) {
+ list_for_each_entry_safe(p, n, &rmc->bucket[i], list) {
list_del(&p->list);
kmem_cache_free(rm_cache, p);
}
@@ -216,7 +210,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
/* Don't care about endianness since only match matters */
memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
- list_for_each_entry_safe(p, n, &rmc->bucket[idx].list, list) {
+ list_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
++entries;
if (time_after(jiffies, p->exp_time) ||
(entries == RMC_QUEUE_MAX_LEN)) {
@@ -235,7 +229,7 @@ int mesh_rmc_check(u8 *sa, struct ieee80211s_hdr *mesh_hdr,
p->seqnum = seqnum;
p->exp_time = jiffies + RMC_TIMEOUT;
memcpy(p->sa, sa, ETH_ALEN);
- list_add(&p->list, &rmc->bucket[idx].list);
+ list_add(&p->list, &rmc->bucket[idx]);
return 0;
}
@@ -264,16 +258,16 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
/* Authentication Protocol identifier */
*pos++ = ifmsh->mesh_auth_id;
/* Mesh Formation Info - number of neighbors */
- neighbors = atomic_read(&ifmsh->mshstats.estab_plinks);
+ neighbors = atomic_read(&ifmsh->estab_plinks);
/* Number of neighbor mesh STAs or 15 whichever is smaller */
neighbors = (neighbors > 15) ? 15 : neighbors;
*pos++ = neighbors << 1;
/* Mesh capability */
- *pos = MESHCONF_CAPAB_FORWARDING;
+ *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
*pos |= ifmsh->accepting_plinks ?
- MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
+ IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ |= ifmsh->adjusting_tbtt ?
- MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00;
*pos++ = 0x00;
return 0;
@@ -355,12 +349,22 @@ int mesh_add_ds_params_ie(struct sk_buff *skb,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- struct ieee80211_channel *chan = local->oper_channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u8 *pos;
if (skb_tailroom(skb) < 3)
return -ENOMEM;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+
sband = local->hw.wiphy->bands[chan->band];
if (sband->band == IEEE80211_BAND_2GHZ) {
pos = skb_put(skb, 2 + 1);
@@ -376,12 +380,13 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u8 *pos;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[band];
if (!sband->ht_cap.ht_supported ||
- sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap))
@@ -397,14 +402,26 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_channel *channel = local->oper_channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type =
- sdata->vif.bss_conf.channel_type;
- struct ieee80211_supported_band *sband =
- local->hw.wiphy->bands[channel->band];
- struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
+ cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef);
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sta_ht_cap *ht_cap;
u8 *pos;
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ channel = chanctx_conf->def.chan;
+ rcu_read_unlock();
+
+ sband = local->hw.wiphy->bands[channel->band];
+ ht_cap = &sband->ht_cap;
+
if (!ht_cap->ht_supported || channel_type == NL80211_CHAN_NO_HT)
return 0;
@@ -412,7 +429,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb,
return -ENOMEM;
pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
- ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type,
+ ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef,
sdata->vif.bss_conf.ht_operation_mode);
return 0;
@@ -610,7 +627,7 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
sdata->vif.bss_conf.basic_rates =
ieee80211_mandatory_rates(sdata->local,
- sdata->local->oper_channel->band);
+ ieee80211_get_sdata_band(sdata));
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_HT |
@@ -680,8 +697,10 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - baselen,
&elems);
- /* ignore beacons from secure mesh peers if our security is off */
- if (elems.rsn_len && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE)
+ /* ignore non-mesh or secure / unsecure mismatch */
+ if ((!elems.mesh_id || !elems.mesh_config) ||
+ (elems.rsn && sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) ||
+ (!elems.rsn && sdata->u.mesh.security != IEEE80211_MESH_SEC_NONE))
return;
if (elems.ds_params && elems.ds_params_len == 1)
@@ -694,8 +713,7 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
- if (elems.mesh_id && elems.mesh_config &&
- mesh_matches_local(sdata, &elems))
+ if (mesh_matches_local(sdata, &elems))
mesh_neighbour_update(sdata, mgmt->sa, &elems);
if (ifmsh->sync_ops)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 25d0f17dec7..84c28c6101c 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -19,20 +19,6 @@
/* Data structures */
/**
- * enum mesh_config_capab_flags - mesh config IE capability flags
- *
- * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish
- * additional mesh peerings with other mesh STAs
- * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs
- * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing
- */
-enum mesh_config_capab_flags {
- MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0),
- MESHCONF_CAPAB_FORWARDING = BIT(3),
- MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5),
-};
-
-/**
* enum mesh_path_flags - mac80211 mesh path flags
*
*
@@ -198,7 +184,7 @@ struct rmc_entry {
};
struct mesh_rmc {
- struct rmc_entry bucket[RMC_BUCKETS];
+ struct list_head bucket[RMC_BUCKETS];
u32 idx_mask;
};
@@ -256,7 +242,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_root_setup(struct ieee80211_if_mesh *ifmsh);
-struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
+const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method);
/* Mesh paths */
int mesh_nexthop_lookup(struct sk_buff *skb,
@@ -324,7 +310,7 @@ extern int mesh_allocated;
static inline int mesh_plink_free_count(struct ieee80211_sub_if_data *sdata)
{
return sdata->u.mesh.mshcfg.dot11MeshMaxPeerLinks -
- atomic_read(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_read(&sdata->u.mesh.estab_plinks);
}
static inline bool mesh_plink_availables(struct ieee80211_sub_if_data *sdata)
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 47aeee2d8db..2659e428b80 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -215,6 +215,7 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
skb->priority = 7;
info->control.vif = &sdata->vif;
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
ieee80211_set_qos_hdr(sdata, skb);
}
@@ -246,11 +247,13 @@ int mesh_path_error_tx(u8 ttl, u8 *target, __le32 target_sn,
return -EAGAIN;
skb = dev_alloc_skb(local->tx_headroom +
+ IEEE80211_ENCRYPT_HEADROOM +
+ IEEE80211_ENCRYPT_TAILROOM +
hdr_len +
2 + 15 /* PERR IE */);
if (!skb)
return -1;
- skb_reserve(skb, local->tx_headroom);
+ skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 3ab34d81689..4b274e9c91a 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -19,12 +19,6 @@
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
-#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries)
-#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout)
-#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout)
-#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout)
-#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks)
-
/* We only need a valid sta if user configured a minimum rssi_threshold. */
#define rssi_threshold_check(sta, sdata) \
(sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
@@ -50,14 +44,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
static inline
u32 mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_inc(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_inc(&sdata->u.mesh.estab_plinks);
return mesh_accept_plinks_update(sdata);
}
static inline
u32 mesh_plink_dec_estab_count(struct ieee80211_sub_if_data *sdata)
{
- atomic_dec(&sdata->u.mesh.mshstats.estab_plinks);
+ atomic_dec(&sdata->u.mesh.estab_plinks);
return mesh_accept_plinks_update(sdata);
}
@@ -117,7 +111,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
u16 ht_opmode;
bool non_ht_sta = false, ht20_sta = false;
- if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT)
+ if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return 0;
rcu_read_lock();
@@ -126,14 +120,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata)
sta->plink_state != NL80211_PLINK_ESTAB)
continue;
- switch (sta->ch_type) {
- case NL80211_CHAN_NO_HT:
+ switch (sta->ch_width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
mpl_dbg(sdata,
"mesh_plink %pM: nonHT sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
non_ht_sta = true;
goto out;
- case NL80211_CHAN_HT20:
+ case NL80211_CHAN_WIDTH_20:
mpl_dbg(sdata,
"mesh_plink %pM: HT20 sta (%pM) is present\n",
sdata->vif.addr, sta->sta.addr);
@@ -148,7 +142,7 @@ out:
if (non_ht_sta)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED;
else if (ht20_sta &&
- sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20)
+ sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20)
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ;
else
ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
@@ -252,6 +246,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
mgmt->u.action.u.self_prot.action_code = action;
if (action != WLAN_SP_MESH_PEERING_CLOSE) {
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
+
/* capability info */
pos = skb_put(skb, 2);
memset(pos, 0, 2);
@@ -260,10 +256,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
pos = skb_put(skb, 2);
memcpy(pos + 2, &plid, 2);
}
- if (ieee80211_add_srates_ie(sdata, skb, true,
- local->oper_channel->band) ||
- ieee80211_add_ext_srates_ie(sdata, skb, true,
- local->oper_channel->band) ||
+ if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
+ ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
mesh_add_rsn_ie(skb, sdata) ||
mesh_add_meshid_ie(skb, sdata) ||
mesh_add_meshconf_ie(skb, sdata))
@@ -343,7 +337,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems)
{
struct ieee80211_local *local = sdata->local;
- enum ieee80211_band band = local->oper_channel->band;
+ enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
struct ieee80211_supported_band *sband;
u32 rates, basic_rates = 0;
struct sta_info *sta;
@@ -378,7 +372,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
sta->sta.supp_rates[band] = rates;
if (elems->ht_cap_elem &&
- sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT)
+ sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems->ht_cap_elem,
&sta->sta.ht_cap);
@@ -386,15 +380,19 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata,
memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap));
if (elems->ht_operation) {
+ struct cfg80211_chan_def chandef;
+
if (!(elems->ht_operation->ht_param &
IEEE80211_HT_PARAM_CHAN_WIDTH_ANY))
sta->sta.ht_cap.cap &=
~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
- sta->ch_type =
- ieee80211_ht_oper_to_channel_type(elems->ht_operation);
+ ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan,
+ elems->ht_operation, &chandef);
+ sta->ch_width = chandef.width;
}
- rate_control_rate_init(sta);
+ if (insert)
+ rate_control_rate_init(sta);
spin_unlock_bh(&sta->lock);
if (insert && sta_info_insert(sta))
@@ -430,6 +428,7 @@ static void mesh_plink_timer(unsigned long data)
struct sta_info *sta;
__le16 llid, plid, reason;
struct ieee80211_sub_if_data *sdata;
+ struct mesh_config *mshcfg;
/*
* This STA is valid because sta_info_destroy() will
@@ -456,12 +455,13 @@ static void mesh_plink_timer(unsigned long data)
llid = sta->llid;
plid = sta->plid;
sdata = sta->sdata;
+ mshcfg = &sdata->u.mesh.mshcfg;
switch (sta->plink_state) {
case NL80211_PLINK_OPN_RCVD:
case NL80211_PLINK_OPN_SNT:
/* retry timer */
- if (sta->plink_retries < dot11MeshMaxRetries(sdata)) {
+ if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) {
u32 rand;
mpl_dbg(sta->sdata,
"Mesh plink for %pM (retry, timeout): %d %d\n",
@@ -484,7 +484,7 @@ static void mesh_plink_timer(unsigned long data)
if (!reason)
reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
sta->plink_state = NL80211_PLINK_HOLDING;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, llid, plid, reason);
@@ -543,7 +543,7 @@ int mesh_plink_open(struct sta_info *sta)
return -EBUSY;
}
sta->plink_state = NL80211_PLINK_OPN_SNT;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mpl_dbg(sdata,
"Mesh plink: starting establishment with %pM\n",
@@ -570,6 +570,7 @@ void mesh_plink_block(struct sta_info *sta)
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status)
{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
struct ieee802_11_elems elems;
struct sta_info *sta;
enum plink_event event;
@@ -777,7 +778,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->plid = plid;
get_random_bytes(&llid, 2);
sta->llid = llid;
- mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata));
+ mesh_plink_timer_set(sta,
+ mshcfg->dot11MeshRetryTimeout);
spin_unlock_bh(&sta->lock);
mesh_plink_frame_tx(sdata,
WLAN_SP_MESH_PEERING_OPEN,
@@ -803,7 +805,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -825,7 +827,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
- dot11MeshConfirmTimeout(sdata)))
+ mshcfg->dot11MeshConfirmTimeout))
sta->ignore_plink_timer = true;
spin_unlock_bh(&sta->lock);
@@ -847,7 +849,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -888,7 +890,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
sta->reason = reason;
sta->plink_state = NL80211_PLINK_HOLDING;
if (!mod_plink_timer(sta,
- dot11MeshHoldingTimeout(sdata)))
+ mshcfg->dot11MeshHoldingTimeout))
sta->ignore_plink_timer = true;
llid = sta->llid;
@@ -923,7 +925,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
changed |= __mesh_plink_deactivate(sta);
sta->plink_state = NL80211_PLINK_HOLDING;
llid = sta->llid;
- mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata));
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
index a16b7b4b1e0..aa8d1e43738 100644
--- a/net/mac80211/mesh_sync.c
+++ b/net/mac80211/mesh_sync.c
@@ -43,7 +43,7 @@ struct sync_method {
static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie)
{
return (ie->mesh_config->meshconf_cap &
- MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
+ IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0;
}
void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
@@ -116,43 +116,13 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
goto no_sync;
}
- if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) {
- /*
- * The mactime is defined as the time the first data symbol
- * of the frame hits the PHY, and the timestamp of the beacon
- * is defined as "the time that the data symbol containing the
- * first bit of the timestamp is transmitted to the PHY plus
- * the transmitting STA's delays through its local PHY from the
- * MAC-PHY interface to its interface with the WM" (802.11
- * 11.1.2)
- *
- * T_r, in 13.13.2.2.2, is just defined as "the frame reception
- * time" but we unless we interpret that time to be the same
- * time of the beacon timestamp, the offset calculation will be
- * off. Below we adjust t_r to be "the time at which the first
- * symbol of the timestamp element in the beacon is received".
- * This correction depends on the rate.
- *
- * Based on similar code in ibss.c
- */
- int rate;
-
- if (rx_status->flag & RX_FLAG_HT) {
- /* TODO:
- * In principle there could be HT-beacons (Dual Beacon
- * HT Operation options), but for now ignore them and
- * just use the primary (i.e. non-HT) beacons for
- * synchronization.
- * */
- goto no_sync;
- } else
- rate = local->hw.wiphy->bands[rx_status->band]->
- bitrates[rx_status->rate_idx].bitrate;
-
- /* 24 bytes of header * 8 bits/byte *
- * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/
- t_r = rx_status->mactime + (24 * 8 * 10 / rate);
- }
+ if (ieee80211_have_rx_timestamp(rx_status))
+ /* time when timestamp field was received */
+ t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
+ 24 + 12 +
+ elems->total_len +
+ FCS_LEN,
+ 24);
/* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
@@ -225,58 +195,20 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
ifmsh->sync_offset_clockdrift_max);
set_bit(MESH_WORK_DRIFT_ADJUST,
&ifmsh->wrkq_flags);
+
+ ifmsh->adjusting_tbtt = true;
} else {
msync_dbg(sdata,
"TBTT : max clockdrift=%lld; too small to adjust\n",
(long long)ifmsh->sync_offset_clockdrift_max);
ifmsh->sync_offset_clockdrift_max = 0;
+
+ ifmsh->adjusting_tbtt = false;
}
spin_unlock_bh(&ifmsh->sync_offset_lock);
}
-static const u8 *mesh_get_vendor_oui(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- u8 offset;
-
- if (!ifmsh->ie || !ifmsh->ie_len)
- return NULL;
-
- offset = ieee80211_ie_split_vendor(ifmsh->ie,
- ifmsh->ie_len, 0);
-
- if (!offset)
- return NULL;
-
- return ifmsh->ie + offset + 2;
-}
-
-static void mesh_sync_vendor_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
- u16 stype,
- struct ieee80211_mgmt *mgmt,
- struct ieee802_11_elems *elems,
- struct ieee80211_rx_status *rx_status)
-{
- const u8 *oui;
-
- WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
- msync_dbg(sdata, "called mesh_sync_vendor_rx_bcn_presp\n");
- oui = mesh_get_vendor_oui(sdata);
- /* here you would implement the vendor offset tracking for this oui */
-}
-
-static void mesh_sync_vendor_adjust_tbtt(struct ieee80211_sub_if_data *sdata)
-{
- const u8 *oui;
-
- WARN_ON(sdata->u.mesh.mesh_sp_id != IEEE80211_SYNC_METHOD_VENDOR);
- msync_dbg(sdata, "called mesh_sync_vendor_adjust_tbtt\n");
- oui = mesh_get_vendor_oui(sdata);
- /* here you would implement the vendor tsf adjustment for this oui */
-}
-
-/* global variable */
-static struct sync_method sync_methods[] = {
+static const struct sync_method sync_methods[] = {
{
.method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.ops = {
@@ -284,18 +216,11 @@ static struct sync_method sync_methods[] = {
.adjust_tbtt = &mesh_sync_offset_adjust_tbtt,
}
},
- {
- .method = IEEE80211_SYNC_METHOD_VENDOR,
- .ops = {
- .rx_bcn_presp = &mesh_sync_vendor_rx_bcn_presp,
- .adjust_tbtt = &mesh_sync_vendor_adjust_tbtt,
- }
- },
};
-struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
+const struct ieee80211_mesh_sync_ops *ieee80211_mesh_sync_ops_get(u8 method)
{
- struct ieee80211_mesh_sync_ops *ops = NULL;
+ const struct ieee80211_mesh_sync_ops *ops = NULL;
u8 i;
for (i = 0 ; i < ARRAY_SIZE(sync_methods); ++i) {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 1b7eed252fe..a3552929a21 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -178,20 +178,32 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
bool disable_40 = false;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return 0;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+ sband = local->hw.wiphy->bands[chan->band];
- switch (sdata->vif.bss_conf.channel_type) {
- case NL80211_CHAN_HT40PLUS:
- if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_40:
+ if (sdata->vif.bss_conf.chandef.chan->center_freq >
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
disable_40 = true;
- break;
- case NL80211_CHAN_HT40MINUS:
- if (local->oper_channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ if (sdata->vif.bss_conf.chandef.chan->center_freq <
+ sdata->vif.bss_conf.chandef.center_freq1 &&
+ chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
disable_40 = true;
break;
default:
@@ -342,8 +354,18 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
/* determine capability flags */
cap = vht_cap.cap;
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
+ if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
+ cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ }
+
/* reserve and fill IE */
- pos = skb_put(skb, sizeof(struct ieee80211_vht_capabilities) + 2);
+ pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2);
ieee80211_ie_build_vht_cap(pos, &vht_cap, cap);
}
@@ -359,11 +381,21 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
int i, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u32 rates = 0;
lockdep_assert_held(&ifmgd->mtx);
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
+ sband = local->hw.wiphy->bands[chan->band];
if (assoc_data->supp_rates_len) {
/*
@@ -392,7 +424,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
4 + /* power capability */
2 + 2 * sband->n_channels + /* supported channels */
2 + sizeof(struct ieee80211_ht_cap) + /* HT */
- 2 + sizeof(struct ieee80211_vht_capabilities) + /* VHT */
+ 2 + sizeof(struct ieee80211_vht_cap) + /* VHT */
assoc_data->ie_len + /* extra IEs */
9, /* WMM */
GFP_KERNEL);
@@ -485,7 +517,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
- *pos++ = local->oper_channel->max_power; /* max tx power */
+ *pos++ = chan->max_power; /* max tx power */
/* 2. supported channels */
/* TODO: get this in reg domain format */
@@ -521,9 +553,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
offset = noffset;
}
- if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (WARN_ON_ONCE((ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT)))
+ ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param,
- sband, local->oper_channel, ifmgd->ap_smps);
+ sband, chan, sdata->smps_mode);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
ieee80211_add_vht_ie(sdata, skb, sband);
@@ -657,18 +693,18 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;
- sdata->local->oper_channel = sdata->local->csa_channel;
+ sdata->local->_oper_channel = sdata->local->csa_channel;
if (!sdata->local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(sdata->local,
IEEE80211_CONF_CHANGE_CHANNEL);
} else {
/* update the device channel directly */
- sdata->local->hw.conf.channel = sdata->local->oper_channel;
+ sdata->local->hw.conf.channel = sdata->local->_oper_channel;
}
/* XXX: shouldn't really modify cfg80211-owned data! */
- ifmgd->associated->channel = sdata->local->oper_channel;
+ ifmgd->associated->channel = sdata->local->_oper_channel;
/* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&sdata->local->hw,
@@ -680,11 +716,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_if_managed *ifmgd;
-
- sdata = vif_to_sdata(vif);
- ifmgd = &sdata->u.mgd;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
trace_api_chswitch_done(sdata, success);
if (!success) {
@@ -723,6 +756,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num,
cbss->channel->band);
+ struct ieee80211_chanctx *chanctx;
ASSERT_MGD_MTX(ifmgd);
@@ -748,10 +782,35 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
return;
}
- sdata->local->csa_channel = new_ch;
-
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
+ if (sdata->local->use_chanctx) {
+ sdata_info(sdata,
+ "not handling channel switch with channel contexts\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ return;
+ }
+
+ mutex_lock(&sdata->local->chanctx_mtx);
+ if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
+ mutex_unlock(&sdata->local->chanctx_mtx);
+ return;
+ }
+ chanctx = container_of(rcu_access_pointer(sdata->vif.chanctx_conf),
+ struct ieee80211_chanctx, conf);
+ if (chanctx->refcount > 1) {
+ sdata_info(sdata,
+ "channel switch with multiple interfaces on the same channel, disconnecting\n");
+ ieee80211_queue_work(&sdata->local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&sdata->local->chanctx_mtx);
+ return;
+ }
+ mutex_unlock(&sdata->local->chanctx_mtx);
+
+ sdata->local->csa_channel = new_ch;
+
if (sw_elem->mode)
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -778,10 +837,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
cbss->beacon_interval));
}
-static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_channel *channel,
- const u8 *country_ie, u8 country_ie_len,
- const u8 *pwr_constr_elem)
+static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *channel,
+ const u8 *country_ie, u8 country_ie_len,
+ const u8 *pwr_constr_elem)
{
struct ieee80211_country_ie_triplet *triplet;
int chan = ieee80211_frequency_to_channel(channel->center_freq);
@@ -790,7 +849,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
/* Invalid IE */
if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
- return;
+ return 0;
triplet = (void *)(country_ie + 3);
country_ie_len -= 3;
@@ -831,19 +890,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata,
}
if (!have_chan_pwr)
- return;
+ return 0;
new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem);
- if (sdata->local->ap_power_level == new_ap_level)
- return;
+ if (sdata->ap_power_level == new_ap_level)
+ return 0;
sdata_info(sdata,
"Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n",
new_ap_level, chan_pwr, *pwr_constr_elem,
sdata->u.mgd.bssid);
- sdata->local->ap_power_level = new_ap_level;
- ieee80211_hw_config(sdata->local, 0);
+ sdata->ap_power_level = new_ap_level;
+ if (__ieee80211_recalc_txpower(sdata))
+ return BSS_CHANGED_TXPOWER;
+ return 0;
}
void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif)
@@ -1013,12 +1074,8 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
if (beaconint_us > latency) {
local->ps_sdata = NULL;
} else {
- struct ieee80211_bss *bss;
int maxslp = 1;
- u8 dtimper;
-
- bss = (void *)found->u.mgd.associated->priv;
- dtimper = bss->dtim_period;
+ u8 dtimper = found->u.mgd.dtim_period;
/* If the TIM IE is invalid, pretend the value is 1 */
if (!dtimper)
@@ -1280,7 +1337,7 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
}
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
- if (sdata->local->oper_channel->band == IEEE80211_BAND_5GHZ)
+ if (ieee80211_get_sdata_band(sdata) == IEEE80211_BAND_5GHZ)
use_short_slot = true;
if (use_protection != bss_conf->use_cts_prot) {
@@ -1321,15 +1378,45 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
+ if (sdata->vif.p2p) {
+ const struct cfg80211_bss_ies *ies;
+
+ rcu_read_lock();
+ ies = rcu_dereference(cbss->ies);
+ if (ies) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(
+ ies->data, ies->len,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ bss_info_changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ }
+ }
+ rcu_read_unlock();
+ }
+
/* just to be sure */
ieee80211_stop_poll(sdata);
ieee80211_led_assoc(local, 1);
- if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
- bss_conf->dtim_period = bss->dtim_period;
- else
+ if (local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+ /*
+ * If the AP is buggy we may get here with no DTIM period
+ * known, so assume it's 1 which is the only safe assumption
+ * in that case, although if the TIM IE is broken powersave
+ * probably just won't work at all.
+ */
+ bss_conf->dtim_period = sdata->u.mgd.dtim_period ?: 1;
+ } else {
bss_conf->dtim_period = 0;
+ }
bss_conf->assoc = 1;
@@ -1350,7 +1437,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
mutex_unlock(&local->iflist_mtx);
- ieee80211_recalc_smps(local);
+ ieee80211_recalc_smps(sdata);
ieee80211_recalc_ps_vif(sdata);
netif_tx_start_all_queues(sdata->dev);
@@ -1443,11 +1530,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
+ sdata->vif.bss_conf.p2p_ctwindow = 0;
+ sdata->vif.bss_conf.p2p_oppps = false;
+
/* on the next assoc, re-program HT parameters */
memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa));
memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask));
- local->ap_power_level = 0;
+ sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
@@ -1465,10 +1555,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
- /* channel(_type) changes are handled by ieee80211_hw_config */
- WARN_ON(!ieee80211_set_channel_type(local, sdata, NL80211_CHAN_NO_HT));
- ieee80211_hw_config(local, 0);
-
/* disassociated - set to defaults now */
ieee80211_set_wmm_default(sdata, false);
@@ -1478,6 +1564,11 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
del_timer_sync(&sdata->u.mgd.chswitch_timer);
sdata->u.mgd.timers_running = 0;
+
+ sdata->vif.bss_conf.dtim_period = 0;
+
+ ifmgd->flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1581,6 +1672,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
} else {
int ssid_len;
+ rcu_read_lock();
ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0;
@@ -1589,7 +1681,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
0, (u32) -1, true, false,
- ifmgd->associated->channel);
+ ifmgd->associated->channel, false);
+ rcu_read_unlock();
}
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@@ -1685,6 +1778,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
else
return NULL;
+ rcu_read_lock();
ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
if (WARN_ON_ONCE(ssid == NULL))
ssid_len = 0;
@@ -1692,10 +1786,10 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
ssid_len = ssid[1];
skb = ieee80211_build_probe_req(sdata, cbss->bssid,
- (u32) -1,
- sdata->local->oper_channel,
+ (u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
+ rcu_read_unlock();
return skb;
}
@@ -1804,6 +1898,8 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
cfg80211_put_bss(auth_data->bss);
@@ -1824,7 +1920,7 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
return;
auth_data->expected_transaction = 4;
drv_mgd_prepare_tx(sdata->local, sdata);
- ieee80211_send_auth(sdata, 3, auth_data->algorithm,
+ ieee80211_send_auth(sdata, 3, auth_data->algorithm, 0,
elems.challenge - 2, elems.challenge_len + 2,
auth_data->bss->bssid, auth_data->bss->bssid,
auth_data->key, auth_data->key_len,
@@ -1858,8 +1954,13 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
status_code = le16_to_cpu(mgmt->u.auth.status_code);
if (auth_alg != ifmgd->auth_data->algorithm ||
- auth_transaction != ifmgd->auth_data->expected_transaction)
+ auth_transaction != ifmgd->auth_data->expected_transaction) {
+ sdata_info(sdata, "%pM unexpected authentication state: alg %d (expected %d) transact %d (expected %d)\n",
+ mgmt->sa, auth_alg, ifmgd->auth_data->algorithm,
+ auth_transaction,
+ ifmgd->auth_data->expected_transaction);
return RX_MGMT_NONE;
+ }
if (status_code != WLAN_STATUS_SUCCESS) {
sdata_info(sdata, "%pM denied authentication (status %d)\n",
@@ -1872,6 +1973,7 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
case WLAN_AUTH_FT:
+ case WLAN_AUTH_SAE:
break;
case WLAN_AUTH_SHARED_KEY:
if (ifmgd->auth_data->expected_transaction != 4) {
@@ -1891,6 +1993,15 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
run_again(ifmgd, ifmgd->auth_data->timeout);
+ if (ifmgd->auth_data->algorithm == WLAN_AUTH_SAE &&
+ ifmgd->auth_data->expected_transaction != 2) {
+ /*
+ * Report auth frame to user space for processing since another
+ * round of Authentication frames is still needed.
+ */
+ return RX_MGMT_CFG80211_RX_AUTH;
+ }
+
/* move station state to auth */
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get(sdata, bssid);
@@ -2030,6 +2141,8 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
+ sdata->u.mgd.flags = 0;
+ ieee80211_vif_release_channel(sdata);
}
kfree(assoc_data);
@@ -2091,15 +2204,20 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
return false;
}
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)];
- if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
sta->supports_40mhz =
sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40;
+ if (elems.vht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_VHT))
+ ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband,
+ elems.vht_cap_elem,
+ &sta->sta.vht_cap);
+
rate_control_rate_init(sta);
if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED)
@@ -2140,7 +2258,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_QOS;
if (elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
cbss->bssid, false);
@@ -2247,9 +2365,9 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
return RX_MGMT_CFG80211_RX_ASSOC;
}
+
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
+ struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status,
struct ieee802_11_elems *elems,
bool beacon)
@@ -2260,11 +2378,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *channel;
bool need_ps = false;
- if (sdata->u.mgd.associated &&
- ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) {
- bss = (void *)sdata->u.mgd.associated->priv;
+ if ((sdata->u.mgd.associated &&
+ ether_addr_equal(mgmt->bssid, sdata->u.mgd.associated->bssid)) ||
+ (sdata->u.mgd.assoc_data &&
+ ether_addr_equal(mgmt->bssid,
+ sdata->u.mgd.assoc_data->bss->bssid))) {
/* not previously set so we may need to recalc */
- need_ps = !bss->dtim_period;
+ need_ps = sdata->u.mgd.associated && !sdata->u.mgd.dtim_period;
+
+ if (elems->tim && !elems->parse_error) {
+ struct ieee80211_tim_ie *tim_ie = elems->tim;
+ sdata->u.mgd.dtim_period = tim_ie->dtim_period;
+ }
}
if (elems->ds_params && elems->ds_params_len == 1)
@@ -2369,8 +2494,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
u32 changed = 0;
- bool erp_valid, directed_tim = false;
+ bool erp_valid;
u8 erp_value = 0;
u32 ncrc;
u8 *bssid;
@@ -2382,8 +2509,19 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (baselen > len)
return;
- if (rx_status->freq != local->oper_channel->center_freq)
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
return;
+ }
+
+ if (rx_status->freq != chanctx_conf->def.chan->center_freq) {
+ rcu_read_unlock();
+ return;
+ }
+ chan = chanctx_conf->def.chan;
+ rcu_read_unlock();
if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
ether_addr_equal(mgmt->bssid, ifmgd->assoc_data->bss->bssid)) {
@@ -2490,11 +2628,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
len - baselen, &elems,
care_about_ies, ncrc);
- if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
- directed_tim = ieee80211_check_tim(elems.tim, elems.tim_len,
- ifmgd->aid);
-
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) {
+ bool directed_tim = ieee80211_check_tim(elems.tim,
+ elems.tim_len,
+ ifmgd->aid);
if (directed_tim) {
if (local->hw.conf.dynamic_ps_timeout > 0) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
@@ -2519,6 +2656,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (sdata->vif.p2p) {
+ u8 noa[2];
+ int ret;
+
+ ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable,
+ len - baselen,
+ IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
+ noa, sizeof(noa));
+ if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) {
+ bss_conf->p2p_oppps = noa[1] & 0x80;
+ bss_conf->p2p_ctwindow = noa[1] & 0x7f;
+ changed |= BSS_CHANGED_P2P_PS;
+ sdata->u.mgd.p2p_noa_index = noa[0];
+ /*
+ * make sure we update all information, the CRC
+ * mechanism doesn't look at P2P attributes.
+ */
+ ifmgd->beacon_crc_valid = false;
+ }
+ }
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -2543,22 +2701,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
- struct ieee80211_supported_band *sband;
-
- sband = local->hw.wiphy->bands[local->oper_channel->band];
-
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_HT))
changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation,
bssid, true);
- }
if (elems.country_elem && elems.pwr_constr_elem &&
mgmt->u.probe_resp.capab_info &
cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT))
- ieee80211_handle_pwr_constr(sdata, local->oper_channel,
- elems.country_elem,
- elems.country_elem_len,
- elems.pwr_constr_elem);
+ changed |= ieee80211_handle_pwr_constr(sdata, chan,
+ elems.country_elem,
+ elems.country_elem_len,
+ elems.pwr_constr_elem);
ieee80211_bss_info_change_notify(sdata, changed);
}
@@ -2703,13 +2856,23 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
drv_mgd_prepare_tx(local, sdata);
if (auth_data->bss->proberesp_ies) {
+ u16 trans = 1;
+ u16 status = 0;
+
sdata_info(sdata, "send auth to %pM (try %d/%d)\n",
auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES);
auth_data->expected_transaction = 2;
- ieee80211_send_auth(sdata, 1, auth_data->algorithm,
- auth_data->ie, auth_data->ie_len,
+
+ if (auth_data->algorithm == WLAN_AUTH_SAE) {
+ trans = auth_data->sae_trans;
+ status = auth_data->sae_status;
+ auth_data->expected_transaction = trans;
+ }
+
+ ieee80211_send_auth(sdata, trans, auth_data->algorithm, status,
+ auth_data->data, auth_data->data_len,
auth_data->bss->bssid,
auth_data->bss->bssid, NULL, 0, 0);
} else {
@@ -2719,16 +2882,20 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
auth_data->bss->bssid, auth_data->tries,
IEEE80211_AUTH_MAX_TRIES);
+ rcu_read_lock();
ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
- if (!ssidie)
+ if (!ssidie) {
+ rcu_read_unlock();
return -EINVAL;
+ }
/*
* Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, false,
- auth_data->bss->channel);
+ auth_data->bss->channel, false);
+ rcu_read_unlock();
}
auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3058,90 +3225,313 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
return 0;
}
+static u32 chandef_downgrade(struct cfg80211_chan_def *c)
+{
+ u32 ret;
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_20:
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ c->width = NL80211_CHAN_WIDTH_20;
+ c->center_freq1 = c->chan->center_freq;
+ ret = IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
+ c->width = NL80211_CHAN_WIDTH_40;
+ ret = IEEE80211_STA_DISABLE_VHT;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ c->center_freq2 = 0;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P80 */
+ tmp /= 4;
+ c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
+ c->width = NL80211_CHAN_WIDTH_80;
+ ret = IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ;
+ break;
+ default:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ WARN_ON_ONCE(1);
+ c->width = NL80211_CHAN_WIDTH_20_NOHT;
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ break;
+ }
+
+ WARN_ON_ONCE(!cfg80211_chandef_valid(c));
+
+ return ret;
+}
+
+static u32
+ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_channel *channel,
+ const struct ieee80211_ht_operation *ht_oper,
+ const struct ieee80211_vht_operation *vht_oper,
+ struct cfg80211_chan_def *chandef)
+{
+ struct cfg80211_chan_def vht_chandef;
+ u32 ht_cfreq, ret;
+
+ chandef->chan = channel;
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = channel->center_freq;
+ chandef->center_freq2 = 0;
+
+ if (!ht_oper || !sband->ht_cap.ht_supported) {
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ chandef->width = NL80211_CHAN_WIDTH_20;
+
+ ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
+ channel->band);
+ /* check that channel matches the right operating channel */
+ if (channel->center_freq != ht_cfreq) {
+ /*
+ * It's possible that some APs are confused here;
+ * Netgear WNDR3700 sometimes reports 4 higher than
+ * the actual channel in association responses, but
+ * since we look at probe response/beacon data here
+ * it should be OK.
+ */
+ sdata_info(sdata,
+ "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
+ channel->center_freq, ht_cfreq,
+ ht_oper->primary_chan, channel->band);
+ ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ /* check 40 MHz support, if we have it */
+ if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
+ switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 += 10;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 -= 10;
+ break;
+ }
+ } else {
+ /* 40 MHz (and 80 MHz) must be supported for VHT */
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!vht_oper || !sband->vht_cap.vht_supported) {
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ vht_chandef.chan = channel;
+ vht_chandef.center_freq1 =
+ ieee80211_channel_to_frequency(vht_oper->center_freq_seg1_idx,
+ channel->band);
+ vht_chandef.center_freq2 = 0;
+
+ if (vht_oper->center_freq_seg2_idx)
+ vht_chandef.center_freq2 =
+ ieee80211_channel_to_frequency(
+ vht_oper->center_freq_seg2_idx,
+ channel->band);
+
+ switch (vht_oper->chan_width) {
+ case IEEE80211_VHT_CHANWIDTH_USE_HT:
+ vht_chandef.width = chandef->width;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_160MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_160;
+ break;
+ case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+ vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
+ break;
+ default:
+ sdata_info(sdata,
+ "AP VHT operation IE has invalid channel width (%d), disable VHT\n",
+ vht_oper->chan_width);
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_valid(&vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information is invalid, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ if (cfg80211_chandef_identical(chandef, &vht_chandef)) {
+ ret = 0;
+ goto out;
+ }
+
+ if (!cfg80211_chandef_compatible(chandef, &vht_chandef)) {
+ sdata_info(sdata,
+ "AP VHT information doesn't match HT, disable VHT\n");
+ ret = IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ *chandef = vht_chandef;
+
+ ret = 0;
+
+ while (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED)) {
+ if (WARN_ON(chandef->width == NL80211_CHAN_WIDTH_20_NOHT)) {
+ ret = IEEE80211_STA_DISABLE_HT |
+ IEEE80211_STA_DISABLE_VHT;
+ goto out;
+ }
+
+ ret = chandef_downgrade(chandef);
+ }
+
+ if (chandef->width != vht_chandef.width)
+ sdata_info(sdata,
+ "local regulatory prevented using AP HT/VHT configuration, downgraded\n");
+
+out:
+ WARN_ON_ONCE(!cfg80211_chandef_valid(chandef));
+ return ret;
+}
+
+static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_bss *cbss)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ht_cap_ie, *vht_cap_ie;
+ const struct ieee80211_ht_cap *ht_cap;
+ const struct ieee80211_vht_cap *vht_cap;
+ u8 chains = 1;
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
+ return chains;
+
+ ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
+ if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
+ ht_cap = (void *)(ht_cap_ie + 2);
+ chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
+ /*
+ * TODO: use "Tx Maximum Number Spatial Streams Supported" and
+ * "Tx Unequal Modulation Supported" fields.
+ */
+ }
+
+ if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
+ return chains;
+
+ vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
+ if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
+ u8 nss;
+ u16 tx_mcs_map;
+
+ vht_cap = (void *)(vht_cap_ie + 2);
+ tx_mcs_map = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map);
+ for (nss = 8; nss > 0; nss--) {
+ if (((tx_mcs_map >> (2 * (nss - 1))) & 3) !=
+ IEEE80211_VHT_MCS_NOT_SUPPORTED)
+ break;
+ }
+ /* TODO: use "Tx Highest Supported Long GI Data Rate" field? */
+ chains = max(chains, nss);
+ }
+
+ return chains;
+}
+
static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- int ht_cfreq;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- const u8 *ht_oper_ie;
const struct ieee80211_ht_operation *ht_oper = NULL;
+ const struct ieee80211_vht_operation *vht_oper = NULL;
struct ieee80211_supported_band *sband;
+ struct cfg80211_chan_def chandef;
+ int ret;
sband = local->hw.wiphy->bands[cbss->channel->band];
- ifmgd->flags &= ~IEEE80211_STA_DISABLE_40MHZ;
+ ifmgd->flags &= ~(IEEE80211_STA_DISABLE_40MHZ |
+ IEEE80211_STA_DISABLE_80P80MHZ |
+ IEEE80211_STA_DISABLE_160MHZ);
- if (sband->ht_cap.ht_supported) {
- ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION,
- cbss->information_elements,
- cbss->len_information_elements);
+ rcu_read_lock();
+
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
+ sband->ht_cap.ht_supported) {
+ const u8 *ht_oper_ie;
+
+ ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
ht_oper = (void *)(ht_oper_ie + 2);
}
- if (ht_oper) {
- ht_cfreq = ieee80211_channel_to_frequency(ht_oper->primary_chan,
- cbss->channel->band);
- /* check that channel matches the right operating channel */
- if (cbss->channel->center_freq != ht_cfreq) {
- /*
- * It's possible that some APs are confused here;
- * Netgear WNDR3700 sometimes reports 4 higher than
- * the actual channel in association responses, but
- * since we look at probe response/beacon data here
- * it should be OK.
- */
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_VHT) &&
+ sband->vht_cap.vht_supported) {
+ const u8 *vht_oper_ie;
+
+ vht_oper_ie = ieee80211_bss_get_ie(cbss,
+ WLAN_EID_VHT_OPERATION);
+ if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
+ vht_oper = (void *)(vht_oper_ie + 2);
+ if (vht_oper && !ht_oper) {
+ vht_oper = NULL;
sdata_info(sdata,
- "Wrong control channel: center-freq: %d ht-cfreq: %d ht->primary_chan: %d band: %d - Disabling HT\n",
- cbss->channel->center_freq,
- ht_cfreq, ht_oper->primary_chan,
- cbss->channel->band);
- ht_oper = NULL;
- } else {
- channel_type = NL80211_CHAN_HT20;
+ "AP advertised VHT without HT, disabling both\n");
+ sdata->flags |= IEEE80211_STA_DISABLE_HT;
+ sdata->flags |= IEEE80211_STA_DISABLE_VHT;
}
}
- if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
- /*
- * cfg80211 already verified that the channel itself can
- * be used, but it didn't check that we can do the right
- * HT type, so do that here as well. If HT40 isn't allowed
- * on this channel, disable 40 MHz operation.
- */
+ ifmgd->flags |= ieee80211_determine_chantype(sdata, sband,
+ cbss->channel,
+ ht_oper, vht_oper,
+ &chandef);
- switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
- case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40PLUS;
- break;
- case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
- if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS)
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- else
- channel_type = NL80211_CHAN_HT40MINUS;
- break;
- }
- }
+ sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
+ local->rx_chains);
- if (!ieee80211_set_channel_type(local, sdata, channel_type)) {
- /* can only fail due to HT40+/- mismatch */
- channel_type = NL80211_CHAN_HT20;
- sdata_info(sdata,
- "disabling 40 MHz due to multi-vif mismatch\n");
- ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ;
- WARN_ON(!ieee80211_set_channel_type(local, sdata,
- channel_type));
- }
+ rcu_read_unlock();
- local->oper_channel = cbss->channel;
- ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+ /* will change later if needed */
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
- return 0;
+ /*
+ * If this fails (possibly due to channel context sharing
+ * on incompatible channels, e.g. 80+80 and 160 sharing the
+ * same control channel) try to use a smaller bandwidth.
+ */
+ ret = ieee80211_vif_use_channel(sdata, &chandef,
+ IEEE80211_CHANCTX_SHARED);
+ while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT)
+ ifmgd->flags |= chandef_downgrade(&chandef);
+ return ret;
}
static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
@@ -3211,7 +3601,7 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.basic_rates = basic_rates;
/* cf. IEEE 802.11 9.2.12 */
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ if (cbss->channel->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
@@ -3273,19 +3663,33 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
case NL80211_AUTHTYPE_NETWORK_EAP:
auth_alg = WLAN_AUTH_LEAP;
break;
+ case NL80211_AUTHTYPE_SAE:
+ auth_alg = WLAN_AUTH_SAE;
+ break;
default:
return -EOPNOTSUPP;
}
- auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL);
+ auth_data = kzalloc(sizeof(*auth_data) + req->sae_data_len +
+ req->ie_len, GFP_KERNEL);
if (!auth_data)
return -ENOMEM;
auth_data->bss = req->bss;
+ if (req->sae_data_len >= 4) {
+ __le16 *pos = (__le16 *) req->sae_data;
+ auth_data->sae_trans = le16_to_cpu(pos[0]);
+ auth_data->sae_status = le16_to_cpu(pos[1]);
+ memcpy(auth_data->data, req->sae_data + 4,
+ req->sae_data_len - 4);
+ auth_data->data_len += req->sae_data_len - 4;
+ }
+
if (req->ie && req->ie_len) {
- memcpy(auth_data->ie, req->ie, req->ie_len);
- auth_data->ie_len = req->ie_len;
+ memcpy(&auth_data->data[auth_data->data_len],
+ req->ie, req->ie_len);
+ auth_data->data_len += req->ie_len;
}
if (req->key && req->key_len) {
@@ -3355,14 +3759,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
const u8 *ssidie, *ht_ie;
int i, err;
- ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
- if (!ssidie)
- return -EINVAL;
-
assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
if (!assoc_data)
return -ENOMEM;
+ rcu_read_lock();
+ ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+ if (!ssidie) {
+ rcu_read_unlock();
+ kfree(assoc_data);
+ return -EINVAL;
+ }
+ memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
+ assoc_data->ssid_len = ssidie[1];
+ rcu_read_unlock();
+
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated)
@@ -3388,13 +3799,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* prepare assoc data */
- /*
- * keep only the 40 MHz disable bit set as it might have
- * been set during authentication already, all other bits
- * should be reset for a new connection
- */
- ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ;
-
ifmgd->beacon_crc_valid = false;
/*
@@ -3408,7 +3812,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
netdev_info(sdata->dev,
"disabling HT/VHT due to WEP/TKIP use\n");
@@ -3416,7 +3820,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
if (req->flags & ASSOC_REQ_DISABLE_HT) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
ifmgd->flags |= IEEE80211_STA_DISABLE_VHT;
}
@@ -3424,7 +3828,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
sband = local->hw.wiphy->bands[req->bss->channel->band];
if (!sband->ht_cap.ht_supported ||
local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) {
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
if (!bss->wmm_used)
netdev_info(sdata->dev,
"disabling HT as WMM/QoS is not supported by the AP\n");
@@ -3452,11 +3856,11 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
if (ifmgd->powersave)
- ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
+ sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
else
- ifmgd->ap_smps = IEEE80211_SMPS_OFF;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
} else
- ifmgd->ap_smps = ifmgd->req_smps;
+ sdata->smps_mode = ifmgd->req_smps;
assoc_data->capability = req->bss->capability;
assoc_data->wmm = bss->wmm_used &&
@@ -3464,12 +3868,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
assoc_data->supp_rates = bss->supp_rates;
assoc_data->supp_rates_len = bss->supp_rates_len;
+ rcu_read_lock();
ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
assoc_data->ap_ht_param =
((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
else
- ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+ ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
+ rcu_read_unlock();
if (bss->wmm_used && bss->uapsd_supported &&
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3480,9 +3886,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
}
- memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
- assoc_data->ssid_len = ssidie[1];
-
if (req->prev_bssid)
memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
@@ -3505,20 +3908,41 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
/* kick off associate process */
ifmgd->assoc_data = assoc_data;
+ ifmgd->dtim_period = 0;
err = ieee80211_prep_connection(sdata, req->bss, true);
if (err)
goto err_clear;
- if (!bss->dtim_period &&
- sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
- /*
- * Wait up to one beacon interval ...
- * should this be more if we miss one?
- */
- sdata_info(sdata, "waiting for beacon from %pM\n",
- ifmgd->bssid);
- assoc_data->timeout = TU_TO_EXP_TIME(req->bss->beacon_interval);
+ if (sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
+ const struct cfg80211_bss_ies *beacon_ies;
+
+ rcu_read_lock();
+ beacon_ies = rcu_dereference(req->bss->beacon_ies);
+ if (!beacon_ies) {
+ /*
+ * Wait up to one beacon interval ...
+ * should this be more if we miss one?
+ */
+ sdata_info(sdata, "waiting for beacon from %pM\n",
+ ifmgd->bssid);
+ assoc_data->timeout =
+ TU_TO_EXP_TIME(req->bss->beacon_interval);
+ } else {
+ const u8 *tim_ie = cfg80211_find_ie(WLAN_EID_TIM,
+ beacon_ies->data,
+ beacon_ies->len);
+ if (tim_ie && tim_ie[1] >=
+ sizeof(struct ieee80211_tim_ie)) {
+ const struct ieee80211_tim_ie *tim;
+ tim = (void *)(tim_ie + 2);
+ ifmgd->dtim_period = tim->dtim_period;
+ }
+ assoc_data->have_beacon = true;
+ assoc_data->sent_assoc = false;
+ assoc_data->timeout = jiffies;
+ }
+ rcu_read_unlock();
} else {
assoc_data->have_beacon = true;
assoc_data->sent_assoc = false;
@@ -3560,40 +3984,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
bool tx = !req->local_state_change;
+ bool sent_frame = false;
mutex_lock(&ifmgd->mtx);
- if (ifmgd->auth_data) {
- ieee80211_destroy_auth_data(sdata, false);
- mutex_unlock(&ifmgd->mtx);
- return 0;
- }
-
sdata_info(sdata,
"deauthenticating from %pM by local choice (reason=%d)\n",
req->bssid, req->reason_code);
- if (ifmgd->associated &&
- ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
- ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
- req->reason_code, tx, frame_buf);
- } else {
+ if (ifmgd->auth_data) {
drv_mgd_prepare_tx(sdata->local, sdata);
ieee80211_send_deauth_disassoc(sdata, req->bssid,
IEEE80211_STYPE_DEAUTH,
req->reason_code, tx,
frame_buf);
+ ieee80211_destroy_auth_data(sdata, false);
+ mutex_unlock(&ifmgd->mtx);
+
+ sent_frame = tx;
+ goto out;
}
+ if (ifmgd->associated &&
+ ether_addr_equal(ifmgd->associated->bssid, req->bssid)) {
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ req->reason_code, tx, frame_buf);
+ sent_frame = tx;
+ }
mutex_unlock(&ifmgd->mtx);
- __cfg80211_send_deauth(sdata->dev, frame_buf,
- IEEE80211_DEAUTH_FRAME_LEN);
-
+ out:
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
+ if (sent_frame)
+ __cfg80211_send_deauth(sdata->dev, frame_buf,
+ IEEE80211_DEAUTH_FRAME_LEN);
+
return 0;
}
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 83608ac1678..a3ad4c3c80a 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -102,11 +102,13 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
ieee80211_sta_reset_conn_monitor(sdata);
}
-void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
- bool offchannel_ps_enable)
+void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ if (WARN_ON(local->use_chanctx))
+ return;
+
/*
* notify the AP about us leaving the channel and stop all
* STA interfaces.
@@ -131,8 +133,7 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
netif_tx_stop_all_queues(sdata->dev);
- if (offchannel_ps_enable &&
- (sdata->vif.type == NL80211_IFTYPE_STATION) &&
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
sdata->u.mgd.associated)
ieee80211_offchannel_ps_enable(sdata);
}
@@ -140,11 +141,13 @@ void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx);
}
-void ieee80211_offchannel_return(struct ieee80211_local *local,
- bool offchannel_ps_disable)
+void ieee80211_offchannel_return(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ if (WARN_ON(local->use_chanctx))
+ return;
+
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
@@ -157,11 +160,9 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
continue;
/* Tell AP we're back */
- if (offchannel_ps_disable &&
- sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.associated)
- ieee80211_offchannel_ps_disable(sdata);
- }
+ if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ sdata->u.mgd.associated)
+ ieee80211_offchannel_ps_disable(sdata);
if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
/*
@@ -193,13 +194,14 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc)
if (roc->mgmt_tx_cookie) {
if (!WARN_ON(!roc->frame)) {
- ieee80211_tx_skb(roc->sdata, roc->frame);
+ ieee80211_tx_skb_tid_band(roc->sdata, roc->frame, 7,
+ roc->chan->band);
roc->frame = NULL;
}
} else {
- cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc,
- roc->chan, roc->chan_type,
- roc->req_duration, GFP_KERNEL);
+ cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie,
+ roc->chan, roc->req_duration,
+ GFP_KERNEL);
}
roc->notified = true;
@@ -276,8 +278,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local)
if (!duration)
duration = 10;
- ret = drv_remain_on_channel(local, roc->chan,
- roc->chan_type,
+ ret = drv_remain_on_channel(local, roc->sdata, roc->chan,
duration);
roc->started = true;
@@ -313,8 +314,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc)
if (!roc->mgmt_tx_cookie)
cfg80211_remain_on_channel_expired(&roc->sdata->wdev,
- (unsigned long)roc,
- roc->chan, roc->chan_type,
+ roc->cookie, roc->chan,
GFP_KERNEL);
list_for_each_entry_safe(dep, tmp, &roc->dependents, list)
@@ -353,7 +353,6 @@ void ieee80211_sw_roc_work(struct work_struct *work)
ieee80211_recalc_idle(local);
local->tmp_channel = roc->chan;
- local->tmp_channel_type = roc->chan_type;
ieee80211_hw_config(local, 0);
/* tell userspace or send frame */
@@ -381,7 +380,7 @@ void ieee80211_sw_roc_work(struct work_struct *work)
local->tmp_channel = NULL;
ieee80211_hw_config(local, 0);
- ieee80211_offchannel_return(local, true);
+ ieee80211_offchannel_return(local);
}
ieee80211_recalc_idle(local);
@@ -458,8 +457,6 @@ void ieee80211_roc_purge(struct ieee80211_sub_if_data *sdata)
list_move_tail(&roc->list, &tmp_list);
roc->abort = true;
}
-
- ieee80211_start_next_roc(local);
mutex_unlock(&local->mtx);
list_for_each_entry_safe(roc, tmp, &tmp_list, list) {
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 5c572e7a1a7..79a48f37d40 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -33,6 +33,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
+ struct ieee80211_chanctx *ctx;
if (!local->open_count)
goto suspend;
@@ -135,12 +136,55 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_BEACON_ENABLED);
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ rcu_access_pointer(sdata->u.ap.beacon))
+ drv_stop_ap(local, sdata);
+
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
drv_remove_interface(local, sdata);
}
sdata = rtnl_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata) {
+ if (local->use_chanctx) {
+ struct ieee80211_chanctx_conf *conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(
+ sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (conf) {
+ ctx = container_of(conf,
+ struct ieee80211_chanctx,
+ conf);
+ drv_unassign_vif_chanctx(local, sdata, ctx);
+ }
+
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
drv_remove_interface(local, sdata);
+ }
+
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ drv_remove_chanctx(local, ctx);
+ mutex_unlock(&local->chanctx_mtx);
/* stop hardware - this must stop RX */
if (local->open_count)
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 3313c117b32..dd88381c53b 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -391,7 +391,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
return;
/* if HT BSS, and we handle a data frame, also try HT rates */
- if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT)
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT)
return;
fc = hdr->frame_control;
@@ -408,8 +408,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
alt_rate.flags |= IEEE80211_TX_RC_MCS;
- if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) ||
- (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS))
+ if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_40)
alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) {
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 10de668eb9f..301386dabf8 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -52,11 +52,21 @@ static inline void rate_control_rate_init(struct sta_info *sta)
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
struct ieee80211_supported_band *sband;
+ struct ieee80211_chanctx_conf *chanctx_conf;
if (!ref)
return;
- sband = local->hw.wiphy->bands[local->oper_channel->band];
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+ rcu_read_unlock();
ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 79633ae06fd..8c5acdc0622 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -154,6 +154,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
struct sk_buff *skb)
{
+ struct minstrel_priv *mp = priv;
struct minstrel_sta_info *mi = priv_sta;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
@@ -181,6 +182,10 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (mi->sample_deferred > 0)
mi->sample_deferred--;
+
+ if (time_after(jiffies, mi->stats_update +
+ (mp->update_interval * HZ) / 1000))
+ minstrel_update_stats(mp, mi);
}
@@ -235,10 +240,6 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
- if (time_after(jiffies, mi->stats_update + (mp->update_interval *
- HZ) / 1000))
- minstrel_update_stats(mp, mi);
-
ndx = mi->max_tp_rate;
if (mrr)
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index fb1d4aa65e8..9f9c453bc45 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -389,9 +389,9 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
- bool last = false;
+ bool last;
int group;
- int i = 0;
+ int i;
if (!msp->is_ht)
return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
@@ -419,13 +419,11 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
mi->sample_packets += info->status.ampdu_len;
+ last = !minstrel_ht_txstat_valid(&ar[0]);
for (i = 0; !last; i++) {
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
!minstrel_ht_txstat_valid(&ar[i + 1]);
- if (!minstrel_ht_txstat_valid(&ar[i]))
- break;
-
group = minstrel_ht_get_group_idx(&ar[i]);
rate = &mi->groups[group].rates[ar[i].idx % 8];
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 61c621e9273..580704eba8b 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -40,6 +40,8 @@
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
struct sk_buff *skb)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
__pskb_trim(skb, skb->len - FCS_LEN);
@@ -47,24 +49,29 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
/* driver bug */
WARN_ON(1);
dev_kfree_skb(skb);
- skb = NULL;
+ return NULL;
}
}
+ if (status->vendor_radiotap_len)
+ __pskb_pull(skb, status->vendor_radiotap_len);
+
return skb;
}
-static inline int should_drop_frame(struct sk_buff *skb,
- int present_fcs_len)
+static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (void *)(skb->data + status->vendor_radiotap_len);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return 1;
- if (unlikely(skb->len < 16 + present_fcs_len))
+ if (unlikely(skb->len < 16 + present_fcs_len +
+ status->vendor_radiotap_len))
return 1;
if (ieee80211_is_ctl(hdr->frame_control) &&
!ieee80211_is_pspoll(hdr->frame_control) &&
@@ -74,32 +81,53 @@ static inline int should_drop_frame(struct sk_buff *skb,
}
static int
-ieee80211_rx_radiotap_len(struct ieee80211_local *local,
- struct ieee80211_rx_status *status)
+ieee80211_rx_radiotap_space(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status)
{
int len;
/* always present fields */
len = sizeof(struct ieee80211_radiotap_header) + 9;
- if (status->flag & RX_FLAG_MACTIME_MPDU)
+ /* allocate extra bitmap */
+ if (status->vendor_radiotap_len)
+ len += 4;
+
+ if (ieee80211_have_rx_timestamp(status)) {
+ len = ALIGN(len, 8);
len += 8;
+ }
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1;
- if (len & 1) /* padding for RX_FLAGS if necessary */
- len++;
+ /* padding for RX_FLAGS if necessary */
+ len = ALIGN(len, 2);
if (status->flag & RX_FLAG_HT) /* HT info */
len += 3;
if (status->flag & RX_FLAG_AMPDU_DETAILS) {
- /* padding */
- while (len & 3)
- len++;
+ len = ALIGN(len, 4);
len += 8;
}
+ if (status->flag & RX_FLAG_VHT) {
+ len = ALIGN(len, 2);
+ len += 12;
+ }
+
+ if (status->vendor_radiotap_len) {
+ if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
+ status->vendor_radiotap_align = 1;
+ /* align standard part of vendor namespace */
+ len = ALIGN(len, 2);
+ /* allocate standard part of vendor namespace */
+ len += 6;
+ /* align vendor-defined part */
+ len = ALIGN(len, status->vendor_radiotap_align);
+ /* vendor-defined part is already in skb */
+ }
+
return len;
}
@@ -118,6 +146,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
u16 rx_flags = 0;
+ int mpdulen;
+
+ mpdulen = skb->len;
+ if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
+ mpdulen += FCS_LEN;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len);
@@ -128,17 +161,30 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
(1 << IEEE80211_RADIOTAP_CHANNEL) |
(1 << IEEE80211_RADIOTAP_ANTENNA) |
(1 << IEEE80211_RADIOTAP_RX_FLAGS));
- rthdr->it_len = cpu_to_le16(rtap_len);
+ rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
- pos = (unsigned char *)(rthdr+1);
+ pos = (unsigned char *)(rthdr + 1);
+
+ if (status->vendor_radiotap_len) {
+ rthdr->it_present |=
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
+ cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
+ put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
+ pos += 4;
+ }
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */
- if (status->flag & RX_FLAG_MACTIME_MPDU) {
- put_unaligned_le64(status->mactime, pos);
- rthdr->it_present |=
- cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
+ if (ieee80211_have_rx_timestamp(status)) {
+ /* padding */
+ while ((pos - (u8 *)rthdr) & 7)
+ *pos++ = 0;
+ put_unaligned_le64(
+ ieee80211_calculate_rx_timestamp(local, status,
+ mpdulen, 0),
+ pos);
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT);
pos += 8;
}
@@ -152,7 +198,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
pos++;
/* IEEE80211_RADIOTAP_RATE */
- if (!rate || status->flag & RX_FLAG_HT) {
+ if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) {
/*
* Without rate information don't add it. If we have,
* MCS information is a separate field in radiotap,
@@ -172,7 +218,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
if (status->band == IEEE80211_BAND_5GHZ)
put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
pos);
- else if (status->flag & RX_FLAG_HT)
+ else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
pos);
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
@@ -205,7 +251,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
/* IEEE80211_RADIOTAP_RX_FLAGS */
/* ensure 2 byte alignment for the 2 byte field as required */
if ((pos - (u8 *)rthdr) & 1)
- pos++;
+ *pos++ = 0;
if (status->flag & RX_FLAG_FAILED_PLCP_CRC)
rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP;
put_unaligned_le16(rx_flags, pos);
@@ -255,6 +301,56 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = 0;
*pos++ = 0;
}
+
+ if (status->flag & RX_FLAG_VHT) {
+ u16 known = local->hw.radiotap_vht_details;
+
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+ /* known field - how to handle 80+80? */
+ if (status->flag & RX_FLAG_80P80MHZ)
+ known &= ~IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
+ put_unaligned_le16(known, pos);
+ pos += 2;
+ /* flags */
+ if (status->flag & RX_FLAG_SHORT_GI)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ pos++;
+ /* bandwidth */
+ if (status->flag & RX_FLAG_80MHZ)
+ *pos++ = 4;
+ else if (status->flag & RX_FLAG_80P80MHZ)
+ *pos++ = 0; /* marked not known above */
+ else if (status->flag & RX_FLAG_160MHZ)
+ *pos++ = 11;
+ else if (status->flag & RX_FLAG_40MHZ)
+ *pos++ = 1;
+ else /* 20 MHz */
+ *pos++ = 0;
+ /* MCS/NSS */
+ *pos = (status->rate_idx << 4) | status->vht_nss;
+ pos += 4;
+ /* coding field */
+ pos++;
+ /* group ID */
+ pos++;
+ /* partial_aid */
+ pos += 2;
+ }
+
+ if (status->vendor_radiotap_len) {
+ /* ensure 2 byte alignment for the vendor field as required */
+ if ((pos - (u8 *)rthdr) & 1)
+ *pos++ = 0;
+ *pos++ = status->vendor_radiotap_oui[0];
+ *pos++ = status->vendor_radiotap_oui[1];
+ *pos++ = status->vendor_radiotap_oui[2];
+ *pos++ = status->vendor_radiotap_subns;
+ put_unaligned_le16(status->vendor_radiotap_len, pos);
+ pos += 2;
+ /* align the actual payload as requested */
+ while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1))
+ *pos++ = 0;
+ }
}
/*
@@ -282,14 +378,11 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
* the SKB because it has a bad FCS/PLCP checksum.
*/
- /* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
-
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* make sure hdr->frame_control is on the linear part */
- if (!pskb_may_pull(origskb, 2)) {
+ /* ensure hdr->frame_control and vendor radiotap data are in skb head */
+ if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) {
dev_kfree_skb(origskb);
return NULL;
}
@@ -303,6 +396,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return remove_monitor_info(local, origskb);
}
+ /* room for the radiotap header based on driver features */
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
+
if (should_drop_frame(origskb, present_fcs_len)) {
/* only need to expand headroom if necessary */
skb = origskb;
@@ -374,7 +470,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return origskb;
}
-
static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -403,10 +498,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx)
*
* We also use that counter for non-QoS STAs.
*/
- seqno_idx = NUM_RX_DATA_QUEUES;
+ seqno_idx = IEEE80211_NUM_TIDS;
security_idx = 0;
if (ieee80211_is_mgmt(hdr->frame_control))
- security_idx = NUM_RX_DATA_QUEUES;
+ security_idx = IEEE80211_NUM_TIDS;
tid = 0;
}
@@ -481,8 +576,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data;
struct ieee80211_mmie *mmie;
- if (skb->len < 24 + sizeof(*mmie) ||
- !is_multicast_ether_addr(hdr->da))
+ if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da))
return -1;
if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr))
@@ -497,9 +591,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return le16_to_cpu(mmie->key_id);
}
-
-static ieee80211_rx_result
-ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
+static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
char *dev_addr = rx->sdata->vif.addr;
@@ -507,7 +599,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
if (is_multicast_ether_addr(hdr->addr1)) {
if (ieee80211_has_tods(hdr->frame_control) ||
- !ieee80211_has_fromds(hdr->frame_control))
+ !ieee80211_has_fromds(hdr->frame_control))
return RX_DROP_MONITOR;
if (ether_addr_equal(hdr->addr3, dev_addr))
return RX_DROP_MONITOR;
@@ -531,10 +623,15 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_action(hdr->frame_control)) {
u8 category;
+
+ /* make sure category field is present */
+ if (rx->skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return RX_DROP_MONITOR;
+
mgmt = (struct ieee80211_mgmt *)hdr;
category = mgmt->u.action.category;
if (category != WLAN_CATEGORY_MESH_ACTION &&
- category != WLAN_CATEGORY_SELF_PROTECTED)
+ category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_MONITOR;
return RX_CONTINUE;
}
@@ -546,7 +643,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
return RX_DROP_MONITOR;
-
}
return RX_CONTINUE;
@@ -570,7 +666,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2)
return (sq1 - sq2) & SEQ_MASK;
}
-
static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
struct tid_ampdu_rx *tid_agg_rx,
int index)
@@ -883,14 +978,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
*/
if (rx->sta && rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
ieee80211_is_data_present(hdr->frame_control)) {
- u16 ethertype;
- u8 *payload;
-
- payload = rx->skb->data +
- ieee80211_hdrlen(hdr->frame_control);
- ethertype = (payload[6] << 8) | payload[7];
- if (cpu_to_be16(ethertype) ==
- rx->sdata->control_port_protocol)
+ unsigned int hdrlen;
+ __be16 ethertype;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (rx->skb->len < hdrlen + 8)
+ return RX_DROP_MONITOR;
+
+ skb_copy_bits(rx->skb, hdrlen + 6, &ethertype, 2);
+ if (ethertype == rx->sdata->control_port_protocol)
return RX_CONTINUE;
}
@@ -1141,12 +1238,19 @@ ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static void ap_sta_ps_start(struct sta_info *sta)
+static void sta_ps_start(struct sta_info *sta)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ struct ps_data *ps;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
- atomic_inc(&sdata->bss->num_sta_ps);
+ atomic_inc(&ps->num_sta_ps);
set_sta_flag(sta, WLAN_STA_PS_STA);
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta);
@@ -1154,7 +1258,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
sta->sta.addr, sta->sta.aid);
}
-static void ap_sta_ps_end(struct sta_info *sta)
+static void sta_ps_end(struct sta_info *sta)
{
ps_dbg(sta->sdata, "STA %pM aid %d exits power save mode\n",
sta->sta.addr, sta->sta.aid);
@@ -1181,9 +1285,9 @@ int ieee80211_sta_ps_transition(struct ieee80211_sta *sta, bool start)
return -EINVAL;
if (start)
- ap_sta_ps_start(sta_inf);
+ sta_ps_start(sta_inf);
else
- ap_sta_ps_end(sta_inf);
+ sta_ps_end(sta_inf);
return 0;
}
@@ -1277,17 +1381,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
/*
* Update last_rx only for IBSS packets which are for the current
- * BSSID to avoid keeping the current IBSS network alive in cases
- * where other STAs start using different BSSID.
+ * BSSID and for station already AUTHORIZED to avoid keeping the
+ * current IBSS network alive in cases where other STAs start
+ * using different BSSID. This will also give the station another
+ * chance to restart the authentication/authorization in case
+ * something went wrong the first time.
*/
if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
NL80211_IFTYPE_ADHOC);
- if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid)) {
+ if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) &&
+ test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
sta->last_rx = jiffies;
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
} else if (!is_multicast_ether_addr(hdr->addr1)) {
@@ -1299,6 +1408,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control)) {
sta->last_rx_rate_idx = status->rate_idx;
sta->last_rx_rate_flag = status->flag;
+ sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
@@ -1335,10 +1445,10 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
*/
if (ieee80211_is_data(hdr->frame_control) &&
!ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_end(sta);
+ sta_ps_end(sta);
} else {
if (ieee80211_has_pm(hdr->frame_control))
- ap_sta_ps_start(sta);
+ sta_ps_start(sta);
}
}
@@ -1384,9 +1494,7 @@ ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
struct sk_buff **skb)
{
struct ieee80211_fragment_entry *entry;
- int idx;
- idx = sdata->fragment_next;
entry = &sdata->fragments[sdata->fragment_next++];
if (sdata->fragment_next >= IEEE80211_FRAGMENT_MAX)
sdata->fragment_next = 0;
@@ -1462,11 +1570,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
hdr = (struct ieee80211_hdr *)rx->skb->data;
fc = hdr->frame_control;
+
+ if (ieee80211_is_ctl(fc))
+ return RX_CONTINUE;
+
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
- (rx->skb)->len < 24 ||
is_multicast_ether_addr(hdr->addr1))) {
/* not fragmented */
goto out;
@@ -1570,18 +1681,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
-static int
-ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
+static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx)
{
- if (unlikely(!rx->sta ||
- !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
+ if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED)))
return -EACCES;
return 0;
}
-static int
-ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
+static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
{
struct sk_buff *skb = rx->skb;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
@@ -1603,8 +1711,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc)
return 0;
}
-static int
-ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
+static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
@@ -1889,6 +1996,20 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ /* make sure fixed part of mesh header is there, also checks skb len */
+ if (!pskb_may_pull(rx->skb, hdrlen + 6))
+ return RX_DROP_MONITOR;
+
+ mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+
+ /* make sure full mesh header is there, also checks skb len */
+ if (!pskb_may_pull(rx->skb,
+ hdrlen + ieee80211_get_mesh_hdrlen(mesh_hdr)))
+ return RX_DROP_MONITOR;
+
+ /* reload pointers */
+ hdr = (struct ieee80211_hdr *) skb->data;
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
/* frame is in RMC, don't forward */
@@ -1897,7 +2018,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
mesh_rmc_check(hdr->addr3, mesh_hdr, rx->sdata))
return RX_DROP_MONITOR;
- if (!ieee80211_is_data(hdr->frame_control))
+ if (!ieee80211_is_data(hdr->frame_control) ||
+ !(status->rx_flags & IEEE80211_RX_RA_MATCH))
return RX_CONTINUE;
if (!mesh_hdr->ttl)
@@ -1911,9 +2033,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
if (is_multicast_ether_addr(hdr->addr1)) {
mpp_addr = hdr->addr3;
proxied_addr = mesh_hdr->eaddr1;
- } else {
+ } else if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6) {
+ /* has_a4 already checked in ieee80211_rx_mesh_check */
mpp_addr = hdr->addr4;
proxied_addr = mesh_hdr->eaddr2;
+ } else {
+ return RX_DROP_MONITOR;
}
rcu_read_lock();
@@ -1941,12 +2066,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
}
skb_set_queue_mapping(skb, q);
- if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
- goto out;
-
if (!--mesh_hdr->ttl) {
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl);
- return RX_DROP_MONITOR;
+ goto out;
}
if (!ifmsh->mshcfg.dot11MeshForwarding)
@@ -1973,7 +2095,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} else {
/* unable to resolve next hop */
mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3,
- 0, reason, fwd_hdr->addr2, sdata);
+ 0, reason, fwd_hdr->addr2, sdata);
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
kfree_skb(fwd_skb);
return RX_DROP_MONITOR;
@@ -2182,7 +2304,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx)
cfg80211_report_obss_beacon(rx->local->hw.wiphy,
rx->skb->data, rx->skb->len,
- status->freq, sig, GFP_ATOMIC);
+ status->freq, sig);
rx->flags |= IEEE80211_RX_BEACON_REPORTED;
}
@@ -2211,7 +2333,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (len < IEEE80211_MIN_ACTION_SIZE)
return RX_DROP_UNUSABLE;
- if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
+ if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
+ mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
return RX_DROP_UNUSABLE;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@@ -2353,6 +2476,10 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
break;
case WLAN_CATEGORY_SELF_PROTECTED:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.self_prot.action_code)))
+ break;
+
switch (mgmt->u.action.u.self_prot.action_code) {
case WLAN_SP_MESH_PEERING_OPEN:
case WLAN_SP_MESH_PEERING_CLOSE:
@@ -2371,10 +2498,14 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
}
break;
case WLAN_CATEGORY_MESH_ACTION:
+ if (len < (IEEE80211_MIN_ACTION_SIZE +
+ sizeof(mgmt->u.action.u.mesh_action.action_code)))
+ break;
+
if (!ieee80211_vif_is_mesh(&sdata->vif))
break;
if (mesh_action_is_path_sel(mgmt) &&
- (!mesh_path_sel_is_hwmp(sdata)))
+ !mesh_path_sel_is_hwmp(sdata))
break;
goto queue;
}
@@ -2430,7 +2561,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
return RX_QUEUED;
}
-
return RX_CONTINUE;
}
@@ -2560,7 +2690,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
goto out_free_skb;
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_len(local, status);
+ needed_headroom = ieee80211_rx_radiotap_space(local, status);
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
@@ -2623,7 +2753,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx,
status = IEEE80211_SKB_RXCB((rx->skb));
sband = rx->local->hw.wiphy->bands[status->band];
- if (!(status->flag & RX_FLAG_HT))
+ if (!(status->flag & RX_FLAG_HT) &&
+ !(status->flag & RX_FLAG_VHT))
rate = &sband->bitrates[status->rate_idx];
ieee80211_rx_cooked_monitor(rx, rate);
@@ -2790,8 +2921,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx,
status->rx_flags &= ~IEEE80211_RX_RA_MATCH;
} else if (!rx->sta) {
int rate_idx;
- if (status->flag & RX_FLAG_HT)
- rate_idx = 0; /* TODO: HT rates */
+ if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
+ rate_idx = 0; /* TODO: HT/VHT rates */
else
rate_idx = status->rate_idx;
ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2,
@@ -2913,10 +3044,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
local->dot11ReceivedFragmentCount++;
- if (ieee80211_is_mgmt(fc))
- err = skb_linearize(skb);
- else
+ if (ieee80211_is_mgmt(fc)) {
+ /* drop frame if too short for header */
+ if (skb->len < ieee80211_hdrlen(fc))
+ err = -ENOBUFS;
+ else
+ err = skb_linearize(skb);
+ } else {
err = !pskb_may_pull(skb, ieee80211_hdrlen(fc));
+ }
if (err) {
dev_kfree_skb(skb);
@@ -3010,8 +3146,7 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
WARN_ON_ONCE(softirq_count() == 0);
- if (WARN_ON(status->band < 0 ||
- status->band >= IEEE80211_NUM_BANDS))
+ if (WARN_ON(status->band >= IEEE80211_NUM_BANDS))
goto drop;
sband = local->hw.wiphy->bands[status->band];
@@ -3056,17 +3191,22 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
* hardware error. The driver should catch hardware
* errors.
*/
- if (WARN((status->rate_idx < 0 ||
- status->rate_idx > 76),
+ if (WARN(status->rate_idx > 76,
"Rate marked as an HT rate but passed "
"status->rate_idx is not "
"an MCS index [0-76]: %d (0x%02x)\n",
status->rate_idx,
status->rate_idx))
goto drop;
+ } else if (status->flag & RX_FLAG_VHT) {
+ if (WARN_ONCE(status->rate_idx > 9 ||
+ !status->vht_nss ||
+ status->vht_nss > 8,
+ "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n",
+ status->rate_idx, status->vht_nss))
+ goto drop;
} else {
- if (WARN_ON(status->rate_idx < 0 ||
- status->rate_idx >= sband->n_bitrates))
+ if (WARN_ON(status->rate_idx >= sband->n_bitrates))
goto drop;
rate = &sband->bitrates[status->rate_idx];
}
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index c4cdbde24fd..bf82e69d060 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -113,18 +113,6 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
bss->valid_data |= IEEE80211_BSS_VALID_ERP;
}
- if (elems->tim && (!elems->parse_error ||
- !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) {
- struct ieee80211_tim_ie *tim_ie = elems->tim;
- bss->dtim_period = tim_ie->dtim_period;
- if (!elems->parse_error)
- bss->valid_data |= IEEE80211_BSS_VALID_DTIM;
- }
-
- /* If the beacon had no TIM IE, or it was invalid, use 1 */
- if (beacon && !bss->dtim_period)
- bss->dtim_period = 1;
-
/* replace old supported rates if we get new values */
if (!elems->parse_error ||
!(bss->valid_data & IEEE80211_BSS_VALID_RATES)) {
@@ -174,7 +162,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
u8 *elements;
struct ieee80211_channel *channel;
size_t baselen;
- int freq;
bool beacon;
struct ieee802_11_elems elems;
@@ -209,13 +196,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
ieee802_11_parse_elems(elements, skb->len - baselen, &elems);
- if (elems.ds_params && elems.ds_params_len == 1)
- freq = ieee80211_channel_to_frequency(elems.ds_params[0],
- rx_status->band);
- else
- freq = rx_status->freq;
-
- channel = ieee80211_get_channel(local->hw.wiphy, freq);
+ channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq);
if (!channel || channel->flags & IEEE80211_CHAN_DISABLED)
return;
@@ -254,6 +235,7 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
local->hw_scan_req->n_channels = n_chans;
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
+ local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band,
req->rates[band], 0);
local->hw_scan_req->ie_len = ielen;
@@ -310,7 +292,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted,
if (!was_hw_scan) {
ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
- ieee80211_offchannel_return(local, true);
+ ieee80211_offchannel_return(local);
}
ieee80211_recalc_idle(local);
@@ -336,6 +318,10 @@ EXPORT_SYMBOL(ieee80211_scan_completed);
static int ieee80211_start_sw_scan(struct ieee80211_local *local)
{
+ /* Software scan is not supported in multi-channel cases */
+ if (local->use_chanctx)
+ return -EOPNOTSUPP;
+
/*
* Hardware/driver doesn't support hw_scan, so use software
* scanning instead. First send a nullfunc frame with power save
@@ -355,7 +341,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- ieee80211_offchannel_stop_vifs(local, true);
+ ieee80211_offchannel_stop_vifs(local);
ieee80211_configure_filter(local);
@@ -417,7 +403,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
local->scan_req->ie, local->scan_req->ie_len,
local->scan_req->rates[band], false,
local->scan_req->no_cck,
- local->hw.conf.channel);
+ local->hw.conf.channel, true);
/*
* After sending probe requests, wait for probe responses
@@ -448,11 +434,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
u8 *ies;
+ local->hw_scan_ies_bufsize = 2 + IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len +
+ req->ie_len;
local->hw_scan_req = kmalloc(
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]) +
- 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
- req->ie_len, GFP_KERNEL);
+ local->hw_scan_ies_bufsize, GFP_KERNEL);
if (!local->hw_scan_req)
return -ENOMEM;
@@ -462,6 +450,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
sizeof(*local->hw_scan_req) +
req->n_channels * sizeof(req->channels[0]);
local->hw_scan_req->ie = ies;
+ local->hw_scan_req->flags = req->flags;
local->hw_scan_band = 0;
@@ -480,7 +469,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
__set_bit(SCAN_HW_SCANNING, &local->scanning);
} else if ((req->n_channels == 1) &&
- (req->channels[0] == local->oper_channel)) {
+ (req->channels[0] == local->_oper_channel)) {
/*
* If we are scanning only on the operating channel
* then we do not need to stop normal activities
@@ -562,6 +551,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
unsigned long min_beacon_int = 0;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
+ enum mac80211_scan_state next_scan_state;
/*
* check if at least one STA interface is associated,
@@ -620,10 +610,18 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
usecs_to_jiffies(min_beacon_int * 1024) *
local->hw.conf.listen_interval);
- if (associated && (!tx_empty || bad_latency || listen_int_exceeded))
- local->next_scan_state = SCAN_SUSPEND;
- else
- local->next_scan_state = SCAN_SET_CHANNEL;
+ if (associated && !tx_empty) {
+ if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ next_scan_state = SCAN_ABORT;
+ else
+ next_scan_state = SCAN_SUSPEND;
+ } else if (associated && (bad_latency || listen_int_exceeded)) {
+ next_scan_state = SCAN_SUSPEND;
+ } else {
+ next_scan_state = SCAN_SET_CHANNEL;
+ }
+
+ local->next_scan_state = next_scan_state;
*next_delay = 0;
}
@@ -680,12 +678,8 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
local->scan_channel = NULL;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
- /*
- * Re-enable vifs and beaconing. Leave PS
- * in off-channel state..will put that back
- * on-channel at the end of scanning.
- */
- ieee80211_offchannel_return(local, false);
+ /* disable PS */
+ ieee80211_offchannel_return(local);
*next_delay = HZ / 5;
/* afterwards, resume scan & go to next channel */
@@ -695,8 +689,7 @@ static void ieee80211_scan_state_suspend(struct ieee80211_local *local,
static void ieee80211_scan_state_resume(struct ieee80211_local *local,
unsigned long *next_delay)
{
- /* PS already is in off-channel mode */
- ieee80211_offchannel_stop_vifs(local, false);
+ ieee80211_offchannel_stop_vifs(local);
if (local->ops->flush) {
drv_flush(local, false);
@@ -794,6 +787,9 @@ void ieee80211_scan_work(struct work_struct *work)
case SCAN_RESUME:
ieee80211_scan_state_resume(local, &next_delay);
break;
+ case SCAN_ABORT:
+ aborted = true;
+ goto out_complete;
}
} while (next_delay == 0);
@@ -819,9 +815,9 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
return res;
}
-int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
- const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan)
+int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
+ const u8 *ssid, u8 ssid_len,
+ struct ieee80211_channel *chan)
{
struct ieee80211_local *local = sdata->local;
int ret = -EBUSY;
@@ -835,22 +831,36 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
/* fill internal scan request */
if (!chan) {
- int i, nchan = 0;
+ int i, max_n;
+ int n_ch = 0;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!local->hw.wiphy->bands[band])
continue;
- for (i = 0;
- i < local->hw.wiphy->bands[band]->n_channels;
- i++) {
- local->int_scan_req->channels[nchan] =
+
+ max_n = local->hw.wiphy->bands[band]->n_channels;
+ for (i = 0; i < max_n; i++) {
+ struct ieee80211_channel *tmp_ch =
&local->hw.wiphy->bands[band]->channels[i];
- nchan++;
+
+ if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_DISABLED))
+ continue;
+
+ local->int_scan_req->channels[n_ch] = tmp_ch;
+ n_ch++;
}
}
- local->int_scan_req->n_channels = nchan;
+ if (WARN_ON_ONCE(n_ch == 0))
+ goto unlock;
+
+ local->int_scan_req->n_channels = n_ch;
} else {
+ if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_DISABLED)))
+ goto unlock;
+
local->int_scan_req->channels[0] = chan;
local->int_scan_req->n_channels = 1;
}
@@ -917,8 +927,11 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
struct cfg80211_sched_scan_request *req)
{
struct ieee80211_local *local = sdata->local;
- struct ieee80211_sched_scan_ies sched_scan_ies;
- int ret, i;
+ struct ieee80211_sched_scan_ies sched_scan_ies = {};
+ int ret, i, iebufsz;
+
+ iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
+ local->scan_ies_len + req->ie_len;
mutex_lock(&local->mtx);
@@ -936,10 +949,7 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
if (!local->hw.wiphy->bands[i])
continue;
- sched_scan_ies.ie[i] = kzalloc(2 + IEEE80211_MAX_SSID_LEN +
- local->scan_ies_len +
- req->ie_len,
- GFP_KERNEL);
+ sched_scan_ies.ie[i] = kzalloc(iebufsz, GFP_KERNEL);
if (!sched_scan_ies.ie[i]) {
ret = -ENOMEM;
goto out_free;
@@ -947,8 +957,8 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
- req->ie, req->ie_len, i,
- (u32) -1, 0);
+ iebufsz, req->ie, req->ie_len,
+ i, (u32) -1, 0);
}
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 0a4e4c04db8..ca9fde19818 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,13 +91,13 @@ static int sta_info_hash_del(struct ieee80211_local *local,
return -ENOENT;
}
-static void free_sta_work(struct work_struct *wk)
+static void cleanup_single_sta(struct sta_info *sta)
{
- struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
int ac, i;
struct tid_ampdu_tx *tid_tx;
struct ieee80211_sub_if_data *sdata = sta->sdata;
struct ieee80211_local *local = sdata->local;
+ struct ps_data *ps;
/*
* At this point, when being called as call_rcu callback,
@@ -107,18 +107,22 @@ static void free_sta_work(struct work_struct *wk)
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
- BUG_ON(!sdata->bss);
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_STA);
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
sta_info_recalc_tim(sta);
}
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->ps_tx_buf[ac]);
- __skb_queue_purge(&sta->tx_filtered[ac]);
+ ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
+ ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
}
#ifdef CONFIG_MAC80211_MESH
@@ -137,22 +141,46 @@ static void free_sta_work(struct work_struct *wk)
* drivers have to handle aggregation stop being requested, followed
* directly by station destruction.
*/
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
if (!tid_tx)
continue;
- __skb_queue_purge(&tid_tx->pending);
+ ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
kfree(tid_tx);
}
sta_info_free(local, sta);
}
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
+{
+ struct sta_info *sta;
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ while (!list_empty(&sdata->cleanup_stations)) {
+ sta = list_first_entry(&sdata->cleanup_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+
+ cleanup_single_sta(sta);
+
+ spin_lock_bh(&sdata->cleanup_stations_lock);
+ }
+
+ spin_unlock_bh(&sdata->cleanup_stations_lock);
+}
+
static void free_sta_rcu(struct rcu_head *h)
{
struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
- ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+ spin_lock(&sdata->cleanup_stations_lock);
+ list_add_tail(&sta->list, &sdata->cleanup_stations);
+ spin_unlock(&sdata->cleanup_stations_lock);
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
}
/* protected by RCU */
@@ -305,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
spin_lock_init(&sta->lock);
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
- INIT_WORK(&sta->free_sta_wk, free_sta_work);
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
mutex_init(&sta->ampdu_mlme.mtx);
@@ -325,7 +352,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return NULL;
}
- for (i = 0; i < STA_TID_NUM; i++) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
/*
* timer_to_tid must be initialized with identity mapping
* to enable session_timer's data differentiation. See
@@ -338,7 +365,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
skb_queue_head_init(&sta->tx_filtered[i]);
}
- for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
@@ -502,22 +529,22 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
-static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_set(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __set_bit() format.
*/
- bss->tim[aid / 8] |= (1 << (aid % 8));
+ tim[id / 8] |= (1 << (id % 8));
}
-static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_clear(u8 *tim, u16 id)
{
/*
* This format has been mandated by the IEEE specifications,
* so this line may not be changed to use the __clear_bit() format.
*/
- bss->tim[aid / 8] &= ~(1 << (aid % 8));
+ tim[id / 8] &= ~(1 << (id % 8));
}
static unsigned long ieee80211_tids_for_ac(int ac)
@@ -541,14 +568,23 @@ static unsigned long ieee80211_tids_for_ac(int ac)
void sta_info_recalc_tim(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
- struct ieee80211_if_ap *bss = sta->sdata->bss;
+ struct ps_data *ps;
unsigned long flags;
bool indicate_tim = false;
u8 ignore_for_tim = sta->sta.uapsd_queues;
int ac;
+ u16 id;
+
+ if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+ sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ if (WARN_ON_ONCE(!sta->sdata->bss))
+ return;
- if (WARN_ON_ONCE(!sta->sdata->bss))
+ ps = &sta->sdata->bss->ps;
+ id = sta->sta.aid;
+ } else {
return;
+ }
/* No need to do anything if the driver does all */
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
@@ -587,9 +623,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
spin_lock_irqsave(&local->tim_lock, flags);
if (indicate_tim)
- __bss_tim_set(bss, sta->sta.aid);
+ __bss_tim_set(ps->tim, id);
else
- __bss_tim_clear(bss, sta->sta.aid);
+ __bss_tim_clear(ps->tim, id);
if (local->ops->set_tim) {
local->tim_in_locked_section = true;
@@ -848,7 +884,7 @@ void sta_info_init(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local)
{
- del_timer(&local->sta_cleanup);
+ del_timer_sync(&local->sta_cleanup);
sta_info_flush(local, NULL);
}
@@ -877,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local,
}
mutex_unlock(&local->sta_mtx);
+ rcu_barrier();
+
+ if (sdata) {
+ ieee80211_cleanup_sdata_stas(sdata);
+ cancel_work_sync(&sdata->cleanup_stations_wk);
+ } else {
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ ieee80211_cleanup_sdata_stas(sdata);
+ cancel_work_sync(&sdata->cleanup_stations_wk);
+ }
+ mutex_unlock(&local->iflist_mtx);
+ }
+
return ret;
}
@@ -893,8 +943,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
continue;
if (time_after(jiffies, sta->last_rx + exp_time)) {
- ibss_dbg(sdata, "expiring inactive STA %pM\n",
- sta->sta.addr);
+ sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
+ sta->sta.addr);
WARN_ON(__sta_info_destroy(sta));
}
}
@@ -948,10 +998,17 @@ static void clear_sta_ps_flags(void *_sta)
{
struct sta_info *sta = _sta;
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ps_data *ps;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+ ps = &sdata->bss->ps;
+ else
+ return;
clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
- atomic_dec(&sdata->bss->num_sta_ps);
+ atomic_dec(&ps->num_sta_ps);
}
/* powersave support code */
@@ -961,10 +1018,11 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
struct sk_buff_head pending;
int filtered = 0, buffered = 0, ac;
+ unsigned long flags;
clear_sta_flag(sta, WLAN_STA_SP);
- BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+ BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
sta->driver_buffered_tids = 0;
if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
@@ -976,12 +1034,16 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
int count = skb_queue_len(&pending), tmp;
+ spin_lock_irqsave(&sta->tx_filtered[ac].lock, flags);
skb_queue_splice_tail_init(&sta->tx_filtered[ac], &pending);
+ spin_unlock_irqrestore(&sta->tx_filtered[ac].lock, flags);
tmp = skb_queue_len(&pending);
filtered += tmp - count;
count = tmp;
+ spin_lock_irqsave(&sta->ps_tx_buf[ac].lock, flags);
skb_queue_splice_tail_init(&sta->ps_tx_buf[ac], &pending);
+ spin_unlock_irqrestore(&sta->ps_tx_buf[ac].lock, flags);
tmp = skb_queue_len(&pending);
buffered += tmp - count;
}
@@ -1008,6 +1070,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
__le16 fc;
bool qos = test_sta_flag(sta, WLAN_STA_WME);
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
if (qos) {
fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
@@ -1057,7 +1120,16 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
- ieee80211_xmit(sdata, skb);
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+
+ ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+ rcu_read_unlock();
}
static void
@@ -1338,7 +1410,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
{
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
- if (WARN_ON(tid >= STA_TID_NUM))
+ if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
return;
if (buffered)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index c88f161f811..37c1889afd3 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -80,7 +80,6 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TOFFSET_KNOWN,
};
-#define STA_TID_NUM 16
#define ADDBA_RESP_INTERVAL HZ
#define HT_AGG_MAX_RETRIES 15
#define HT_AGG_BURST_RETRIES 3
@@ -197,15 +196,15 @@ struct tid_ampdu_rx {
struct sta_ampdu_mlme {
struct mutex mtx;
/* rx */
- struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM];
- unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)];
- unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)];
+ struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS];
+ unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
+ unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)];
/* tx */
struct work_struct work;
- struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM];
- struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM];
- unsigned long last_addba_req_time[STA_TID_NUM];
- u8 addba_req_num[STA_TID_NUM];
+ struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS];
+ struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS];
+ unsigned long last_addba_req_time[IEEE80211_NUM_TIDS];
+ u8 addba_req_num[IEEE80211_NUM_TIDS];
u8 dialog_token_allocator;
};
@@ -228,6 +227,7 @@ struct sta_ampdu_mlme {
* "the" transmit rate
* @last_rx_rate_idx: rx status rate index of the last data packet
* @last_rx_rate_flag: rx status flag of the last data packet
+ * @last_rx_rate_vht_nss: rx status nss of last data packet
* @lock: used for locking all fields that require locking, see comments
* in the header file.
* @drv_unblock_wk: used for driver PS unblocking
@@ -250,6 +250,7 @@ struct sta_ampdu_mlme {
* @rx_dropped: number of dropped MPDUs from this STA
* @last_signal: signal of last received frame from this STA
* @avg_signal: moving average of signal of received frames from this STA
+ * @last_ack_signal: signal of last received Ack frame from this STA
* @last_seq_ctrl: last received seq/frag number from this STA (per RX queue)
* @tx_filtered_count: number of frames the hardware filtered for this STA
* @tx_retry_failed: number of frames that failed retry
@@ -273,7 +274,7 @@ struct sta_ampdu_mlme {
* @t_offset: timing offset relative to this host
* @t_offset_setpoint: reference timing offset of this sta to be used when
* calculating clockdrift
- * @ch_type: peer's channel type
+ * @ch_width: peer's channel width
* @debugfs: debug filesystem info
* @dead: set to true when sta is unlinked
* @uploaded: set to true when sta is uploaded to the driver
@@ -298,7 +299,6 @@ struct sta_info {
spinlock_t lock;
struct work_struct drv_unblock_wk;
- struct work_struct free_sta_wk;
u16 listen_interval;
@@ -329,8 +329,9 @@ struct sta_info {
unsigned long rx_dropped;
int last_signal;
struct ewma avg_signal;
+ int last_ack_signal;
/* Plus 1 for non-QoS frames */
- __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1];
+ __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1];
/* Updated from TX status path only, no locking requirements */
unsigned long tx_filtered_count;
@@ -344,14 +345,15 @@ struct sta_info {
unsigned long tx_fragments;
struct ieee80211_tx_rate last_tx_rate;
int last_rx_rate_idx;
- int last_rx_rate_flag;
+ u32 last_rx_rate_flag;
+ u8 last_rx_rate_vht_nss;
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
/*
* Aggregation information, locked with lock.
*/
struct sta_ampdu_mlme ampdu_mlme;
- u8 timer_to_tid[STA_TID_NUM];
+ u8 timer_to_tid[IEEE80211_NUM_TIDS];
#ifdef CONFIG_MAC80211_MESH
/*
@@ -369,7 +371,7 @@ struct sta_info {
struct timer_list plink_timer;
s64 t_offset;
s64 t_offset_setpoint;
- enum nl80211_channel_type ch_type;
+ enum nl80211_chan_width ch_width;
#endif
#ifdef CONFIG_MAC80211_DEBUGFS
@@ -551,6 +553,8 @@ int sta_info_flush(struct ieee80211_local *local,
void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate,
struct rate_info *rinfo);
+void sta_set_rate_info_rx(struct sta_info *sta,
+ struct rate_info *rinfo);
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time);
@@ -558,4 +562,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
+
#endif /* STA_INFO_H */
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 3af0cc4130f..07d99578a2b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -189,30 +189,31 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb)
}
if (ieee80211_is_action(mgmt->frame_control) &&
- sdata->vif.type == NL80211_IFTYPE_STATION &&
mgmt->u.action.category == WLAN_CATEGORY_HT &&
- mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS) {
+ mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS &&
+ sdata->vif.type == NL80211_IFTYPE_STATION &&
+ ieee80211_sdata_running(sdata)) {
/*
* This update looks racy, but isn't -- if we come
* here we've definitely got a station that we're
* talking to, and on a managed interface that can
* only be the AP. And the only other place updating
- * this variable is before we're associated.
+ * this variable in managed mode is before association.
*/
switch (mgmt->u.action.u.ht_smps.smps_control) {
case WLAN_HT_SMPS_CONTROL_DYNAMIC:
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_DYNAMIC;
+ sdata->smps_mode = IEEE80211_SMPS_DYNAMIC;
break;
case WLAN_HT_SMPS_CONTROL_STATIC:
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_STATIC;
+ sdata->smps_mode = IEEE80211_SMPS_STATIC;
break;
case WLAN_HT_SMPS_CONTROL_DISABLED:
default: /* shouldn't happen since we don't send that */
- sta->sdata->u.mgd.ap_smps = IEEE80211_SMPS_OFF;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
break;
}
- ieee80211_queue_work(&local->hw, &local->recalc_smps);
+ ieee80211_queue_work(&local->hw, &sdata->recalc_smps);
}
}
@@ -324,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
}
+static void ieee80211_report_used_skb(struct ieee80211_local *local,
+ struct sk_buff *skb, bool dropped)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ bool acked = info->flags & IEEE80211_TX_STAT_ACK;
+
+ if (dropped)
+ acked = false;
+
+ if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
+ struct ieee80211_sub_if_data *sdata = NULL;
+ struct ieee80211_sub_if_data *iter_sdata;
+ u64 cookie = (unsigned long)skb;
+
+ rcu_read_lock();
+
+ if (skb->dev) {
+ list_for_each_entry_rcu(iter_sdata, &local->interfaces,
+ list) {
+ if (!iter_sdata->dev)
+ continue;
+
+ if (skb->dev == iter_sdata->dev) {
+ sdata = iter_sdata;
+ break;
+ }
+ }
+ } else {
+ sdata = rcu_dereference(local->p2p_sdata);
+ }
+
+ if (!sdata)
+ skb->dev = NULL;
+ else if (ieee80211_is_nullfunc(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control)) {
+ cfg80211_probe_status(sdata->dev, hdr->addr1,
+ cookie, acked, GFP_ATOMIC);
+ } else {
+ cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
+ skb->len, acked, GFP_ATOMIC);
+ }
+
+ rcu_read_unlock();
+ }
+
+ if (unlikely(info->ack_frame_id)) {
+ struct sk_buff *ack_skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&local->ack_status_lock, flags);
+ ack_skb = idr_find(&local->ack_status_frames,
+ info->ack_frame_id);
+ if (ack_skb)
+ idr_remove(&local->ack_status_frames,
+ info->ack_frame_id);
+ spin_unlock_irqrestore(&local->ack_status_lock, flags);
+
+ if (ack_skb) {
+ if (!dropped) {
+ /* consumes ack_skb */
+ skb_complete_wifi_ack(ack_skb, acked);
+ } else {
+ dev_kfree_skb_any(ack_skb);
+ }
+ }
+ }
+}
+
/*
* Use a static threshold for now, best value to be determined
* by testing ...
@@ -432,7 +502,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
IEEE80211_BAR_CTRL_TID_INFO_MASK) >>
IEEE80211_BAR_CTRL_TID_INFO_SHIFT;
- ieee80211_set_bar_pending(sta, tid, ssn);
+ if (local->hw.flags &
+ IEEE80211_HW_TEARDOWN_AGGR_ON_BAR_FAIL)
+ ieee80211_stop_tx_ba_session(&sta->sta, tid);
+ else
+ ieee80211_set_bar_pending(sta, tid, ssn);
}
}
@@ -469,6 +543,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
sta->lost_packets = 0;
}
}
+
+ if (acked)
+ sta->last_ack_signal = info->status.ack_signal;
}
rcu_read_unlock();
@@ -515,62 +592,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
msecs_to_jiffies(10));
}
- if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) {
- u64 cookie = (unsigned long)skb;
- bool found = false;
-
- acked = info->flags & IEEE80211_TX_STAT_ACK;
-
- rcu_read_lock();
-
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- if (!sdata->dev)
- continue;
-
- if (skb->dev != sdata->dev)
- continue;
-
- found = true;
- break;
- }
-
- if (!skb->dev) {
- sdata = rcu_dereference(local->p2p_sdata);
- if (sdata)
- found = true;
- }
-
- if (!found)
- skb->dev = NULL;
- else if (ieee80211_is_nullfunc(hdr->frame_control) ||
- ieee80211_is_qos_nullfunc(hdr->frame_control)) {
- cfg80211_probe_status(sdata->dev, hdr->addr1,
- cookie, acked, GFP_ATOMIC);
- } else {
- cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data,
- skb->len, acked, GFP_ATOMIC);
- }
-
- rcu_read_unlock();
- }
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- skb_complete_wifi_ack(ack_skb,
- info->flags & IEEE80211_TX_STAT_ACK);
- }
+ ieee80211_report_used_skb(local, skb, false);
/* this was a transmitted frame, but now we want to reuse it */
skb_orphan(skb);
@@ -646,25 +668,17 @@ EXPORT_SYMBOL(ieee80211_report_low_ack);
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
- if (unlikely(info->ack_frame_id)) {
- struct sk_buff *ack_skb;
- unsigned long flags;
-
- spin_lock_irqsave(&local->ack_status_lock, flags);
- ack_skb = idr_find(&local->ack_status_frames,
- info->ack_frame_id);
- if (ack_skb)
- idr_remove(&local->ack_status_frames,
- info->ack_frame_id);
- spin_unlock_irqrestore(&local->ack_status_lock, flags);
-
- /* consumes ack_skb */
- if (ack_skb)
- dev_kfree_skb_any(ack_skb);
- }
+ ieee80211_report_used_skb(local, skb, true);
dev_kfree_skb_any(skb);
}
EXPORT_SYMBOL(ieee80211_free_txskb);
+
+void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
+ struct sk_buff_head *skbs)
+{
+ struct sk_buff *skb;
+
+ while ((skb = __skb_dequeue(skbs)))
+ ieee80211_free_txskb(hw, skb);
+}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 18d9c8a52e9..a8270b441a6 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -28,6 +28,25 @@
#define VIF_PR_FMT " vif:%s(%d%s)"
#define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : ""
+#define CHANCTX_ENTRY __field(u32, control_freq) \
+ __field(u32, chan_width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2) \
+ __field(u8, rx_chains_static) \
+ __field(u8, rx_chains_dynamic)
+#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\
+ __entry->chan_width = ctx->conf.def.width; \
+ __entry->center_freq1 = ctx->conf.def.center_freq1; \
+ __entry->center_freq2 = ctx->conf.def.center_freq2; \
+ __entry->rx_chains_static = ctx->conf.rx_chains_static; \
+ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
+#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d"
+#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \
+ __entry->center_freq1, __entry->center_freq2, \
+ __entry->rx_chains_static, __entry->rx_chains_dynamic
+
+
+
/*
* Tracing for driver callbacks.
*/
@@ -301,20 +320,37 @@ TRACE_EVENT(drv_bss_info_changed,
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
+ __field(u32, changed)
__field(bool, assoc)
+ __field(bool, ibss_joined)
+ __field(bool, ibss_creator)
__field(u16, aid)
__field(bool, cts)
__field(bool, shortpre)
__field(bool, shortslot)
+ __field(bool, enable_beacon)
__field(u8, dtimper)
__field(u16, bcnint)
__field(u16, assoc_cap)
__field(u64, sync_tsf)
__field(u32, sync_device_ts)
__field(u32, basic_rates)
- __field(u32, changed)
- __field(bool, enable_beacon)
+ __array(int, mcast_rate, IEEE80211_NUM_BANDS)
__field(u16, ht_operation_mode)
+ __field(s32, cqm_rssi_thold);
+ __field(s32, cqm_rssi_hyst);
+ __field(u32, channel_width);
+ __field(u32, channel_cfreq1);
+ __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt);
+ __field(bool, arp_filter_enabled);
+ __field(bool, qos);
+ __field(bool, idle);
+ __field(bool, ps);
+ __dynamic_array(u8, ssid, info->ssid_len);
+ __field(bool, hidden_ssid);
+ __field(int, txpower)
+ __field(u8, p2p_ctwindow)
+ __field(bool, p2p_oppps)
),
TP_fast_assign(
@@ -323,17 +359,36 @@ TRACE_EVENT(drv_bss_info_changed,
__entry->changed = changed;
__entry->aid = info->aid;
__entry->assoc = info->assoc;
+ __entry->ibss_joined = info->ibss_joined;
+ __entry->ibss_creator = info->ibss_creator;
__entry->shortpre = info->use_short_preamble;
__entry->cts = info->use_cts_prot;
__entry->shortslot = info->use_short_slot;
+ __entry->enable_beacon = info->enable_beacon;
__entry->dtimper = info->dtim_period;
__entry->bcnint = info->beacon_int;
__entry->assoc_cap = info->assoc_capability;
__entry->sync_tsf = info->sync_tsf;
__entry->sync_device_ts = info->sync_device_ts;
__entry->basic_rates = info->basic_rates;
- __entry->enable_beacon = info->enable_beacon;
+ memcpy(__entry->mcast_rate, info->mcast_rate,
+ sizeof(__entry->mcast_rate));
__entry->ht_operation_mode = info->ht_operation_mode;
+ __entry->cqm_rssi_thold = info->cqm_rssi_thold;
+ __entry->cqm_rssi_hyst = info->cqm_rssi_hyst;
+ __entry->channel_width = info->chandef.width;
+ __entry->channel_cfreq1 = info->chandef.center_freq1;
+ memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list,
+ sizeof(u32) * info->arp_addr_cnt);
+ __entry->arp_filter_enabled = info->arp_filter_enabled;
+ __entry->qos = info->qos;
+ __entry->idle = info->idle;
+ __entry->ps = info->ps;
+ memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+ __entry->hidden_ssid = info->hidden_ssid;
+ __entry->txpower = info->txpower;
+ __entry->p2p_ctwindow = info->p2p_ctwindow;
+ __entry->p2p_oppps = info->p2p_oppps;
),
TP_printk(
@@ -971,28 +1026,31 @@ TRACE_EVENT(drv_get_antenna,
);
TRACE_EVENT(drv_remain_on_channel,
- TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan,
- enum nl80211_channel_type chantype, unsigned int duration),
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
- TP_ARGS(local, chan, chantype, duration),
+ TP_ARGS(local, sdata, chan, duration),
TP_STRUCT__entry(
LOCAL_ENTRY
+ VIF_ENTRY
__field(int, center_freq)
- __field(int, channel_type)
__field(unsigned int, duration)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
__entry->center_freq = chan->center_freq;
- __entry->channel_type = chantype;
__entry->duration = duration;
),
TP_printk(
- LOCAL_PR_FMT " freq:%dMHz duration:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->duration
+ LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms",
+ LOCAL_PR_ARG, VIF_PR_ARG,
+ __entry->center_freq, __entry->duration
)
);
@@ -1001,34 +1059,6 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel,
TP_ARGS(local)
);
-TRACE_EVENT(drv_offchannel_tx,
- TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
- unsigned int wait),
-
- TP_ARGS(local, skb, chan, channel_type, wait),
-
- TP_STRUCT__entry(
- LOCAL_ENTRY
- __field(int, center_freq)
- __field(int, channel_type)
- __field(unsigned int, wait)
- ),
-
- TP_fast_assign(
- LOCAL_ASSIGN;
- __entry->center_freq = chan->center_freq;
- __entry->channel_type = channel_type;
- __entry->wait = wait;
- ),
-
- TP_printk(
- LOCAL_PR_FMT " freq:%dMHz, wait:%dms",
- LOCAL_PR_ARG, __entry->center_freq, __entry->wait
- )
-);
-
TRACE_EVENT(drv_set_ringparam,
TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx),
@@ -1256,6 +1286,146 @@ DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
TP_ARGS(local, sdata)
);
+DECLARE_EVENT_CLASS(local_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+
+ TP_ARGS(local, ctx),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ CHANCTX_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ CHANCTX_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT CHANCTX_PR_FMT,
+ LOCAL_PR_ARG, CHANCTX_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_chanctx, drv_add_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, ctx)
+);
+
+DEFINE_EVENT(local_chanctx, drv_remove_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, ctx)
+);
+
+TRACE_EVENT(drv_change_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx,
+ u32 changed),
+
+ TP_ARGS(local, ctx, changed),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ CHANCTX_ENTRY
+ __field(u32, changed)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ CHANCTX_ASSIGN;
+ __entry->changed = changed;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT CHANCTX_PR_FMT " changed:%#x",
+ LOCAL_PR_ARG, CHANCTX_PR_ARG, __entry->changed
+ )
+);
+
+DECLARE_EVENT_CLASS(local_sdata_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+
+ TP_ARGS(local, sdata, ctx),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANCTX_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANCTX_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT CHANCTX_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANCTX_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_sdata_chanctx, drv_assign_vif_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, sdata, ctx)
+);
+
+DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_chanctx *ctx),
+ TP_ARGS(local, sdata, ctx)
+);
+
+TRACE_EVENT(drv_start_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_bss_conf *info),
+
+ TP_ARGS(local, sdata, info),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, dtimper)
+ __field(u16, bcnint)
+ __dynamic_array(u8, ssid, info->ssid_len);
+ __field(bool, hidden_ssid);
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->dtimper = info->dtim_period;
+ __entry->bcnint = info->beacon_int;
+ memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
+ __entry->hidden_ssid = info->hidden_ssid;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG
+ )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
+DEFINE_EVENT(local_only_evt, drv_restart_complete,
+ TP_PROTO(struct ieee80211_local *local),
+ TP_ARGS(local)
+);
+
/*
* Tracing for API calls that drivers call.
*/
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c9bf83f3665..467c1d1b66f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -324,22 +324,20 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
- /*
- * virtual interfaces are protected by RCU
- */
- rcu_read_lock();
-
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- struct ieee80211_if_ap *ap;
- if (sdata->vif.type != NL80211_IFTYPE_AP)
+ struct ps_data *ps;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ ps = &sdata->u.ap.ps;
+ else
continue;
- ap = &sdata->u.ap;
- skb = skb_dequeue(&ap->ps_bc_buf);
+
+ skb = skb_dequeue(&ps->bc_buf);
if (skb) {
purged++;
dev_kfree_skb(skb);
}
- total += skb_queue_len(&ap->ps_bc_buf);
+ total += skb_queue_len(&ps->bc_buf);
}
/*
@@ -360,8 +358,6 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
}
}
- rcu_read_unlock();
-
local->total_ps_buffered = total;
ps_dbg_hw(&local->hw, "PS buffers full - purged %d frames\n", purged);
}
@@ -371,6 +367,7 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
+ struct ps_data *ps;
/*
* broadcast/multicast frame
@@ -380,16 +377,24 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
* This is done either by the hardware or us.
*/
- /* powersaving STAs only in AP/VLAN mode */
- if (!tx->sdata->bss)
+ /* powersaving STAs currently only in AP/VLAN mode */
+ if (tx->sdata->vif.type == NL80211_IFTYPE_AP ||
+ tx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+ if (!tx->sdata->bss)
+ return TX_CONTINUE;
+
+ ps = &tx->sdata->bss->ps;
+ } else {
return TX_CONTINUE;
+ }
+
/* no buffering for ordered frames */
if (ieee80211_has_order(hdr->frame_control))
return TX_CONTINUE;
/* no stations in PS mode */
- if (!atomic_read(&tx->sdata->bss->num_sta_ps))
+ if (!atomic_read(&ps->num_sta_ps))
return TX_CONTINUE;
info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
@@ -404,14 +409,14 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
purge_old_ps_buffers(tx->local);
- if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) {
+ if (skb_queue_len(&ps->bc_buf) >= AP_MAX_BC_BUFFER) {
ps_dbg(tx->sdata,
"BC TX buffer full - dropping the oldest frame\n");
- dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
+ dev_kfree_skb(skb_dequeue(&ps->bc_buf));
} else
tx->local->total_ps_buffered++;
- skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
+ skb_queue_tail(&ps->bc_buf, tx->skb);
return TX_QUEUED;
}
@@ -951,7 +956,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
fragnum = 0;
skb_queue_walk(&tx->skbs, skb) {
- int next_len;
const __le16 morefrags = cpu_to_le16(IEEE80211_FCTL_MOREFRAGS);
hdr = (void *)skb->data;
@@ -970,7 +974,6 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
info->flags &= ~IEEE80211_TX_CTL_RATE_CTRL_PROBE;
} else {
hdr->frame_control &= ~morefrags;
- next_len = 0;
}
hdr->seq_ctrl |= cpu_to_le16(fragnum & IEEE80211_SCTL_FRAG);
fragnum++;
@@ -1358,7 +1361,7 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
if (tx->skb)
ieee80211_free_txskb(&tx->local->hw, tx->skb);
else
- __skb_queue_purge(&tx->skbs);
+ ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
return -1;
} else if (unlikely(res == TX_QUEUED)) {
I802_DEBUG_INC(tx->local->tx_handlers_queued);
@@ -1372,7 +1375,8 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending)
+ struct sk_buff *skb, bool txpending,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1386,20 +1390,18 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
return true;
}
- rcu_read_lock();
-
/* initialises tx */
led_len = skb->len;
res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
if (unlikely(res_prepare == TX_DROP)) {
ieee80211_free_txskb(&local->hw, skb);
- goto out;
+ return true;
} else if (unlikely(res_prepare == TX_QUEUED)) {
- goto out;
+ return true;
}
- info->band = local->hw.conf.channel->band;
+ info->band = band;
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
@@ -1410,8 +1412,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
if (!invoke_tx_handlers(&tx))
result = __ieee80211_tx(local, &tx.skbs, led_len,
tx.sta, txpending);
- out:
- rcu_read_unlock();
+
return result;
}
@@ -1446,7 +1447,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1454,8 +1456,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
int headroom;
bool may_encrypt;
- rcu_read_lock();
-
may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
headroom = local->tx_headroom;
@@ -1466,7 +1466,6 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
if (ieee80211_skb_resize(sdata, skb, headroom, may_encrypt)) {
ieee80211_free_txskb(&local->hw, skb);
- rcu_read_unlock();
return;
}
@@ -1478,13 +1477,11 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
!is_multicast_ether_addr(hdr->addr1) &&
mesh_nexthop_resolve(skb, sdata)) {
/* skb queued: don't free */
- rcu_read_unlock();
return;
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false);
- rcu_read_unlock();
+ ieee80211_tx(sdata, skb, false, band);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1574,7 +1571,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
- struct ieee80211_channel *chan = local->hw.conf.channel;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_channel *chan;
struct ieee80211_radiotap_header *prthdr =
(struct ieee80211_radiotap_header *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1583,26 +1581,6 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
u16 len_rthdr;
int hdrlen;
- /*
- * Frame injection is not allowed if beaconing is not allowed
- * or if we need radar detection. Beaconing is usually not allowed when
- * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
- * Passive scan is also used in world regulatory domains where
- * your country is not known and as such it should be treated as
- * NO TX unless the channel is explicitly allowed in which case
- * your current regulatory domain would not have the passive scan
- * flag.
- *
- * Since AP mode uses monitor interfaces to inject/TX management
- * frames we can make AP mode the exception to this rule once it
- * supports radar detection as its implementation can deal with
- * radar detection by itself. We can do that later by adding a
- * monitor flag interfaces used for AP support.
- */
- if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_PASSIVE_SCAN)))
- goto fail;
-
/* check for not even having the fixed radiotap header part */
if (unlikely(skb->len < sizeof(struct ieee80211_radiotap_header)))
goto fail; /* too short to be possibly valid */
@@ -1688,11 +1666,48 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
}
}
- ieee80211_xmit(sdata, skb);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ tmp_sdata = rcu_dereference(local->monitor_sdata);
+ if (tmp_sdata)
+ chanctx_conf =
+ rcu_dereference(tmp_sdata->vif.chanctx_conf);
+ }
+
+ if (chanctx_conf)
+ chan = chanctx_conf->def.chan;
+ else if (!local->use_chanctx)
+ chan = local->_oper_channel;
+ else
+ goto fail_rcu;
+
+ /*
+ * Frame injection is not allowed if beaconing is not allowed
+ * or if we need radar detection. Beaconing is usually not allowed when
+ * the mode or operation (Adhoc, AP, Mesh) does not support DFS.
+ * Passive scan is also used in world regulatory domains where
+ * your country is not known and as such it should be treated as
+ * NO TX unless the channel is explicitly allowed in which case
+ * your current regulatory domain would not have the passive scan
+ * flag.
+ *
+ * Since AP mode uses monitor interfaces to inject/TX management
+ * frames we can make AP mode the exception to this rule once it
+ * supports radar detection as its implementation can deal with
+ * radar detection by itself. We can do that later by adding a
+ * monitor flag interfaces used for AP support.
+ */
+ if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
+ IEEE80211_CHAN_PASSIVE_SCAN)))
+ goto fail_rcu;
+
+ ieee80211_xmit(sdata, skb, chan->band);
rcu_read_unlock();
return NETDEV_TX_OK;
+fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK; /* meaning, we dealt with the skb */
@@ -1734,6 +1749,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
bool multicast;
u32 info_flags = 0;
u16 info_id = 0;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_sub_if_data *ap_sdata;
+ enum ieee80211_band band;
if (unlikely(skb->len < ETH_HLEN))
goto fail;
@@ -1743,9 +1761,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
+ rcu_read_lock();
+
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
- rcu_read_lock();
sta = rcu_dereference(sdata->u.vlan.sta);
if (sta) {
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1758,7 +1777,12 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
+ ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
+ u.ap);
+ chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
if (sta)
break;
/* fall through */
@@ -1769,6 +1793,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, sdata->vif.addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 24;
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_WDS:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
@@ -1778,15 +1807,20 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
+ /*
+ * This is the exception! WDS style interfaces are prohibited
+ * when channel contexts are in used so this must be valid
+ */
+ band = local->hw.conf.channel->band;
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
- goto fail;
+ goto fail_rcu;
}
- rcu_read_lock();
+
if (!is_multicast_ether_addr(skb->data)) {
mpath = mesh_path_lookup(skb->data, sdata);
if (!mpath)
@@ -1803,7 +1837,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
!(mppath && !ether_addr_equal(mppath->mpp, skb->data))) {
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
skb->data, skb->data + ETH_ALEN);
- rcu_read_unlock();
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
sdata, NULL, NULL);
} else {
@@ -1819,7 +1852,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
mesh_da = mppath->mpp;
else if (mpath)
mesh_da = mpath->dst;
- rcu_read_unlock();
hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
mesh_da, sdata->vif.addr);
@@ -1839,13 +1871,16 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
skb->data + ETH_ALEN);
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
#endif
case NL80211_IFTYPE_STATION:
if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) {
bool tdls_peer = false;
- rcu_read_lock();
sta = sta_info_get(sdata, skb->data);
if (sta) {
authorized = test_sta_flag(sta,
@@ -1856,7 +1891,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
tdls_auth = test_sta_flag(sta,
WLAN_STA_TDLS_PEER_AUTH);
}
- rcu_read_unlock();
/*
* If the TDLS link is enabled, send everything
@@ -1871,7 +1905,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
if (tdls_direct) {
/* link during setup - throw out frames to peer */
if (!tdls_auth)
- goto fail;
+ goto fail_rcu;
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1896,6 +1930,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
}
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
@@ -1903,9 +1941,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf)
+ goto fail_rcu;
+ band = chanctx_conf->def.chan->band;
break;
default:
- goto fail;
+ goto fail_rcu;
}
/*
@@ -1915,13 +1957,11 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
multicast = is_multicast_ether_addr(hdr.addr1);
if (!multicast) {
- rcu_read_lock();
sta = sta_info_get(sdata, hdr.addr1);
if (sta) {
authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED);
wme_sta = test_sta_flag(sta, WLAN_STA_WME);
}
- rcu_read_unlock();
}
/* For mesh, the use of the QoS header is mandatory */
@@ -1949,7 +1989,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail;
+ goto fail_rcu;
}
if (unlikely(!multicast && skb->sk &&
@@ -2004,7 +2044,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
kfree_skb(tmp_skb);
if (!skb)
- goto fail;
+ goto fail_rcu;
}
hdr.frame_control = fc;
@@ -2052,7 +2092,8 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
- return NETDEV_TX_OK;
+ skb = NULL;
+ goto fail_rcu;
}
}
@@ -2104,10 +2145,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
info->flags = info_flags;
info->ack_frame_id = info_id;
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
+ rcu_read_unlock();
return NETDEV_TX_OK;
+ fail_rcu:
+ rcu_read_unlock();
fail:
dev_kfree_skb(skb);
return NETDEV_TX_OK;
@@ -2120,10 +2164,13 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
void ieee80211_clear_tx_pending(struct ieee80211_local *local)
{
+ struct sk_buff *skb;
int i;
- for (i = 0; i < local->hw.queues; i++)
- skb_queue_purge(&local->pending[i]);
+ for (i = 0; i < local->hw.queues; i++) {
+ while ((skb = skb_dequeue(&local->pending[i])) != NULL)
+ ieee80211_free_txskb(&local->hw, skb);
+ }
}
/*
@@ -2139,11 +2186,18 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
struct sta_info *sta;
struct ieee80211_hdr *hdr;
bool result;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(info->control.vif);
if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
- result = ieee80211_tx(sdata, skb, true);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (unlikely(!chanctx_conf)) {
+ dev_kfree_skb(skb);
+ return true;
+ }
+ result = ieee80211_tx(sdata, skb, true,
+ chanctx_conf->def.chan->band);
} else {
struct sk_buff_head skbs;
@@ -2211,9 +2265,8 @@ void ieee80211_tx_pending(unsigned long data)
/* functions for drivers to get certain frames */
static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_if_ap *bss,
- struct sk_buff *skb,
- struct beacon_data *beacon)
+ struct ps_data *ps,
+ struct sk_buff *skb)
{
u8 *pos, *tim;
int aid0 = 0;
@@ -2221,27 +2274,27 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
/* Generate bitmap for TIM only if there are any STAs in power save
* mode. */
- if (atomic_read(&bss->num_sta_ps) > 0)
+ if (atomic_read(&ps->num_sta_ps) > 0)
/* in the hope that this is faster than
* checking byte-for-byte */
- have_bits = !bitmap_empty((unsigned long*)bss->tim,
+ have_bits = !bitmap_empty((unsigned long*)ps->tim,
IEEE80211_MAX_AID+1);
- if (bss->dtim_count == 0)
- bss->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
+ if (ps->dtim_count == 0)
+ ps->dtim_count = sdata->vif.bss_conf.dtim_period - 1;
else
- bss->dtim_count--;
+ ps->dtim_count--;
tim = pos = (u8 *) skb_put(skb, 6);
*pos++ = WLAN_EID_TIM;
*pos++ = 4;
- *pos++ = bss->dtim_count;
+ *pos++ = ps->dtim_count;
*pos++ = sdata->vif.bss_conf.dtim_period;
- if (bss->dtim_count == 0 && !skb_queue_empty(&bss->ps_bc_buf))
+ if (ps->dtim_count == 0 && !skb_queue_empty(&ps->bc_buf))
aid0 = 1;
- bss->dtim_bc_mc = aid0 == 1;
+ ps->dtim_bc_mc = aid0 == 1;
if (have_bits) {
/* Find largest even number N1 so that bits numbered 1 through
@@ -2249,14 +2302,14 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
* (N2 + 1) x 8 through 2007 are 0. */
n1 = 0;
for (i = 0; i < IEEE80211_MAX_TIM_LEN; i++) {
- if (bss->tim[i]) {
+ if (ps->tim[i]) {
n1 = i & 0xfe;
break;
}
}
n2 = n1;
for (i = IEEE80211_MAX_TIM_LEN - 1; i >= n1; i--) {
- if (bss->tim[i]) {
+ if (ps->tim[i]) {
n2 = i;
break;
}
@@ -2266,7 +2319,7 @@ static void ieee80211_beacon_add_tim(struct ieee80211_sub_if_data *sdata,
*pos++ = n1 | aid0;
/* Part Virt Bitmap */
skb_put(skb, n2 - n1);
- memcpy(pos, bss->tim + n1, n2 - n1 + 1);
+ memcpy(pos, ps->tim + n1, n2 - n1 + 1);
tim[1] = n2 - n1 + 4;
} else {
@@ -2283,16 +2336,16 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_info *info;
struct ieee80211_sub_if_data *sdata = NULL;
- struct ieee80211_if_ap *ap = NULL;
- struct beacon_data *beacon;
- enum ieee80211_band band = local->oper_channel->band;
+ enum ieee80211_band band;
struct ieee80211_tx_rate_control txrc;
+ struct ieee80211_chanctx_conf *chanctx_conf;
rcu_read_lock();
sdata = vif_to_sdata(vif);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!ieee80211_sdata_running(sdata))
+ if (!ieee80211_sdata_running(sdata) || !chanctx_conf)
goto out;
if (tim_offset)
@@ -2301,8 +2354,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*tim_length = 0;
if (sdata->vif.type == NL80211_IFTYPE_AP) {
- ap = &sdata->u.ap;
- beacon = rcu_dereference(ap->beacon);
+ struct ieee80211_if_ap *ap = &sdata->u.ap;
+ struct beacon_data *beacon = rcu_dereference(ap->beacon);
+
if (beacon) {
/*
* headroom, head length,
@@ -2326,14 +2380,12 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
* of the tim bitmap in mac80211 and the driver.
*/
if (local->tim_in_locked_section) {
- ieee80211_beacon_add_tim(sdata, ap, skb,
- beacon);
+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
} else {
unsigned long flags;
spin_lock_irqsave(&local->tim_lock, flags);
- ieee80211_beacon_add_tim(sdata, ap, skb,
- beacon);
+ ieee80211_beacon_add_tim(sdata, &ap->ps, skb);
spin_unlock_irqrestore(&local->tim_lock, flags);
}
@@ -2409,6 +2461,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
*pos++ = WLAN_EID_SSID;
*pos++ = 0x0;
+ band = chanctx_conf->def.chan->band;
+
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
mesh_add_ds_params_ie(skb, sdata) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -2426,6 +2480,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
goto out;
}
+ band = chanctx_conf->def.chan->band;
+
info = IEEE80211_SKB_CB(skb);
info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
@@ -2570,7 +2626,7 @@ EXPORT_SYMBOL(ieee80211_nullfunc_get);
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len)
+ size_t tailroom)
{
struct ieee80211_sub_if_data *sdata;
struct ieee80211_local *local;
@@ -2584,7 +2640,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
- ie_ssid_len + ie_len);
+ ie_ssid_len + tailroom);
if (!skb)
return NULL;
@@ -2605,11 +2661,6 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
memcpy(pos, ssid, ssid_len);
pos += ssid_len;
- if (ie) {
- pos = skb_put(skb, ie_len);
- memcpy(pos, ie, ie_len);
- }
-
return skb;
}
EXPORT_SYMBOL(ieee80211_probereq_get);
@@ -2653,29 +2704,40 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
struct sk_buff *skb = NULL;
struct ieee80211_tx_data tx;
struct ieee80211_sub_if_data *sdata;
- struct ieee80211_if_ap *bss = NULL;
- struct beacon_data *beacon;
+ struct ps_data *ps;
struct ieee80211_tx_info *info;
+ struct ieee80211_chanctx_conf *chanctx_conf;
sdata = vif_to_sdata(vif);
- bss = &sdata->u.ap;
rcu_read_lock();
- beacon = rcu_dereference(bss->beacon);
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (!chanctx_conf)
+ goto out;
- if (sdata->vif.type != NL80211_IFTYPE_AP || !beacon || !beacon->head)
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ struct beacon_data *beacon =
+ rcu_dereference(sdata->u.ap.beacon);
+
+ if (!beacon || !beacon->head)
+ goto out;
+
+ ps = &sdata->u.ap.ps;
+ } else {
goto out;
+ }
- if (bss->dtim_count != 0 || !bss->dtim_bc_mc)
+ if (ps->dtim_count != 0 || !ps->dtim_bc_mc)
goto out; /* send buffered bc/mc only after DTIM beacon */
while (1) {
- skb = skb_dequeue(&bss->ps_bc_buf);
+ skb = skb_dequeue(&ps->bc_buf);
if (!skb)
goto out;
local->total_ps_buffered--;
- if (!skb_queue_empty(&bss->ps_bc_buf) && skb->len >= 2) {
+ if (!skb_queue_empty(&ps->bc_buf) && skb->len >= 2) {
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) skb->data;
/* more buffered multicast/broadcast frames ==> set
@@ -2693,7 +2755,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
tx.flags |= IEEE80211_TX_PS_BUFFERED;
- info->band = local->oper_channel->band;
+ info->band = chanctx_conf->def.chan->band;
if (invoke_tx_handlers(&tx))
skb = NULL;
@@ -2704,8 +2766,9 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
-void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid)
+void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, int tid,
+ enum ieee80211_band band)
{
int ac = ieee802_1d_to_ac[tid & 7];
@@ -2722,6 +2785,6 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb);
+ ieee80211_xmit(sdata, skb, band);
local_bh_enable();
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 94e58687397..f11e8c540db 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
EXPORT_SYMBOL(ieee80211_wake_queues);
void ieee80211_iterate_active_interfaces(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
@@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces(
sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx));
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
mutex_unlock(&local->iflist_mtx);
@@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces(
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
- struct ieee80211_hw *hw,
+ struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
@@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic(
default:
break;
}
+ if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
+ !(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
+ continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
}
sdata = rcu_dereference(local->monitor_sdata);
- if (sdata)
+ if (sdata &&
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
rcu_read_unlock();
@@ -643,13 +653,41 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
break;
}
- if (id != WLAN_EID_VENDOR_SPECIFIC &&
- id != WLAN_EID_QUIET &&
- test_bit(id, seen_elems)) {
- elems->parse_error = true;
- left -= elen;
- pos += elen;
- continue;
+ switch (id) {
+ case WLAN_EID_SSID:
+ case WLAN_EID_SUPP_RATES:
+ case WLAN_EID_FH_PARAMS:
+ case WLAN_EID_DS_PARAMS:
+ case WLAN_EID_CF_PARAMS:
+ case WLAN_EID_TIM:
+ case WLAN_EID_IBSS_PARAMS:
+ case WLAN_EID_CHALLENGE:
+ case WLAN_EID_RSN:
+ case WLAN_EID_ERP_INFO:
+ case WLAN_EID_EXT_SUPP_RATES:
+ case WLAN_EID_HT_CAPABILITY:
+ case WLAN_EID_HT_OPERATION:
+ case WLAN_EID_VHT_CAPABILITY:
+ case WLAN_EID_VHT_OPERATION:
+ case WLAN_EID_MESH_ID:
+ case WLAN_EID_MESH_CONFIG:
+ case WLAN_EID_PEER_MGMT:
+ case WLAN_EID_PREQ:
+ case WLAN_EID_PREP:
+ case WLAN_EID_PERR:
+ case WLAN_EID_RANN:
+ case WLAN_EID_CHANNEL_SWITCH:
+ case WLAN_EID_EXT_CHANSWITCH_ANN:
+ case WLAN_EID_COUNTRY:
+ case WLAN_EID_PWR_CONSTRAINT:
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ if (test_bit(id, seen_elems)) {
+ elems->parse_error = true;
+ left -= elen;
+ pos += elen;
+ continue;
+ }
+ break;
}
if (calc_crc && id < 64 && (filter & (1ULL << id)))
@@ -741,6 +779,18 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
else
elem_parse_failed = true;
break;
+ case WLAN_EID_VHT_CAPABILITY:
+ if (elen >= sizeof(struct ieee80211_vht_cap))
+ elems->vht_cap_elem = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
+ case WLAN_EID_VHT_OPERATION:
+ if (elen >= sizeof(struct ieee80211_vht_operation))
+ elems->vht_operation = (void *)pos;
+ else
+ elem_parse_failed = true;
+ break;
case WLAN_EID_MESH_ID:
elems->mesh_id = pos;
elems->mesh_id_len = elen;
@@ -809,7 +859,7 @@ u32 ieee802_11_parse_elems_crc(u8 *start, size_t len,
if (elem_parse_failed)
elems->parse_error = true;
else
- set_bit(id, seen_elems);
+ __set_bit(id, seen_elems);
left -= elen;
pos += elen;
@@ -832,6 +882,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_queue_params qparam;
+ struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
bool use_11b, enable_qos;
int aCWmin, aCWmax;
@@ -844,8 +895,12 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
memset(&qparam, 0, sizeof(qparam));
- use_11b = (local->oper_channel->band == IEEE80211_BAND_2GHZ) &&
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ use_11b = (chanctx_conf &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) &&
!(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE);
+ rcu_read_unlock();
/*
* By default disable QoS in STA mode for old access points, which do
@@ -924,7 +979,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
const size_t supp_rates_len,
const u8 *supp_rates)
{
- struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
int i, have_higher_than_11mbit = 0;
/* cf. IEEE 802.11 9.2.12 */
@@ -932,11 +987,16 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
if ((supp_rates[i] & 0x7f) * 5 > 110)
have_higher_than_11mbit = 1;
- if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+
+ if (chanctx_conf &&
+ chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
have_higher_than_11mbit)
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+ rcu_read_unlock();
ieee80211_set_wmm_default(sdata, true);
}
@@ -968,7 +1028,7 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
}
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
- u16 transaction, u16 auth_alg,
+ u16 transaction, u16 auth_alg, u16 status,
u8 *extra, size_t extra_len, const u8 *da,
const u8 *bssid, const u8 *key, u8 key_len, u8 key_idx)
{
@@ -993,7 +1053,7 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg);
mgmt->u.auth.auth_transaction = cpu_to_le16(transaction);
- mgmt->u.auth.status_code = cpu_to_le16(0);
+ mgmt->u.auth.status_code = cpu_to_le16(status);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
@@ -1047,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
- const u8 *ie, size_t ie_len,
+ size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
u8 channel)
{
struct ieee80211_supported_band *sband;
- u8 *pos;
+ u8 *pos = buffer, *end = buffer + buffer_len;
size_t offset = 0, noffset;
int supp_rates_len, i;
u8 rates[32];
@@ -1063,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
if (WARN_ON_ONCE(!sband))
return 0;
- pos = buffer;
-
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
@@ -1074,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
supp_rates_len = min_t(int, num_rates, 8);
+ if (end - pos < 2 + supp_rates_len)
+ goto out_err;
*pos++ = WLAN_EID_SUPP_RATES;
*pos++ = supp_rates_len;
memcpy(pos, rates, supp_rates_len);
@@ -1090,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
before_extrates,
ARRAY_SIZE(before_extrates),
offset);
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
@@ -1097,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
ext_rates_len = num_rates - supp_rates_len;
if (ext_rates_len > 0) {
+ if (end - pos < 2 + ext_rates_len)
+ goto out_err;
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = ext_rates_len;
memcpy(pos, rates + supp_rates_len, ext_rates_len);
@@ -1104,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
}
if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+ if (end - pos < 3)
+ goto out_err;
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
*pos++ = channel;
@@ -1122,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
noffset = ieee80211_ie_split(ie, ie_len,
before_ht, ARRAY_SIZE(before_ht),
offset);
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
offset = noffset;
}
- if (sband->ht_cap.ht_supported)
+ if (sband->ht_cap.ht_supported) {
+ if (end - pos < 2 + sizeof(struct ieee80211_ht_cap))
+ goto out_err;
pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
sband->ht_cap.cap);
+ }
/*
* If adding more here, adjust code in main.c
@@ -1139,15 +1210,23 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
/* add any remaining custom IEs */
if (ie && ie_len) {
noffset = ie_len;
+ if (end - pos < noffset - offset)
+ goto out_err;
memcpy(pos, ie + offset, noffset - offset);
pos += noffset - offset;
}
- if (sband->vht_cap.vht_supported)
+ if (sband->vht_cap.vht_supported) {
+ if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
+ goto out_err;
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
sband->vht_cap.cap);
+ }
return pos - buffer;
+ out_err:
+ WARN_ONCE(1, "not enough space for preq IEs\n");
+ return pos - buffer;
}
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
@@ -1160,14 +1239,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- size_t buf_len;
- u8 *buf;
u8 chan_no;
-
- /* FIXME: come up with a proper value */
- buf = kmalloc(200 + ie_len, GFP_KERNEL);
- if (!buf)
- return NULL;
+ int ies_len;
/*
* Do not send DS Channel parameter for directed probe requests
@@ -1179,14 +1252,16 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
else
chan_no = ieee80211_frequency_to_channel(chan->center_freq);
- buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band,
- ratemask, chan_no);
-
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
- ssid, ssid_len,
- buf, buf_len);
+ ssid, ssid_len, 100 + ie_len);
if (!skb)
- goto out;
+ return NULL;
+
+ ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
+ skb_tailroom(skb),
+ ie, ie_len, chan->band,
+ ratemask, chan_no);
+ skb_put(skb, ies_len);
if (dst) {
mgmt = (struct ieee80211_mgmt *) skb->data;
@@ -1196,9 +1271,6 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
- out:
- kfree(buf);
-
return skb;
}
@@ -1206,7 +1278,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, bool no_cck,
- struct ieee80211_channel *channel)
+ struct ieee80211_channel *channel, bool scan)
{
struct sk_buff *skb;
@@ -1217,7 +1289,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
if (no_cck)
IEEE80211_SKB_CB(skb)->flags |=
IEEE80211_TX_CTL_NO_CCK_RATE;
- ieee80211_tx_skb(sdata, skb);
+ if (scan)
+ ieee80211_tx_skb_tid_band(sdata, skb, 7, channel->band);
+ else
+ ieee80211_tx_skb(sdata, skb);
}
}
@@ -1280,6 +1355,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_chanctx *ctx;
struct sta_info *sta;
int res, i;
@@ -1352,6 +1428,46 @@ int ieee80211_reconfig(struct ieee80211_local *local)
res = drv_add_interface(local, sdata);
}
+ /* add channel contexts */
+ if (local->use_chanctx) {
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list)
+ WARN_ON(drv_add_chanctx(local, ctx));
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
+ sdata = rtnl_dereference(local->monitor_sdata);
+ if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) {
+ struct ieee80211_chanctx_conf *ctx_conf;
+
+ mutex_lock(&local->chanctx_mtx);
+ ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (ctx_conf) {
+ ctx = container_of(ctx_conf, struct ieee80211_chanctx,
+ conf);
+ drv_assign_vif_chanctx(local, sdata, ctx);
+ }
+ mutex_unlock(&local->chanctx_mtx);
+ }
+
/* add STAs back */
mutex_lock(&local->sta_mtx);
list_for_each_entry(sta, &local->sta_list, list) {
@@ -1407,7 +1523,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
BSS_CHANGED_BSSID |
BSS_CHANGED_CQM |
BSS_CHANGED_QOS |
- BSS_CHANGED_IDLE;
+ BSS_CHANGED_IDLE |
+ BSS_CHANGED_TXPOWER;
switch (sdata->vif.type) {
case NL80211_IFTYPE_STATION:
@@ -1422,11 +1539,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
changed |= BSS_CHANGED_IBSS;
/* fall through */
case NL80211_IFTYPE_AP:
- changed |= BSS_CHANGED_SSID;
+ changed |= BSS_CHANGED_SSID | BSS_CHANGED_P2P_PS;
- if (sdata->vif.type == NL80211_IFTYPE_AP)
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
changed |= BSS_CHANGED_AP_PROBE_RESP;
+ if (rcu_access_pointer(sdata->u.ap.beacon))
+ drv_start_ap(local, sdata);
+ }
+
/* fall through */
case NL80211_IFTYPE_MESH_POINT:
changed |= BSS_CHANGED_BEACON |
@@ -1463,6 +1584,8 @@ int ieee80211_reconfig(struct ieee80211_local *local)
list_for_each_entry(sdata, &local->interfaces, list) {
if (sdata->vif.type != NL80211_IFTYPE_STATION)
continue;
+ if (!sdata->u.mgd.associated)
+ continue;
ieee80211_send_nullfunc(local, sdata, 0);
}
@@ -1523,8 +1646,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* If this is for hw restart things are still running.
* We may want to change that later, however.
*/
- if (!local->suspended)
+ if (!local->suspended) {
+ drv_restart_complete(local);
return 0;
+ }
#ifdef CONFIG_PM
/* first set suspended false, then resuming */
@@ -1587,68 +1712,24 @@ void ieee80211_resume_disconnect(struct ieee80211_vif *vif)
}
EXPORT_SYMBOL_GPL(ieee80211_resume_disconnect);
-static int check_mgd_smps(struct ieee80211_if_managed *ifmgd,
- enum ieee80211_smps_mode *smps_mode)
-{
- if (ifmgd->associated) {
- *smps_mode = ifmgd->ap_smps;
-
- if (*smps_mode == IEEE80211_SMPS_AUTOMATIC) {
- if (ifmgd->powersave)
- *smps_mode = IEEE80211_SMPS_DYNAMIC;
- else
- *smps_mode = IEEE80211_SMPS_OFF;
- }
-
- return 1;
- }
-
- return 0;
-}
-
-void ieee80211_recalc_smps(struct ieee80211_local *local)
+void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata;
- enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_OFF;
- int count = 0;
-
- mutex_lock(&local->iflist_mtx);
-
- /*
- * This function could be improved to handle multiple
- * interfaces better, but right now it makes any
- * non-station interfaces force SM PS to be turned
- * off. If there are multiple station interfaces it
- * could also use the best possible mode, e.g. if
- * one is in static and the other in dynamic then
- * dynamic is ok.
- */
-
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (!ieee80211_sdata_running(sdata))
- continue;
- if (sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE)
- continue;
- if (sdata->vif.type != NL80211_IFTYPE_STATION)
- goto set;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
- count += check_mgd_smps(&sdata->u.mgd, &smps_mode);
+ mutex_lock(&local->chanctx_mtx);
- if (count > 1) {
- smps_mode = IEEE80211_SMPS_OFF;
- break;
- }
- }
+ chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
- if (smps_mode == local->smps_mode)
+ if (WARN_ON_ONCE(!chanctx_conf))
goto unlock;
- set:
- local->smps_mode = smps_mode;
- /* changed flag is auto-detected for this */
- ieee80211_hw_config(local, 0);
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ ieee80211_recalc_smps_chanctx(local, chanctx);
unlock:
- mutex_unlock(&local->iflist_mtx);
+ mutex_unlock(&local->chanctx_mtx);
}
static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
@@ -1788,8 +1869,8 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
__le32 tmp;
*pos++ = WLAN_EID_VHT_CAPABILITY;
- *pos++ = sizeof(struct ieee80211_vht_capabilities);
- memset(pos, 0, sizeof(struct ieee80211_vht_capabilities));
+ *pos++ = sizeof(struct ieee80211_vht_cap);
+ memset(pos, 0, sizeof(struct ieee80211_vht_cap));
/* capability flags */
tmp = cpu_to_le32(cap);
@@ -1804,8 +1885,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
}
u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
- struct ieee80211_channel *channel,
- enum nl80211_channel_type channel_type,
+ const struct cfg80211_chan_def *chandef,
u16 prot_mode)
{
struct ieee80211_ht_operation *ht_oper;
@@ -1813,23 +1893,25 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
*pos++ = WLAN_EID_HT_OPERATION;
*pos++ = sizeof(struct ieee80211_ht_operation);
ht_oper = (struct ieee80211_ht_operation *)pos;
- ht_oper->primary_chan =
- ieee80211_frequency_to_channel(channel->center_freq);
- switch (channel_type) {
- case NL80211_CHAN_HT40MINUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
- break;
- case NL80211_CHAN_HT40PLUS:
- ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ ht_oper->primary_chan = ieee80211_frequency_to_channel(
+ chandef->chan->center_freq);
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_160:
+ case NL80211_CHAN_WIDTH_80P80:
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+ else
+ ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
break;
- case NL80211_CHAN_HT20:
default:
ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE;
break;
}
if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 &&
- channel_type != NL80211_CHAN_NO_HT &&
- channel_type != NL80211_CHAN_HT20)
+ chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef->width != NL80211_CHAN_WIDTH_20)
ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY;
ht_oper->operation_mode = cpu_to_le16(prot_mode);
@@ -1843,13 +1925,17 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
return pos + sizeof(struct ieee80211_ht_operation);
}
-enum nl80211_channel_type
-ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
+void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
+ struct ieee80211_ht_operation *ht_oper,
+ struct cfg80211_chan_def *chandef)
{
enum nl80211_channel_type channel_type;
- if (!ht_oper)
- return NL80211_CHAN_NO_HT;
+ if (!ht_oper) {
+ cfg80211_chandef_create(chandef, control_chan,
+ NL80211_CHAN_NO_HT);
+ return;
+ }
switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
@@ -1865,7 +1951,7 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper)
channel_type = NL80211_CHAN_NO_HT;
}
- return channel_type;
+ cfg80211_chandef_create(chandef, control_chan, channel_type);
}
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
@@ -1947,3 +2033,84 @@ int ieee80211_ave_rssi(struct ieee80211_vif *vif)
return ifmgd->ave_beacon_signal;
}
EXPORT_SYMBOL_GPL(ieee80211_ave_rssi);
+
+u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs)
+{
+ if (!mcs)
+ return 1;
+
+ /* TODO: consider rx_highest */
+
+ if (mcs->rx_mask[3])
+ return 4;
+ if (mcs->rx_mask[2])
+ return 3;
+ if (mcs->rx_mask[1])
+ return 2;
+ return 1;
+}
+
+/**
+ * ieee80211_calculate_rx_timestamp - calculate timestamp in frame
+ * @local: mac80211 hw info struct
+ * @status: RX status
+ * @mpdu_len: total MPDU length (including FCS)
+ * @mpdu_offset: offset into MPDU to calculate timestamp at
+ *
+ * This function calculates the RX timestamp at the given MPDU offset, taking
+ * into account what the RX timestamp was. An offset of 0 will just normalize
+ * the timestamp to TSF at beginning of MPDU reception.
+ */
+u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ unsigned int mpdu_len,
+ unsigned int mpdu_offset)
+{
+ u64 ts = status->mactime;
+ struct rate_info ri;
+ u16 rate;
+
+ if (WARN_ON(!ieee80211_have_rx_timestamp(status)))
+ return 0;
+
+ memset(&ri, 0, sizeof(ri));
+
+ /* Fill cfg80211 rate info */
+ if (status->flag & RX_FLAG_HT) {
+ ri.mcs = status->rate_idx;
+ ri.flags |= RATE_INFO_FLAGS_MCS;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else if (status->flag & RX_FLAG_VHT) {
+ ri.flags |= RATE_INFO_FLAGS_VHT_MCS;
+ ri.mcs = status->rate_idx;
+ ri.nss = status->vht_nss;
+ if (status->flag & RX_FLAG_40MHZ)
+ ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_80P80MHZ)
+ ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_160MHZ)
+ ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH;
+ if (status->flag & RX_FLAG_SHORT_GI)
+ ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
+ } else {
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[status->band];
+ ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+ }
+
+ rate = cfg80211_calculate_bitrate(&ri);
+
+ /* rewind from end of MPDU */
+ if (status->flag & RX_FLAG_MACTIME_END)
+ ts -= mpdu_len * 8 * 10 / rate;
+
+ ts += mpdu_offset * 8 * 10 / rate;
+
+ return ts;
+}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
new file mode 100644
index 00000000000..f311388aeed
--- /dev/null
+++ b/net/mac80211/vht.c
@@ -0,0 +1,35 @@
+/*
+ * VHT handling
+ *
+ * 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/ieee80211.h>
+#include <linux/export.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+
+void ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_vht_cap *vht_cap_ie,
+ struct ieee80211_sta_vht_cap *vht_cap)
+{
+ if (WARN_ON_ONCE(!vht_cap))
+ return;
+
+ memset(vht_cap, 0, sizeof(*vht_cap));
+
+ if (!vht_cap_ie || !sband->vht_cap.vht_supported)
+ return;
+
+ vht_cap->vht_supported = true;
+
+ vht_cap->cap = le32_to_cpu(vht_cap_ie->vht_cap_info);
+
+ /* Copy peer MCS info, the driver might need them. */
+ memcpy(&vht_cap->vht_mcs, &vht_cap_ie->supp_mcs,
+ sizeof(struct ieee80211_vht_mcs_info));
+}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index cea06e9f26f..906f00cd6d2 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -160,31 +160,37 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
return ieee80211_downgrade_queue(sdata, skb);
}
+/**
+ * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
+ *
+ * @sdata: local subif
+ * @skb: packet to be updated
+ */
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ u8 *p;
+ u8 ack_policy, tid;
- /* Fill in the QoS header if there is one. */
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *p = ieee80211_get_qos_ctl(hdr);
- u8 ack_policy, tid;
-
- tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
+ if (!ieee80211_is_data_qos(hdr->frame_control))
+ return;
- /* preserve EOSP bit */
- ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
+ p = ieee80211_get_qos_ctl(hdr);
+ tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
- if (is_multicast_ether_addr(hdr->addr1) ||
- sdata->noack_map & BIT(tid)) {
- ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
- info->flags |= IEEE80211_TX_CTL_NO_ACK;
- }
+ /* preserve EOSP bit */
+ ack_policy = *p & IEEE80211_QOS_CTL_EOSP;
- /* qos header is 2 bytes */
- *p++ = ack_policy | tid;
- *p = ieee80211_vif_is_mesh(&sdata->vif) ?
- (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
+ if (is_multicast_ether_addr(hdr->addr1) ||
+ sdata->noack_map & BIT(tid)) {
+ ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
+ info->flags |= IEEE80211_TX_CTL_NO_ACK;
}
+
+ /* qos header is 2 bytes */
+ *p++ = ack_policy | tid;
+ *p = ieee80211_vif_is_mesh(&sdata->vif) ?
+ (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0;
}
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 8bd2f5c6a56..c175ee866ff 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -104,7 +104,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
*/
if (status->flag & (RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED)) {
if (status->flag & RX_FLAG_MMIC_ERROR)
- goto mic_fail;
+ goto mic_fail_no_key;
if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key &&
rx->key->conf.cipher == WLAN_CIPHER_SUITE_TKIP)
@@ -161,6 +161,9 @@ update_iv:
return RX_CONTINUE;
mic_fail:
+ rx->key->u.tkip.mic_failures++;
+
+mic_fail_no_key:
/*
* In some cases the key can be unset - e.g. a multicast packet, in
* a driver that supports HW encryption. Send up the key idx only if
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
index e748aed290a..b7c7f815dea 100644
--- a/net/mac802154/ieee802154_dev.c
+++ b/net/mac802154/ieee802154_dev.c
@@ -224,9 +224,9 @@ void ieee802154_free_device(struct ieee802154_dev *hw)
BUG_ON(!list_empty(&priv->slaves));
- wpan_phy_free(priv->phy);
-
mutex_destroy(&priv->slaves_mtx);
+
+ wpan_phy_free(priv->phy);
}
EXPORT_SYMBOL(ieee802154_free_device);
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index 1a4df39c722..4e09d070995 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -85,6 +85,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
if (!(priv->phy->channels_supported[page] & (1 << chan))) {
WARN_ON(1);
+ kfree_skb(skb);
return NETDEV_TX_OK;
}
@@ -98,13 +99,15 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
}
if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) {
- dev_kfree_skb(skb);
+ kfree_skb(skb);
return NETDEV_TX_OK;
}
work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC);
- if (!work)
+ if (!work) {
+ kfree_skb(skb);
return NETDEV_TX_BUSY;
+ }
INIT_WORK(&work->work, mac802154_xmit_worker);
work->skb = skb;
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
index f30f6d4beea..199b92261e9 100644
--- a/net/mac802154/wpan.c
+++ b/net/mac802154/wpan.c
@@ -327,8 +327,10 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
if (chan == MAC802154_CHAN_NONE ||
page >= WPAN_NUM_PAGES ||
- chan >= WPAN_NUM_CHANNELS)
+ chan >= WPAN_NUM_CHANNELS) {
+ kfree_skb(skb);
return NETDEV_TX_OK;
+ }
skb->skb_iif = dev->ifindex;
dev->stats.tx_packets++;
@@ -387,7 +389,7 @@ void mac802154_wpan_setup(struct net_device *dev)
static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
{
- return netif_rx(skb);
+ return netif_rx_ni(skb);
}
static int
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index fefa514b991..49e96df5fbc 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -680,6 +680,13 @@ config NETFILTER_XT_TARGET_NFQUEUE
To compile it as a module, choose M here. If unsure, say N.
+config NETFILTER_XT_TARGET_NOTRACK
+ tristate '"NOTRACK" target support (DEPRECATED)'
+ depends on NF_CONNTRACK
+ depends on IP_NF_RAW || IP6_NF_RAW
+ depends on NETFILTER_ADVANCED
+ select NETFILTER_XT_TARGET_CT
+
config NETFILTER_XT_TARGET_RATEEST
tristate '"RATEEST" target support'
depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 68912dadf13..a9c488b6c50 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -295,8 +295,6 @@ void __init netfilter_init(void)
panic("cannot create netfilter proc entry");
#endif
- if (netfilter_queue_init() < 0)
- panic("cannot initialize nf_queue");
if (netfilter_log_init() < 0)
panic("cannot initialize nf_log");
}
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 778465f217f..6d6d8f2b033 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -28,9 +28,10 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */
static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */
static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */
-static struct ip_set **ip_set_list; /* all individual sets */
+static struct ip_set * __rcu *ip_set_list; /* all individual sets */
static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */
+#define IP_SET_INC 64
#define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0)
static unsigned int max_sets;
@@ -42,6 +43,12 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
MODULE_DESCRIPTION("core IP set support");
MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET);
+/* When the nfnl mutex is held: */
+#define nfnl_dereference(p) \
+ rcu_dereference_protected(p, 1)
+#define nfnl_set(id) \
+ nfnl_dereference(ip_set_list)[id]
+
/*
* The set types are implemented in modules and registered set types
* can be found in ip_set_type_list. Adding/deleting types is
@@ -321,19 +328,19 @@ EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6);
*/
static inline void
-__ip_set_get(ip_set_id_t index)
+__ip_set_get(struct ip_set *set)
{
write_lock_bh(&ip_set_ref_lock);
- ip_set_list[index]->ref++;
+ set->ref++;
write_unlock_bh(&ip_set_ref_lock);
}
static inline void
-__ip_set_put(ip_set_id_t index)
+__ip_set_put(struct ip_set *set)
{
write_lock_bh(&ip_set_ref_lock);
- BUG_ON(ip_set_list[index]->ref == 0);
- ip_set_list[index]->ref--;
+ BUG_ON(set->ref == 0);
+ set->ref--;
write_unlock_bh(&ip_set_ref_lock);
}
@@ -344,12 +351,25 @@ __ip_set_put(ip_set_id_t index)
* so it can't be destroyed (or changed) under our foot.
*/
+static inline struct ip_set *
+ip_set_rcu_get(ip_set_id_t index)
+{
+ struct ip_set *set;
+
+ rcu_read_lock();
+ /* ip_set_list itself needs to be protected */
+ set = rcu_dereference(ip_set_list)[index];
+ rcu_read_unlock();
+
+ return set;
+}
+
int
ip_set_test(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_list[index];
+ struct ip_set *set = ip_set_rcu_get(index);
int ret = 0;
BUG_ON(set == NULL);
@@ -388,7 +408,7 @@ ip_set_add(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_list[index];
+ struct ip_set *set = ip_set_rcu_get(index);
int ret;
BUG_ON(set == NULL);
@@ -411,7 +431,7 @@ ip_set_del(ip_set_id_t index, const struct sk_buff *skb,
const struct xt_action_param *par,
const struct ip_set_adt_opt *opt)
{
- struct ip_set *set = ip_set_list[index];
+ struct ip_set *set = ip_set_rcu_get(index);
int ret = 0;
BUG_ON(set == NULL);
@@ -440,14 +460,17 @@ ip_set_get_byname(const char *name, struct ip_set **set)
ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
+ rcu_read_lock();
for (i = 0; i < ip_set_max; i++) {
- s = ip_set_list[i];
+ s = rcu_dereference(ip_set_list)[i];
if (s != NULL && STREQ(s->name, name)) {
- __ip_set_get(i);
+ __ip_set_get(s);
index = i;
*set = s;
+ break;
}
}
+ rcu_read_unlock();
return index;
}
@@ -462,8 +485,13 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname);
void
ip_set_put_byindex(ip_set_id_t index)
{
- if (ip_set_list[index] != NULL)
- __ip_set_put(index);
+ struct ip_set *set;
+
+ rcu_read_lock();
+ set = rcu_dereference(ip_set_list)[index];
+ if (set != NULL)
+ __ip_set_put(set);
+ rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ip_set_put_byindex);
@@ -477,7 +505,7 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex);
const char *
ip_set_name_byindex(ip_set_id_t index)
{
- const struct ip_set *set = ip_set_list[index];
+ const struct ip_set *set = ip_set_rcu_get(index);
BUG_ON(set == NULL);
BUG_ON(set->ref == 0);
@@ -501,11 +529,18 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex);
ip_set_id_t
ip_set_nfnl_get(const char *name)
{
+ ip_set_id_t i, index = IPSET_INVALID_ID;
struct ip_set *s;
- ip_set_id_t index;
nfnl_lock();
- index = ip_set_get_byname(name, &s);
+ for (i = 0; i < ip_set_max; i++) {
+ s = nfnl_set(i);
+ if (s != NULL && STREQ(s->name, name)) {
+ __ip_set_get(s);
+ index = i;
+ break;
+ }
+ }
nfnl_unlock();
return index;
@@ -521,12 +556,15 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get);
ip_set_id_t
ip_set_nfnl_get_byindex(ip_set_id_t index)
{
+ struct ip_set *set;
+
if (index > ip_set_max)
return IPSET_INVALID_ID;
nfnl_lock();
- if (ip_set_list[index])
- __ip_set_get(index);
+ set = nfnl_set(index);
+ if (set)
+ __ip_set_get(set);
else
index = IPSET_INVALID_ID;
nfnl_unlock();
@@ -545,8 +583,11 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex);
void
ip_set_nfnl_put(ip_set_id_t index)
{
+ struct ip_set *set;
nfnl_lock();
- ip_set_put_byindex(index);
+ set = nfnl_set(index);
+ if (set != NULL)
+ __ip_set_put(set);
nfnl_unlock();
}
EXPORT_SYMBOL_GPL(ip_set_nfnl_put);
@@ -603,41 +644,46 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = {
[IPSET_ATTR_DATA] = { .type = NLA_NESTED },
};
-static ip_set_id_t
-find_set_id(const char *name)
+static struct ip_set *
+find_set_and_id(const char *name, ip_set_id_t *id)
{
- ip_set_id_t i, index = IPSET_INVALID_ID;
- const struct ip_set *set;
+ struct ip_set *set = NULL;
+ ip_set_id_t i;
- for (i = 0; index == IPSET_INVALID_ID && i < ip_set_max; i++) {
- set = ip_set_list[i];
- if (set != NULL && STREQ(set->name, name))
- index = i;
+ *id = IPSET_INVALID_ID;
+ for (i = 0; i < ip_set_max; i++) {
+ set = nfnl_set(i);
+ if (set != NULL && STREQ(set->name, name)) {
+ *id = i;
+ break;
+ }
}
- return index;
+ return (*id == IPSET_INVALID_ID ? NULL : set);
}
static inline struct ip_set *
find_set(const char *name)
{
- ip_set_id_t index = find_set_id(name);
+ ip_set_id_t id;
- return index == IPSET_INVALID_ID ? NULL : ip_set_list[index];
+ return find_set_and_id(name, &id);
}
static int
find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set)
{
+ struct ip_set *s;
ip_set_id_t i;
*index = IPSET_INVALID_ID;
for (i = 0; i < ip_set_max; i++) {
- if (ip_set_list[i] == NULL) {
+ s = nfnl_set(i);
+ if (s == NULL) {
if (*index == IPSET_INVALID_ID)
*index = i;
- } else if (STREQ(name, ip_set_list[i]->name)) {
+ } else if (STREQ(name, s->name)) {
/* Name clash */
- *set = ip_set_list[i];
+ *set = s;
return -EEXIST;
}
}
@@ -730,10 +776,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
* and check clashing.
*/
ret = find_free_id(set->name, &index, &clash);
- if (ret != 0) {
+ if (ret == -EEXIST) {
/* If this is the same set and requested, ignore error */
- if (ret == -EEXIST &&
- (flags & IPSET_FLAG_EXIST) &&
+ if ((flags & IPSET_FLAG_EXIST) &&
STREQ(set->type->name, clash->type->name) &&
set->type->family == clash->type->family &&
set->type->revision_min == clash->type->revision_min &&
@@ -741,13 +786,36 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb,
set->variant->same_set(set, clash))
ret = 0;
goto cleanup;
- }
+ } else if (ret == -IPSET_ERR_MAX_SETS) {
+ struct ip_set **list, **tmp;
+ ip_set_id_t i = ip_set_max + IP_SET_INC;
+
+ if (i < ip_set_max || i == IPSET_INVALID_ID)
+ /* Wraparound */
+ goto cleanup;
+
+ list = kzalloc(sizeof(struct ip_set *) * i, GFP_KERNEL);
+ if (!list)
+ goto cleanup;
+ /* nfnl mutex is held, both lists are valid */
+ tmp = nfnl_dereference(ip_set_list);
+ memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max);
+ rcu_assign_pointer(ip_set_list, list);
+ /* Make sure all current packets have passed through */
+ synchronize_net();
+ /* Use new list */
+ index = ip_set_max;
+ ip_set_max = i;
+ kfree(tmp);
+ ret = 0;
+ } else if (ret)
+ goto cleanup;
/*
* Finally! Add our shiny new set to the list, and be done.
*/
pr_debug("create: '%s' created with index %u!\n", set->name, index);
- ip_set_list[index] = set;
+ nfnl_set(index) = set;
return ret;
@@ -772,10 +840,10 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = {
static void
ip_set_destroy_set(ip_set_id_t index)
{
- struct ip_set *set = ip_set_list[index];
+ struct ip_set *set = nfnl_set(index);
pr_debug("set: %s\n", set->name);
- ip_set_list[index] = NULL;
+ nfnl_set(index) = NULL;
/* Must call it without holding any lock */
set->variant->destroy(set);
@@ -788,6 +856,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set *s;
ip_set_id_t i;
int ret = 0;
@@ -807,22 +876,24 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb,
read_lock_bh(&ip_set_ref_lock);
if (!attr[IPSET_ATTR_SETNAME]) {
for (i = 0; i < ip_set_max; i++) {
- if (ip_set_list[i] != NULL && ip_set_list[i]->ref) {
+ s = nfnl_set(i);
+ if (s != NULL && s->ref) {
ret = -IPSET_ERR_BUSY;
goto out;
}
}
read_unlock_bh(&ip_set_ref_lock);
for (i = 0; i < ip_set_max; i++) {
- if (ip_set_list[i] != NULL)
+ s = nfnl_set(i);
+ if (s != NULL)
ip_set_destroy_set(i);
}
} else {
- i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (i == IPSET_INVALID_ID) {
+ s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i);
+ if (s == NULL) {
ret = -ENOENT;
goto out;
- } else if (ip_set_list[i]->ref) {
+ } else if (s->ref) {
ret = -IPSET_ERR_BUSY;
goto out;
}
@@ -853,21 +924,24 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
+ struct ip_set *s;
ip_set_id_t i;
if (unlikely(protocol_failed(attr)))
return -IPSET_ERR_PROTOCOL;
if (!attr[IPSET_ATTR_SETNAME]) {
- for (i = 0; i < ip_set_max; i++)
- if (ip_set_list[i] != NULL)
- ip_set_flush_set(ip_set_list[i]);
+ for (i = 0; i < ip_set_max; i++) {
+ s = nfnl_set(i);
+ if (s != NULL)
+ ip_set_flush_set(s);
+ }
} else {
- i = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (i == IPSET_INVALID_ID)
+ s = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ if (s == NULL)
return -ENOENT;
- ip_set_flush_set(ip_set_list[i]);
+ ip_set_flush_set(s);
}
return 0;
@@ -889,7 +963,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nlattr * const attr[])
{
- struct ip_set *set;
+ struct ip_set *set, *s;
const char *name2;
ip_set_id_t i;
int ret = 0;
@@ -911,8 +985,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb,
name2 = nla_data(attr[IPSET_ATTR_SETNAME2]);
for (i = 0; i < ip_set_max; i++) {
- if (ip_set_list[i] != NULL &&
- STREQ(ip_set_list[i]->name, name2)) {
+ s = nfnl_set(i);
+ if (s != NULL && STREQ(s->name, name2)) {
ret = -IPSET_ERR_EXIST_SETNAME2;
goto out;
}
@@ -947,17 +1021,14 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
attr[IPSET_ATTR_SETNAME2] == NULL))
return -IPSET_ERR_PROTOCOL;
- from_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (from_id == IPSET_INVALID_ID)
+ from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id);
+ if (from == NULL)
return -ENOENT;
- to_id = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME2]));
- if (to_id == IPSET_INVALID_ID)
+ to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id);
+ if (to == NULL)
return -IPSET_ERR_EXIST_SETNAME2;
- from = ip_set_list[from_id];
- to = ip_set_list[to_id];
-
/* Features must not change.
* Not an artificial restriction anymore, as we must prevent
* possible loops created by swapping in setlist type of sets. */
@@ -971,8 +1042,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb,
write_lock_bh(&ip_set_ref_lock);
swap(from->ref, to->ref);
- ip_set_list[from_id] = to;
- ip_set_list[to_id] = from;
+ nfnl_set(from_id) = to;
+ nfnl_set(to_id) = from;
write_unlock_bh(&ip_set_ref_lock);
return 0;
@@ -992,7 +1063,7 @@ static int
ip_set_dump_done(struct netlink_callback *cb)
{
if (cb->args[2]) {
- pr_debug("release set %s\n", ip_set_list[cb->args[1]]->name);
+ pr_debug("release set %s\n", nfnl_set(cb->args[1])->name);
ip_set_put_byindex((ip_set_id_t) cb->args[1]);
}
return 0;
@@ -1030,8 +1101,11 @@ dump_init(struct netlink_callback *cb)
*/
if (cda[IPSET_ATTR_SETNAME]) {
- index = find_set_id(nla_data(cda[IPSET_ATTR_SETNAME]));
- if (index == IPSET_INVALID_ID)
+ struct ip_set *set;
+
+ set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]),
+ &index);
+ if (set == NULL)
return -ENOENT;
dump_type = DUMP_ONE;
@@ -1081,7 +1155,7 @@ dump_last:
dump_type, dump_flags, cb->args[1]);
for (; cb->args[1] < max; cb->args[1]++) {
index = (ip_set_id_t) cb->args[1];
- set = ip_set_list[index];
+ set = nfnl_set(index);
if (set == NULL) {
if (dump_type == DUMP_ONE) {
ret = -ENOENT;
@@ -1100,7 +1174,7 @@ dump_last:
if (!cb->args[2]) {
/* Start listing: make sure set won't be destroyed */
pr_debug("reference set\n");
- __ip_set_get(index);
+ __ip_set_get(set);
}
nlh = start_msg(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, flags,
@@ -1159,7 +1233,7 @@ next_set:
release_refcount:
/* If there was an error or set is done, release set */
if (ret || !cb->args[2]) {
- pr_debug("release set %s\n", ip_set_list[index]->name);
+ pr_debug("release set %s\n", nfnl_set(index)->name);
ip_set_put_byindex(index);
cb->args[2] = 0;
}
@@ -1409,17 +1483,15 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb,
const struct ip_set *set;
struct sk_buff *skb2;
struct nlmsghdr *nlh2;
- ip_set_id_t index;
int ret = 0;
if (unlikely(protocol_failed(attr) ||
attr[IPSET_ATTR_SETNAME] == NULL))
return -IPSET_ERR_PROTOCOL;
- index = find_set_id(nla_data(attr[IPSET_ATTR_SETNAME]));
- if (index == IPSET_INVALID_ID)
+ set = find_set(nla_data(attr[IPSET_ATTR_SETNAME]));
+ if (set == NULL)
return -ENOENT;
- set = ip_set_list[index];
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (skb2 == NULL)
@@ -1643,7 +1715,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
void *data;
int copylen = *len, ret = 0;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (optval != SO_IP_SET)
return -EBADF;
@@ -1684,6 +1756,7 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
}
case IP_SET_OP_GET_BYNAME: {
struct ip_set_req_get_set *req_get = data;
+ ip_set_id_t id;
if (*len != sizeof(struct ip_set_req_get_set)) {
ret = -EINVAL;
@@ -1691,12 +1764,14 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
}
req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0';
nfnl_lock();
- req_get->set.index = find_set_id(req_get->set.name);
+ find_set_and_id(req_get->set.name, &id);
+ req_get->set.index = id;
nfnl_unlock();
goto copy;
}
case IP_SET_OP_GET_BYINDEX: {
struct ip_set_req_get_set *req_get = data;
+ struct ip_set *set;
if (*len != sizeof(struct ip_set_req_get_set) ||
req_get->set.index >= ip_set_max) {
@@ -1704,9 +1779,8 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
goto done;
}
nfnl_lock();
- strncpy(req_get->set.name,
- ip_set_list[req_get->set.index]
- ? ip_set_list[req_get->set.index]->name : "",
+ set = nfnl_set(req_get->set.index);
+ strncpy(req_get->set.name, set ? set->name : "",
IPSET_MAXNAMELEN);
nfnl_unlock();
goto copy;
@@ -1737,6 +1811,7 @@ static struct nf_sockopt_ops so_set __read_mostly = {
static int __init
ip_set_init(void)
{
+ struct ip_set **list;
int ret;
if (max_sets)
@@ -1744,22 +1819,22 @@ ip_set_init(void)
if (ip_set_max >= IPSET_INVALID_ID)
ip_set_max = IPSET_INVALID_ID - 1;
- ip_set_list = kzalloc(sizeof(struct ip_set *) * ip_set_max,
- GFP_KERNEL);
- if (!ip_set_list)
+ list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL);
+ if (!list)
return -ENOMEM;
+ rcu_assign_pointer(ip_set_list, list);
ret = nfnetlink_subsys_register(&ip_set_netlink_subsys);
if (ret != 0) {
pr_err("ip_set: cannot register with nfnetlink.\n");
- kfree(ip_set_list);
+ kfree(list);
return ret;
}
ret = nf_register_sockopt(&so_set);
if (ret != 0) {
pr_err("SO_SET registry failed: %d\n", ret);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
- kfree(ip_set_list);
+ kfree(list);
return ret;
}
@@ -1770,10 +1845,12 @@ ip_set_init(void)
static void __exit
ip_set_fini(void)
{
+ struct ip_set **list = rcu_dereference_protected(ip_set_list, 1);
+
/* There can't be any existing set */
nf_unregister_sockopt(&so_set);
nfnetlink_subsys_unregister(&ip_set_netlink_subsys);
- kfree(ip_set_list);
+ kfree(list);
pr_debug("these are the famous last words\n");
}
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index ec3dba5dcd6..5c0b78528e5 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -173,6 +173,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
return adtfn(set, &nip, timeout, flags);
}
+ ip_to = ip;
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@@ -185,8 +186,7 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
if (!cidr || cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip_set_mask_from_to(ip, ip_to, cidr);
- } else
- ip_to = ip;
+ }
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index 0171f7502fa..6283351f4ee 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -162,7 +162,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem data = { };
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
@@ -210,7 +210,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
return ip_set_eexist(ret, flags) ? 0 : ret;
}
- ip = ntohl(data.ip);
+ ip_to = ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@@ -223,8 +223,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
if (!cidr || cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip_set_mask_from_to(ip, ip_to, cidr);
- } else
- ip_to = ip;
+ }
port_to = port = ntohs(data.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index 6344ef551ec..6a21271c8d5 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -166,7 +166,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem data = { };
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to, p = 0, port, port_to;
u32 timeout = h->timeout;
bool with_ports = false;
int ret;
@@ -218,7 +218,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
return ip_set_eexist(ret, flags) ? 0 : ret;
}
- ip = ntohl(data.ip);
+ ip_to = ip = ntohl(data.ip);
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@@ -231,8 +231,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
if (!cidr || cidr > 32)
return -IPSET_ERR_INVALID_CIDR;
ip_set_mask_from_to(ip, ip_to, cidr);
- } else
- ip_to = ip;
+ }
port_to = port = ntohs(data.port);
if (with_ports && tb[IPSET_ATTR_PORT_TO]) {
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index cb71f9a774e..2d5cd4ee30e 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -215,8 +215,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
const struct ip_set_hash *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem data = { .cidr = HOST_MASK - 1 };
- u32 ip, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to, ip2_last, ip2;
+ u32 ip, ip_to, p = 0, port, port_to;
+ u32 ip2_from, ip2_to, ip2_last, ip2;
u32 timeout = h->timeout;
bool with_ports = false;
u8 cidr;
@@ -286,6 +286,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
return ip_set_eexist(ret, flags) ? 0 : ret;
}
+ ip_to = ip;
if (tb[IPSET_ATTR_IP_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
if (ret)
@@ -306,6 +307,8 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
if (port > port_to)
swap(port, port_to);
}
+
+ ip2_to = ip2_from;
if (tb[IPSET_ATTR_IP2_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
if (ret)
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index b9a63381e34..45a101439bc 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -793,7 +793,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = {
[IPSET_ATTR_IP] = { .type = NLA_NESTED },
[IPSET_ATTR_IP_TO] = { .type = NLA_NESTED },
[IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING,
- .len = IPSET_MAXNAMELEN - 1 },
+ .len = IFNAMSIZ - 1 },
[IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
[IPSET_ATTR_CIDR] = { .type = NLA_U8 },
[IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
diff --git a/net/netfilter/ipvs/Kconfig b/net/netfilter/ipvs/Kconfig
index 8b2cffdfdd9..0c3b1670b0d 100644
--- a/net/netfilter/ipvs/Kconfig
+++ b/net/netfilter/ipvs/Kconfig
@@ -28,12 +28,11 @@ if IP_VS
config IP_VS_IPV6
bool "IPv6 support for IPVS"
depends on IPV6 = y || IP_VS = IPV6
+ select IP6_NF_IPTABLES
---help---
- Add IPv6 support to IPVS. This is incomplete and might be dangerous.
+ Add IPv6 support to IPVS.
- See http://www.mindbasket.com/ipvs for more information.
-
- Say N if unsure.
+ Say Y if unsure.
config IP_VS_DEBUG
bool "IP virtual server debugging"
diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c
index 1548df9a752..30e764ad021 100644
--- a/net/netfilter/ipvs/ip_vs_conn.c
+++ b/net/netfilter/ipvs/ip_vs_conn.c
@@ -308,13 +308,12 @@ struct ip_vs_conn *ip_vs_conn_in_get(const struct ip_vs_conn_param *p)
static int
ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb,
const struct ip_vs_iphdr *iph,
- unsigned int proto_off, int inverse,
- struct ip_vs_conn_param *p)
+ int inverse, struct ip_vs_conn_param *p)
{
__be16 _ports[2], *pptr;
struct net *net = skb_net(skb);
- pptr = skb_header_pointer(skb, proto_off, sizeof(_ports), _ports);
+ pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
if (pptr == NULL)
return 1;
@@ -329,12 +328,11 @@ ip_vs_conn_fill_param_proto(int af, const struct sk_buff *skb,
struct ip_vs_conn *
ip_vs_conn_in_get_proto(int af, const struct sk_buff *skb,
- const struct ip_vs_iphdr *iph,
- unsigned int proto_off, int inverse)
+ const struct ip_vs_iphdr *iph, int inverse)
{
struct ip_vs_conn_param p;
- if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p))
+ if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
return NULL;
return ip_vs_conn_in_get(&p);
@@ -432,12 +430,11 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p)
struct ip_vs_conn *
ip_vs_conn_out_get_proto(int af, const struct sk_buff *skb,
- const struct ip_vs_iphdr *iph,
- unsigned int proto_off, int inverse)
+ const struct ip_vs_iphdr *iph, int inverse)
{
struct ip_vs_conn_param p;
- if (ip_vs_conn_fill_param_proto(af, skb, iph, proto_off, inverse, &p))
+ if (ip_vs_conn_fill_param_proto(af, skb, iph, inverse, &p))
return NULL;
return ip_vs_conn_out_get(&p);
diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c
index 58918e20f9d..47edf5a40a5 100644
--- a/net/netfilter/ipvs/ip_vs_core.c
+++ b/net/netfilter/ipvs/ip_vs_core.c
@@ -222,11 +222,10 @@ ip_vs_conn_fill_param_persist(const struct ip_vs_service *svc,
*/
static struct ip_vs_conn *
ip_vs_sched_persist(struct ip_vs_service *svc,
- struct sk_buff *skb,
- __be16 src_port, __be16 dst_port, int *ignored)
+ struct sk_buff *skb, __be16 src_port, __be16 dst_port,
+ int *ignored, struct ip_vs_iphdr *iph)
{
struct ip_vs_conn *cp = NULL;
- struct ip_vs_iphdr iph;
struct ip_vs_dest *dest;
struct ip_vs_conn *ct;
__be16 dport = 0; /* destination port to forward */
@@ -236,20 +235,18 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
union nf_inet_addr snet; /* source network of the client,
after masking */
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
-
/* Mask saddr with the netmask to adjust template granularity */
#ifdef CONFIG_IP_VS_IPV6
if (svc->af == AF_INET6)
- ipv6_addr_prefix(&snet.in6, &iph.saddr.in6, svc->netmask);
+ ipv6_addr_prefix(&snet.in6, &iph->saddr.in6, svc->netmask);
else
#endif
- snet.ip = iph.saddr.ip & svc->netmask;
+ snet.ip = iph->saddr.ip & svc->netmask;
IP_VS_DBG_BUF(6, "p-schedule: src %s:%u dest %s:%u "
"mnet %s\n",
- IP_VS_DBG_ADDR(svc->af, &iph.saddr), ntohs(src_port),
- IP_VS_DBG_ADDR(svc->af, &iph.daddr), ntohs(dst_port),
+ IP_VS_DBG_ADDR(svc->af, &iph->saddr), ntohs(src_port),
+ IP_VS_DBG_ADDR(svc->af, &iph->daddr), ntohs(dst_port),
IP_VS_DBG_ADDR(svc->af, &snet));
/*
@@ -266,8 +263,8 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
* is created for other persistent services.
*/
{
- int protocol = iph.protocol;
- const union nf_inet_addr *vaddr = &iph.daddr;
+ int protocol = iph->protocol;
+ const union nf_inet_addr *vaddr = &iph->daddr;
__be16 vport = 0;
if (dst_port == svc->port) {
@@ -342,14 +339,14 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
dport = dest->port;
flags = (svc->flags & IP_VS_SVC_F_ONEPACKET
- && iph.protocol == IPPROTO_UDP)?
+ && iph->protocol == IPPROTO_UDP) ?
IP_VS_CONN_F_ONE_PACKET : 0;
/*
* Create a new connection according to the template
*/
- ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol, &iph.saddr,
- src_port, &iph.daddr, dst_port, &param);
+ ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol, &iph->saddr,
+ src_port, &iph->daddr, dst_port, &param);
cp = ip_vs_conn_new(&param, &dest->addr, dport, flags, dest, skb->mark);
if (cp == NULL) {
@@ -392,18 +389,20 @@ ip_vs_sched_persist(struct ip_vs_service *svc,
*/
struct ip_vs_conn *
ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
- struct ip_vs_proto_data *pd, int *ignored)
+ struct ip_vs_proto_data *pd, int *ignored,
+ struct ip_vs_iphdr *iph)
{
struct ip_vs_protocol *pp = pd->pp;
struct ip_vs_conn *cp = NULL;
- struct ip_vs_iphdr iph;
struct ip_vs_dest *dest;
__be16 _ports[2], *pptr;
unsigned int flags;
*ignored = 1;
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
- pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
+ /*
+ * IPv6 frags, only the first hit here.
+ */
+ pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
if (pptr == NULL)
return NULL;
@@ -423,7 +422,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
* Do not schedule replies from local real server.
*/
if ((!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
- (cp = pp->conn_in_get(svc->af, skb, &iph, iph.len, 1))) {
+ (cp = pp->conn_in_get(svc->af, skb, iph, 1))) {
IP_VS_DBG_PKT(12, svc->af, pp, skb, 0,
"Not scheduling reply for existing connection");
__ip_vs_conn_put(cp);
@@ -434,7 +433,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
* Persistent service
*/
if (svc->flags & IP_VS_SVC_F_PERSISTENT)
- return ip_vs_sched_persist(svc, skb, pptr[0], pptr[1], ignored);
+ return ip_vs_sched_persist(svc, skb, pptr[0], pptr[1], ignored,
+ iph);
*ignored = 0;
@@ -456,7 +456,7 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
}
flags = (svc->flags & IP_VS_SVC_F_ONEPACKET
- && iph.protocol == IPPROTO_UDP)?
+ && iph->protocol == IPPROTO_UDP) ?
IP_VS_CONN_F_ONE_PACKET : 0;
/*
@@ -465,9 +465,9 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
{
struct ip_vs_conn_param p;
- ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol,
- &iph.saddr, pptr[0], &iph.daddr, pptr[1],
- &p);
+ ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol,
+ &iph->saddr, pptr[0], &iph->daddr,
+ pptr[1], &p);
cp = ip_vs_conn_new(&p, &dest->addr,
dest->port ? dest->port : pptr[1],
flags, dest, skb->mark);
@@ -496,19 +496,16 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb,
* no destination is available for a new connection.
*/
int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
- struct ip_vs_proto_data *pd)
+ struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph)
{
__be16 _ports[2], *pptr;
- struct ip_vs_iphdr iph;
#ifdef CONFIG_SYSCTL
struct net *net;
struct netns_ipvs *ipvs;
int unicast;
#endif
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
-
- pptr = skb_header_pointer(skb, iph.len, sizeof(_ports), _ports);
+ pptr = frag_safe_skb_hp(skb, iph->len, sizeof(_ports), _ports, iph);
if (pptr == NULL) {
ip_vs_service_put(svc);
return NF_DROP;
@@ -519,10 +516,10 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
#ifdef CONFIG_IP_VS_IPV6
if (svc->af == AF_INET6)
- unicast = ipv6_addr_type(&iph.daddr.in6) & IPV6_ADDR_UNICAST;
+ unicast = ipv6_addr_type(&iph->daddr.in6) & IPV6_ADDR_UNICAST;
else
#endif
- unicast = (inet_addr_type(net, iph.daddr.ip) == RTN_UNICAST);
+ unicast = (inet_addr_type(net, iph->daddr.ip) == RTN_UNICAST);
/* if it is fwmark-based service, the cache_bypass sysctl is up
and the destination is a non-local unicast, then create
@@ -532,7 +529,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
int ret;
struct ip_vs_conn *cp;
unsigned int flags = (svc->flags & IP_VS_SVC_F_ONEPACKET &&
- iph.protocol == IPPROTO_UDP)?
+ iph->protocol == IPPROTO_UDP) ?
IP_VS_CONN_F_ONE_PACKET : 0;
union nf_inet_addr daddr = { .all = { 0, 0, 0, 0 } };
@@ -542,9 +539,9 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
IP_VS_DBG(6, "%s(): create a cache_bypass entry\n", __func__);
{
struct ip_vs_conn_param p;
- ip_vs_conn_fill_param(svc->net, svc->af, iph.protocol,
- &iph.saddr, pptr[0],
- &iph.daddr, pptr[1], &p);
+ ip_vs_conn_fill_param(svc->net, svc->af, iph->protocol,
+ &iph->saddr, pptr[0],
+ &iph->daddr, pptr[1], &p);
cp = ip_vs_conn_new(&p, &daddr, 0,
IP_VS_CONN_F_BYPASS | flags,
NULL, skb->mark);
@@ -559,7 +556,7 @@ int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb,
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
/* transmit the first SYN packet */
- ret = cp->packet_xmit(skb, cp, pd->pp);
+ ret = cp->packet_xmit(skb, cp, pd->pp, iph);
/* do not touch skb anymore */
atomic_inc(&cp->in_pkts);
@@ -654,14 +651,6 @@ static inline int ip_vs_gather_frags(struct sk_buff *skb, u_int32_t user)
return err;
}
-#ifdef CONFIG_IP_VS_IPV6
-static inline int ip_vs_gather_frags_v6(struct sk_buff *skb, u_int32_t user)
-{
- /* TODO IPv6: Find out what to do here for IPv6 */
- return 0;
-}
-#endif
-
static int ip_vs_route_me_harder(int af, struct sk_buff *skb)
{
#ifdef CONFIG_IP_VS_IPV6
@@ -732,10 +721,19 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
struct ip_vs_conn *cp, int inout)
{
struct ipv6hdr *iph = ipv6_hdr(skb);
- unsigned int icmp_offset = sizeof(struct ipv6hdr);
- struct icmp6hdr *icmph = (struct icmp6hdr *)(skb_network_header(skb) +
- icmp_offset);
- struct ipv6hdr *ciph = (struct ipv6hdr *)(icmph + 1);
+ unsigned int icmp_offset = 0;
+ unsigned int offs = 0; /* header offset*/
+ int protocol;
+ struct icmp6hdr *icmph;
+ struct ipv6hdr *ciph;
+ unsigned short fragoffs;
+
+ ipv6_find_hdr(skb, &icmp_offset, IPPROTO_ICMPV6, &fragoffs, NULL);
+ icmph = (struct icmp6hdr *)(skb_network_header(skb) + icmp_offset);
+ offs = icmp_offset + sizeof(struct icmp6hdr);
+ ciph = (struct ipv6hdr *)(skb_network_header(skb) + offs);
+
+ protocol = ipv6_find_hdr(skb, &offs, -1, &fragoffs, NULL);
if (inout) {
iph->saddr = cp->vaddr.in6;
@@ -746,10 +744,13 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp,
}
/* the TCP/UDP/SCTP port */
- if (IPPROTO_TCP == ciph->nexthdr || IPPROTO_UDP == ciph->nexthdr ||
- IPPROTO_SCTP == ciph->nexthdr) {
- __be16 *ports = (void *)ciph + sizeof(struct ipv6hdr);
+ if (!fragoffs && (IPPROTO_TCP == protocol || IPPROTO_UDP == protocol ||
+ IPPROTO_SCTP == protocol)) {
+ __be16 *ports = (void *)(skb_network_header(skb) + offs);
+ IP_VS_DBG(11, "%s() changed port %d to %d\n", __func__,
+ ntohs(inout ? ports[1] : ports[0]),
+ ntohs(inout ? cp->vport : cp->dport));
if (inout)
ports[1] = cp->vport;
else
@@ -898,51 +899,35 @@ static int ip_vs_out_icmp(struct sk_buff *skb, int *related,
IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset,
"Checking outgoing ICMP for");
- offset += cih->ihl * 4;
-
- ip_vs_fill_iphdr(AF_INET, cih, &ciph);
+ ip_vs_fill_ip4hdr(cih, &ciph);
+ ciph.len += offset;
/* The embedded headers contain source and dest in reverse order */
- cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1);
+ cp = pp->conn_out_get(AF_INET, skb, &ciph, 1);
if (!cp)
return NF_ACCEPT;
snet.ip = iph->saddr;
return handle_response_icmp(AF_INET, skb, &snet, cih->protocol, cp,
- pp, offset, ihl);
+ pp, ciph.len, ihl);
}
#ifdef CONFIG_IP_VS_IPV6
static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
- unsigned int hooknum)
+ unsigned int hooknum, struct ip_vs_iphdr *ipvsh)
{
- struct ipv6hdr *iph;
struct icmp6hdr _icmph, *ic;
- struct ipv6hdr _ciph, *cih; /* The ip header contained
- within the ICMP */
- struct ip_vs_iphdr ciph;
+ struct ipv6hdr _ip6h, *ip6h; /* The ip header contained within ICMP */
+ struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
- unsigned int offset;
union nf_inet_addr snet;
+ unsigned int writable;
*related = 1;
-
- /* reassemble IP fragments */
- if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
- if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
- return NF_STOLEN;
- }
-
- iph = ipv6_hdr(skb);
- offset = sizeof(struct ipv6hdr);
- ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+ ic = frag_safe_skb_hp(skb, ipvsh->len, sizeof(_icmph), &_icmph, ipvsh);
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Outgoing ICMPv6 (%d,%d) %pI6->%pI6\n",
- ic->icmp6_type, ntohs(icmpv6_id(ic)),
- &iph->saddr, &iph->daddr);
-
/*
* Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy
@@ -950,42 +935,45 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related,
* this means that some packets will manage to get a long way
* down this stack and then be rejected, but that's life.
*/
- if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) &&
- (ic->icmp6_type != ICMPV6_PKT_TOOBIG) &&
- (ic->icmp6_type != ICMPV6_TIME_EXCEED)) {
+ if (ic->icmp6_type & ICMPV6_INFOMSG_MASK) {
*related = 0;
return NF_ACCEPT;
}
+ /* Fragment header that is before ICMP header tells us that:
+ * it's not an error message since they can't be fragmented.
+ */
+ if (ipvsh->flags & IP6_FH_F_FRAG)
+ return NF_DROP;
+
+ IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n",
+ ic->icmp6_type, ntohs(icmpv6_id(ic)),
+ &ipvsh->saddr, &ipvsh->daddr);
/* Now find the contained IP header */
- offset += sizeof(_icmph);
- cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (cih == NULL)
+ ciph.len = ipvsh->len + sizeof(_icmph);
+ ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
+ if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */
-
- pp = ip_vs_proto_get(cih->nexthdr);
+ ciph.saddr.in6 = ip6h->saddr; /* conn_out_get() handles reverse order */
+ ciph.daddr.in6 = ip6h->daddr;
+ /* skip possible IPv6 exthdrs of contained IPv6 packet */
+ ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
+ if (ciph.protocol < 0)
+ return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
+
+ pp = ip_vs_proto_get(ciph.protocol);
if (!pp)
return NF_ACCEPT;
- /* Is the embedded protocol header present? */
- /* TODO: we don't support fragmentation at the moment anyways */
- if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag))
- return NF_ACCEPT;
-
- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
- "Checking outgoing ICMPv6 for");
-
- offset += sizeof(struct ipv6hdr);
-
- ip_vs_fill_iphdr(AF_INET6, cih, &ciph);
/* The embedded headers contain source and dest in reverse order */
- cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1);
+ cp = pp->conn_out_get(AF_INET6, skb, &ciph, 1);
if (!cp)
return NF_ACCEPT;
- snet.in6 = iph->saddr;
- return handle_response_icmp(AF_INET6, skb, &snet, cih->nexthdr, cp,
- pp, offset, sizeof(struct ipv6hdr));
+ snet.in6 = ciph.saddr.in6;
+ writable = ciph.len;
+ return handle_response_icmp(AF_INET6, skb, &snet, ciph.protocol, cp,
+ pp, writable, sizeof(struct ipv6hdr));
}
#endif
@@ -1018,17 +1006,17 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len)
*/
static unsigned int
handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- struct ip_vs_conn *cp, int ihl)
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
struct ip_vs_protocol *pp = pd->pp;
IP_VS_DBG_PKT(11, af, pp, skb, 0, "Outgoing packet");
- if (!skb_make_writable(skb, ihl))
+ if (!skb_make_writable(skb, iph->len))
goto drop;
/* mangle the packet */
- if (pp->snat_handler && !pp->snat_handler(skb, pp, cp))
+ if (pp->snat_handler && !pp->snat_handler(skb, pp, cp, iph))
goto drop;
#ifdef CONFIG_IP_VS_IPV6
@@ -1115,17 +1103,22 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable)
return NF_ACCEPT;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_skb(af, skb, &iph);
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
+ if (!iph.fragoffs && skb_nfct_reasm(skb)) {
+ struct sk_buff *reasm = skb_nfct_reasm(skb);
+ /* Save fw mark for coming frags */
+ reasm->ipvs_property = 1;
+ reasm->mark = skb->mark;
+ }
if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
int related;
int verdict = ip_vs_out_icmp_v6(skb, &related,
- hooknum);
+ hooknum, &iph);
if (related)
return verdict;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
} else
#endif
@@ -1135,7 +1128,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
pd = ip_vs_proto_data_get(net, iph.protocol);
@@ -1145,39 +1137,31 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af)
/* reassemble IP fragments */
#ifdef CONFIG_IP_VS_IPV6
- if (af == AF_INET6) {
- if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
- if (ip_vs_gather_frags_v6(skb,
- ip_vs_defrag_user(hooknum)))
- return NF_STOLEN;
- }
-
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
- } else
+ if (af == AF_INET)
#endif
if (unlikely(ip_is_fragment(ip_hdr(skb)) && !pp->dont_defrag)) {
if (ip_vs_gather_frags(skb,
ip_vs_defrag_user(hooknum)))
return NF_STOLEN;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+ ip_vs_fill_ip4hdr(skb_network_header(skb), &iph);
}
/*
* Check if the packet belongs to an existing entry
*/
- cp = pp->conn_out_get(af, skb, &iph, iph.len, 0);
+ cp = pp->conn_out_get(af, skb, &iph, 0);
if (likely(cp))
- return handle_response(af, skb, pd, cp, iph.len);
+ return handle_response(af, skb, pd, cp, &iph);
if (sysctl_nat_icmp_send(net) &&
(pp->protocol == IPPROTO_TCP ||
pp->protocol == IPPROTO_UDP ||
pp->protocol == IPPROTO_SCTP)) {
__be16 _ports[2], *pptr;
- pptr = skb_header_pointer(skb, iph.len,
- sizeof(_ports), _ports);
+ pptr = frag_safe_skb_hp(skb, iph.len,
+ sizeof(_ports), _ports, &iph);
if (pptr == NULL)
return NF_ACCEPT; /* Not for me */
if (ip_vs_lookup_real_service(net, af, iph.protocol,
@@ -1375,13 +1359,13 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum)
"Checking incoming ICMP for");
offset2 = offset;
- offset += cih->ihl * 4;
-
- ip_vs_fill_iphdr(AF_INET, cih, &ciph);
+ ip_vs_fill_ip4hdr(cih, &ciph);
+ ciph.len += offset;
+ offset = ciph.len;
/* The embedded headers contain source and dest in reverse order.
* For IPIP this is error for request, not for reply.
*/
- cp = pp->conn_in_get(AF_INET, skb, &ciph, offset, ipip ? 0 : 1);
+ cp = pp->conn_in_get(AF_INET, skb, &ciph, ipip ? 0 : 1);
if (!cp)
return NF_ACCEPT;
@@ -1450,7 +1434,7 @@ ignore_ipip:
ip_vs_in_stats(cp, skb);
if (IPPROTO_TCP == cih->protocol || IPPROTO_UDP == cih->protocol)
offset += 2 * sizeof(__u16);
- verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum);
+ verdict = ip_vs_icmp_xmit(skb, cp, pp, offset, hooknum, &ciph);
out:
__ip_vs_conn_put(cp);
@@ -1459,38 +1443,24 @@ out:
}
#ifdef CONFIG_IP_VS_IPV6
-static int
-ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
+static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related,
+ unsigned int hooknum, struct ip_vs_iphdr *iph)
{
struct net *net = NULL;
- struct ipv6hdr *iph;
+ struct ipv6hdr _ip6h, *ip6h;
struct icmp6hdr _icmph, *ic;
- struct ipv6hdr _ciph, *cih; /* The ip header contained
- within the ICMP */
- struct ip_vs_iphdr ciph;
+ struct ip_vs_iphdr ciph = {.flags = 0, .fragoffs = 0};/*Contained IP */
struct ip_vs_conn *cp;
struct ip_vs_protocol *pp;
struct ip_vs_proto_data *pd;
- unsigned int offset, verdict;
+ unsigned int offs_ciph, writable, verdict;
*related = 1;
- /* reassemble IP fragments */
- if (ipv6_hdr(skb)->nexthdr == IPPROTO_FRAGMENT) {
- if (ip_vs_gather_frags_v6(skb, ip_vs_defrag_user(hooknum)))
- return NF_STOLEN;
- }
-
- iph = ipv6_hdr(skb);
- offset = sizeof(struct ipv6hdr);
- ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph);
+ ic = frag_safe_skb_hp(skb, iph->len, sizeof(_icmph), &_icmph, iph);
if (ic == NULL)
return NF_DROP;
- IP_VS_DBG(12, "Incoming ICMPv6 (%d,%d) %pI6->%pI6\n",
- ic->icmp6_type, ntohs(icmpv6_id(ic)),
- &iph->saddr, &iph->daddr);
-
/*
* Work through seeing if this is for us.
* These checks are supposed to be in an order that means easy
@@ -1498,47 +1468,71 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum)
* this means that some packets will manage to get a long way
* down this stack and then be rejected, but that's life.
*/
- if ((ic->icmp6_type != ICMPV6_DEST_UNREACH) &&
- (ic->icmp6_type != ICMPV6_PKT_TOOBIG) &&
- (ic->icmp6_type != ICMPV6_TIME_EXCEED)) {
+ if (ic->icmp6_type & ICMPV6_INFOMSG_MASK) {
*related = 0;
return NF_ACCEPT;
}
+ /* Fragment header that is before ICMP header tells us that:
+ * it's not an error message since they can't be fragmented.
+ */
+ if (iph->flags & IP6_FH_F_FRAG)
+ return NF_DROP;
+
+ IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n",
+ ic->icmp6_type, ntohs(icmpv6_id(ic)),
+ &iph->saddr, &iph->daddr);
/* Now find the contained IP header */
- offset += sizeof(_icmph);
- cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph);
- if (cih == NULL)
+ ciph.len = iph->len + sizeof(_icmph);
+ offs_ciph = ciph.len; /* Save ip header offset */
+ ip6h = skb_header_pointer(skb, ciph.len, sizeof(_ip6h), &_ip6h);
+ if (ip6h == NULL)
return NF_ACCEPT; /* The packet looks wrong, ignore */
+ ciph.saddr.in6 = ip6h->saddr; /* conn_in_get() handles reverse order */
+ ciph.daddr.in6 = ip6h->daddr;
+ /* skip possible IPv6 exthdrs of contained IPv6 packet */
+ ciph.protocol = ipv6_find_hdr(skb, &ciph.len, -1, &ciph.fragoffs, NULL);
+ if (ciph.protocol < 0)
+ return NF_ACCEPT; /* Contained IPv6 hdr looks wrong, ignore */
net = skb_net(skb);
- pd = ip_vs_proto_data_get(net, cih->nexthdr);
+ pd = ip_vs_proto_data_get(net, ciph.protocol);
if (!pd)
return NF_ACCEPT;
pp = pd->pp;
- /* Is the embedded protocol header present? */
- /* TODO: we don't support fragmentation at the moment anyways */
- if (unlikely(cih->nexthdr == IPPROTO_FRAGMENT && pp->dont_defrag))
+ /* Cannot handle fragmented embedded protocol */
+ if (ciph.fragoffs)
return NF_ACCEPT;
- IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offset,
+ IP_VS_DBG_PKT(11, AF_INET6, pp, skb, offs_ciph,
"Checking incoming ICMPv6 for");
- offset += sizeof(struct ipv6hdr);
+ /* The embedded headers contain source and dest in reverse order
+ * if not from localhost
+ */
+ cp = pp->conn_in_get(AF_INET6, skb, &ciph,
+ (hooknum == NF_INET_LOCAL_OUT) ? 0 : 1);
- ip_vs_fill_iphdr(AF_INET6, cih, &ciph);
- /* The embedded headers contain source and dest in reverse order */
- cp = pp->conn_in_get(AF_INET6, skb, &ciph, offset, 1);
if (!cp)
return NF_ACCEPT;
+ /* VS/TUN, VS/DR and LOCALNODE just let it go */
+ if ((hooknum == NF_INET_LOCAL_OUT) &&
+ (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ)) {
+ __ip_vs_conn_put(cp);
+ return NF_ACCEPT;
+ }
/* do the statistics and put it back */
ip_vs_in_stats(cp, skb);
- if (IPPROTO_TCP == cih->nexthdr || IPPROTO_UDP == cih->nexthdr ||
- IPPROTO_SCTP == cih->nexthdr)
- offset += 2 * sizeof(__u16);
- verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, offset, hooknum);
+
+ /* Need to mangle contained IPv6 header in ICMPv6 packet */
+ writable = ciph.len;
+ if (IPPROTO_TCP == ciph.protocol || IPPROTO_UDP == ciph.protocol ||
+ IPPROTO_SCTP == ciph.protocol)
+ writable += 2 * sizeof(__u16); /* Also mangle ports */
+
+ verdict = ip_vs_icmp_xmit_v6(skb, cp, pp, writable, hooknum, &ciph);
__ip_vs_conn_put(cp);
@@ -1574,7 +1568,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (unlikely((skb->pkt_type != PACKET_HOST &&
hooknum != NF_INET_LOCAL_OUT) ||
!skb_dst(skb))) {
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_skb(af, skb, &iph);
IP_VS_DBG_BUF(12, "packet type=%d proto=%d daddr=%s"
" ignored in hook %u\n",
skb->pkt_type, iph.protocol,
@@ -1586,7 +1580,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (!net_ipvs(net)->enable)
return NF_ACCEPT;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_skb(af, skb, &iph);
/* Bad... Do not break raw sockets */
if (unlikely(skb->sk != NULL && hooknum == NF_INET_LOCAL_OUT &&
@@ -1600,13 +1594,19 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
#ifdef CONFIG_IP_VS_IPV6
if (af == AF_INET6) {
+ if (!iph.fragoffs && skb_nfct_reasm(skb)) {
+ struct sk_buff *reasm = skb_nfct_reasm(skb);
+ /* Save fw mark for coming frags. */
+ reasm->ipvs_property = 1;
+ reasm->mark = skb->mark;
+ }
if (unlikely(iph.protocol == IPPROTO_ICMPV6)) {
int related;
- int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum);
+ int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum,
+ &iph);
if (related)
return verdict;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
} else
#endif
@@ -1616,7 +1616,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
if (related)
return verdict;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
}
/* Protocol supported? */
@@ -1627,12 +1626,15 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
/*
* Check if the packet belongs to an existing connection entry
*/
- cp = pp->conn_in_get(af, skb, &iph, iph.len, 0);
-
- if (unlikely(!cp)) {
+ cp = pp->conn_in_get(af, skb, &iph, 0);
+ if (unlikely(!cp) && !iph.fragoffs) {
+ /* No (second) fragments need to enter here, as nf_defrag_ipv6
+ * replayed fragment zero will already have created the cp
+ */
int v;
- if (!pp->conn_schedule(af, skb, pd, &v, &cp))
+ /* Schedule and create new connection entry into &cp */
+ if (!pp->conn_schedule(af, skb, pd, &v, &cp, &iph))
return v;
}
@@ -1640,6 +1642,14 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
/* sorry, all this trouble for a no-hit :) */
IP_VS_DBG_PKT(12, af, pp, skb, 0,
"ip_vs_in: packet continues traversal as normal");
+ if (iph.fragoffs && !skb_nfct_reasm(skb)) {
+ /* Fragment that couldn't be mapped to a conn entry
+ * and don't have any pointer to a reasm skb
+ * is missing module nf_defrag_ipv6
+ */
+ IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n");
+ IP_VS_DBG_PKT(7, af, pp, skb, 0, "unhandled fragment");
+ }
return NF_ACCEPT;
}
@@ -1662,7 +1672,7 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af)
ip_vs_in_stats(cp, skb);
ip_vs_set_state(cp, IP_VS_DIR_INPUT, skb, pd);
if (cp->packet_xmit)
- ret = cp->packet_xmit(skb, cp, pp);
+ ret = cp->packet_xmit(skb, cp, pp, &iph);
/* do not touch skb anymore */
else {
IP_VS_DBG_RL("warning: packet_xmit is null");
@@ -1724,6 +1734,38 @@ ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb,
#ifdef CONFIG_IP_VS_IPV6
/*
+ * AF_INET6 fragment handling
+ * Copy info from first fragment, to the rest of them.
+ */
+static unsigned int
+ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ struct sk_buff *reasm = skb_nfct_reasm(skb);
+ struct net *net;
+
+ /* Skip if not a "replay" from nf_ct_frag6_output or first fragment.
+ * ipvs_property is set when checking first fragment
+ * in ip_vs_in() and ip_vs_out().
+ */
+ if (reasm)
+ IP_VS_DBG(2, "Fragment recv prop:%d\n", reasm->ipvs_property);
+ if (!reasm || !reasm->ipvs_property)
+ return NF_ACCEPT;
+
+ net = skb_net(skb);
+ if (!net_ipvs(net)->enable)
+ return NF_ACCEPT;
+
+ /* Copy stored fw mark, saved in ip_vs_{in,out} */
+ skb->mark = reasm->mark;
+
+ return NF_ACCEPT;
+}
+
+/*
* AF_INET6 handler in NF_INET_LOCAL_IN chain
* Schedule and forward packets from remote clients
*/
@@ -1793,8 +1835,10 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
{
int r;
struct net *net;
+ struct ip_vs_iphdr iphdr;
- if (ipv6_hdr(skb)->nexthdr != IPPROTO_ICMPV6)
+ ip_vs_fill_iph_skb(AF_INET6, skb, &iphdr);
+ if (iphdr.protocol != IPPROTO_ICMPV6)
return NF_ACCEPT;
/* ipvs enabled in this netns ? */
@@ -1802,7 +1846,7 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb,
if (!net_ipvs(net)->enable)
return NF_ACCEPT;
- return ip_vs_in_icmp_v6(skb, &r, hooknum);
+ return ip_vs_in_icmp_v6(skb, &r, hooknum, &iphdr);
}
#endif
@@ -1860,6 +1904,14 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = {
.priority = 100,
},
#ifdef CONFIG_IP_VS_IPV6
+ /* After mangle & nat fetch 2:nd fragment and following */
+ {
+ .hook = ip_vs_preroute_frag6,
+ .owner = THIS_MODULE,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = NF_IP6_PRI_NAT_DST + 1,
+ },
/* After packet filtering, change source only for VS/NAT */
{
.hook = ip_vs_reply6,
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index c4ee43710aa..ec664cbb119 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2339,7 +2339,7 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
struct ip_vs_dest_user_kern udest;
struct netns_ipvs *ipvs = net_ipvs(net);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_SET_MAX)
@@ -2632,7 +2632,7 @@ do_ip_vs_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
struct netns_ipvs *ipvs = net_ipvs(net);
BUG_ON(!net);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (cmd < IP_VS_BASE_CTL || cmd > IP_VS_SO_GET_MAX)
@@ -3699,6 +3699,10 @@ static int __net_init ip_vs_control_net_init_sysctl(struct net *net)
tbl = kmemdup(vs_vars, sizeof(vs_vars), GFP_KERNEL);
if (tbl == NULL)
return -ENOMEM;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ tbl[0].procname = NULL;
} else
tbl = vs_vars;
/* Initialize sysctl defaults */
diff --git a/net/netfilter/ipvs/ip_vs_dh.c b/net/netfilter/ipvs/ip_vs_dh.c
index 8b7dca9ea42..7f3b0cc00b7 100644
--- a/net/netfilter/ipvs/ip_vs_dh.c
+++ b/net/netfilter/ipvs/ip_vs_dh.c
@@ -215,7 +215,7 @@ ip_vs_dh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dh_bucket *tbl;
struct ip_vs_iphdr iph;
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index df646ccf08a..fdd89b9564e 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -479,7 +479,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblc_entry *en;
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
@@ -560,6 +560,11 @@ static int __net_init __ip_vs_lblc_init(struct net *net)
GFP_KERNEL);
if (ipvs->lblc_ctl_table == NULL)
return -ENOMEM;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ ipvs->lblc_ctl_table[0].procname = NULL;
+
} else
ipvs->lblc_ctl_table = vs_vars_table;
ipvs->sysctl_lblc_expiration = DEFAULT_EXPIRATION;
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index 570e31ea427..c03b6a3ade2 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -649,7 +649,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_dest *dest = NULL;
struct ip_vs_lblcr_entry *en;
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "%s(): Scheduling...\n", __func__);
@@ -754,6 +754,10 @@ static int __net_init __ip_vs_lblcr_init(struct net *net)
GFP_KERNEL);
if (ipvs->lblcr_ctl_table == NULL)
return -ENOMEM;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ ipvs->lblcr_ctl_table[0].procname = NULL;
} else
ipvs->lblcr_ctl_table = vs_vars_table;
ipvs->sysctl_lblcr_expiration = DEFAULT_EXPIRATION;
diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c
index 022e77e1e76..c8beafd401a 100644
--- a/net/netfilter/ipvs/ip_vs_nfct.c
+++ b/net/netfilter/ipvs/ip_vs_nfct.c
@@ -82,7 +82,7 @@ void
ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, int outin)
{
enum ip_conntrack_info ctinfo;
- struct nf_conn *ct = ct = nf_ct_get(skb, &ctinfo);
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
struct nf_conntrack_tuple new_tuple;
if (ct == NULL || nf_ct_is_confirmed(ct) || nf_ct_is_untracked(ct) ||
diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c
index 1aa5cac748c..12475ef88da 100644
--- a/net/netfilter/ipvs/ip_vs_pe_sip.c
+++ b/net/netfilter/ipvs/ip_vs_pe_sip.c
@@ -68,23 +68,31 @@ static int get_callid(const char *dptr, unsigned int dataoff,
static int
ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb)
{
+ struct sk_buff *reasm = skb_nfct_reasm(skb);
struct ip_vs_iphdr iph;
unsigned int dataoff, datalen, matchoff, matchlen;
const char *dptr;
int retc;
- ip_vs_fill_iphdr(p->af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_skb(p->af, skb, &iph);
/* Only useful with UDP */
if (iph.protocol != IPPROTO_UDP)
return -EINVAL;
+ /* todo: IPv6 fragments:
+ * I think this only should be done for the first fragment. /HS
+ */
+ if (reasm) {
+ skb = reasm;
+ dataoff = iph.thoff_reasm + sizeof(struct udphdr);
+ } else
+ dataoff = iph.len + sizeof(struct udphdr);
- /* No Data ? */
- dataoff = iph.len + sizeof(struct udphdr);
if (dataoff >= skb->len)
return -EINVAL;
-
- if ((retc=skb_linearize(skb)) < 0)
+ /* todo: Check if this will mess-up the reasm skb !!! /HS */
+ retc = skb_linearize(skb);
+ if (retc < 0)
return retc;
dptr = skb->data + dataoff;
datalen = skb->len - dataoff;
diff --git a/net/netfilter/ipvs/ip_vs_proto.c b/net/netfilter/ipvs/ip_vs_proto.c
index 50d82186da8..939f7fbe9b4 100644
--- a/net/netfilter/ipvs/ip_vs_proto.c
+++ b/net/netfilter/ipvs/ip_vs_proto.c
@@ -280,17 +280,17 @@ ip_vs_tcpudp_debug_packet_v6(struct ip_vs_protocol *pp,
if (ih == NULL)
sprintf(buf, "TRUNCATED");
else if (ih->nexthdr == IPPROTO_FRAGMENT)
- sprintf(buf, "%pI6->%pI6 frag", &ih->saddr, &ih->daddr);
+ sprintf(buf, "%pI6c->%pI6c frag", &ih->saddr, &ih->daddr);
else {
__be16 _ports[2], *pptr;
pptr = skb_header_pointer(skb, offset + sizeof(struct ipv6hdr),
sizeof(_ports), _ports);
if (pptr == NULL)
- sprintf(buf, "TRUNCATED %pI6->%pI6",
+ sprintf(buf, "TRUNCATED %pI6c->%pI6c",
&ih->saddr, &ih->daddr);
else
- sprintf(buf, "%pI6:%u->%pI6:%u",
+ sprintf(buf, "%pI6c:%u->%pI6c:%u",
&ih->saddr, ntohs(pptr[0]),
&ih->daddr, ntohs(pptr[1]));
}
diff --git a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
index 5b8eb8b12c3..5de3dd312c0 100644
--- a/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_ah_esp.c
@@ -57,7 +57,7 @@ ah_esp_conn_fill_param_proto(struct net *net, int af,
static struct ip_vs_conn *
ah_esp_conn_in_get(int af, const struct sk_buff *skb,
- const struct ip_vs_iphdr *iph, unsigned int proto_off,
+ const struct ip_vs_iphdr *iph,
int inverse)
{
struct ip_vs_conn *cp;
@@ -85,9 +85,7 @@ ah_esp_conn_in_get(int af, const struct sk_buff *skb,
static struct ip_vs_conn *
ah_esp_conn_out_get(int af, const struct sk_buff *skb,
- const struct ip_vs_iphdr *iph,
- unsigned int proto_off,
- int inverse)
+ const struct ip_vs_iphdr *iph, int inverse)
{
struct ip_vs_conn *cp;
struct ip_vs_conn_param p;
@@ -110,7 +108,8 @@ ah_esp_conn_out_get(int af, const struct sk_buff *skb,
static int
ah_esp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- int *verdict, struct ip_vs_conn **cpp)
+ int *verdict, struct ip_vs_conn **cpp,
+ struct ip_vs_iphdr *iph)
{
/*
* AH/ESP is only related traffic. Pass the packet to IP stack.
diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c
index 9f3fb751c49..746048b13ef 100644
--- a/net/netfilter/ipvs/ip_vs_proto_sctp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c
@@ -10,28 +10,26 @@
static int
sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- int *verdict, struct ip_vs_conn **cpp)
+ int *verdict, struct ip_vs_conn **cpp,
+ struct ip_vs_iphdr *iph)
{
struct net *net;
struct ip_vs_service *svc;
sctp_chunkhdr_t _schunkh, *sch;
sctp_sctphdr_t *sh, _sctph;
- struct ip_vs_iphdr iph;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
-
- sh = skb_header_pointer(skb, iph.len, sizeof(_sctph), &_sctph);
+ sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph);
if (sh == NULL)
return 0;
- sch = skb_header_pointer(skb, iph.len + sizeof(sctp_sctphdr_t),
+ sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t),
sizeof(_schunkh), &_schunkh);
if (sch == NULL)
return 0;
net = skb_net(skb);
if ((sch->type == SCTP_CID_INIT) &&
- (svc = ip_vs_service_get(net, af, skb->mark, iph.protocol,
- &iph.daddr, sh->dest))) {
+ (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
+ &iph->daddr, sh->dest))) {
int ignored;
if (ip_vs_todrop(net_ipvs(net))) {
@@ -47,10 +45,10 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the
* incoming connection, and create a connection entry.
*/
- *cpp = ip_vs_schedule(svc, skb, pd, &ignored);
+ *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) {
if (!ignored)
- *verdict = ip_vs_leave(svc, skb, pd);
+ *verdict = ip_vs_leave(svc, skb, pd, iph);
else {
ip_vs_service_put(svc);
*verdict = NF_DROP;
@@ -64,20 +62,18 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
}
static int
-sctp_snat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
sctp_sctphdr_t *sctph;
- unsigned int sctphoff;
+ unsigned int sctphoff = iph->len;
struct sk_buff *iter;
__be32 crc32;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- sctphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- sctphoff = ip_hdrlen(skb);
/* csum_check requires unshared skb */
if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
@@ -108,20 +104,18 @@ sctp_snat_handler(struct sk_buff *skb,
}
static int
-sctp_dnat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
sctp_sctphdr_t *sctph;
- unsigned int sctphoff;
+ unsigned int sctphoff = iph->len;
struct sk_buff *iter;
__be32 crc32;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- sctphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- sctphoff = ip_hdrlen(skb);
/* csum_check requires unshared skb */
if (!skb_make_writable(skb, sctphoff + sizeof(*sctph)))
diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c
index cd609cc6272..9af653a7582 100644
--- a/net/netfilter/ipvs/ip_vs_proto_tcp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c
@@ -33,16 +33,14 @@
static int
tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- int *verdict, struct ip_vs_conn **cpp)
+ int *verdict, struct ip_vs_conn **cpp,
+ struct ip_vs_iphdr *iph)
{
struct net *net;
struct ip_vs_service *svc;
struct tcphdr _tcph, *th;
- struct ip_vs_iphdr iph;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
-
- th = skb_header_pointer(skb, iph.len, sizeof(_tcph), &_tcph);
+ th = skb_header_pointer(skb, iph->len, sizeof(_tcph), &_tcph);
if (th == NULL) {
*verdict = NF_DROP;
return 0;
@@ -50,8 +48,8 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
net = skb_net(skb);
/* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */
if (th->syn &&
- (svc = ip_vs_service_get(net, af, skb->mark, iph.protocol,
- &iph.daddr, th->dest))) {
+ (svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
+ &iph->daddr, th->dest))) {
int ignored;
if (ip_vs_todrop(net_ipvs(net))) {
@@ -68,10 +66,10 @@ tcp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the
* incoming connection, and create a connection entry.
*/
- *cpp = ip_vs_schedule(svc, skb, pd, &ignored);
+ *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) {
if (!ignored)
- *verdict = ip_vs_leave(svc, skb, pd);
+ *verdict = ip_vs_leave(svc, skb, pd, iph);
else {
ip_vs_service_put(svc);
*verdict = NF_DROP;
@@ -128,20 +126,18 @@ tcp_partial_csum_update(int af, struct tcphdr *tcph,
static int
-tcp_snat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+tcp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
struct tcphdr *tcph;
- unsigned int tcphoff;
+ unsigned int tcphoff = iph->len;
int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- tcphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- tcphoff = ip_hdrlen(skb);
oldlen = skb->len - tcphoff;
/* csum_check requires unshared skb */
@@ -208,20 +204,18 @@ tcp_snat_handler(struct sk_buff *skb,
static int
-tcp_dnat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+tcp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
struct tcphdr *tcph;
- unsigned int tcphoff;
+ unsigned int tcphoff = iph->len;
int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- tcphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- tcphoff = ip_hdrlen(skb);
oldlen = skb->len - tcphoff;
/* csum_check requires unshared skb */
diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c
index 2fedb2dcb3d..503a842c90d 100644
--- a/net/netfilter/ipvs/ip_vs_proto_udp.c
+++ b/net/netfilter/ipvs/ip_vs_proto_udp.c
@@ -30,23 +30,22 @@
static int
udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
- int *verdict, struct ip_vs_conn **cpp)
+ int *verdict, struct ip_vs_conn **cpp,
+ struct ip_vs_iphdr *iph)
{
struct net *net;
struct ip_vs_service *svc;
struct udphdr _udph, *uh;
- struct ip_vs_iphdr iph;
- ip_vs_fill_iphdr(af, skb_network_header(skb), &iph);
-
- uh = skb_header_pointer(skb, iph.len, sizeof(_udph), &_udph);
+ /* IPv6 fragments, only first fragment will hit this */
+ uh = skb_header_pointer(skb, iph->len, sizeof(_udph), &_udph);
if (uh == NULL) {
*verdict = NF_DROP;
return 0;
}
net = skb_net(skb);
- svc = ip_vs_service_get(net, af, skb->mark, iph.protocol,
- &iph.daddr, uh->dest);
+ svc = ip_vs_service_get(net, af, skb->mark, iph->protocol,
+ &iph->daddr, uh->dest);
if (svc) {
int ignored;
@@ -64,10 +63,10 @@ udp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd,
* Let the virtual server select a real server for the
* incoming connection, and create a connection entry.
*/
- *cpp = ip_vs_schedule(svc, skb, pd, &ignored);
+ *cpp = ip_vs_schedule(svc, skb, pd, &ignored, iph);
if (!*cpp && ignored <= 0) {
if (!ignored)
- *verdict = ip_vs_leave(svc, skb, pd);
+ *verdict = ip_vs_leave(svc, skb, pd, iph);
else {
ip_vs_service_put(svc);
*verdict = NF_DROP;
@@ -125,20 +124,18 @@ udp_partial_csum_update(int af, struct udphdr *uhdr,
static int
-udp_snat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+udp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
struct udphdr *udph;
- unsigned int udphoff;
+ unsigned int udphoff = iph->len;
int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- udphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- udphoff = ip_hdrlen(skb);
oldlen = skb->len - udphoff;
/* csum_check requires unshared skb */
@@ -210,20 +207,18 @@ udp_snat_handler(struct sk_buff *skb,
static int
-udp_dnat_handler(struct sk_buff *skb,
- struct ip_vs_protocol *pp, struct ip_vs_conn *cp)
+udp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp,
+ struct ip_vs_conn *cp, struct ip_vs_iphdr *iph)
{
struct udphdr *udph;
- unsigned int udphoff;
+ unsigned int udphoff = iph->len;
int oldlen;
int payload_csum = 0;
#ifdef CONFIG_IP_VS_IPV6
- if (cp->af == AF_INET6)
- udphoff = sizeof(struct ipv6hdr);
- else
+ if (cp->af == AF_INET6 && iph->fragoffs)
+ return 1;
#endif
- udphoff = ip_hdrlen(skb);
oldlen = skb->len - udphoff;
/* csum_check requires unshared skb */
diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c
index 08dbdd5bc18..d6bf20d6cdb 100644
--- a/net/netfilter/ipvs/ip_vs_sched.c
+++ b/net/netfilter/ipvs/ip_vs_sched.c
@@ -159,7 +159,7 @@ void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg)
svc->fwmark, msg);
#ifdef CONFIG_IP_VS_IPV6
} else if (svc->af == AF_INET6) {
- IP_VS_ERR_RL("%s: %s [%pI6]:%d - %s\n",
+ IP_VS_ERR_RL("%s: %s [%pI6c]:%d - %s\n",
svc->scheduler->name,
ip_vs_proto_name(svc->protocol),
&svc->addr.in6, ntohs(svc->port), msg);
diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c
index 05126521743..e3312699462 100644
--- a/net/netfilter/ipvs/ip_vs_sh.c
+++ b/net/netfilter/ipvs/ip_vs_sh.c
@@ -228,7 +228,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb)
struct ip_vs_sh_bucket *tbl;
struct ip_vs_iphdr iph;
- ip_vs_fill_iphdr(svc->af, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_addr_only(svc->af, skb, &iph);
IP_VS_DBG(6, "ip_vs_sh_schedule(): Scheduling...\n");
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index cc4c8095681..ee6b7a9f1ec 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -338,7 +338,7 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
local = __ip_vs_is_local_route6(rt);
if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) &
rt_mode)) {
- IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6\n",
+ IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI6c\n",
local ? "local":"non-local", daddr);
dst_release(&rt->dst);
return NULL;
@@ -346,8 +346,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
if (local && !(rt_mode & IP_VS_RT_MODE_RDR) &&
!((ort = (struct rt6_info *) skb_dst(skb)) &&
__ip_vs_is_local_route6(ort))) {
- IP_VS_DBG_RL("Redirect from non-local address %pI6 to local "
- "requires NAT method, dest: %pI6\n",
+ IP_VS_DBG_RL("Redirect from non-local address %pI6c to local "
+ "requires NAT method, dest: %pI6c\n",
&ipv6_hdr(skb)->daddr, daddr);
dst_release(&rt->dst);
return NULL;
@@ -355,8 +355,8 @@ __ip_vs_get_out_rt_v6(struct sk_buff *skb, struct ip_vs_dest *dest,
if (unlikely(!local && (!skb->dev || skb->dev->flags & IFF_LOOPBACK) &&
ipv6_addr_type(&ipv6_hdr(skb)->saddr) &
IPV6_ADDR_LOOPBACK)) {
- IP_VS_DBG_RL("Stopping traffic from loopback address %pI6 "
- "to non-local address, dest: %pI6\n",
+ IP_VS_DBG_RL("Stopping traffic from loopback address %pI6c "
+ "to non-local address, dest: %pI6c\n",
&ipv6_hdr(skb)->saddr, daddr);
dst_release(&rt->dst);
return NULL;
@@ -427,7 +427,7 @@ do { \
*/
int
ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
/* we do not touch skb and do not need pskb ptr */
IP_VS_XMIT(NFPROTO_IPV4, skb, cp, 1);
@@ -441,7 +441,7 @@ ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
*/
int
ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
struct rtable *rt; /* Route to the other host */
struct iphdr *iph = ip_hdr(skb);
@@ -496,16 +496,16 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{
struct rt6_info *rt; /* Route to the other host */
- struct ipv6hdr *iph = ipv6_hdr(skb);
int mtu;
EnterFunction(10);
- if (!(rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr, NULL, 0,
- IP_VS_RT_MODE_NON_LOCAL)))
+ rt = __ip_vs_get_out_rt_v6(skb, NULL, &iph->daddr.in6, NULL, 0,
+ IP_VS_RT_MODE_NON_LOCAL);
+ if (!rt)
goto tx_error_icmp;
/* MTU checking */
@@ -516,7 +516,9 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev;
}
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ /* only send ICMP too big on first fragment */
+ if (!iph->fragoffs)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
dst_release(&rt->dst);
IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error;
@@ -559,7 +561,7 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
*/
int
ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
struct rtable *rt; /* Route to the other host */
int mtu;
@@ -592,7 +594,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
if (cp->flags & IP_VS_CONN_F_SYNC && local) {
enum ip_conntrack_info ctinfo;
- struct nf_conn *ct = ct = nf_ct_get(skb, &ctinfo);
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct)) {
IP_VS_DBG_RL_PKT(10, AF_INET, pp, skb, 0,
@@ -629,7 +631,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error_put;
/* mangle the packet */
- if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp))
+ if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, ipvsh))
goto tx_error_put;
ip_hdr(skb)->daddr = cp->daddr.ip;
ip_send_check(ip_hdr(skb));
@@ -677,7 +679,7 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{
struct rt6_info *rt; /* Route to the other host */
int mtu;
@@ -686,10 +688,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
EnterFunction(10);
/* check if it is a connection of no-client-port */
- if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) {
+ if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !iph->fragoffs)) {
__be16 _pt, *p;
- p = skb_header_pointer(skb, sizeof(struct ipv6hdr),
- sizeof(_pt), &_pt);
+ p = skb_header_pointer(skb, iph->len, sizeof(_pt), &_pt);
if (p == NULL)
goto tx_error;
ip_vs_conn_fill_cport(cp, *p);
@@ -709,7 +710,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
if (cp->flags & IP_VS_CONN_F_SYNC && local) {
enum ip_conntrack_info ctinfo;
- struct nf_conn *ct = ct = nf_ct_get(skb, &ctinfo);
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct)) {
IP_VS_DBG_RL_PKT(10, AF_INET6, pp, skb, 0,
@@ -737,7 +738,9 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev;
}
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ /* only send ICMP too big on first fragment */
+ if (!iph->fragoffs)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL_PKT(0, AF_INET6, pp, skb, 0,
"ip_vs_nat_xmit_v6(): frag needed for");
goto tx_error_put;
@@ -751,7 +754,7 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
goto tx_error_put;
/* mangle the packet */
- if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp))
+ if (pp->dnat_handler && !pp->dnat_handler(skb, pp, cp, iph))
goto tx_error;
ipv6_hdr(skb)->daddr = cp->daddr.in6;
@@ -812,7 +815,7 @@ tx_error_put:
*/
int
ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
struct netns_ipvs *ipvs = net_ipvs(skb_net(skb));
struct rtable *rt; /* Route to the other host */
@@ -932,7 +935,7 @@ tx_error_put:
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
struct rt6_info *rt; /* Route to the other host */
struct in6_addr saddr; /* Source for tunnel */
@@ -972,7 +975,9 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev;
}
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ /* only send ICMP too big on first fragment */
+ if (!ipvsh->fragoffs)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error_put;
}
@@ -1053,7 +1058,7 @@ tx_error_put:
*/
int
ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *ipvsh)
{
struct rtable *rt; /* Route to the other host */
struct iphdr *iph = ip_hdr(skb);
@@ -1115,7 +1120,7 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp)
+ struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph)
{
struct rt6_info *rt; /* Route to the other host */
int mtu;
@@ -1139,7 +1144,9 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev;
}
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ /* only send ICMP too big on first fragment */
+ if (!iph->fragoffs)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
dst_release(&rt->dst);
IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error;
@@ -1183,7 +1190,8 @@ tx_error:
*/
int
ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset, unsigned int hooknum)
+ struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+ struct ip_vs_iphdr *iph)
{
struct rtable *rt; /* Route to the other host */
int mtu;
@@ -1198,7 +1206,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
- rc = cp->packet_xmit(skb, cp, pp);
+ rc = cp->packet_xmit(skb, cp, pp, iph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@@ -1227,7 +1235,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
if (cp->flags & IP_VS_CONN_F_SYNC && local) {
enum ip_conntrack_info ctinfo;
- struct nf_conn *ct = ct = nf_ct_get(skb, &ctinfo);
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct)) {
IP_VS_DBG(10, "%s(): "
@@ -1304,7 +1312,8 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
#ifdef CONFIG_IP_VS_IPV6
int
ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
- struct ip_vs_protocol *pp, int offset, unsigned int hooknum)
+ struct ip_vs_protocol *pp, int offset, unsigned int hooknum,
+ struct ip_vs_iphdr *iph)
{
struct rt6_info *rt; /* Route to the other host */
int mtu;
@@ -1319,7 +1328,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
translate address/port back */
if (IP_VS_FWD_METHOD(cp) != IP_VS_CONN_F_MASQ) {
if (cp->packet_xmit)
- rc = cp->packet_xmit(skb, cp, pp);
+ rc = cp->packet_xmit(skb, cp, pp, iph);
else
rc = NF_ACCEPT;
/* do not touch skb anymore */
@@ -1347,7 +1356,7 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
if (cp->flags & IP_VS_CONN_F_SYNC && local) {
enum ip_conntrack_info ctinfo;
- struct nf_conn *ct = ct = nf_ct_get(skb, &ctinfo);
+ struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
if (ct && !nf_ct_is_untracked(ct)) {
IP_VS_DBG(10, "%s(): "
@@ -1375,7 +1384,9 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp,
skb->dev = net->loopback_dev;
}
- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ /* only send ICMP too big on first fragment */
+ if (!iph->fragoffs)
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
IP_VS_DBG_RL("%s(): frag needed\n", __func__);
goto tx_error_put;
}
diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c
index d61e0782a79..7df424e2d10 100644
--- a/net/netfilter/nf_conntrack_acct.c
+++ b/net/netfilter/nf_conntrack_acct.c
@@ -69,6 +69,10 @@ static int nf_conntrack_acct_init_sysctl(struct net *net)
table[0].data = &net->ct.sysctl_acct;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->ct.acct_sysctl_header = register_net_sysctl(net, "net/netfilter",
table);
if (!net->ct.acct_sysctl_header) {
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 0f241be28f9..e4a0c4fb3a7 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -221,11 +221,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
* too. */
nf_ct_remove_expectations(ct);
- /* We overload first tuple to link into unconfirmed list. */
- if (!nf_ct_is_confirmed(ct)) {
- BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
- hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
- }
+ /* We overload first tuple to link into unconfirmed or dying list.*/
+ BUG_ON(hlist_nulls_unhashed(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode));
+ hlist_nulls_del_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
NF_CT_STAT_INC(net, delete);
spin_unlock_bh(&nf_conntrack_lock);
@@ -247,6 +245,9 @@ void nf_ct_delete_from_lists(struct nf_conn *ct)
* Otherwise we can get spurious warnings. */
NF_CT_STAT_INC(net, delete_list);
clean_from_lists(ct);
+ /* add this conntrack to the dying list */
+ hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
+ &net->ct.dying);
spin_unlock_bh(&nf_conntrack_lock);
}
EXPORT_SYMBOL_GPL(nf_ct_delete_from_lists);
@@ -268,31 +269,23 @@ static void death_by_event(unsigned long ul_conntrack)
}
/* we've got the event delivered, now it's dying */
set_bit(IPS_DYING_BIT, &ct->status);
- spin_lock(&nf_conntrack_lock);
- hlist_nulls_del(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode);
- spin_unlock(&nf_conntrack_lock);
nf_ct_put(ct);
}
-void nf_ct_insert_dying_list(struct nf_conn *ct)
+void nf_ct_dying_timeout(struct nf_conn *ct)
{
struct net *net = nf_ct_net(ct);
struct nf_conntrack_ecache *ecache = nf_ct_ecache_find(ct);
BUG_ON(ecache == NULL);
- /* add this conntrack to the dying list */
- spin_lock_bh(&nf_conntrack_lock);
- hlist_nulls_add_head(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
- &net->ct.dying);
- spin_unlock_bh(&nf_conntrack_lock);
/* set a new timer to retry event delivery */
setup_timer(&ecache->timeout, death_by_event, (unsigned long)ct);
ecache->timeout.expires = jiffies +
(random32() % net->ct.sysctl_events_retry_timeout);
add_timer(&ecache->timeout);
}
-EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list);
+EXPORT_SYMBOL_GPL(nf_ct_dying_timeout);
static void death_by_timeout(unsigned long ul_conntrack)
{
@@ -307,7 +300,7 @@ static void death_by_timeout(unsigned long ul_conntrack)
unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
/* destroy event was not delivered */
nf_ct_delete_from_lists(ct);
- nf_ct_insert_dying_list(ct);
+ nf_ct_dying_timeout(ct);
return;
}
set_bit(IPS_DYING_BIT, &ct->status);
@@ -1383,11 +1376,12 @@ void nf_conntrack_cleanup(struct net *net)
synchronize_net();
nf_conntrack_proto_fini(net);
nf_conntrack_cleanup_net(net);
+}
- if (net_eq(net, &init_net)) {
- RCU_INIT_POINTER(nf_ct_destroy, NULL);
- nf_conntrack_cleanup_init_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)
@@ -1416,7 +1410,7 @@ EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable);
int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
{
- int i, bucket;
+ int i, bucket, rc;
unsigned int hashsize, old_size;
struct hlist_nulls_head *hash, *old_hash;
struct nf_conntrack_tuple_hash *h;
@@ -1429,7 +1423,9 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
if (!nf_conntrack_htable_size)
return param_set_uint(val, kp);
- hashsize = simple_strtoul(val, NULL, 0);
+ rc = kstrtouint(val, 0, &hashsize);
+ if (rc)
+ return rc;
if (!hashsize)
return -EINVAL;
@@ -1531,6 +1527,7 @@ err_extend:
*/
#define UNCONFIRMED_NULLS_VAL ((1<<30)+0)
#define DYING_NULLS_VAL ((1<<30)+1)
+#define TEMPLATE_NULLS_VAL ((1<<30)+2)
static int nf_conntrack_init_net(struct net *net)
{
@@ -1539,6 +1536,7 @@ static int nf_conntrack_init_net(struct net *net)
atomic_set(&net->ct.count, 0);
INIT_HLIST_NULLS_HEAD(&net->ct.unconfirmed, UNCONFIRMED_NULLS_VAL);
INIT_HLIST_NULLS_HEAD(&net->ct.dying, DYING_NULLS_VAL);
+ INIT_HLIST_NULLS_HEAD(&net->ct.tmpl, TEMPLATE_NULLS_VAL);
net->ct.stat = alloc_percpu(struct ip_conntrack_stat);
if (!net->ct.stat) {
ret = -ENOMEM;
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index de9781b6464..faa978f1714 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -196,6 +196,10 @@ static int nf_conntrack_event_init_sysctl(struct net *net)
table[0].data = &net->ct.sysctl_events;
table[1].data = &net->ct.sysctl_events_retry_timeout;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->ct.event_sysctl_header =
register_net_sysctl(net, "net/netfilter", table);
if (!net->ct.event_sysctl_header) {
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 1b30b0dee70..962795e839a 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -753,7 +753,8 @@ static int callforward_do_filter(const union nf_inet_addr *src,
flowi4_to_flowi(&fl1), false)) {
if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
flowi4_to_flowi(&fl2), false)) {
- if (rt1->rt_gateway == rt2->rt_gateway &&
+ if (rt_nexthop(rt1, fl1.daddr) ==
+ rt_nexthop(rt2, fl2.daddr) &&
rt1->dst.dev == rt2->dst.dev)
ret = 1;
dst_release(&rt2->dst);
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index c4bc637feb7..884f2b39319 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -64,6 +64,10 @@ static int nf_conntrack_helper_init_sysctl(struct net *net)
table[0].data = &net->ct.sysctl_auto_assign_helper;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->ct.helper_sysctl_header =
register_net_sysctl(net, "net/netfilter", table);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 7bbfb3deea3..627b0e50b23 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -898,7 +898,8 @@ ctnetlink_parse_zone(const struct nlattr *attr, u16 *zone)
}
static const struct nla_policy help_nla_policy[CTA_HELP_MAX+1] = {
- [CTA_HELP_NAME] = { .type = NLA_NUL_STRING },
+ [CTA_HELP_NAME] = { .type = NLA_NUL_STRING,
+ .len = NF_CT_HELPER_NAME_LEN - 1 },
};
static inline int
@@ -932,6 +933,8 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
[CTA_ID] = { .type = NLA_U32 },
[CTA_NAT_DST] = { .type = NLA_NESTED },
[CTA_TUPLE_MASTER] = { .type = NLA_NESTED },
+ [CTA_NAT_SEQ_ADJ_ORIG] = { .type = NLA_NESTED },
+ [CTA_NAT_SEQ_ADJ_REPLY] = { .type = NLA_NESTED },
[CTA_ZONE] = { .type = NLA_U16 },
[CTA_MARK_MASK] = { .type = NLA_U32 },
};
@@ -989,7 +992,7 @@ ctnetlink_del_conntrack(struct sock *ctnl, struct sk_buff *skb,
nlmsg_report(nlh)) < 0) {
nf_ct_delete_from_lists(ct);
/* we failed to report the event, try later */
- nf_ct_insert_dying_list(ct);
+ nf_ct_dying_timeout(ct);
nf_ct_put(ct);
return 0;
}
@@ -1089,6 +1092,112 @@ out:
return err == -EAGAIN ? -ENOBUFS : err;
}
+static int ctnetlink_done_list(struct netlink_callback *cb)
+{
+ if (cb->args[1])
+ nf_ct_put((struct nf_conn *)cb->args[1]);
+ return 0;
+}
+
+static int
+ctnetlink_dump_list(struct sk_buff *skb, struct netlink_callback *cb,
+ struct hlist_nulls_head *list)
+{
+ struct nf_conn *ct, *last;
+ struct nf_conntrack_tuple_hash *h;
+ struct hlist_nulls_node *n;
+ struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+ u_int8_t l3proto = nfmsg->nfgen_family;
+ int res;
+
+ if (cb->args[2])
+ return 0;
+
+ spin_lock_bh(&nf_conntrack_lock);
+ last = (struct nf_conn *)cb->args[1];
+restart:
+ hlist_nulls_for_each_entry(h, n, list, hnnode) {
+ ct = nf_ct_tuplehash_to_ctrack(h);
+ if (l3proto && nf_ct_l3num(ct) != l3proto)
+ continue;
+ if (cb->args[1]) {
+ if (ct != last)
+ continue;
+ cb->args[1] = 0;
+ }
+ rcu_read_lock();
+ res = ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NFNL_MSG_TYPE(cb->nlh->nlmsg_type),
+ ct);
+ rcu_read_unlock();
+ if (res < 0) {
+ nf_conntrack_get(&ct->ct_general);
+ cb->args[1] = (unsigned long)ct;
+ goto out;
+ }
+ }
+ if (cb->args[1]) {
+ cb->args[1] = 0;
+ goto restart;
+ } else
+ cb->args[2] = 1;
+out:
+ spin_unlock_bh(&nf_conntrack_lock);
+ if (last)
+ nf_ct_put(last);
+
+ return skb->len;
+}
+
+static int
+ctnetlink_dump_dying(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+
+ return ctnetlink_dump_list(skb, cb, &net->ct.dying);
+}
+
+static int
+ctnetlink_get_ct_dying(struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
+{
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = ctnetlink_dump_dying,
+ .done = ctnetlink_done_list,
+ };
+ return netlink_dump_start(ctnl, skb, nlh, &c);
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int
+ctnetlink_dump_unconfirmed(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+
+ return ctnetlink_dump_list(skb, cb, &net->ct.unconfirmed);
+}
+
+static int
+ctnetlink_get_ct_unconfirmed(struct sock *ctnl, struct sk_buff *skb,
+ const struct nlmsghdr *nlh,
+ const struct nlattr * const cda[])
+{
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = ctnetlink_dump_unconfirmed,
+ .done = ctnetlink_done_list,
+ };
+ return netlink_dump_start(ctnl, skb, nlh, &c);
+ }
+
+ return -EOPNOTSUPP;
+}
+
#ifdef CONFIG_NF_NAT_NEEDED
static int
ctnetlink_parse_nat_setup(struct nf_conn *ct,
@@ -2216,7 +2325,8 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
[CTA_EXPECT_MASK] = { .type = NLA_NESTED },
[CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 },
[CTA_EXPECT_ID] = { .type = NLA_U32 },
- [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING },
+ [CTA_EXPECT_HELP_NAME] = { .type = NLA_NUL_STRING,
+ .len = NF_CT_HELPER_NAME_LEN - 1 },
[CTA_EXPECT_ZONE] = { .type = NLA_U16 },
[CTA_EXPECT_FLAGS] = { .type = NLA_U32 },
[CTA_EXPECT_CLASS] = { .type = NLA_U32 },
@@ -2514,7 +2624,7 @@ ctnetlink_create_expect(struct net *net, u16 zone,
if (!help) {
if (!cda[CTA_EXPECT_TIMEOUT]) {
err = -EINVAL;
- goto out;
+ goto err_out;
}
exp->timeout.expires =
jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ;
@@ -2712,6 +2822,8 @@ static const struct nfnl_callback ctnl_cb[IPCTNL_MSG_MAX] = {
.policy = ct_nla_policy },
[IPCTNL_MSG_CT_GET_STATS_CPU] = { .call = ctnetlink_stat_ct_cpu },
[IPCTNL_MSG_CT_GET_STATS] = { .call = ctnetlink_stat_ct },
+ [IPCTNL_MSG_CT_GET_DYING] = { .call = ctnetlink_get_ct_dying },
+ [IPCTNL_MSG_CT_GET_UNCONFIRMED] = { .call = ctnetlink_get_ct_unconfirmed },
};
static const struct nfnl_callback ctnl_exp_cb[IPCTNL_MSG_EXP_MAX] = {
diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c
index 6535326cf07..a8ae287bc7a 100644
--- a/net/netfilter/nf_conntrack_proto_dccp.c
+++ b/net/netfilter/nf_conntrack_proto_dccp.c
@@ -815,7 +815,7 @@ static struct ctl_table dccp_sysctl_table[] = {
};
#endif /* CONFIG_SYSCTL */
-static int dccp_kmemdup_sysctl_table(struct nf_proto_net *pn,
+static int dccp_kmemdup_sysctl_table(struct net *net, struct nf_proto_net *pn,
struct dccp_net *dn)
{
#ifdef CONFIG_SYSCTL
@@ -836,6 +836,10 @@ static int dccp_kmemdup_sysctl_table(struct nf_proto_net *pn,
pn->ctl_table[5].data = &dn->dccp_timeout[CT_DCCP_CLOSING];
pn->ctl_table[6].data = &dn->dccp_timeout[CT_DCCP_TIMEWAIT];
pn->ctl_table[7].data = &dn->dccp_loose;
+
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ pn->ctl_table[0].procname = NULL;
#endif
return 0;
}
@@ -857,7 +861,7 @@ static int dccp_init_net(struct net *net, u_int16_t proto)
dn->dccp_timeout[CT_DCCP_TIMEWAIT] = 2 * DCCP_MSL;
}
- return dccp_kmemdup_sysctl_table(pn, dn);
+ return dccp_kmemdup_sysctl_table(net, pn, dn);
}
static struct nf_conntrack_l4proto dccp_proto4 __read_mostly = {
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 61f9285111d..83876e9877f 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1353,6 +1353,8 @@ static const struct nla_policy tcp_timeout_nla_policy[CTA_TIMEOUT_TCP_MAX+1] = {
[CTA_TIMEOUT_TCP_TIME_WAIT] = { .type = NLA_U32 },
[CTA_TIMEOUT_TCP_CLOSE] = { .type = NLA_U32 },
[CTA_TIMEOUT_TCP_SYN_SENT2] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_RETRANS] = { .type = NLA_U32 },
+ [CTA_TIMEOUT_TCP_UNACK] = { .type = NLA_U32 },
};
#endif /* CONFIG_NF_CT_NETLINK_TIMEOUT */
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 9b3943252a5..e7185c68481 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -489,6 +489,10 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
table[3].data = &net->ct.sysctl_checksum;
table[4].data = &net->ct.sysctl_log_invalid;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->ct.sysctl_header = register_net_sysctl(net, "net/netfilter", table);
if (!net->ct.sysctl_header)
goto out_unregister_netfilter;
@@ -571,6 +575,7 @@ static int __init nf_conntrack_standalone_init(void)
static void __exit nf_conntrack_standalone_fini(void)
{
unregister_pernet_subsys(&nf_conntrack_net_ops);
+ nf_conntrack_cleanup_end();
}
module_init(nf_conntrack_standalone_init);
diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c
index dbb364f62d6..7ea8026f07c 100644
--- a/net/netfilter/nf_conntrack_timestamp.c
+++ b/net/netfilter/nf_conntrack_timestamp.c
@@ -51,6 +51,10 @@ static int nf_conntrack_tstamp_init_sysctl(struct net *net)
table[0].data = &net->ct.sysctl_tstamp;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->ct.tstamp_sysctl_header = register_net_sysctl(net, "net/netfilter",
table);
if (!net->ct.tstamp_sysctl_header) {
diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c
index 8d2cf9ec37a..d812c1235b3 100644
--- a/net/netfilter/nf_queue.c
+++ b/net/netfilter/nf_queue.c
@@ -14,84 +14,32 @@
#include "nf_internals.h"
/*
- * A queue handler may be registered for each protocol. Each is protected by
- * long term mutex. The handler must provide an an outfn() to accept packets
- * for queueing and must reinject all packets it receives, no matter what.
+ * Hook for nfnetlink_queue to register its queue handler.
+ * We do this so that most of the NFQUEUE code can be modular.
+ *
+ * Once the queue is registered it must reinject all packets it
+ * receives, no matter what.
*/
-static const struct nf_queue_handler __rcu *queue_handler[NFPROTO_NUMPROTO] __read_mostly;
-
-static DEFINE_MUTEX(queue_handler_mutex);
+static const struct nf_queue_handler __rcu *queue_handler __read_mostly;
/* return EBUSY when somebody else is registered, return EEXIST if the
* same handler is registered, return 0 in case of success. */
-int nf_register_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh)
+void nf_register_queue_handler(const struct nf_queue_handler *qh)
{
- int ret;
- const struct nf_queue_handler *old;
-
- if (pf >= ARRAY_SIZE(queue_handler))
- return -EINVAL;
-
- mutex_lock(&queue_handler_mutex);
- old = rcu_dereference_protected(queue_handler[pf],
- lockdep_is_held(&queue_handler_mutex));
- if (old == qh)
- ret = -EEXIST;
- else if (old)
- ret = -EBUSY;
- else {
- rcu_assign_pointer(queue_handler[pf], qh);
- ret = 0;
- }
- mutex_unlock(&queue_handler_mutex);
-
- return ret;
+ /* should never happen, we only have one queueing backend in kernel */
+ WARN_ON(rcu_access_pointer(queue_handler));
+ rcu_assign_pointer(queue_handler, qh);
}
EXPORT_SYMBOL(nf_register_queue_handler);
/* The caller must flush their queue before this */
-int nf_unregister_queue_handler(u_int8_t pf, const struct nf_queue_handler *qh)
+void nf_unregister_queue_handler(void)
{
- const struct nf_queue_handler *old;
-
- if (pf >= ARRAY_SIZE(queue_handler))
- return -EINVAL;
-
- mutex_lock(&queue_handler_mutex);
- old = rcu_dereference_protected(queue_handler[pf],
- lockdep_is_held(&queue_handler_mutex));
- if (old && old != qh) {
- mutex_unlock(&queue_handler_mutex);
- return -EINVAL;
- }
-
- RCU_INIT_POINTER(queue_handler[pf], NULL);
- mutex_unlock(&queue_handler_mutex);
-
+ RCU_INIT_POINTER(queue_handler, NULL);
synchronize_rcu();
-
- return 0;
}
EXPORT_SYMBOL(nf_unregister_queue_handler);
-void nf_unregister_queue_handlers(const struct nf_queue_handler *qh)
-{
- u_int8_t pf;
-
- mutex_lock(&queue_handler_mutex);
- for (pf = 0; pf < ARRAY_SIZE(queue_handler); pf++) {
- if (rcu_dereference_protected(
- queue_handler[pf],
- lockdep_is_held(&queue_handler_mutex)
- ) == qh)
- RCU_INIT_POINTER(queue_handler[pf], NULL);
- }
- mutex_unlock(&queue_handler_mutex);
-
- synchronize_rcu();
-}
-EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
-
static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
{
/* Release those devices we held, or Alexey will kill me. */
@@ -137,7 +85,7 @@ static int __nf_queue(struct sk_buff *skb,
/* QUEUE == DROP if no one is waiting, to be safe. */
rcu_read_lock();
- qh = rcu_dereference(queue_handler[pf]);
+ qh = rcu_dereference(queue_handler);
if (!qh) {
status = -ESRCH;
goto err_unlock;
@@ -344,77 +292,3 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
kfree(entry);
}
EXPORT_SYMBOL(nf_reinject);
-
-#ifdef CONFIG_PROC_FS
-static void *seq_start(struct seq_file *seq, loff_t *pos)
-{
- if (*pos >= ARRAY_SIZE(queue_handler))
- return NULL;
-
- return pos;
-}
-
-static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
-{
- (*pos)++;
-
- if (*pos >= ARRAY_SIZE(queue_handler))
- return NULL;
-
- return pos;
-}
-
-static void seq_stop(struct seq_file *s, void *v)
-{
-
-}
-
-static int seq_show(struct seq_file *s, void *v)
-{
- int ret;
- loff_t *pos = v;
- const struct nf_queue_handler *qh;
-
- rcu_read_lock();
- qh = rcu_dereference(queue_handler[*pos]);
- if (!qh)
- ret = seq_printf(s, "%2lld NONE\n", *pos);
- else
- ret = seq_printf(s, "%2lld %s\n", *pos, qh->name);
- rcu_read_unlock();
-
- return ret;
-}
-
-static const struct seq_operations nfqueue_seq_ops = {
- .start = seq_start,
- .next = seq_next,
- .stop = seq_stop,
- .show = seq_show,
-};
-
-static int nfqueue_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, &nfqueue_seq_ops);
-}
-
-static const struct file_operations nfqueue_file_ops = {
- .owner = THIS_MODULE,
- .open = nfqueue_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-#endif /* PROC_FS */
-
-
-int __init netfilter_queue_init(void)
-{
-#ifdef CONFIG_PROC_FS
- if (!proc_create("nf_queue", S_IRUGO,
- proc_net_netfilter, &nfqueue_file_ops))
- return -1;
-#endif
- return 0;
-}
-
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index ffb92c03a35..58a09b7c3f6 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -138,7 +138,7 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
const struct nfnetlink_subsystem *ss;
int type, err;
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* All the messages must at least contain nfgenmsg */
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 8847b4d8be0..701c88a20fe 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -41,7 +41,8 @@ MODULE_DESCRIPTION("cttimeout: Extended Netfilter Connection Tracking timeout tu
static LIST_HEAD(cttimeout_list);
static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = {
- [CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING },
+ [CTA_TIMEOUT_NAME] = { .type = NLA_NUL_STRING,
+ .len = CTNL_TIMEOUT_NAME_MAX - 1},
[CTA_TIMEOUT_L3PROTO] = { .type = NLA_U16 },
[CTA_TIMEOUT_L4PROTO] = { .type = NLA_U8 },
[CTA_TIMEOUT_DATA] = { .type = NLA_NESTED },
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 9f199f2e31f..92fd8eca0d3 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -13,6 +13,7 @@
*/
#include <linux/module.h>
#include <linux/skbuff.h>
+#include <linux/if_arp.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
@@ -384,6 +385,7 @@ __build_packet_message(struct nfulnl_instance *inst,
struct nfgenmsg *nfmsg;
sk_buff_data_t old_tail = inst->skb->tail;
struct sock *sk;
+ const unsigned char *hwhdrp;
nlh = nlmsg_put(inst->skb, 0, 0,
NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET,
@@ -485,9 +487,17 @@ __build_packet_message(struct nfulnl_instance *inst,
if (indev && skb_mac_header_was_set(skb)) {
if (nla_put_be16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)) ||
nla_put_be16(inst->skb, NFULA_HWLEN,
- htons(skb->dev->hard_header_len)) ||
- nla_put(inst->skb, NFULA_HWHEADER, skb->dev->hard_header_len,
- skb_mac_header(skb)))
+ htons(skb->dev->hard_header_len)))
+ goto nla_put_failure;
+
+ hwhdrp = skb_mac_header(skb);
+
+ if (skb->dev->type == ARPHRD_SIT)
+ hwhdrp -= ETH_HLEN;
+
+ if (hwhdrp >= skb->head &&
+ nla_put(inst->skb, NFULA_HWHEADER,
+ skb->dev->hard_header_len, hwhdrp))
goto nla_put_failure;
}
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index e12d44e75b2..3158d87b56a 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -809,7 +809,6 @@ static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = {
};
static const struct nf_queue_handler nfqh = {
- .name = "nf_queue",
.outfn = &nfqnl_enqueue_packet,
};
@@ -827,14 +826,10 @@ nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb,
if (nfqa[NFQA_CFG_CMD]) {
cmd = nla_data(nfqa[NFQA_CFG_CMD]);
- /* Commands without queue context - might sleep */
+ /* Obsolete commands without queue context */
switch (cmd->command) {
- case NFQNL_CFG_CMD_PF_BIND:
- return nf_register_queue_handler(ntohs(cmd->pf),
- &nfqh);
- case NFQNL_CFG_CMD_PF_UNBIND:
- return nf_unregister_queue_handler(ntohs(cmd->pf),
- &nfqh);
+ case NFQNL_CFG_CMD_PF_BIND: return 0;
+ case NFQNL_CFG_CMD_PF_UNBIND: return 0;
}
}
@@ -1074,6 +1069,7 @@ static int __init nfnetlink_queue_init(void)
#endif
register_netdevice_notifier(&nfqnl_dev_notifier);
+ nf_register_queue_handler(&nfqh);
return status;
#ifdef CONFIG_PROC_FS
@@ -1087,7 +1083,7 @@ cleanup_netlink_notifier:
static void __exit nfnetlink_queue_fini(void)
{
- nf_unregister_queue_handlers(&nfqh);
+ nf_unregister_queue_handler();
unregister_netdevice_notifier(&nfqnl_dev_notifier);
#ifdef CONFIG_PROC_FS
remove_proc_entry("nfnetlink_queue", proc_net_netfilter);
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 8d987c3573f..7b3a9e5999c 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -345,19 +345,27 @@ int xt_find_revision(u8 af, const char *name, u8 revision, int target,
}
EXPORT_SYMBOL_GPL(xt_find_revision);
-static char *textify_hooks(char *buf, size_t size, unsigned int mask)
+static char *
+textify_hooks(char *buf, size_t size, unsigned int mask, uint8_t nfproto)
{
- static const char *const names[] = {
+ static const char *const inetbr_names[] = {
"PREROUTING", "INPUT", "FORWARD",
"OUTPUT", "POSTROUTING", "BROUTING",
};
- unsigned int i;
+ static const char *const arp_names[] = {
+ "INPUT", "FORWARD", "OUTPUT",
+ };
+ const char *const *names;
+ unsigned int i, max;
char *p = buf;
bool np = false;
int res;
+ names = (nfproto == NFPROTO_ARP) ? arp_names : inetbr_names;
+ max = (nfproto == NFPROTO_ARP) ? ARRAY_SIZE(arp_names) :
+ ARRAY_SIZE(inetbr_names);
*p = '\0';
- for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ for (i = 0; i < max; ++i) {
if (!(mask & (1 << i)))
continue;
res = snprintf(p, size, "%s%s", np ? "/" : "", names[i]);
@@ -402,8 +410,10 @@ int xt_check_match(struct xt_mtchk_param *par,
pr_err("%s_tables: %s match: used from hooks %s, but only "
"valid from %s\n",
xt_prefix[par->family], par->match->name,
- textify_hooks(used, sizeof(used), par->hook_mask),
- textify_hooks(allow, sizeof(allow), par->match->hooks));
+ textify_hooks(used, sizeof(used), par->hook_mask,
+ par->family),
+ textify_hooks(allow, sizeof(allow), par->match->hooks,
+ par->family));
return -EINVAL;
}
if (par->match->proto && (par->match->proto != proto || inv_proto)) {
@@ -575,8 +585,10 @@ int xt_check_target(struct xt_tgchk_param *par,
pr_err("%s_tables: %s target: used from hooks %s, but only "
"usable from %s\n",
xt_prefix[par->family], par->target->name,
- textify_hooks(used, sizeof(used), par->hook_mask),
- textify_hooks(allow, sizeof(allow), par->target->hooks));
+ textify_hooks(used, sizeof(used), par->hook_mask,
+ par->family),
+ textify_hooks(allow, sizeof(allow), par->target->hooks,
+ par->family));
return -EINVAL;
}
if (par->target->proto && (par->target->proto != proto || inv_proto)) {
diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c
index ae7f5daeee4..bde009ed8d3 100644
--- a/net/netfilter/xt_CT.c
+++ b/net/netfilter/xt_CT.c
@@ -109,7 +109,7 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
struct xt_ct_target_info *info = par->targinfo;
struct nf_conntrack_tuple t;
struct nf_conn *ct;
- int ret;
+ int ret = -EOPNOTSUPP;
if (info->flags & ~XT_CT_NOTRACK)
return -EINVAL;
@@ -149,6 +149,10 @@ static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
__set_bit(IPS_TEMPLATE_BIT, &ct->status);
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
+
+ /* Overload tuple linked list to put us in template list. */
+ hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
+ &par->net->ct.tmpl);
out:
info->ct = ct;
return 0;
@@ -243,7 +247,7 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
struct xt_ct_target_info_v1 *info = par->targinfo;
struct nf_conntrack_tuple t;
struct nf_conn *ct;
- int ret;
+ int ret = -EOPNOTSUPP;
if (info->flags & ~XT_CT_NOTRACK)
return -EINVAL;
@@ -289,6 +293,10 @@ static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
__set_bit(IPS_TEMPLATE_BIT, &ct->status);
__set_bit(IPS_CONFIRMED_BIT, &ct->status);
+
+ /* Overload tuple linked list to put us in template list. */
+ hlist_nulls_add_head_rcu(&ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode,
+ &par->net->ct.tmpl);
out:
info->ct = ct;
return 0;
@@ -377,14 +385,60 @@ static struct xt_target xt_ct_tg_reg[] __read_mostly = {
},
};
+static unsigned int
+notrack_tg(struct sk_buff *skb, const struct xt_action_param *par)
+{
+ /* Previously seen (loopback)? Ignore. */
+ if (skb->nfct != NULL)
+ return XT_CONTINUE;
+
+ skb->nfct = &nf_ct_untracked_get()->ct_general;
+ skb->nfctinfo = IP_CT_NEW;
+ nf_conntrack_get(skb->nfct);
+
+ return XT_CONTINUE;
+}
+
+static int notrack_chk(const struct xt_tgchk_param *par)
+{
+ if (!par->net->xt.notrack_deprecated_warning) {
+ pr_info("netfilter: NOTRACK target is deprecated, "
+ "use CT instead or upgrade iptables\n");
+ par->net->xt.notrack_deprecated_warning = true;
+ }
+ return 0;
+}
+
+static struct xt_target notrack_tg_reg __read_mostly = {
+ .name = "NOTRACK",
+ .revision = 0,
+ .family = NFPROTO_UNSPEC,
+ .checkentry = notrack_chk,
+ .target = notrack_tg,
+ .table = "raw",
+ .me = THIS_MODULE,
+};
+
static int __init xt_ct_tg_init(void)
{
- return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
+ int ret;
+
+ ret = xt_register_target(&notrack_tg_reg);
+ if (ret < 0)
+ return ret;
+
+ ret = xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
+ if (ret < 0) {
+ xt_unregister_target(&notrack_tg_reg);
+ return ret;
+ }
+ return 0;
}
static void __exit xt_ct_tg_exit(void)
{
xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
+ xt_unregister_target(&notrack_tg_reg);
}
module_init(xt_ct_tg_init);
@@ -394,3 +448,5 @@ MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Xtables: connection tracking target");
MODULE_ALIAS("ipt_CT");
MODULE_ALIAS("ip6t_CT");
+MODULE_ALIAS("ipt_NOTRACK");
+MODULE_ALIAS("ip6t_NOTRACK");
diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c
index 1686ca1b53a..73b73f687c5 100644
--- a/net/netfilter/xt_HMARK.c
+++ b/net/netfilter/xt_HMARK.c
@@ -167,7 +167,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
const struct xt_hmark_info *info)
{
struct ipv6hdr *ip6, _ip6;
- int flag = IP6T_FH_F_AUTH;
+ int flag = IP6_FH_F_AUTH;
unsigned int nhoff = 0;
u16 fragoff = 0;
int nexthdr;
@@ -177,7 +177,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
if (nexthdr < 0)
return 0;
/* No need to check for icmp errors on fragments */
- if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
+ if ((flag & IP6_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6))
goto noicmp;
/* Use inner header in case of ICMP errors */
if (get_inner6_hdr(skb, &nhoff)) {
@@ -185,7 +185,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t,
if (ip6 == NULL)
return -1;
/* If AH present, use SPI like in ESP. */
- flag = IP6T_FH_F_AUTH;
+ flag = IP6_FH_F_AUTH;
nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag);
if (nexthdr < 0)
return -1;
@@ -201,7 +201,7 @@ noicmp:
if (t->proto == IPPROTO_ICMPV6)
return 0;
- if (flag & IP6T_FH_F_FRAG)
+ if (flag & IP6_FH_F_FRAG)
return 0;
hmark_set_tuple_ports(skb, nhoff, t, info);
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index 26a668a84aa..a9d7af953ce 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -157,11 +157,22 @@ dsthash_find(const struct xt_hashlimit_htable *ht,
/* allocate dsthash_ent, initialize dst, put in htable and lock it */
static struct dsthash_ent *
dsthash_alloc_init(struct xt_hashlimit_htable *ht,
- const struct dsthash_dst *dst)
+ const struct dsthash_dst *dst, bool *race)
{
struct dsthash_ent *ent;
spin_lock(&ht->lock);
+
+ /* Two or more packets may race to create the same entry in the
+ * hashtable, double check if this packet lost race.
+ */
+ ent = dsthash_find(ht, dst);
+ if (ent != NULL) {
+ spin_unlock(&ht->lock);
+ *race = true;
+ return ent;
+ }
+
/* initialize hash with random val at the time we allocate
* the first hashtable entry */
if (unlikely(!ht->rnd_initialized)) {
@@ -318,7 +329,10 @@ static void htable_destroy(struct xt_hashlimit_htable *hinfo)
parent = hashlimit_net->ipt_hashlimit;
else
parent = hashlimit_net->ip6t_hashlimit;
- remove_proc_entry(hinfo->pde->name, parent);
+
+ if(parent != NULL)
+ remove_proc_entry(hinfo->pde->name, parent);
+
htable_selective_cleanup(hinfo, select_all);
vfree(hinfo);
}
@@ -585,6 +599,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
unsigned long now = jiffies;
struct dsthash_ent *dh;
struct dsthash_dst dst;
+ bool race = false;
u32 cost;
if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0)
@@ -593,13 +608,18 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
rcu_read_lock_bh();
dh = dsthash_find(hinfo, &dst);
if (dh == NULL) {
- dh = dsthash_alloc_init(hinfo, &dst);
+ dh = dsthash_alloc_init(hinfo, &dst, &race);
if (dh == NULL) {
rcu_read_unlock_bh();
goto hotdrop;
+ } else if (race) {
+ /* Already got an entry, update expiration timeout */
+ dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
+ rateinfo_recalc(dh, now, hinfo->cfg.mode);
+ } else {
+ dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
+ rateinfo_init(dh, hinfo);
}
- dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
- rateinfo_init(dh, hinfo);
} else {
/* update expiration timeout */
dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
@@ -856,6 +876,27 @@ static int __net_init hashlimit_proc_net_init(struct net *net)
static void __net_exit hashlimit_proc_net_exit(struct net *net)
{
+ struct xt_hashlimit_htable *hinfo;
+ struct hlist_node *pos;
+ struct proc_dir_entry *pde;
+ struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
+
+ /* recent_net_exit() is called before recent_mt_destroy(). Make sure
+ * that the parent xt_recent proc entry is is empty before trying to
+ * remove it.
+ */
+ mutex_lock(&hashlimit_mutex);
+ pde = hashlimit_net->ipt_hashlimit;
+ if (pde == NULL)
+ pde = hashlimit_net->ip6t_hashlimit;
+
+ hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node)
+ remove_proc_entry(hinfo->pde->name, pde);
+
+ hashlimit_net->ipt_hashlimit = NULL;
+ hashlimit_net->ip6t_hashlimit = NULL;
+ mutex_unlock(&hashlimit_mutex);
+
proc_net_remove(net, "ipt_hashlimit");
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
proc_net_remove(net, "ip6t_hashlimit");
@@ -872,9 +913,6 @@ static int __net_init hashlimit_net_init(struct net *net)
static void __net_exit hashlimit_net_exit(struct net *net)
{
- struct hashlimit_net *hashlimit_net = hashlimit_pernet(net);
-
- BUG_ON(!hlist_empty(&hashlimit_net->htables));
hashlimit_proc_net_exit(net);
}
diff --git a/net/netfilter/xt_ipvs.c b/net/netfilter/xt_ipvs.c
index bb10b0717f1..8d47c3780fd 100644
--- a/net/netfilter/xt_ipvs.c
+++ b/net/netfilter/xt_ipvs.c
@@ -67,7 +67,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
goto out;
}
- ip_vs_fill_iphdr(family, skb_network_header(skb), &iph);
+ ip_vs_fill_iph_skb(family, skb, &iph);
if (data->bitmask & XT_IPVS_PROTO)
if ((iph.protocol == data->l4proto) ^
@@ -85,7 +85,7 @@ ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
/*
* Check if the packet belongs to an existing entry
*/
- cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */);
+ cp = pp->conn_out_get(family, skb, &iph, 1 /* inverse */);
if (unlikely(cp == NULL)) {
match = false;
goto out;
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index 4635c9b0045..978efc9b555 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -29,6 +29,7 @@
#include <linux/skbuff.h>
#include <linux/inet.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
@@ -310,6 +311,14 @@ out:
return ret;
}
+static void recent_table_free(void *addr)
+{
+ if (is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+
static int recent_mt_check(const struct xt_mtchk_param *par,
const struct xt_recent_mtinfo_v1 *info)
{
@@ -322,6 +331,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
#endif
unsigned int i;
int ret = -EINVAL;
+ size_t sz;
if (unlikely(!hash_rnd_inited)) {
get_random_bytes(&hash_rnd, sizeof(hash_rnd));
@@ -360,8 +370,11 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
goto out;
}
- t = kzalloc(sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size,
- GFP_KERNEL);
+ sz = sizeof(*t) + sizeof(t->iphash[0]) * ip_list_hash_size;
+ if (sz <= PAGE_SIZE)
+ t = kzalloc(sz, GFP_KERNEL);
+ else
+ t = vzalloc(sz);
if (t == NULL) {
ret = -ENOMEM;
goto out;
@@ -377,14 +390,14 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
uid = make_kuid(&init_user_ns, ip_list_uid);
gid = make_kgid(&init_user_ns, ip_list_gid);
if (!uid_valid(uid) || !gid_valid(gid)) {
- kfree(t);
+ recent_table_free(t);
ret = -EINVAL;
goto out;
}
pde = proc_create_data(t->name, ip_list_perms, recent_net->xt_recent,
&recent_mt_fops, t);
if (pde == NULL) {
- kfree(t);
+ recent_table_free(t);
ret = -ENOMEM;
goto out;
}
@@ -431,10 +444,11 @@ static void recent_mt_destroy(const struct xt_mtdtor_param *par)
list_del(&t->list);
spin_unlock_bh(&recent_lock);
#ifdef CONFIG_PROC_FS
- remove_proc_entry(t->name, recent_net->xt_recent);
+ if (recent_net->xt_recent != NULL)
+ remove_proc_entry(t->name, recent_net->xt_recent);
#endif
recent_table_flush(t);
- kfree(t);
+ recent_table_free(t);
}
mutex_unlock(&recent_mutex);
}
@@ -615,6 +629,20 @@ static int __net_init recent_proc_net_init(struct net *net)
static void __net_exit recent_proc_net_exit(struct net *net)
{
+ struct recent_net *recent_net = recent_pernet(net);
+ struct recent_table *t;
+
+ /* recent_net_exit() is called before recent_mt_destroy(). Make sure
+ * that the parent xt_recent proc entry is is empty before trying to
+ * remove it.
+ */
+ spin_lock_bh(&recent_lock);
+ list_for_each_entry(t, &recent_net->tables, list)
+ remove_proc_entry(t->name, recent_net->xt_recent);
+
+ recent_net->xt_recent = NULL;
+ spin_unlock_bh(&recent_lock);
+
proc_net_remove(net, "xt_recent");
}
#else
@@ -638,9 +666,6 @@ static int __net_init recent_net_init(struct net *net)
static void __net_exit recent_net_exit(struct net *net)
{
- struct recent_net *recent_net = recent_pernet(net);
-
- BUG_ON(!list_empty(&recent_net->tables));
recent_proc_net_exit(net);
}
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 4da797fa5ec..c0353d55d56 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -612,7 +612,7 @@ retry:
static inline int netlink_capable(const struct socket *sock, unsigned int flag)
{
return (nl_table[sock->sk->sk_protocol].flags & flag) ||
- capable(CAP_NET_ADMIN);
+ ns_capable(sock_net(sock->sk)->user_ns, CAP_NET_ADMIN);
}
static void
@@ -669,6 +669,9 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
struct sockaddr_nl *nladdr = (struct sockaddr_nl *)addr;
int err;
+ if (addr_len < sizeof(struct sockaddr_nl))
+ return -EINVAL;
+
if (nladdr->nl_family != AF_NETLINK)
return -EINVAL;
@@ -2059,7 +2062,7 @@ static int netlink_seq_show(struct seq_file *seq, void *v)
struct sock *s = v;
struct netlink_sock *nlk = nlk_sk(s);
- seq_printf(seq, "%pK %-3d %-6d %08x %-8d %-8d %pK %-8d %-8d %-8lu\n",
+ seq_printf(seq, "%pK %-3d %-6u %08x %-8d %-8d %pK %-8d %-8d %-8lu\n",
s,
s->sk_protocol,
nlk->portid,
diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig
index 8d8d9bc4b6f..60c3bbb63e8 100644
--- a/net/nfc/Kconfig
+++ b/net/nfc/Kconfig
@@ -3,8 +3,8 @@
#
menuconfig NFC
- depends on NET && EXPERIMENTAL
- tristate "NFC subsystem support (EXPERIMENTAL)"
+ depends on NET
+ tristate "NFC subsystem support"
default n
help
Say Y here if you want to build support for NFC (Near field
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 479bee36dc3..aa64ea44167 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -40,6 +40,9 @@
int nfc_devlist_generation;
DEFINE_MUTEX(nfc_devlist_mutex);
+/* NFC device ID bitmap */
+static DEFINE_IDA(nfc_index_ida);
+
/**
* nfc_dev_up - turn on the NFC device
*
@@ -181,6 +184,7 @@ int nfc_stop_poll(struct nfc_dev *dev)
dev->ops->stop_poll(dev);
dev->polling = false;
+ dev->rf_mode = NFC_RF_NONE;
error:
device_unlock(&dev->dev);
@@ -194,7 +198,7 @@ static struct nfc_target *nfc_find_target(struct nfc_dev *dev, u32 target_idx)
if (dev->n_targets == 0)
return NULL;
- for (i = 0; i < dev->n_targets ; i++) {
+ for (i = 0; i < dev->n_targets; i++) {
if (dev->targets[i].idx == target_idx)
return &dev->targets[i];
}
@@ -274,12 +278,14 @@ int nfc_dep_link_down(struct nfc_dev *dev)
if (!rc) {
dev->dep_link_up = false;
dev->active_target = NULL;
+ dev->rf_mode = NFC_RF_NONE;
nfc_llcp_mac_is_down(dev);
nfc_genl_dep_link_down_event(dev);
}
error:
device_unlock(&dev->dev);
+
return rc;
}
@@ -503,6 +509,7 @@ EXPORT_SYMBOL(nfc_tm_activated);
int nfc_tm_deactivated(struct nfc_dev *dev)
{
dev->dep_link_up = false;
+ dev->rf_mode = NFC_RF_NONE;
return nfc_genl_tm_deactivated(dev);
}
@@ -697,6 +704,8 @@ static void nfc_check_pres_work(struct work_struct *work)
if (dev->active_target && timer_pending(&dev->check_pres_timer) == 0) {
rc = dev->ops->check_presence(dev, dev->active_target);
+ if (rc == -EOPNOTSUPP)
+ goto exit;
if (!rc) {
mod_timer(&dev->check_pres_timer, jiffies +
msecs_to_jiffies(NFC_CHECK_PRES_FREQ_MS));
@@ -708,6 +717,7 @@ static void nfc_check_pres_work(struct work_struct *work)
}
}
+exit:
device_unlock(&dev->dev);
}
@@ -753,7 +763,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
u32 supported_protocols,
int tx_headroom, int tx_tailroom)
{
- static atomic_t dev_no = ATOMIC_INIT(0);
struct nfc_dev *dev;
if (!ops->start_poll || !ops->stop_poll || !ops->activate_target ||
@@ -767,11 +776,6 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
if (!dev)
return NULL;
- dev->dev.class = &nfc_class;
- dev->idx = atomic_inc_return(&dev_no) - 1;
- dev_set_name(&dev->dev, "nfc%d", dev->idx);
- device_initialize(&dev->dev);
-
dev->ops = ops;
dev->supported_protocols = supported_protocols;
dev->tx_headroom = tx_headroom;
@@ -779,6 +783,7 @@ struct nfc_dev *nfc_allocate_device(struct nfc_ops *ops,
nfc_genl_data_init(&dev->genl_data);
+ dev->rf_mode = NFC_RF_NONE;
/* first generation must not be 0 */
dev->targets_generation = 1;
@@ -806,6 +811,14 @@ int nfc_register_device(struct nfc_dev *dev)
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
+ dev->idx = ida_simple_get(&nfc_index_ida, 0, 0, GFP_KERNEL);
+ if (dev->idx < 0)
+ return dev->idx;
+
+ dev->dev.class = &nfc_class;
+ dev_set_name(&dev->dev, "nfc%d", dev->idx);
+ device_initialize(&dev->dev);
+
mutex_lock(&nfc_devlist_mutex);
nfc_devlist_generation++;
rc = device_add(&dev->dev);
@@ -834,10 +847,12 @@ EXPORT_SYMBOL(nfc_register_device);
*/
void nfc_unregister_device(struct nfc_dev *dev)
{
- int rc;
+ int rc, id;
pr_debug("dev_name=%s\n", dev_name(&dev->dev));
+ id = dev->idx;
+
mutex_lock(&nfc_devlist_mutex);
nfc_devlist_generation++;
@@ -856,6 +871,8 @@ void nfc_unregister_device(struct nfc_dev *dev)
pr_debug("The userspace won't be notified that the device %s was removed\n",
dev_name(&dev->dev));
+ ida_simple_remove(&nfc_index_ida, id);
+
}
EXPORT_SYMBOL(nfc_unregister_device);
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 71c6a7086b8..7d99410e6c1 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -257,16 +257,16 @@ static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
*result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
NFC_HCI_ADM_CREATE_PIPE,
(u8 *) &params, sizeof(params), &skb);
- if (*result == 0) {
- resp = (struct hci_create_pipe_resp *)skb->data;
- pipe = resp->pipe;
- kfree_skb(skb);
+ if (*result < 0)
+ return NFC_HCI_INVALID_PIPE;
- pr_debug("pipe created=%d\n", pipe);
+ resp = (struct hci_create_pipe_resp *)skb->data;
+ pipe = resp->pipe;
+ kfree_skb(skb);
- return pipe;
- } else
- return NFC_HCI_INVALID_PIPE;
+ pr_debug("pipe created=%d\n", pipe);
+
+ return pipe;
}
static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
@@ -279,8 +279,6 @@ static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
{
- int r;
-
u8 param[2];
/* TODO: Find out what the identity reference data is
@@ -288,10 +286,8 @@ static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
pr_debug("\n");
- r = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
- NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
-
- return 0;
+ return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
+ NFC_HCI_ADM_CLEAR_ALL_PIPE, param, 2, NULL);
}
int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
@@ -348,7 +344,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
return -EADDRINUSE;
if (pipe != NFC_HCI_INVALID_PIPE)
- goto pipe_is_open;
+ goto open_pipe;
switch (dest_gate) {
case NFC_HCI_LINK_MGMT_GATE:
@@ -365,6 +361,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
break;
}
+open_pipe:
r = nfc_hci_open_pipe(hdev, pipe);
if (r < 0) {
if (pipe_created)
@@ -375,7 +372,6 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
return r;
}
-pipe_is_open:
hdev->gate2pipe[dest_gate] = pipe;
return 0;
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 5fbb6e40793..7bea574d593 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -33,17 +33,20 @@
/* Largest headroom needed for outgoing HCI commands */
#define HCI_CMDS_HEADROOM 1
-static int nfc_hci_result_to_errno(u8 result)
+int nfc_hci_result_to_errno(u8 result)
{
switch (result) {
case NFC_HCI_ANY_OK:
return 0;
+ case NFC_HCI_ANY_E_REG_PAR_UNKNOWN:
+ return -EOPNOTSUPP;
case NFC_HCI_ANY_E_TIMEOUT:
return -ETIME;
default:
return -1;
}
}
+EXPORT_SYMBOL(nfc_hci_result_to_errno);
static void nfc_hci_msg_tx_work(struct work_struct *work)
{
@@ -65,8 +68,9 @@ static void nfc_hci_msg_tx_work(struct work_struct *work)
-ETIME);
kfree(hdev->cmd_pending_msg);
hdev->cmd_pending_msg = NULL;
- } else
+ } else {
goto exit;
+ }
}
next_msg:
@@ -166,7 +170,7 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
kfree_skb(skb);
}
-static u32 nfc_hci_sak_to_protocol(u8 sak)
+u32 nfc_hci_sak_to_protocol(u8 sak)
{
switch (NFC_HCI_TYPE_A_SEL_PROT(sak)) {
case NFC_HCI_TYPE_A_SEL_PROT_MIFARE:
@@ -181,8 +185,9 @@ static u32 nfc_hci_sak_to_protocol(u8 sak)
return 0xffffffff;
}
}
+EXPORT_SYMBOL(nfc_hci_sak_to_protocol);
-static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
+int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
{
struct nfc_target *targets;
struct sk_buff *atqa_skb = NULL;
@@ -263,7 +268,9 @@ static int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate)
break;
}
- targets->hci_reader_gate = gate;
+ /* if driver set the new gate, we will skip the old one */
+ if (targets->hci_reader_gate == 0x00)
+ targets->hci_reader_gate = gate;
r = nfc_targets_found(hdev->ndev, targets, 1);
@@ -275,11 +282,18 @@ exit:
return r;
}
+EXPORT_SYMBOL(nfc_hci_target_discovered);
void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
struct sk_buff *skb)
{
int r = 0;
+ u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+
+ if (gate == 0xff) {
+ pr_err("Discarded event %x to unopened pipe %x\n", event, pipe);
+ goto exit;
+ }
switch (event) {
case NFC_HCI_EVT_TARGET_DISCOVERED:
@@ -303,12 +317,14 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event,
goto exit;
}
- r = nfc_hci_target_discovered(hdev,
- nfc_hci_pipe2gate(hdev, pipe));
+ r = nfc_hci_target_discovered(hdev, gate);
break;
default:
- /* TODO: Unknown events are hardware specific
- * pass them to the driver (needs a new hci_ops) */
+ if (hdev->ops->event_received) {
+ hdev->ops->event_received(hdev, gate, event, skb);
+ return;
+ }
+
break;
}
@@ -410,6 +426,10 @@ static int hci_dev_version(struct nfc_hci_dev *hdev)
r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
NFC_HCI_ID_MGMT_VERSION_SW, &skb);
+ if (r == -EOPNOTSUPP) {
+ pr_info("Software/Hardware info not available\n");
+ return 0;
+ }
if (r < 0)
return r;
@@ -527,7 +547,8 @@ static int hci_start_poll(struct nfc_dev *nfc_dev,
return hdev->ops->start_poll(hdev, im_protocols, tm_protocols);
else
return nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
- NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
+ NFC_HCI_EVT_READER_REQUESTED,
+ NULL, 0);
}
static void hci_stop_poll(struct nfc_dev *nfc_dev)
@@ -538,6 +559,28 @@ static void hci_stop_poll(struct nfc_dev *nfc_dev)
NFC_HCI_EVT_END_OPERATION, NULL, 0);
}
+static int hci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
+ __u8 comm_mode, __u8 *gb, size_t gb_len)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->dep_link_up)
+ return hdev->ops->dep_link_up(hdev, target, comm_mode,
+ gb, gb_len);
+
+ return 0;
+}
+
+static int hci_dep_link_down(struct nfc_dev *nfc_dev)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->dep_link_down)
+ return hdev->ops->dep_link_down(hdev);
+
+ return 0;
+}
+
static int hci_activate_target(struct nfc_dev *nfc_dev,
struct nfc_target *target, u32 protocol)
{
@@ -586,8 +629,8 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
switch (target->hci_reader_gate) {
case NFC_HCI_RF_READER_A_GATE:
case NFC_HCI_RF_READER_B_GATE:
- if (hdev->ops->data_exchange) {
- r = hdev->ops->data_exchange(hdev, target, skb, cb,
+ if (hdev->ops->im_transceive) {
+ r = hdev->ops->im_transceive(hdev, target, skb, cb,
cb_context);
if (r <= 0) /* handled */
break;
@@ -604,14 +647,14 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
skb->len, hci_transceive_cb, hdev);
break;
default:
- if (hdev->ops->data_exchange) {
- r = hdev->ops->data_exchange(hdev, target, skb, cb,
+ if (hdev->ops->im_transceive) {
+ r = hdev->ops->im_transceive(hdev, target, skb, cb,
cb_context);
if (r == 1)
r = -ENOTSUPP;
- }
- else
+ } else {
r = -ENOTSUPP;
+ }
break;
}
@@ -620,6 +663,16 @@ static int hci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
return r;
}
+static int hci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->tm_send)
+ return hdev->ops->tm_send(hdev, skb);
+ else
+ return -ENOTSUPP;
+}
+
static int hci_check_presence(struct nfc_dev *nfc_dev,
struct nfc_target *target)
{
@@ -723,9 +776,12 @@ static struct nfc_ops hci_nfc_ops = {
.dev_down = hci_dev_down,
.start_poll = hci_start_poll,
.stop_poll = hci_stop_poll,
+ .dep_link_up = hci_dep_link_up,
+ .dep_link_down = hci_dep_link_down,
.activate_target = hci_activate_target,
.deactivate_target = hci_deactivate_target,
.im_transceive = hci_transceive,
+ .tm_send = hci_tm_send,
.check_presence = hci_check_presence,
};
@@ -848,7 +904,7 @@ void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err)
}
EXPORT_SYMBOL(nfc_hci_driver_failure);
-void inline nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
+void nfc_hci_recv_frame(struct nfc_hci_dev *hdev, struct sk_buff *skb)
{
nfc_llc_rcv_from_drv(hdev->llc, skb);
}
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
index ae1205ded87..fe5e966e5b8 100644
--- a/net/nfc/hci/llc.c
+++ b/net/nfc/hci/llc.c
@@ -72,7 +72,7 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops)
llc_engine->ops = ops;
INIT_LIST_HEAD(&llc_engine->entry);
- list_add_tail (&llc_engine->entry, &llc_engines);
+ list_add_tail(&llc_engine->entry, &llc_engines);
return 0;
}
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c
index 01cbc72943c..27b313befc3 100644
--- a/net/nfc/hci/llc_shdlc.c
+++ b/net/nfc/hci/llc_shdlc.c
@@ -634,9 +634,9 @@ static void llc_shdlc_sm_work(struct work_struct *work)
r = llc_shdlc_connect_initiate(shdlc);
else
r = -ETIME;
- if (r < 0)
+ if (r < 0) {
llc_shdlc_connect_complete(shdlc, r);
- else {
+ } else {
mod_timer(&shdlc->connect_timer, jiffies +
msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS));
@@ -682,9 +682,8 @@ static void llc_shdlc_sm_work(struct work_struct *work)
llc_shdlc_handle_send_queue(shdlc);
}
- if (shdlc->hard_fault) {
+ if (shdlc->hard_fault)
shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault);
- }
break;
default:
break;
diff --git a/net/nfc/llcp/Kconfig b/net/nfc/llcp/Kconfig
index fbf5e815090..a1a41cd6825 100644
--- a/net/nfc/llcp/Kconfig
+++ b/net/nfc/llcp/Kconfig
@@ -1,6 +1,6 @@
config NFC_LLCP
- depends on NFC && EXPERIMENTAL
- bool "NFC LLCP support (EXPERIMENTAL)"
+ depends on NFC
+ bool "NFC LLCP support"
default n
help
Say Y here if you want to build support for a kernel NFC LLCP
diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c
index c45ccd6c094..df24be48d4d 100644
--- a/net/nfc/llcp/commands.c
+++ b/net/nfc/llcp/commands.c
@@ -261,7 +261,6 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
struct sk_buff *skb;
struct nfc_dev *dev;
struct nfc_llcp_local *local;
- u16 size = 0;
pr_debug("Sending DISC\n");
@@ -273,17 +272,10 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock)
if (dev == NULL)
return -ENODEV;
- size += LLCP_HEADER_SIZE;
- size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
-
- skb = alloc_skb(size, GFP_ATOMIC);
+ skb = llcp_allocate_pdu(sock, LLCP_PDU_DISC, 0);
if (skb == NULL)
return -ENOMEM;
- skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
-
- skb = llcp_add_header(skb, sock->dsap, sock->ssap, LLCP_PDU_DISC);
-
skb_queue_tail(&local->tx_queue, skb);
return 0;
@@ -324,8 +316,7 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
struct sk_buff *skb;
u8 *service_name_tlv = NULL, service_name_tlv_length;
u8 *miux_tlv = NULL, miux_tlv_length;
- u8 *rw_tlv = NULL, rw_tlv_length, rw;
- __be16 miux;
+ u8 *rw_tlv = NULL, rw_tlv_length;
int err;
u16 size = 0;
@@ -343,13 +334,11 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
size += service_name_tlv_length;
}
- miux = cpu_to_be16(LLCP_MAX_MIUX);
- miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
+ miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
&miux_tlv_length);
size += miux_tlv_length;
- rw = LLCP_MAX_RW;
- rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
+ rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
size += rw_tlv_length;
pr_debug("SKB size %d SN length %zu\n", size, sock->service_name_len);
@@ -386,8 +375,7 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
struct nfc_llcp_local *local;
struct sk_buff *skb;
u8 *miux_tlv = NULL, miux_tlv_length;
- u8 *rw_tlv = NULL, rw_tlv_length, rw;
- __be16 miux;
+ u8 *rw_tlv = NULL, rw_tlv_length;
int err;
u16 size = 0;
@@ -397,13 +385,11 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
if (local == NULL)
return -ENODEV;
- miux = cpu_to_be16(LLCP_MAX_MIUX);
- miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
+ miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
&miux_tlv_length);
size += miux_tlv_length;
- rw = LLCP_MAX_RW;
- rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &rw, 0, &rw_tlv_length);
+ rw_tlv = nfc_llcp_build_tlv(LLCP_TLV_RW, &local->rw, 0, &rw_tlv_length);
size += rw_tlv_length;
skb = llcp_allocate_pdu(sock, LLCP_PDU_CC, size);
@@ -428,6 +414,52 @@ error_tlv:
return err;
}
+int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap)
+{
+ struct sk_buff *skb;
+ struct nfc_dev *dev;
+ u8 *sdres_tlv = NULL, sdres_tlv_length, sdres[2];
+ u16 size = 0;
+
+ pr_debug("Sending SNL tid 0x%x sap 0x%x\n", tid, sap);
+
+ if (local == NULL)
+ return -ENODEV;
+
+ dev = local->dev;
+ if (dev == NULL)
+ return -ENODEV;
+
+ sdres[0] = tid;
+ sdres[1] = sap;
+ sdres_tlv = nfc_llcp_build_tlv(LLCP_TLV_SDRES, sdres, 0,
+ &sdres_tlv_length);
+ if (sdres_tlv == NULL)
+ return -ENOMEM;
+
+ size += LLCP_HEADER_SIZE;
+ size += dev->tx_headroom + dev->tx_tailroom + NFC_HEADER_SIZE;
+ size += sdres_tlv_length;
+
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (skb == NULL) {
+ kfree(sdres_tlv);
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, dev->tx_headroom + NFC_HEADER_SIZE);
+
+ skb = llcp_add_header(skb, LLCP_SAP_SDP, LLCP_SAP_SDP, LLCP_PDU_SNL);
+
+ memcpy(skb_put(skb, sdres_tlv_length), sdres_tlv, sdres_tlv_length);
+
+ skb_queue_tail(&local->tx_queue, skb);
+
+ kfree(sdres_tlv);
+
+ return 0;
+}
+
int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason)
{
struct sk_buff *skb;
@@ -496,6 +528,23 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
if (local == NULL)
return -ENODEV;
+ /* Remote is ready but has not acknowledged our frames */
+ if((sock->remote_ready &&
+ skb_queue_len(&sock->tx_pending_queue) >= sock->rw &&
+ skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+ pr_err("Pending queue is full %d frames\n",
+ skb_queue_len(&sock->tx_pending_queue));
+ return -ENOBUFS;
+ }
+
+ /* Remote is not ready and we've been queueing enough frames */
+ if ((!sock->remote_ready &&
+ skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) {
+ pr_err("Tx queue is full %d frames\n",
+ skb_queue_len(&sock->tx_queue));
+ return -ENOBUFS;
+ }
+
msg_data = kzalloc(len, GFP_KERNEL);
if (msg_data == NULL)
return -ENOMEM;
@@ -541,6 +590,63 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
return len;
}
+int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
+ struct msghdr *msg, size_t len)
+{
+ struct sk_buff *pdu;
+ struct nfc_llcp_local *local;
+ size_t frag_len = 0, remaining_len;
+ u8 *msg_ptr, *msg_data;
+ int err;
+
+ pr_debug("Send UI frame len %zd\n", len);
+
+ local = sock->local;
+ if (local == NULL)
+ return -ENODEV;
+
+ msg_data = kzalloc(len, GFP_KERNEL);
+ if (msg_data == NULL)
+ return -ENOMEM;
+
+ if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ kfree(msg_data);
+ return -EFAULT;
+ }
+
+ remaining_len = len;
+ msg_ptr = msg_data;
+
+ while (remaining_len > 0) {
+
+ frag_len = min_t(size_t, sock->miu, remaining_len);
+
+ pr_debug("Fragment %zd bytes remaining %zd",
+ frag_len, remaining_len);
+
+ pdu = nfc_alloc_send_skb(sock->dev, &sock->sk, MSG_DONTWAIT,
+ frag_len + LLCP_HEADER_SIZE, &err);
+ if (pdu == NULL) {
+ pr_err("Could not allocate PDU\n");
+ continue;
+ }
+
+ pdu = llcp_add_header(pdu, dsap, ssap, LLCP_PDU_UI);
+
+ memcpy(skb_put(pdu, frag_len), msg_ptr, frag_len);
+
+ /* No need to check for the peer RW for UI frames */
+ skb_queue_tail(&local->tx_queue, pdu);
+
+ remaining_len -= frag_len;
+ msg_ptr += frag_len;
+ }
+
+ kfree(msg_data);
+
+ return len;
+}
+
int nfc_llcp_send_rr(struct nfc_llcp_sock *sock)
{
struct sk_buff *skb;
diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c
index cc10d073c33..ec43914c92a 100644
--- a/net/nfc/llcp/llcp.c
+++ b/net/nfc/llcp/llcp.c
@@ -45,12 +45,38 @@ void nfc_llcp_sock_unlink(struct llcp_sock_list *l, struct sock *sk)
write_unlock(&l->lock);
}
+static void nfc_llcp_socket_purge(struct nfc_llcp_sock *sock)
+{
+ struct nfc_llcp_local *local = sock->local;
+ struct sk_buff *s, *tmp;
+
+ pr_debug("%p\n", &sock->sk);
+
+ skb_queue_purge(&sock->tx_queue);
+ skb_queue_purge(&sock->tx_pending_queue);
+ skb_queue_purge(&sock->tx_backlog_queue);
+
+ if (local == NULL)
+ return;
+
+ /* Search for local pending SKBs that are related to this socket */
+ skb_queue_walk_safe(&local->tx_queue, s, tmp) {
+ if (s->sk != &sock->sk)
+ continue;
+
+ skb_unlink(s, &local->tx_queue);
+ kfree_skb(s);
+ }
+}
+
static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
{
struct sock *sk;
struct hlist_node *node, *tmp;
struct nfc_llcp_sock *llcp_sock;
+ skb_queue_purge(&local->tx_queue);
+
write_lock(&local->sockets.lock);
sk_for_each_safe(sk, node, tmp, &local->sockets.head) {
@@ -58,6 +84,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
bh_lock_sock(sk);
+ nfc_llcp_socket_purge(llcp_sock);
+
if (sk->sk_state == LLCP_CONNECTED)
nfc_put_device(llcp_sock->dev);
@@ -65,7 +93,8 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
struct nfc_llcp_sock *lsk, *n;
struct sock *accept_sk;
- list_for_each_entry_safe(lsk, n, &llcp_sock->accept_queue,
+ list_for_each_entry_safe(lsk, n,
+ &llcp_sock->accept_queue,
accept_queue) {
accept_sk = &lsk->sk;
bh_lock_sock(accept_sk);
@@ -85,6 +114,16 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool listen)
}
}
+ /*
+ * If we have a connection less socket bound, we keep it alive
+ * if the device is still present.
+ */
+ if (sk->sk_state == LLCP_BOUND && sk->sk_type == SOCK_DGRAM &&
+ listen == true) {
+ bh_unlock_sock(sk);
+ continue;
+ }
+
sk->sk_state = LLCP_CLOSED;
bh_unlock_sock(sk);
@@ -134,7 +173,7 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
{
struct sock *sk;
struct hlist_node *node;
- struct nfc_llcp_sock *llcp_sock;
+ struct nfc_llcp_sock *llcp_sock, *tmp_sock;
pr_debug("ssap dsap %d %d\n", ssap, dsap);
@@ -146,10 +185,12 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
llcp_sock = NULL;
sk_for_each(sk, node, &local->sockets.head) {
- llcp_sock = nfc_llcp_sock(sk);
+ tmp_sock = nfc_llcp_sock(sk);
- if (llcp_sock->ssap == ssap && llcp_sock->dsap == dsap)
+ if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) {
+ llcp_sock = tmp_sock;
break;
+ }
}
read_unlock(&local->sockets.lock);
@@ -249,7 +290,12 @@ struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
pr_debug("llcp sock %p\n", tmp_sock);
- if (tmp_sock->sk.sk_state != LLCP_LISTEN)
+ if (tmp_sock->sk.sk_type == SOCK_STREAM &&
+ tmp_sock->sk.sk_state != LLCP_LISTEN)
+ continue;
+
+ if (tmp_sock->sk.sk_type == SOCK_DGRAM &&
+ tmp_sock->sk.sk_state != LLCP_BOUND)
continue;
if (tmp_sock->service_name == NULL ||
@@ -421,10 +467,9 @@ static u8 nfc_llcp_reserve_sdp_ssap(struct nfc_llcp_local *local)
static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
{
u8 *gb_cur, *version_tlv, version, version_length;
- u8 *lto_tlv, lto, lto_length;
+ u8 *lto_tlv, lto_length;
u8 *wks_tlv, wks_length;
u8 *miux_tlv, miux_length;
- __be16 miux;
u8 gb_len = 0;
int ret = 0;
@@ -433,9 +478,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
1, &version_length);
gb_len += version_length;
- /* 1500 ms */
- lto = 150;
- lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &lto, 1, &lto_length);
+ lto_tlv = nfc_llcp_build_tlv(LLCP_TLV_LTO, &local->lto, 1, &lto_length);
gb_len += lto_length;
pr_debug("Local wks 0x%lx\n", local->local_wks);
@@ -443,8 +486,7 @@ static int nfc_llcp_build_gb(struct nfc_llcp_local *local)
&wks_length);
gb_len += wks_length;
- miux = cpu_to_be16(LLCP_MAX_MIUX);
- miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&miux, 0,
+ miux_tlv = nfc_llcp_build_tlv(LLCP_TLV_MIUX, (u8 *)&local->miux, 0,
&miux_length);
gb_len += miux_length;
@@ -610,7 +652,12 @@ static void nfc_llcp_tx_work(struct work_struct *work)
if (skb != NULL) {
sk = skb->sk;
llcp_sock = nfc_llcp_sock(sk);
- if (llcp_sock != NULL) {
+
+ if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
+ nfc_llcp_send_symm(local->dev);
+ } else {
+ struct sk_buff *copy_skb = NULL;
+ u8 ptype = nfc_llcp_ptype(skb);
int ret;
pr_debug("Sending pending skb\n");
@@ -618,24 +665,29 @@ static void nfc_llcp_tx_work(struct work_struct *work)
DUMP_PREFIX_OFFSET, 16, 1,
skb->data, skb->len, true);
+ if (ptype == LLCP_PDU_I)
+ copy_skb = skb_copy(skb, GFP_ATOMIC);
+
nfc_llcp_send_to_raw_sock(local, skb,
NFC_LLCP_DIRECTION_TX);
ret = nfc_data_exchange(local->dev, local->target_idx,
skb, nfc_llcp_recv, local);
- if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) {
- skb = skb_get(skb);
- skb_queue_tail(&llcp_sock->tx_pending_queue,
- skb);
+ if (ret) {
+ kfree_skb(copy_skb);
+ goto out;
}
- } else {
- nfc_llcp_send_symm(local->dev);
+
+ if (ptype == LLCP_PDU_I && copy_skb)
+ skb_queue_tail(&llcp_sock->tx_pending_queue,
+ copy_skb);
}
} else {
nfc_llcp_send_symm(local->dev);
}
+out:
mod_timer(&local->link_timer,
jiffies + msecs_to_jiffies(2 * local->remote_lto));
}
@@ -704,6 +756,39 @@ static u8 *nfc_llcp_connect_sn(struct sk_buff *skb, size_t *sn_len)
return NULL;
}
+static void nfc_llcp_recv_ui(struct nfc_llcp_local *local,
+ struct sk_buff *skb)
+{
+ struct nfc_llcp_sock *llcp_sock;
+ struct nfc_llcp_ui_cb *ui_cb;
+ u8 dsap, ssap;
+
+ dsap = nfc_llcp_dsap(skb);
+ ssap = nfc_llcp_ssap(skb);
+
+ ui_cb = nfc_llcp_ui_skb_cb(skb);
+ ui_cb->dsap = dsap;
+ ui_cb->ssap = ssap;
+
+ printk("%s %d %d\n", __func__, dsap, ssap);
+
+ pr_debug("%d %d\n", dsap, ssap);
+
+ /* We're looking for a bound socket, not a client one */
+ llcp_sock = nfc_llcp_sock_get(local, dsap, LLCP_SAP_SDP);
+ if (llcp_sock == NULL || llcp_sock->sk.sk_type != SOCK_DGRAM)
+ return;
+
+ /* There is no sequence with UI frames */
+ skb_pull(skb, LLCP_HEADER_SIZE);
+ if (sock_queue_rcv_skb(&llcp_sock->sk, skb)) {
+ pr_err("receive queue is full\n");
+ skb_queue_head(&llcp_sock->tx_backlog_queue, skb);
+ }
+
+ nfc_llcp_sock_put(llcp_sock);
+}
+
static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
struct sk_buff *skb)
{
@@ -823,9 +908,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
fail:
/* Send DM */
nfc_llcp_send_dm(local, dsap, ssap, reason);
-
- return;
-
}
int nfc_llcp_queue_i_frames(struct nfc_llcp_sock *sock)
@@ -903,15 +985,18 @@ static void nfc_llcp_recv_hdlc(struct nfc_llcp_local *local,
/* Remove skbs from the pending queue */
if (llcp_sock->send_ack_n != nr) {
struct sk_buff *s, *tmp;
+ u8 n;
llcp_sock->send_ack_n = nr;
/* Remove and free all skbs until ns == nr */
skb_queue_walk_safe(&llcp_sock->tx_pending_queue, s, tmp) {
+ n = nfc_llcp_ns(s);
+
skb_unlink(s, &llcp_sock->tx_pending_queue);
kfree_skb(s);
- if (nfc_llcp_ns(s) == nr)
+ if (n == nr)
break;
}
@@ -953,6 +1038,9 @@ static void nfc_llcp_recv_disc(struct nfc_llcp_local *local,
sk = &llcp_sock->sk;
lock_sock(sk);
+
+ nfc_llcp_socket_purge(llcp_sock);
+
if (sk->sk_state == LLCP_CLOSED) {
release_sock(sk);
nfc_llcp_sock_put(llcp_sock);
@@ -1027,7 +1115,7 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
}
if (llcp_sock == NULL) {
- pr_err("Invalid DM\n");
+ pr_debug("Already closed\n");
return;
}
@@ -1038,8 +1126,100 @@ static void nfc_llcp_recv_dm(struct nfc_llcp_local *local, struct sk_buff *skb)
sk->sk_state_change(sk);
nfc_llcp_sock_put(llcp_sock);
+}
- return;
+static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
+ struct sk_buff *skb)
+{
+ struct nfc_llcp_sock *llcp_sock;
+ u8 dsap, ssap, *tlv, type, length, tid, sap;
+ u16 tlv_len, offset;
+ char *service_name;
+ size_t service_name_len;
+
+ dsap = nfc_llcp_dsap(skb);
+ ssap = nfc_llcp_ssap(skb);
+
+ pr_debug("%d %d\n", dsap, ssap);
+
+ if (dsap != LLCP_SAP_SDP || ssap != LLCP_SAP_SDP) {
+ pr_err("Wrong SNL SAP\n");
+ return;
+ }
+
+ tlv = &skb->data[LLCP_HEADER_SIZE];
+ tlv_len = skb->len - LLCP_HEADER_SIZE;
+ offset = 0;
+
+ while (offset < tlv_len) {
+ type = tlv[0];
+ length = tlv[1];
+
+ switch (type) {
+ case LLCP_TLV_SDREQ:
+ tid = tlv[2];
+ service_name = (char *) &tlv[3];
+ service_name_len = length - 1;
+
+ pr_debug("Looking for %.16s\n", service_name);
+
+ if (service_name_len == strlen("urn:nfc:sn:sdp") &&
+ !strncmp(service_name, "urn:nfc:sn:sdp",
+ service_name_len)) {
+ sap = 1;
+ goto send_snl;
+ }
+
+ llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
+ service_name_len);
+ if (!llcp_sock) {
+ sap = 0;
+ goto send_snl;
+ }
+
+ /*
+ * We found a socket but its ssap has not been reserved
+ * yet. We need to assign it for good and send a reply.
+ * The ssap will be freed when the socket is closed.
+ */
+ if (llcp_sock->ssap == LLCP_SDP_UNBOUND) {
+ atomic_t *client_count;
+
+ sap = nfc_llcp_reserve_sdp_ssap(local);
+
+ pr_debug("Reserving %d\n", sap);
+
+ if (sap == LLCP_SAP_MAX) {
+ sap = 0;
+ goto send_snl;
+ }
+
+ client_count =
+ &local->local_sdp_cnt[sap -
+ LLCP_WKS_NUM_SAP];
+
+ atomic_inc(client_count);
+
+ llcp_sock->ssap = sap;
+ llcp_sock->reserved_ssap = sap;
+ } else {
+ sap = llcp_sock->ssap;
+ }
+
+ pr_debug("%p %d\n", llcp_sock, sap);
+
+send_snl:
+ nfc_llcp_send_snl(local, tid, sap);
+ break;
+
+ default:
+ pr_err("Invalid SNL tlv value 0x%x\n", type);
+ break;
+ }
+
+ offset += length + 2;
+ tlv += length + 2;
+ }
}
static void nfc_llcp_rx_work(struct work_struct *work)
@@ -1072,6 +1252,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
pr_debug("SYMM\n");
break;
+ case LLCP_PDU_UI:
+ pr_debug("UI\n");
+ nfc_llcp_recv_ui(local, skb);
+ break;
+
case LLCP_PDU_CONNECT:
pr_debug("CONNECT\n");
nfc_llcp_recv_connect(local, skb);
@@ -1092,6 +1277,11 @@ static void nfc_llcp_rx_work(struct work_struct *work)
nfc_llcp_recv_dm(local, skb);
break;
+ case LLCP_PDU_SNL:
+ pr_debug("SNL\n");
+ nfc_llcp_recv_snl(local, skb);
+ break;
+
case LLCP_PDU_I:
case LLCP_PDU_RR:
case LLCP_PDU_RNR:
@@ -1104,8 +1294,6 @@ static void nfc_llcp_rx_work(struct work_struct *work)
schedule_work(&local->tx_work);
kfree_skb(local->rx_pending);
local->rx_pending = NULL;
-
- return;
}
void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
@@ -1121,8 +1309,6 @@ void nfc_llcp_recv(void *data, struct sk_buff *skb, int err)
local->rx_pending = skb_get(skb);
del_timer(&local->link_timer);
schedule_work(&local->rx_work);
-
- return;
}
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
@@ -1205,12 +1391,16 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
rwlock_init(&local->connecting_sockets.lock);
rwlock_init(&local->raw_sockets.lock);
+ local->lto = 150; /* 1500 ms */
+ local->rw = LLCP_MAX_RW;
+ local->miux = cpu_to_be16(LLCP_MAX_MIUX);
+
nfc_llcp_build_gb(local);
local->remote_miu = LLCP_DEFAULT_MIU;
local->remote_lto = LLCP_DEFAULT_LTO;
- list_add(&llcp_devices, &local->list);
+ list_add(&local->list, &llcp_devices);
return 0;
}
diff --git a/net/nfc/llcp/llcp.h b/net/nfc/llcp/llcp.h
index fdb2d24e60b..0d62366f8cc 100644
--- a/net/nfc/llcp/llcp.h
+++ b/net/nfc/llcp/llcp.h
@@ -64,6 +64,9 @@ struct nfc_llcp_local {
u32 target_idx;
u8 rf_mode;
u8 comm_mode;
+ u8 lto;
+ u8 rw;
+ __be16 miux;
unsigned long local_wks; /* Well known services */
unsigned long local_sdp; /* Local services */
unsigned long local_sap; /* Local SAPs, not available for discovery */
@@ -124,6 +127,13 @@ struct nfc_llcp_sock {
struct sock *parent;
};
+struct nfc_llcp_ui_cb {
+ __u8 dsap;
+ __u8 ssap;
+};
+
+#define nfc_llcp_ui_skb_cb(__skb) ((struct nfc_llcp_ui_cb *)&((__skb)->cb[0]))
+
#define nfc_llcp_sock(sk) ((struct nfc_llcp_sock *) (sk))
#define nfc_llcp_dev(sk) (nfc_llcp_sock((sk))->dev)
@@ -209,10 +219,13 @@ int nfc_llcp_disconnect(struct nfc_llcp_sock *sock);
int nfc_llcp_send_symm(struct nfc_dev *dev);
int nfc_llcp_send_connect(struct nfc_llcp_sock *sock);
int nfc_llcp_send_cc(struct nfc_llcp_sock *sock);
+int nfc_llcp_send_snl(struct nfc_llcp_local *local, u8 tid, u8 sap);
int nfc_llcp_send_dm(struct nfc_llcp_local *local, u8 ssap, u8 dsap, u8 reason);
int nfc_llcp_send_disconnect(struct nfc_llcp_sock *sock);
int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
struct msghdr *msg, size_t len);
+int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
+ struct msghdr *msg, size_t len);
int nfc_llcp_send_rr(struct nfc_llcp_sock *sock);
/* Socket API */
diff --git a/net/nfc/llcp/sock.c b/net/nfc/llcp/sock.c
index 63e4cdc9237..fea22eb41b8 100644
--- a/net/nfc/llcp/sock.c
+++ b/net/nfc/llcp/sock.c
@@ -205,8 +205,8 @@ static int llcp_sock_listen(struct socket *sock, int backlog)
lock_sock(sk);
- if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM)
- || sk->sk_state != LLCP_BOUND) {
+ if ((sock->type != SOCK_SEQPACKET && sock->type != SOCK_STREAM) ||
+ sk->sk_state != LLCP_BOUND) {
ret = -EBADFD;
goto error;
}
@@ -608,6 +608,21 @@ static int llcp_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
lock_sock(sk);
+ if (sk->sk_type == SOCK_DGRAM) {
+ struct sockaddr_nfc_llcp *addr =
+ (struct sockaddr_nfc_llcp *)msg->msg_name;
+
+ if (msg->msg_namelen < sizeof(*addr)) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
+ release_sock(sk);
+
+ return nfc_llcp_send_ui_frame(llcp_sock, addr->dsap, addr->ssap,
+ msg, len);
+ }
+
if (sk->sk_state != LLCP_CONNECTED) {
release_sock(sk);
return -ENOTCONN;
@@ -663,11 +678,28 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
return -EFAULT;
}
+ if (sk->sk_type == SOCK_DGRAM && msg->msg_name) {
+ struct nfc_llcp_ui_cb *ui_cb = nfc_llcp_ui_skb_cb(skb);
+ struct sockaddr_nfc_llcp sockaddr;
+
+ pr_debug("Datagram socket %d %d\n", ui_cb->dsap, ui_cb->ssap);
+
+ sockaddr.sa_family = AF_NFC;
+ sockaddr.nfc_protocol = NFC_PROTO_NFC_DEP;
+ sockaddr.dsap = ui_cb->dsap;
+ sockaddr.ssap = ui_cb->ssap;
+
+ memcpy(msg->msg_name, &sockaddr, sizeof(sockaddr));
+ msg->msg_namelen = sizeof(sockaddr);
+ }
+
/* Mark read part of skb as used */
if (!(flags & MSG_PEEK)) {
/* SOCK_STREAM: re-queue skb if it contains unreceived data */
- if (sk->sk_type == SOCK_STREAM || sk->sk_type == SOCK_RAW) {
+ if (sk->sk_type == SOCK_STREAM ||
+ sk->sk_type == SOCK_DGRAM ||
+ sk->sk_type == SOCK_RAW) {
skb_pull(skb, copied);
if (skb->len) {
skb_queue_head(&sk->sk_receive_queue, skb);
diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig
index decdc49b26d..6d69b5f0f19 100644
--- a/net/nfc/nci/Kconfig
+++ b/net/nfc/nci/Kconfig
@@ -1,6 +1,6 @@
config NFC_NCI
- depends on NFC && EXPERIMENTAL
- tristate "NCI protocol support (EXPERIMENTAL)"
+ depends on NFC
+ tristate "NCI protocol support"
default n
help
NCI (NFC Controller Interface) is a communication protocol between
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index acf9abb7d99..5f98dc1bf03 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -205,10 +205,10 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
cmd.num_disc_configs = 0;
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_JEWEL_MASK
- || protocols & NFC_PROTO_MIFARE_MASK
- || protocols & NFC_PROTO_ISO14443_MASK
- || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (protocols & NFC_PROTO_JEWEL_MASK ||
+ protocols & NFC_PROTO_MIFARE_MASK ||
+ protocols & NFC_PROTO_ISO14443_MASK ||
+ protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_A_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -224,8 +224,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_FELICA_MASK
- || protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (protocols & NFC_PROTO_FELICA_MASK ||
+ protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_F_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -414,13 +414,13 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
struct nci_set_config_param param;
__u8 local_gb[NFC_MAX_GT_LEN];
- int i, rc = 0;
+ int i;
param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
if ((param.val == NULL) || (param.len == 0))
- return rc;
+ return 0;
- if (param.len > NCI_MAX_PARAM_LEN)
+ if (param.len > NFC_MAX_GT_LEN)
return -EINVAL;
for (i = 0; i < param.len; i++)
@@ -429,10 +429,8 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
param.id = NCI_PN_ATR_REQ_GEN_BYTES;
param.val = local_gb;
- rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
- msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
-
- return rc;
+ return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+ msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
}
static int nci_start_poll(struct nfc_dev *nfc_dev,
@@ -579,7 +577,6 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
}
}
-
static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
__u8 comm_mode, __u8 *gb, size_t gb_len)
{
@@ -806,8 +803,8 @@ int nci_recv_frame(struct sk_buff *skb)
pr_debug("len %d\n", skb->len);
- if (!ndev || (!test_bit(NCI_UP, &ndev->flags)
- && !test_bit(NCI_INIT, &ndev->flags))) {
+ if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
+ !test_bit(NCI_INIT, &ndev->flags))) {
kfree_skb(skb);
return -ENXIO;
}
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index c1b5285cbde..3568ae16786 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -29,6 +29,8 @@
#include "nfc.h"
+#include "llcp/llcp.h"
+
static struct genl_multicast_group nfc_genl_event_mcgrp = {
.name = NFC_GENL_MCAST_EVENT_NAME,
};
@@ -364,7 +366,8 @@ static int nfc_genl_send_device(struct sk_buff *msg, struct nfc_dev *dev,
if (nla_put_string(msg, NFC_ATTR_DEVICE_NAME, nfc_device_name(dev)) ||
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
nla_put_u32(msg, NFC_ATTR_PROTOCOLS, dev->supported_protocols) ||
- nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up))
+ nla_put_u8(msg, NFC_ATTR_DEVICE_POWERED, dev->dev_up) ||
+ nla_put_u8(msg, NFC_ATTR_RF_MODE, dev->rf_mode))
goto nla_put_failure;
return genlmsg_end(msg, hdr);
@@ -590,7 +593,7 @@ static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info)
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
((!info->attrs[NFC_ATTR_IM_PROTOCOLS] &&
!info->attrs[NFC_ATTR_PROTOCOLS]) &&
- !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
+ !info->attrs[NFC_ATTR_TM_PROTOCOLS]))
return -EINVAL;
idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
@@ -715,6 +718,146 @@ static int nfc_genl_dep_link_down(struct sk_buff *skb, struct genl_info *info)
return rc;
}
+static int nfc_genl_send_params(struct sk_buff *msg,
+ struct nfc_llcp_local *local,
+ u32 portid, u32 seq)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, 0,
+ NFC_CMD_LLC_GET_PARAMS);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, local->dev->idx) ||
+ nla_put_u8(msg, NFC_ATTR_LLC_PARAM_LTO, local->lto) ||
+ nla_put_u8(msg, NFC_ATTR_LLC_PARAM_RW, local->rw) ||
+ nla_put_u16(msg, NFC_ATTR_LLC_PARAM_MIUX, be16_to_cpu(local->miux)))
+ goto nla_put_failure;
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_llc_get_params(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ struct nfc_llcp_local *local;
+ int rc = 0;
+ struct sk_buff *msg = NULL;
+ u32 idx;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ device_lock(&dev->dev);
+
+ local = nfc_llcp_find_local(dev);
+ if (!local) {
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ rc = nfc_genl_send_params(msg, local, info->snd_portid, info->snd_seq);
+
+exit:
+ device_unlock(&dev->dev);
+
+ nfc_put_device(dev);
+
+ if (rc < 0) {
+ if (msg)
+ nlmsg_free(msg);
+
+ return rc;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nfc_genl_llc_set_params(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ struct nfc_llcp_local *local;
+ u8 rw = 0;
+ u16 miux = 0;
+ u32 idx;
+ int rc = 0;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
+ (!info->attrs[NFC_ATTR_LLC_PARAM_LTO] &&
+ !info->attrs[NFC_ATTR_LLC_PARAM_RW] &&
+ !info->attrs[NFC_ATTR_LLC_PARAM_MIUX]))
+ return -EINVAL;
+
+ if (info->attrs[NFC_ATTR_LLC_PARAM_RW]) {
+ rw = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_RW]);
+
+ if (rw > LLCP_MAX_RW)
+ return -EINVAL;
+ }
+
+ if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX]) {
+ miux = nla_get_u16(info->attrs[NFC_ATTR_LLC_PARAM_MIUX]);
+
+ if (miux > LLCP_MAX_MIUX)
+ return -EINVAL;
+ }
+
+ idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(idx);
+ if (!dev)
+ return -ENODEV;
+
+ device_lock(&dev->dev);
+
+ local = nfc_llcp_find_local(dev);
+ if (!local) {
+ nfc_put_device(dev);
+ rc = -ENODEV;
+ goto exit;
+ }
+
+ if (info->attrs[NFC_ATTR_LLC_PARAM_LTO]) {
+ if (dev->dep_link_up) {
+ rc = -EINPROGRESS;
+ goto exit;
+ }
+
+ local->lto = nla_get_u8(info->attrs[NFC_ATTR_LLC_PARAM_LTO]);
+ }
+
+ if (info->attrs[NFC_ATTR_LLC_PARAM_RW])
+ local->rw = rw;
+
+ if (info->attrs[NFC_ATTR_LLC_PARAM_MIUX])
+ local->miux = cpu_to_be16(miux);
+
+exit:
+ device_unlock(&dev->dev);
+
+ nfc_put_device(dev);
+
+ return rc;
+}
+
static struct genl_ops nfc_genl_ops[] = {
{
.cmd = NFC_CMD_GET_DEVICE,
@@ -759,6 +902,16 @@ static struct genl_ops nfc_genl_ops[] = {
.done = nfc_genl_dump_targets_done,
.policy = nfc_genl_policy,
},
+ {
+ .cmd = NFC_CMD_LLC_GET_PARAMS,
+ .doit = nfc_genl_llc_get_params,
+ .policy = nfc_genl_policy,
+ },
+ {
+ .cmd = NFC_CMD_LLC_SET_PARAMS,
+ .doit = nfc_genl_llc_set_params,
+ .policy = nfc_genl_policy,
+ },
};
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index c5e42b79a41..87d914d2876 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -56,6 +56,7 @@ void nfc_llcp_unregister_device(struct nfc_dev *dev);
int nfc_llcp_set_remote_gb(struct nfc_dev *dev, u8 *gb, u8 gb_len);
u8 *nfc_llcp_general_bytes(struct nfc_dev *dev, size_t *general_bytes_len);
int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb);
+struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev);
int __init nfc_llcp_init(void);
void nfc_llcp_exit(void);
@@ -97,6 +98,11 @@ static inline int nfc_llcp_data_received(struct nfc_dev *dev,
return 0;
}
+static inline struct nfc_llcp_local *nfc_llcp_find_local(struct nfc_dev *dev)
+{
+ return NULL;
+}
+
static inline int nfc_llcp_init(void)
{
return 0;
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 8b8a6a2b2ba..313bf1bc848 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -256,7 +256,6 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
return rc ? : copied;
}
-
static const struct proto_ops rawsock_ops = {
.family = PF_NFC,
.owner = THIS_MODULE,
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 08114478cb8..ac2defeeba8 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -28,6 +28,7 @@
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
+#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/dsfield.h>
@@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
*addr = new_addr;
}
+static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
+ __be32 addr[4], const __be32 new_addr[4])
+{
+ int transport_len = skb->len - skb_transport_offset(skb);
+
+ if (l4_proto == IPPROTO_TCP) {
+ if (likely(transport_len >= sizeof(struct tcphdr)))
+ inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
+ addr, new_addr, 1);
+ } else if (l4_proto == IPPROTO_UDP) {
+ if (likely(transport_len >= sizeof(struct udphdr))) {
+ struct udphdr *uh = udp_hdr(skb);
+
+ if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+ inet_proto_csum_replace16(&uh->check, skb,
+ addr, new_addr, 1);
+ if (!uh->check)
+ uh->check = CSUM_MANGLED_0;
+ }
+ }
+ }
+}
+
+static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
+ __be32 addr[4], const __be32 new_addr[4],
+ bool recalculate_csum)
+{
+ if (recalculate_csum)
+ update_ipv6_checksum(skb, l4_proto, addr, new_addr);
+
+ skb->rxhash = 0;
+ memcpy(addr, new_addr, sizeof(__be32[4]));
+}
+
+static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc)
+{
+ nh->priority = tc >> 4;
+ nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4);
+}
+
+static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl)
+{
+ nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16;
+ nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8;
+ nh->flow_lbl[2] = fl & 0x000000FF;
+}
+
static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
{
csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
@@ -195,6 +243,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
return 0;
}
+static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
+{
+ struct ipv6hdr *nh;
+ int err;
+ __be32 *saddr;
+ __be32 *daddr;
+
+ err = make_writable(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr));
+ if (unlikely(err))
+ return err;
+
+ nh = ipv6_hdr(skb);
+ saddr = (__be32 *)&nh->saddr;
+ daddr = (__be32 *)&nh->daddr;
+
+ if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src)))
+ set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr,
+ ipv6_key->ipv6_src, true);
+
+ if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) {
+ unsigned int offset = 0;
+ int flags = IP6_FH_F_SKIP_RH;
+ bool recalc_csum = true;
+
+ if (ipv6_ext_hdr(nh->nexthdr))
+ recalc_csum = ipv6_find_hdr(skb, &offset,
+ NEXTHDR_ROUTING, NULL,
+ &flags) != NEXTHDR_ROUTING;
+
+ set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr,
+ ipv6_key->ipv6_dst, recalc_csum);
+ }
+
+ set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
+ set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label));
+ nh->hop_limit = ipv6_key->ipv6_hlimit;
+
+ return 0;
+}
+
/* Must follow make_writable() since that can move the skb data. */
static void set_tp_port(struct sk_buff *skb, __be16 *port,
__be16 new_port, __sum16 *check)
@@ -339,6 +428,10 @@ static int execute_set_action(struct sk_buff *skb,
skb->priority = nla_get_u32(nested_attr);
break;
+ case OVS_KEY_ATTR_SKB_MARK:
+ skb->mark = nla_get_u32(nested_attr);
+ break;
+
case OVS_KEY_ATTR_ETHERNET:
err = set_eth_addr(skb, nla_data(nested_attr));
break;
@@ -347,6 +440,10 @@ static int execute_set_action(struct sk_buff *skb,
err = set_ipv4(skb, nla_data(nested_attr));
break;
+ case OVS_KEY_ATTR_IPV6:
+ err = set_ipv6(skb, nla_data(nested_attr));
+ break;
+
case OVS_KEY_ATTR_TCP:
err = set_tcp(skb, nla_data(nested_attr));
break;
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 4c4b62ccc7d..f996db34324 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -208,7 +208,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
int error;
int key_len;
- stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
+ stats = this_cpu_ptr(dp->stats_percpu);
/* Extract flow from 'skb' into 'key'. */
error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
@@ -282,7 +282,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
return 0;
err:
- stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());
+ stats = this_cpu_ptr(dp->stats_percpu);
u64_stats_update_begin(&stats->sync);
stats->n_lost++;
@@ -479,8 +479,10 @@ static int validate_set(const struct nlattr *a,
switch (key_type) {
const struct ovs_key_ipv4 *ipv4_key;
+ const struct ovs_key_ipv6 *ipv6_key;
case OVS_KEY_ATTR_PRIORITY:
+ case OVS_KEY_ATTR_SKB_MARK:
case OVS_KEY_ATTR_ETHERNET:
break;
@@ -500,6 +502,25 @@ static int validate_set(const struct nlattr *a,
break;
+ case OVS_KEY_ATTR_IPV6:
+ if (flow_key->eth.type != htons(ETH_P_IPV6))
+ return -EINVAL;
+
+ if (!flow_key->ip.proto)
+ return -EINVAL;
+
+ ipv6_key = nla_data(ovs_key);
+ if (ipv6_key->ipv6_proto != flow_key->ip.proto)
+ return -EINVAL;
+
+ if (ipv6_key->ipv6_frag != flow_key->ip.frag)
+ return -EINVAL;
+
+ if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000)
+ return -EINVAL;
+
+ break;
+
case OVS_KEY_ATTR_TCP:
if (flow_key->ip.proto != IPPROTO_TCP)
return -EINVAL;
@@ -675,6 +696,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
goto err_flow_free;
err = ovs_flow_metadata_from_nlattrs(&flow->key.phy.priority,
+ &flow->key.phy.skb_mark,
&flow->key.phy.in_port,
a[OVS_PACKET_ATTR_KEY]);
if (err)
@@ -694,6 +716,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
OVS_CB(packet)->flow = flow;
packet->priority = flow->key.phy.priority;
+ packet->mark = flow->key.phy.skb_mark;
rcu_read_lock();
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 98c70630ad0..c3294cebc4f 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -604,6 +604,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
key->phy.priority = skb->priority;
key->phy.in_port = in_port;
+ key->phy.skb_mark = skb->mark;
skb_reset_mac_header(skb);
@@ -689,7 +690,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
}
}
- } else if (key->eth.type == htons(ETH_P_ARP) && arphdr_ok(skb)) {
+ } else if ((key->eth.type == htons(ETH_P_ARP) ||
+ key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) {
struct arp_eth_header *arp;
arp = (struct arp_eth_header *)skb_network_header(skb);
@@ -702,15 +704,11 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key,
/* We only match on the lower 8 bits of the opcode. */
if (ntohs(arp->ar_op) <= 0xff)
key->ip.proto = ntohs(arp->ar_op);
-
- if (key->ip.proto == ARPOP_REQUEST
- || key->ip.proto == ARPOP_REPLY) {
- memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src));
- memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
- memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
- memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
- key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
- }
+ memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src));
+ memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst));
+ memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN);
+ memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN);
+ key_len = SW_FLOW_KEY_OFFSET(ipv4.arp);
}
} else if (key->eth.type == htons(ETH_P_IPV6)) {
int nh_len; /* IPv6 Header + Extensions */
@@ -806,6 +804,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_ENCAP] = -1,
[OVS_KEY_ATTR_PRIORITY] = sizeof(u32),
[OVS_KEY_ATTR_IN_PORT] = sizeof(u32),
+ [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32),
[OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
[OVS_KEY_ATTR_VLAN] = sizeof(__be16),
[OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16),
@@ -991,6 +990,10 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
} else {
swkey->phy.in_port = DP_MAX_PORTS;
}
+ if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) {
+ swkey->phy.skb_mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]);
+ attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK);
+ }
/* Data attributes. */
if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET)))
@@ -1090,7 +1093,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
if (err)
return err;
}
- } else if (swkey->eth.type == htons(ETH_P_ARP)) {
+ } else if (swkey->eth.type == htons(ETH_P_ARP) ||
+ swkey->eth.type == htons(ETH_P_RARP)) {
const struct ovs_key_arp *arp_key;
if (!(attrs & (1 << OVS_KEY_ATTR_ARP)))
@@ -1117,6 +1121,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
/**
* ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key.
+ * @priority: receives the skb priority
+ * @mark: receives the skb mark
* @in_port: receives the extracted input port.
* @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
* sequence.
@@ -1126,7 +1132,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
* get the metadata, that is, the parts of the flow key that cannot be
* extracted from the packet itself.
*/
-int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port,
+int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port,
const struct nlattr *attr)
{
const struct nlattr *nla;
@@ -1134,6 +1140,7 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port,
*in_port = DP_MAX_PORTS;
*priority = 0;
+ *mark = 0;
nla_for_each_nested(nla, attr, rem) {
int type = nla_type(nla);
@@ -1152,6 +1159,10 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port,
return -EINVAL;
*in_port = nla_get_u32(nla);
break;
+
+ case OVS_KEY_ATTR_SKB_MARK:
+ *mark = nla_get_u32(nla);
+ break;
}
}
}
@@ -1173,6 +1184,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port))
goto nla_put_failure;
+ if (swkey->phy.skb_mark &&
+ nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark))
+ goto nla_put_failure;
+
nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key));
if (!nla)
goto nla_put_failure;
@@ -1226,7 +1241,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb)
ipv6_key->ipv6_tclass = swkey->ip.tos;
ipv6_key->ipv6_hlimit = swkey->ip.ttl;
ipv6_key->ipv6_frag = swkey->ip.frag;
- } else if (swkey->eth.type == htons(ETH_P_ARP)) {
+ } else if (swkey->eth.type == htons(ETH_P_ARP) ||
+ swkey->eth.type == htons(ETH_P_RARP)) {
struct ovs_key_arp *arp_key;
nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key));
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 14a324eb017..a7bb60ff3b5 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -43,6 +43,7 @@ struct sw_flow_actions {
struct sw_flow_key {
struct {
u32 priority; /* Packet QoS priority. */
+ u32 skb_mark; /* SKB mark. */
u16 in_port; /* Input switch port (or DP_MAX_PORTS). */
} phy;
struct {
@@ -144,6 +145,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
* ------ --- ------ -----
* OVS_KEY_ATTR_PRIORITY 4 -- 4 8
* OVS_KEY_ATTR_IN_PORT 4 -- 4 8
+ * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8
* OVS_KEY_ATTR_ETHERNET 12 -- 4 16
* OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype)
* OVS_KEY_ATTR_8021Q 4 -- 4 8
@@ -153,14 +155,14 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies);
* OVS_KEY_ATTR_ICMPV6 2 2 4 8
* OVS_KEY_ATTR_ND 28 -- 4 32
* -------------------------------------------------
- * total 144
+ * total 152
*/
-#define FLOW_BUFSIZE 144
+#define FLOW_BUFSIZE 152
int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *);
int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp,
const struct nlattr *);
-int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port,
+int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port,
const struct nlattr *);
#define MAX_ACTIONS_BUFSIZE (16 * 1024)
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index 3c1e58ba714..a9327e2e48c 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -114,6 +114,15 @@ error:
return ERR_PTR(err);
}
+static void free_port_rcu(struct rcu_head *rcu)
+{
+ struct netdev_vport *netdev_vport = container_of(rcu,
+ struct netdev_vport, rcu);
+
+ dev_put(netdev_vport->dev);
+ ovs_vport_free(vport_from_priv(netdev_vport));
+}
+
static void netdev_destroy(struct vport *vport)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
@@ -122,10 +131,7 @@ static void netdev_destroy(struct vport *vport)
netdev_rx_handler_unregister(netdev_vport->dev);
dev_set_promiscuity(netdev_vport->dev, -1);
- synchronize_rcu();
-
- dev_put(netdev_vport->dev);
- ovs_vport_free(vport);
+ call_rcu(&netdev_vport->rcu, free_port_rcu);
}
const char *ovs_netdev_get_name(const struct vport *vport)
@@ -158,7 +164,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
- ovs_dp_name(vport->dp),
+ netdev_vport->dev->name,
packet_length(skb), mtu);
goto error;
}
diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h
index f7072a25c60..6478079b341 100644
--- a/net/openvswitch/vport-netdev.h
+++ b/net/openvswitch/vport-netdev.h
@@ -20,12 +20,15 @@
#define VPORT_NETDEV_H 1
#include <linux/netdevice.h>
+#include <linux/rcupdate.h>
#include "vport.h"
struct vport *ovs_netdev_get_vport(struct net_device *dev);
struct netdev_vport {
+ struct rcu_head rcu;
+
struct net_device *dev;
};
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 03779e8a262..70af0bedbac 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -333,8 +333,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
{
struct vport_percpu_stats *stats;
- stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());
-
+ stats = this_cpu_ptr(vport->percpu_stats);
u64_stats_update_begin(&stats->sync);
stats->rx_packets++;
stats->rx_bytes += skb->len;
@@ -359,7 +358,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb)
if (likely(sent)) {
struct vport_percpu_stats *stats;
- stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());
+ stats = this_cpu_ptr(vport->percpu_stats);
u64_stats_update_begin(&stats->sync);
stats->tx_packets++;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 94060edbbd7..e639645e8fe 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1881,7 +1881,35 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
- data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll);
+ if (po->tp_tx_has_off) {
+ int off_min, off_max, off;
+ off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
+ off_max = po->tx_ring.frame_size - tp_len;
+ if (sock->type == SOCK_DGRAM) {
+ switch (po->tp_version) {
+ case TPACKET_V2:
+ off = ph.h2->tp_net;
+ break;
+ default:
+ off = ph.h1->tp_net;
+ break;
+ }
+ } else {
+ switch (po->tp_version) {
+ case TPACKET_V2:
+ off = ph.h2->tp_mac;
+ break;
+ default:
+ off = ph.h1->tp_mac;
+ break;
+ }
+ }
+ if (unlikely((off < off_min) || (off_max < off)))
+ return -EINVAL;
+ data = ph.raw + off;
+ } else {
+ data = ph.raw + po->tp_hdrlen - sizeof(struct sockaddr_ll);
+ }
to_write = tp_len;
if (sock->type == SOCK_DGRAM) {
@@ -1907,7 +1935,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
to_write -= dev->hard_header_len;
}
- err = -EFAULT;
offset = offset_in_page(data);
len_max = PAGE_SIZE - offset;
len = ((to_write > len_max) ? len_max : to_write);
@@ -1957,7 +1984,6 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
mutex_lock(&po->pg_vec_lock);
- err = -EBUSY;
if (saddr == NULL) {
dev = po->prot_hook.dev;
proto = po->num;
@@ -2478,7 +2504,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
__be16 proto = (__force __be16)protocol; /* weird, but documented */
int err;
- if (!capable(CAP_NET_RAW))
+ if (!ns_capable(net->user_ns, CAP_NET_RAW))
return -EPERM;
if (sock->type != SOCK_DGRAM && sock->type != SOCK_RAW &&
sock->type != SOCK_PACKET)
@@ -3111,6 +3137,19 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
return fanout_add(sk, val & 0xffff, val >> 16);
}
+ case PACKET_TX_HAS_OFF:
+ {
+ unsigned int val;
+
+ if (optlen != sizeof(val))
+ return -EINVAL;
+ if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
+ return -EBUSY;
+ if (copy_from_user(&val, optval, sizeof(val)))
+ return -EFAULT;
+ po->tp_tx_has_off = !!val;
+ return 0;
+ }
default:
return -ENOPROTOOPT;
}
@@ -3202,6 +3241,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
((u32)po->fanout->type << 16)) :
0);
break;
+ case PACKET_TX_HAS_OFF:
+ val = po->tp_tx_has_off;
+ break;
default:
return -ENOPROTOOPT;
}
diff --git a/net/packet/internal.h b/net/packet/internal.h
index 44945f6b725..e84cab8cb7a 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -109,6 +109,7 @@ struct packet_sock {
unsigned int tp_hdrlen;
unsigned int tp_reserve;
unsigned int tp_loss:1;
+ unsigned int tp_tx_has_off:1;
unsigned int tp_tstamp;
struct packet_type prot_hook ____cacheline_aligned_in_smp;
};
diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c
index 83a8389619a..0193630d306 100644
--- a/net/phonet/pn_netlink.c
+++ b/net/phonet/pn_netlink.c
@@ -70,6 +70,9 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)
int err;
u8 pnaddr;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
@@ -230,6 +233,9 @@ static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)
int err;
u8 dst;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
diff --git a/net/rds/ib.h b/net/rds/ib.h
index 8d2b3d5a7c2..7280ab8810c 100644
--- a/net/rds/ib.h
+++ b/net/rds/ib.h
@@ -50,7 +50,7 @@ struct rds_ib_cache_head {
};
struct rds_ib_refill_cache {
- struct rds_ib_cache_head *percpu;
+ struct rds_ib_cache_head __percpu *percpu;
struct list_head *xfer;
struct list_head *ready;
};
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index a1e11627747..31b74f5e61a 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -434,12 +434,11 @@ static u32 rds_ib_protocol_compatible(struct rdma_cm_event *event)
version = RDS_PROTOCOL_3_0;
while ((common >>= 1) != 0)
version++;
- }
- printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using "
- "incompatible protocol version %u.%u\n",
- &dp->dp_saddr,
- dp->dp_protocol_major,
- dp->dp_protocol_minor);
+ } else
+ printk_ratelimited(KERN_NOTICE "RDS: Connection from %pI4 using incompatible protocol version %u.%u\n",
+ &dp->dp_saddr,
+ dp->dp_protocol_major,
+ dp->dp_protocol_minor);
return version;
}
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index 8d194912c69..8eb9501e3d6 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -339,8 +339,8 @@ static int rds_ib_recv_refill_one(struct rds_connection *conn,
sge->length = sizeof(struct rds_header);
sge = &recv->r_sge[1];
- sge->addr = sg_dma_address(&recv->r_frag->f_sg);
- sge->length = sg_dma_len(&recv->r_frag->f_sg);
+ sge->addr = ib_sg_dma_address(ic->i_cm_id->device, &recv->r_frag->f_sg);
+ sge->length = ib_sg_dma_len(ic->i_cm_id->device, &recv->r_frag->f_sg);
ret = 0;
out:
@@ -381,7 +381,10 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill)
ret = ib_post_recv(ic->i_cm_id->qp, &recv->r_wr, &failed_wr);
rdsdebug("recv %p ibinc %p page %p addr %lu ret %d\n", recv,
recv->r_ibinc, sg_page(&recv->r_frag->f_sg),
- (long) sg_dma_address(&recv->r_frag->f_sg), ret);
+ (long) ib_sg_dma_address(
+ ic->i_cm_id->device,
+ &recv->r_frag->f_sg),
+ ret);
if (ret) {
rds_ib_conn_error(conn, "recv post on "
"%pI4 returned %d, disconnecting and "
@@ -418,20 +421,21 @@ static void rds_ib_recv_cache_put(struct list_head *new_item,
struct rds_ib_refill_cache *cache)
{
unsigned long flags;
- struct rds_ib_cache_head *chp;
struct list_head *old;
+ struct list_head __percpu *chpfirst;
local_irq_save(flags);
- chp = per_cpu_ptr(cache->percpu, smp_processor_id());
- if (!chp->first)
+ chpfirst = __this_cpu_read(cache->percpu->first);
+ if (!chpfirst)
INIT_LIST_HEAD(new_item);
else /* put on front */
- list_add_tail(new_item, chp->first);
- chp->first = new_item;
- chp->count++;
+ list_add_tail(new_item, chpfirst);
- if (chp->count < RDS_IB_RECYCLE_BATCH_COUNT)
+ __this_cpu_write(chpfirst, new_item);
+ __this_cpu_inc(cache->percpu->count);
+
+ if (__this_cpu_read(cache->percpu->count) < RDS_IB_RECYCLE_BATCH_COUNT)
goto end;
/*
@@ -443,12 +447,13 @@ static void rds_ib_recv_cache_put(struct list_head *new_item,
do {
old = xchg(&cache->xfer, NULL);
if (old)
- list_splice_entire_tail(old, chp->first);
- old = cmpxchg(&cache->xfer, NULL, chp->first);
+ list_splice_entire_tail(old, chpfirst);
+ old = cmpxchg(&cache->xfer, NULL, chpfirst);
} while (old);
- chp->first = NULL;
- chp->count = 0;
+
+ __this_cpu_write(chpfirst, NULL);
+ __this_cpu_write(cache->percpu->count, 0);
end:
local_irq_restore(flags);
}
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 865adb61685..78fc0937948 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -213,7 +213,7 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
static struct platform_driver rfkill_gpio_driver = {
.probe = rfkill_gpio_probe,
- .remove = __devexit_p(rfkill_gpio_remove),
+ .remove = rfkill_gpio_remove,
.driver = {
.name = "rfkill_gpio",
.owner = THIS_MODULE,
diff --git a/net/rfkill/rfkill-regulator.c b/net/rfkill/rfkill-regulator.c
index 11da3018a85..4b5ab21ecb2 100644
--- a/net/rfkill/rfkill-regulator.c
+++ b/net/rfkill/rfkill-regulator.c
@@ -55,7 +55,7 @@ struct rfkill_ops rfkill_regulator_ops = {
.set_block = rfkill_regulator_set_block,
};
-static int __devinit rfkill_regulator_probe(struct platform_device *pdev)
+static int rfkill_regulator_probe(struct platform_device *pdev)
{
struct rfkill_regulator_platform_data *pdata = pdev->dev.platform_data;
struct rfkill_regulator_data *rfkill_data;
@@ -122,7 +122,7 @@ out:
return ret;
}
-static int __devexit rfkill_regulator_remove(struct platform_device *pdev)
+static int rfkill_regulator_remove(struct platform_device *pdev)
{
struct rfkill_regulator_data *rfkill_data = platform_get_drvdata(pdev);
struct rfkill *rf_kill = rfkill_data->rf_kill;
@@ -137,7 +137,7 @@ static int __devexit rfkill_regulator_remove(struct platform_device *pdev)
static struct platform_driver rfkill_regulator_driver = {
.probe = rfkill_regulator_probe,
- .remove = __devexit_p(rfkill_regulator_remove),
+ .remove = rfkill_regulator_remove,
.driver = {
.name = "rfkill-regulator",
.owner = THIS_MODULE,
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 62fb51face8..235e01acac5 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -509,7 +509,7 @@ config NET_EMATCH_TEXT
config NET_EMATCH_CANID
tristate "CAN Identifier"
- depends on NET_EMATCH && CAN
+ depends on NET_EMATCH && (CAN=y || CAN=m)
---help---
Say Y here if you want to be able to classify CAN frames based
on CAN Identifier.
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 102761d294c..65d240cbf74 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -987,6 +987,9 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
u32 portid = skb ? NETLINK_CB(skb).portid : 0;
int ret = 0, ovr = 0;
+ if ((n->nlmsg_type != RTM_GETACTION) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL);
if (ret < 0)
return ret;
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 7ae02892437..ff55ed6c49b 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -139,6 +139,8 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
int err;
int tp_created = 0;
+ if ((n->nlmsg_type != RTM_GETTFILTER) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
replay:
t = nlmsg_data(n);
protocol = TC_H_MIN(t->tcm_info);
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 2ecde225ae6..6db7855b902 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -17,6 +17,7 @@
#include <linux/skbuff.h>
#include <linux/cgroup.h>
#include <linux/rcupdate.h>
+#include <linux/fdtable.h>
#include <net/rtnetlink.h>
#include <net/pkt_cls.h>
#include <net/sock.h>
@@ -34,25 +35,51 @@ static inline struct cgroup_cls_state *task_cls_state(struct task_struct *p)
struct cgroup_cls_state, css);
}
-static struct cgroup_subsys_state *cgrp_create(struct cgroup *cgrp)
+static struct cgroup_subsys_state *cgrp_css_alloc(struct cgroup *cgrp)
{
struct cgroup_cls_state *cs;
cs = kzalloc(sizeof(*cs), GFP_KERNEL);
if (!cs)
return ERR_PTR(-ENOMEM);
+ return &cs->css;
+}
+static int cgrp_css_online(struct cgroup *cgrp)
+{
if (cgrp->parent)
- cs->classid = cgrp_cls_state(cgrp->parent)->classid;
-
- return &cs->css;
+ cgrp_cls_state(cgrp)->classid =
+ cgrp_cls_state(cgrp->parent)->classid;
+ return 0;
}
-static void cgrp_destroy(struct cgroup *cgrp)
+static void cgrp_css_free(struct cgroup *cgrp)
{
kfree(cgrp_cls_state(cgrp));
}
+static int update_classid(const void *v, struct file *file, unsigned n)
+{
+ int err;
+ struct socket *sock = sock_from_file(file, &err);
+ if (sock)
+ sock->sk->sk_classid = (u32)(unsigned long)v;
+ return 0;
+}
+
+static void cgrp_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
+{
+ struct task_struct *p;
+ void *v;
+
+ cgroup_taskset_for_each(p, cgrp, tset) {
+ task_lock(p);
+ v = (void *)(unsigned long)task_cls_classid(p);
+ iterate_fd(p->files, 0, update_classid, v);
+ task_unlock(p);
+ }
+}
+
static u64 read_classid(struct cgroup *cgrp, struct cftype *cft)
{
return cgrp_cls_state(cgrp)->classid;
@@ -75,20 +102,13 @@ static struct cftype ss_files[] = {
struct cgroup_subsys net_cls_subsys = {
.name = "net_cls",
- .create = cgrp_create,
- .destroy = cgrp_destroy,
+ .css_alloc = cgrp_css_alloc,
+ .css_online = cgrp_css_online,
+ .css_free = cgrp_css_free,
+ .attach = cgrp_attach,
.subsys_id = net_cls_subsys_id,
.base_cftypes = ss_files,
.module = THIS_MODULE,
-
- /*
- * While net_cls cgroup has the rudimentary hierarchy support of
- * inheriting the parent's classid on cgroup creation, it doesn't
- * properly propagates config changes in ancestors to their
- * descendents. A child should follow the parent's configuration
- * but be allowed to override it. Fix it and remove the following.
- */
- .broken_hierarchy = true,
};
struct cls_cgroup_head {
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index a18d975db59..d84f7e734cd 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -495,16 +495,15 @@ EXPORT_SYMBOL(qdisc_watchdog_init);
void qdisc_watchdog_schedule(struct qdisc_watchdog *wd, psched_time_t expires)
{
- ktime_t time;
-
if (test_bit(__QDISC_STATE_DEACTIVATED,
&qdisc_root_sleeping(wd->qdisc)->state))
return;
qdisc_throttled(wd->qdisc);
- time = ktime_set(0, 0);
- time = ktime_add_ns(time, PSCHED_TICKS2NS(expires));
- hrtimer_start(&wd->timer, time, HRTIMER_MODE_ABS);
+
+ hrtimer_start(&wd->timer,
+ ns_to_ktime(PSCHED_TICKS2NS(expires)),
+ HRTIMER_MODE_ABS);
}
EXPORT_SYMBOL(qdisc_watchdog_schedule);
@@ -834,6 +833,8 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
goto err_out3;
}
lockdep_set_class(qdisc_lock(sch), &qdisc_tx_lock);
+ if (!netif_is_multiqueue(dev))
+ sch->flags |= TCQ_F_ONETXQUEUE;
}
sch->handle = handle;
@@ -981,6 +982,9 @@ static int tc_get_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
struct Qdisc *p = NULL;
int err;
+ if ((n->nlmsg_type != RTM_GETQDISC) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
dev = __dev_get_by_index(net, tcm->tcm_ifindex);
if (!dev)
return -ENODEV;
@@ -1044,6 +1048,9 @@ static int tc_modify_qdisc(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
struct Qdisc *q, *p;
int err;
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
replay:
/* Reinit, just in case something touches this. */
tcm = nlmsg_data(n);
@@ -1380,6 +1387,9 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, void *arg)
u32 qid = TC_H_MAJ(clid);
int err;
+ if ((n->nlmsg_type != RTM_GETTCLASS) && !capable(CAP_NET_ADMIN))
+ return -EPERM;
+
dev = __dev_get_by_index(net, tcm->tcm_ifindex);
if (!dev)
return -ENODEV;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 564b9fc8efd..0e19948470b 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -509,8 +509,7 @@ static void cbq_ovl_delay(struct cbq_class *cl)
cl->cpriority = TC_CBQ_MAXPRIO;
q->pmask |= (1<<TC_CBQ_MAXPRIO);
- expires = ktime_set(0, 0);
- expires = ktime_add_ns(expires, PSCHED_TICKS2NS(sched));
+ expires = ns_to_ktime(PSCHED_TICKS2NS(sched));
if (hrtimer_try_to_cancel(&q->delay_timer) &&
ktime_to_ns(ktime_sub(
hrtimer_get_expires(&q->delay_timer),
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index aefc1504dc8..5d81a447851 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -53,20 +53,19 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
static inline struct sk_buff *dequeue_skb(struct Qdisc *q)
{
struct sk_buff *skb = q->gso_skb;
+ const struct netdev_queue *txq = q->dev_queue;
if (unlikely(skb)) {
- struct net_device *dev = qdisc_dev(q);
- struct netdev_queue *txq;
-
/* check the reason of requeuing without tx lock first */
- txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
+ txq = netdev_get_tx_queue(txq->dev, skb_get_queue_mapping(skb));
if (!netif_xmit_frozen_or_stopped(txq)) {
q->gso_skb = NULL;
q->q.qlen--;
} else
skb = NULL;
} else {
- skb = q->dequeue(q);
+ if (!(q->flags & TCQ_F_ONETXQUEUE) || !netif_xmit_frozen_or_stopped(txq))
+ skb = q->dequeue(q);
}
return skb;
@@ -686,6 +685,8 @@ static void attach_one_default_qdisc(struct net_device *dev,
netdev_info(dev, "activation failed\n");
return;
}
+ if (!netif_is_multiqueue(dev))
+ qdisc->flags |= TCQ_F_ONETXQUEUE;
}
dev_queue->qdisc_sleeping = qdisc;
}
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 9d75b776131..51561eafcb7 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -71,6 +71,12 @@ enum htb_cmode {
HTB_CAN_SEND /* class can send */
};
+struct htb_rate_cfg {
+ u64 rate_bps;
+ u32 mult;
+ u32 shift;
+};
+
/* interior & leaf nodes; props specific to leaves are marked L: */
struct htb_class {
struct Qdisc_class_common common;
@@ -118,11 +124,11 @@ struct htb_class {
int filter_cnt;
/* token bucket parameters */
- struct qdisc_rate_table *rate; /* rate table of the class itself */
- struct qdisc_rate_table *ceil; /* ceiling rate (limits borrows too) */
- long buffer, cbuffer; /* token bucket depth/rate */
+ struct htb_rate_cfg rate;
+ struct htb_rate_cfg ceil;
+ s64 buffer, cbuffer; /* token bucket depth/rate */
psched_tdiff_t mbuffer; /* max wait time */
- long tokens, ctokens; /* current number of tokens */
+ s64 tokens, ctokens; /* current number of tokens */
psched_time_t t_c; /* checkpoint time */
};
@@ -162,6 +168,45 @@ struct htb_sched {
struct work_struct work;
};
+static u64 l2t_ns(struct htb_rate_cfg *r, unsigned int len)
+{
+ return ((u64)len * r->mult) >> r->shift;
+}
+
+static void htb_precompute_ratedata(struct htb_rate_cfg *r)
+{
+ u64 factor;
+ u64 mult;
+ int shift;
+
+ r->shift = 0;
+ r->mult = 1;
+ /*
+ * Calibrate mult, shift so that token counting is accurate
+ * for smallest packet size (64 bytes). Token (time in ns) is
+ * computed as (bytes * 8) * NSEC_PER_SEC / rate_bps. It will
+ * work as long as the smallest packet transfer time can be
+ * accurately represented in nanosec.
+ */
+ if (r->rate_bps > 0) {
+ /*
+ * Higher shift gives better accuracy. Find the largest
+ * shift such that mult fits in 32 bits.
+ */
+ for (shift = 0; shift < 16; shift++) {
+ r->shift = shift;
+ factor = 8LLU * NSEC_PER_SEC * (1 << r->shift);
+ mult = div64_u64(factor, r->rate_bps);
+ if (mult > UINT_MAX)
+ break;
+ }
+
+ r->shift = shift - 1;
+ factor = 8LLU * NSEC_PER_SEC * (1 << r->shift);
+ r->mult = div64_u64(factor, r->rate_bps);
+ }
+}
+
/* find class in global hash table using given handle */
static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch)
{
@@ -273,7 +318,7 @@ static void htb_add_to_id_tree(struct rb_root *root,
* already in the queue.
*/
static void htb_add_to_wait_tree(struct htb_sched *q,
- struct htb_class *cl, long delay)
+ struct htb_class *cl, s64 delay)
{
struct rb_node **p = &q->wait_pq[cl->level].rb_node, *parent = NULL;
@@ -441,14 +486,14 @@ static void htb_deactivate_prios(struct htb_sched *q, struct htb_class *cl)
htb_remove_class_from_row(q, cl, mask);
}
-static inline long htb_lowater(const struct htb_class *cl)
+static inline s64 htb_lowater(const struct htb_class *cl)
{
if (htb_hysteresis)
return cl->cmode != HTB_CANT_SEND ? -cl->cbuffer : 0;
else
return 0;
}
-static inline long htb_hiwater(const struct htb_class *cl)
+static inline s64 htb_hiwater(const struct htb_class *cl)
{
if (htb_hysteresis)
return cl->cmode == HTB_CAN_SEND ? -cl->buffer : 0;
@@ -469,9 +514,9 @@ static inline long htb_hiwater(const struct htb_class *cl)
* mode transitions per time unit. The speed gain is about 1/6.
*/
static inline enum htb_cmode
-htb_class_mode(struct htb_class *cl, long *diff)
+htb_class_mode(struct htb_class *cl, s64 *diff)
{
- long toks;
+ s64 toks;
if ((toks = (cl->ctokens + *diff)) < htb_lowater(cl)) {
*diff = -toks;
@@ -495,7 +540,7 @@ htb_class_mode(struct htb_class *cl, long *diff)
* to mode other than HTB_CAN_SEND (see htb_add_to_wait_tree).
*/
static void
-htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, long *diff)
+htb_change_class_mode(struct htb_sched *q, struct htb_class *cl, s64 *diff)
{
enum htb_cmode new_mode = htb_class_mode(cl, diff);
@@ -581,26 +626,26 @@ static int htb_enqueue(struct sk_buff *skb, struct Qdisc *sch)
return NET_XMIT_SUCCESS;
}
-static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, long diff)
+static inline void htb_accnt_tokens(struct htb_class *cl, int bytes, s64 diff)
{
- long toks = diff + cl->tokens;
+ s64 toks = diff + cl->tokens;
if (toks > cl->buffer)
toks = cl->buffer;
- toks -= (long) qdisc_l2t(cl->rate, bytes);
+ toks -= (s64) l2t_ns(&cl->rate, bytes);
if (toks <= -cl->mbuffer)
toks = 1 - cl->mbuffer;
cl->tokens = toks;
}
-static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, long diff)
+static inline void htb_accnt_ctokens(struct htb_class *cl, int bytes, s64 diff)
{
- long toks = diff + cl->ctokens;
+ s64 toks = diff + cl->ctokens;
if (toks > cl->cbuffer)
toks = cl->cbuffer;
- toks -= (long) qdisc_l2t(cl->ceil, bytes);
+ toks -= (s64) l2t_ns(&cl->ceil, bytes);
if (toks <= -cl->mbuffer)
toks = 1 - cl->mbuffer;
@@ -623,10 +668,10 @@ static void htb_charge_class(struct htb_sched *q, struct htb_class *cl,
{
int bytes = qdisc_pkt_len(skb);
enum htb_cmode old_mode;
- long diff;
+ s64 diff;
while (cl) {
- diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
+ diff = min_t(s64, q->now - cl->t_c, cl->mbuffer);
if (cl->level >= level) {
if (cl->level == level)
cl->xstats.lends++;
@@ -673,7 +718,7 @@ static psched_time_t htb_do_events(struct htb_sched *q, int level,
unsigned long stop_at = start + 2;
while (time_before(jiffies, stop_at)) {
struct htb_class *cl;
- long diff;
+ s64 diff;
struct rb_node *p = rb_first(&q->wait_pq[level]);
if (!p)
@@ -684,7 +729,7 @@ static psched_time_t htb_do_events(struct htb_sched *q, int level,
return cl->pq_key;
htb_safe_rb_erase(p, q->wait_pq + level);
- diff = psched_tdiff_bounded(q->now, cl->t_c, cl->mbuffer);
+ diff = min_t(s64, q->now - cl->t_c, cl->mbuffer);
htb_change_class_mode(q, cl, &diff);
if (cl->cmode != HTB_CAN_SEND)
htb_add_to_wait_tree(q, cl, diff);
@@ -871,10 +916,10 @@ ok:
if (!sch->q.qlen)
goto fin;
- q->now = psched_get_time();
+ q->now = ktime_to_ns(ktime_get());
start_at = jiffies;
- next_event = q->now + 5 * PSCHED_TICKS_PER_SEC;
+ next_event = q->now + 5LLU * NSEC_PER_SEC;
for (level = 0; level < TC_HTB_MAXDEPTH; level++) {
/* common case optimization - skip event handler quickly */
@@ -884,7 +929,7 @@ ok:
if (q->now >= q->near_ev_cache[level]) {
event = htb_do_events(q, level, start_at);
if (!event)
- event = q->now + PSCHED_TICKS_PER_SEC;
+ event = q->now + NSEC_PER_SEC;
q->near_ev_cache[level] = event;
} else
event = q->near_ev_cache[level];
@@ -903,10 +948,17 @@ ok:
}
}
sch->qstats.overlimits++;
- if (likely(next_event > q->now))
- qdisc_watchdog_schedule(&q->watchdog, next_event);
- else
+ if (likely(next_event > q->now)) {
+ if (!test_bit(__QDISC_STATE_DEACTIVATED,
+ &qdisc_root_sleeping(q->watchdog.qdisc)->state)) {
+ ktime_t time = ns_to_ktime(next_event);
+ qdisc_throttled(q->watchdog.qdisc);
+ hrtimer_start(&q->watchdog.timer, time,
+ HRTIMER_MODE_ABS);
+ }
+ } else {
schedule_work(&q->work);
+ }
fin:
return skb;
}
@@ -1082,9 +1134,9 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg,
memset(&opt, 0, sizeof(opt));
- opt.rate = cl->rate->rate;
+ opt.rate.rate = cl->rate.rate_bps >> 3;
opt.buffer = cl->buffer;
- opt.ceil = cl->ceil->rate;
+ opt.ceil.rate = cl->ceil.rate_bps >> 3;
opt.cbuffer = cl->cbuffer;
opt.quantum = cl->quantum;
opt.prio = cl->prio;
@@ -1203,9 +1255,6 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
qdisc_destroy(cl->un.leaf.q);
}
gen_kill_estimator(&cl->bstats, &cl->rate_est);
- qdisc_put_rtab(cl->rate);
- qdisc_put_rtab(cl->ceil);
-
tcf_destroy_chain(&cl->filter_list);
kfree(cl);
}
@@ -1307,7 +1356,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
struct htb_sched *q = qdisc_priv(sch);
struct htb_class *cl = (struct htb_class *)*arg, *parent;
struct nlattr *opt = tca[TCA_OPTIONS];
- struct qdisc_rate_table *rtab = NULL, *ctab = NULL;
struct nlattr *tb[__TCA_HTB_MAX];
struct tc_htb_opt *hopt;
@@ -1326,10 +1374,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
parent = parentid == TC_H_ROOT ? NULL : htb_find(parentid, sch);
hopt = nla_data(tb[TCA_HTB_PARMS]);
-
- rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]);
- ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]);
- if (!rtab || !ctab)
+ if (!hopt->rate.rate || !hopt->ceil.rate)
goto failure;
if (!cl) { /* new class */
@@ -1439,7 +1484,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
* is really leaf before changing cl->un.leaf !
*/
if (!cl->level) {
- cl->quantum = rtab->rate.rate / q->rate2quantum;
+ cl->quantum = hopt->rate.rate / q->rate2quantum;
if (!hopt->quantum && cl->quantum < 1000) {
pr_warning(
"HTB: quantum of class %X is small. Consider r2q change.\n",
@@ -1460,12 +1505,16 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
cl->buffer = hopt->buffer;
cl->cbuffer = hopt->cbuffer;
- if (cl->rate)
- qdisc_put_rtab(cl->rate);
- cl->rate = rtab;
- if (cl->ceil)
- qdisc_put_rtab(cl->ceil);
- cl->ceil = ctab;
+
+ cl->rate.rate_bps = (u64)hopt->rate.rate << 3;
+ cl->ceil.rate_bps = (u64)hopt->ceil.rate << 3;
+
+ htb_precompute_ratedata(&cl->rate);
+ htb_precompute_ratedata(&cl->ceil);
+
+ cl->buffer = hopt->buffer << PSCHED_SHIFT;
+ cl->cbuffer = hopt->buffer << PSCHED_SHIFT;
+
sch_tree_unlock(sch);
qdisc_class_hash_grow(sch, &q->clhash);
@@ -1474,10 +1523,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
return 0;
failure:
- if (rtab)
- qdisc_put_rtab(rtab);
- if (ctab)
- qdisc_put_rtab(ctab);
return err;
}
diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
index 0a4b2f9a009..5da78a19ac9 100644
--- a/net/sched/sch_mq.c
+++ b/net/sched/sch_mq.c
@@ -63,6 +63,7 @@ static int mq_init(struct Qdisc *sch, struct nlattr *opt)
if (qdisc == NULL)
goto err;
priv->qdiscs[ntx] = qdisc;
+ qdisc->flags |= TCQ_F_ONETXQUEUE;
}
sch->flags |= TCQ_F_MQROOT;
@@ -150,7 +151,8 @@ static int mq_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
dev_deactivate(dev);
*old = dev_graft_qdisc(dev_queue, new);
-
+ if (new)
+ new->flags |= TCQ_F_ONETXQUEUE;
if (dev->flags & IFF_UP)
dev_activate(dev);
return 0;
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index d1831ca966d..accec33c454 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -132,6 +132,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt)
goto err;
}
priv->qdiscs[i] = qdisc;
+ qdisc->flags |= TCQ_F_ONETXQUEUE;
}
/* If the mqprio options indicate that hardware should own
@@ -205,6 +206,9 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
*old = dev_graft_qdisc(dev_queue, new);
+ if (new)
+ new->flags |= TCQ_F_ONETXQUEUE;
+
if (dev->flags & IFF_UP)
dev_activate(dev);
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index f0dd83cff90..6ed37652a4c 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -1,7 +1,8 @@
/*
- * net/sched/sch_qfq.c Quick Fair Queueing Scheduler.
+ * net/sched/sch_qfq.c Quick Fair Queueing Plus Scheduler.
*
* Copyright (c) 2009 Fabio Checconi, Luigi Rizzo, and Paolo Valente.
+ * Copyright (c) 2012 Paolo Valente.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -19,12 +20,18 @@
#include <net/pkt_cls.h>
-/* Quick Fair Queueing
- ===================
+/* Quick Fair Queueing Plus
+ ========================
Sources:
- Fabio Checconi, Luigi Rizzo, and Paolo Valente: "QFQ: Efficient
+ [1] Paolo Valente,
+ "Reducing the Execution Time of Fair-Queueing Schedulers."
+ http://algo.ing.unimo.it/people/paolo/agg-sched/agg-sched.pdf
+
+ Sources for QFQ:
+
+ [2] Fabio Checconi, Luigi Rizzo, and Paolo Valente: "QFQ: Efficient
Packet Scheduling with Tight Bandwidth Distribution Guarantees."
See also:
@@ -33,6 +40,20 @@
/*
+ QFQ+ divides classes into aggregates of at most MAX_AGG_CLASSES
+ classes. Each aggregate is timestamped with a virtual start time S
+ and a virtual finish time F, and scheduled according to its
+ timestamps. S and F are computed as a function of a system virtual
+ time function V. The classes within each aggregate are instead
+ scheduled with DRR.
+
+ To speed up operations, QFQ+ divides also aggregates into a limited
+ number of groups. Which group a class belongs to depends on the
+ ratio between the maximum packet length for the class and the weight
+ of the class. Groups have their own S and F. In the end, QFQ+
+ schedules groups, then aggregates within groups, then classes within
+ aggregates. See [1] and [2] for a full description.
+
Virtual time computations.
S, F and V are all computed in fixed point arithmetic with
@@ -76,26 +97,28 @@
#define QFQ_MAX_SLOTS 32
/*
- * Shifts used for class<->group mapping. We allow class weights that are
- * in the range [1, 2^MAX_WSHIFT], and we try to map each class i to the
+ * Shifts used for aggregate<->group mapping. We allow class weights that are
+ * in the range [1, 2^MAX_WSHIFT], and we try to map each aggregate i to the
* group with the smallest index that can support the L_i / r_i configured
- * for the class.
+ * for the classes in the aggregate.
*
* grp->index is the index of the group; and grp->slot_shift
* is the shift for the corresponding (scaled) sigma_i.
*/
-#define QFQ_MAX_INDEX 19
-#define QFQ_MAX_WSHIFT 16
+#define QFQ_MAX_INDEX 24
+#define QFQ_MAX_WSHIFT 10
-#define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT)
-#define QFQ_MAX_WSUM (2*QFQ_MAX_WEIGHT)
+#define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT) /* see qfq_slot_insert */
+#define QFQ_MAX_WSUM (64*QFQ_MAX_WEIGHT)
#define FRAC_BITS 30 /* fixed point arithmetic */
#define ONE_FP (1UL << FRAC_BITS)
#define IWSUM (ONE_FP/QFQ_MAX_WSUM)
-#define QFQ_MTU_SHIFT 11
-#define QFQ_MIN_SLOT_SHIFT (FRAC_BITS + QFQ_MTU_SHIFT - QFQ_MAX_INDEX)
+#define QFQ_MTU_SHIFT 16 /* to support TSO/GSO */
+#define QFQ_MIN_LMAX 512 /* see qfq_slot_insert */
+
+#define QFQ_MAX_AGG_CLASSES 8 /* max num classes per aggregate allowed */
/*
* Possible group states. These values are used as indexes for the bitmaps
@@ -105,6 +128,8 @@ enum qfq_state { ER, IR, EB, IB, QFQ_MAX_STATE };
struct qfq_group;
+struct qfq_aggregate;
+
struct qfq_class {
struct Qdisc_class_common common;
@@ -115,7 +140,12 @@ struct qfq_class {
struct gnet_stats_queue qstats;
struct gnet_stats_rate_est rate_est;
struct Qdisc *qdisc;
+ struct list_head alist; /* Link for active-classes list. */
+ struct qfq_aggregate *agg; /* Parent aggregate. */
+ int deficit; /* DRR deficit counter. */
+};
+struct qfq_aggregate {
struct hlist_node next; /* Link for the slot list. */
u64 S, F; /* flow timestamps (exact) */
@@ -126,8 +156,18 @@ struct qfq_class {
struct qfq_group *grp;
/* these are copied from the flowset. */
- u32 inv_w; /* ONE_FP/weight */
- u32 lmax; /* Max packet size for this flow. */
+ u32 class_weight; /* Weight of each class in this aggregate. */
+ /* Max pkt size for the classes in this aggregate, DRR quantum. */
+ int lmax;
+
+ u32 inv_w; /* ONE_FP/(sum of weights of classes in aggr.). */
+ u32 budgetmax; /* Max budget for this aggregate. */
+ u32 initial_budget, budget; /* Initial and current budget. */
+
+ int num_classes; /* Number of classes in this aggr. */
+ struct list_head active; /* DRR queue of active classes. */
+
+ struct hlist_node nonfull_next; /* See nonfull_aggs in qfq_sched. */
};
struct qfq_group {
@@ -137,7 +177,7 @@ struct qfq_group {
unsigned int front; /* Index of the front slot. */
unsigned long full_slots; /* non-empty slots */
- /* Array of RR lists of active classes. */
+ /* Array of RR lists of active aggregates. */
struct hlist_head slots[QFQ_MAX_SLOTS];
};
@@ -145,13 +185,28 @@ struct qfq_sched {
struct tcf_proto *filter_list;
struct Qdisc_class_hash clhash;
- u64 V; /* Precise virtual time. */
- u32 wsum; /* weight sum */
+ u64 oldV, V; /* Precise virtual times. */
+ struct qfq_aggregate *in_serv_agg; /* Aggregate being served. */
+ u32 num_active_agg; /* Num. of active aggregates */
+ u32 wsum; /* weight sum */
unsigned long bitmaps[QFQ_MAX_STATE]; /* Group bitmaps. */
struct qfq_group groups[QFQ_MAX_INDEX + 1]; /* The groups. */
+ u32 min_slot_shift; /* Index of the group-0 bit in the bitmaps. */
+
+ u32 max_agg_classes; /* Max number of classes per aggr. */
+ struct hlist_head nonfull_aggs; /* Aggs with room for more classes. */
};
+/*
+ * Possible reasons why the timestamps of an aggregate are updated
+ * enqueue: the aggregate switches from idle to active and must scheduled
+ * for service
+ * requeue: the aggregate finishes its budget, so it stops being served and
+ * must be rescheduled for service
+ */
+enum update_reason {enqueue, requeue};
+
static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
{
struct qfq_sched *q = qdisc_priv(sch);
@@ -181,18 +236,18 @@ static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = {
* index = log_2(maxlen/weight) but we need to apply the scaling.
* This is used only once at flow creation.
*/
-static int qfq_calc_index(u32 inv_w, unsigned int maxlen)
+static int qfq_calc_index(u32 inv_w, unsigned int maxlen, u32 min_slot_shift)
{
u64 slot_size = (u64)maxlen * inv_w;
unsigned long size_map;
int index = 0;
- size_map = slot_size >> QFQ_MIN_SLOT_SHIFT;
+ size_map = slot_size >> min_slot_shift;
if (!size_map)
goto out;
index = __fls(size_map) + 1; /* basically a log_2 */
- index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1)));
+ index -= !(slot_size - (1ULL << (index + min_slot_shift - 1)));
if (index < 0)
index = 0;
@@ -203,32 +258,140 @@ out:
return index;
}
-/* Length of the next packet (0 if the queue is empty). */
-static unsigned int qdisc_peek_len(struct Qdisc *sch)
+static void qfq_deactivate_agg(struct qfq_sched *, struct qfq_aggregate *);
+static void qfq_activate_agg(struct qfq_sched *, struct qfq_aggregate *,
+ enum update_reason);
+
+static void qfq_init_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
+ u32 lmax, u32 weight)
{
- struct sk_buff *skb;
+ INIT_LIST_HEAD(&agg->active);
+ hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs);
+
+ agg->lmax = lmax;
+ agg->class_weight = weight;
+}
+
+static struct qfq_aggregate *qfq_find_agg(struct qfq_sched *q,
+ u32 lmax, u32 weight)
+{
+ struct qfq_aggregate *agg;
+ struct hlist_node *n;
+
+ hlist_for_each_entry(agg, n, &q->nonfull_aggs, nonfull_next)
+ if (agg->lmax == lmax && agg->class_weight == weight)
+ return agg;
+
+ return NULL;
+}
+
+
+/* Update aggregate as a function of the new number of classes. */
+static void qfq_update_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
+ int new_num_classes)
+{
+ u32 new_agg_weight;
- skb = sch->ops->peek(sch);
- return skb ? qdisc_pkt_len(skb) : 0;
+ if (new_num_classes == q->max_agg_classes)
+ hlist_del_init(&agg->nonfull_next);
+
+ if (agg->num_classes > new_num_classes &&
+ new_num_classes == q->max_agg_classes - 1) /* agg no more full */
+ hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs);
+
+ agg->budgetmax = new_num_classes * agg->lmax;
+ new_agg_weight = agg->class_weight * new_num_classes;
+ agg->inv_w = ONE_FP/new_agg_weight;
+
+ if (agg->grp == NULL) {
+ int i = qfq_calc_index(agg->inv_w, agg->budgetmax,
+ q->min_slot_shift);
+ agg->grp = &q->groups[i];
+ }
+
+ q->wsum +=
+ (int) agg->class_weight * (new_num_classes - agg->num_classes);
+
+ agg->num_classes = new_num_classes;
+}
+
+/* Add class to aggregate. */
+static void qfq_add_to_agg(struct qfq_sched *q,
+ struct qfq_aggregate *agg,
+ struct qfq_class *cl)
+{
+ cl->agg = agg;
+
+ qfq_update_agg(q, agg, agg->num_classes+1);
+ if (cl->qdisc->q.qlen > 0) { /* adding an active class */
+ list_add_tail(&cl->alist, &agg->active);
+ if (list_first_entry(&agg->active, struct qfq_class, alist) ==
+ cl && q->in_serv_agg != agg) /* agg was inactive */
+ qfq_activate_agg(q, agg, enqueue); /* schedule agg */
+ }
+}
+
+static struct qfq_aggregate *qfq_choose_next_agg(struct qfq_sched *);
+
+static void qfq_destroy_agg(struct qfq_sched *q, struct qfq_aggregate *agg)
+{
+ if (!hlist_unhashed(&agg->nonfull_next))
+ hlist_del_init(&agg->nonfull_next);
+ if (q->in_serv_agg == agg)
+ q->in_serv_agg = qfq_choose_next_agg(q);
+ kfree(agg);
}
-static void qfq_deactivate_class(struct qfq_sched *, struct qfq_class *);
-static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
- unsigned int len);
+/* Deschedule class from within its parent aggregate. */
+static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl)
+{
+ struct qfq_aggregate *agg = cl->agg;
-static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl,
- u32 lmax, u32 inv_w, int delta_w)
+
+ list_del(&cl->alist); /* remove from RR queue of the aggregate */
+ if (list_empty(&agg->active)) /* agg is now inactive */
+ qfq_deactivate_agg(q, agg);
+}
+
+/* Remove class from its parent aggregate. */
+static void qfq_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
{
- int i;
+ struct qfq_aggregate *agg = cl->agg;
- /* update qfq-specific data */
- cl->lmax = lmax;
- cl->inv_w = inv_w;
- i = qfq_calc_index(cl->inv_w, cl->lmax);
+ cl->agg = NULL;
+ if (agg->num_classes == 1) { /* agg being emptied, destroy it */
+ qfq_destroy_agg(q, agg);
+ return;
+ }
+ qfq_update_agg(q, agg, agg->num_classes-1);
+}
- cl->grp = &q->groups[i];
+/* Deschedule class and remove it from its parent aggregate. */
+static void qfq_deact_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl)
+{
+ if (cl->qdisc->q.qlen > 0) /* class is active */
+ qfq_deactivate_class(q, cl);
- q->wsum += delta_w;
+ qfq_rm_from_agg(q, cl);
+}
+
+/* Move class to a new aggregate, matching the new class weight and/or lmax */
+static int qfq_change_agg(struct Qdisc *sch, struct qfq_class *cl, u32 weight,
+ u32 lmax)
+{
+ struct qfq_sched *q = qdisc_priv(sch);
+ struct qfq_aggregate *new_agg = qfq_find_agg(q, lmax, weight);
+
+ if (new_agg == NULL) { /* create new aggregate */
+ new_agg = kzalloc(sizeof(*new_agg), GFP_ATOMIC);
+ if (new_agg == NULL)
+ return -ENOBUFS;
+ qfq_init_agg(q, new_agg, lmax, weight);
+ }
+ qfq_deact_rm_from_agg(q, cl);
+ qfq_add_to_agg(q, new_agg, cl);
+
+ return 0;
}
static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
@@ -236,9 +399,11 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
{
struct qfq_sched *q = qdisc_priv(sch);
struct qfq_class *cl = (struct qfq_class *)*arg;
+ bool existing = false;
struct nlattr *tb[TCA_QFQ_MAX + 1];
+ struct qfq_aggregate *new_agg = NULL;
u32 weight, lmax, inv_w;
- int i, err;
+ int err;
int delta_w;
if (tca[TCA_OPTIONS] == NULL) {
@@ -259,27 +424,32 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
} else
weight = 1;
- inv_w = ONE_FP / weight;
- weight = ONE_FP / inv_w;
- delta_w = weight - (cl ? ONE_FP / cl->inv_w : 0);
- if (q->wsum + delta_w > QFQ_MAX_WSUM) {
- pr_notice("qfq: total weight out of range (%u + %u)\n",
- delta_w, q->wsum);
- return -EINVAL;
- }
-
if (tb[TCA_QFQ_LMAX]) {
lmax = nla_get_u32(tb[TCA_QFQ_LMAX]);
- if (!lmax || lmax > (1UL << QFQ_MTU_SHIFT)) {
+ if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) {
pr_notice("qfq: invalid max length %u\n", lmax);
return -EINVAL;
}
} else
- lmax = 1UL << QFQ_MTU_SHIFT;
+ lmax = psched_mtu(qdisc_dev(sch));
+
+ inv_w = ONE_FP / weight;
+ weight = ONE_FP / inv_w;
- if (cl != NULL) {
- bool need_reactivation = false;
+ if (cl != NULL &&
+ lmax == cl->agg->lmax &&
+ weight == cl->agg->class_weight)
+ return 0; /* nothing to change */
+ delta_w = weight - (cl ? cl->agg->class_weight : 0);
+
+ if (q->wsum + delta_w > QFQ_MAX_WSUM) {
+ pr_notice("qfq: total weight out of range (%d + %u)\n",
+ delta_w, q->wsum);
+ return -EINVAL;
+ }
+
+ if (cl != NULL) { /* modify existing class */
if (tca[TCA_RATE]) {
err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
qdisc_root_sleeping_lock(sch),
@@ -287,41 +457,18 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
if (err)
return err;
}
-
- if (lmax == cl->lmax && inv_w == cl->inv_w)
- return 0; /* nothing to update */
-
- i = qfq_calc_index(inv_w, lmax);
- sch_tree_lock(sch);
- if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) {
- /*
- * shift cl->F back, to not charge the
- * class for the not-yet-served head
- * packet
- */
- cl->F = cl->S;
- /* remove class from its slot in the old group */
- qfq_deactivate_class(q, cl);
- need_reactivation = true;
- }
-
- qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
-
- if (need_reactivation) /* activate in new group */
- qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc));
- sch_tree_unlock(sch);
-
- return 0;
+ existing = true;
+ goto set_change_agg;
}
+ /* create and init new class */
cl = kzalloc(sizeof(struct qfq_class), GFP_KERNEL);
if (cl == NULL)
return -ENOBUFS;
cl->refcnt = 1;
cl->common.classid = classid;
-
- qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
+ cl->deficit = lmax;
cl->qdisc = qdisc_create_dflt(sch->dev_queue,
&pfifo_qdisc_ops, classid);
@@ -332,11 +479,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
err = gen_new_estimator(&cl->bstats, &cl->rate_est,
qdisc_root_sleeping_lock(sch),
tca[TCA_RATE]);
- if (err) {
- qdisc_destroy(cl->qdisc);
- kfree(cl);
- return err;
- }
+ if (err)
+ goto destroy_class;
}
sch_tree_lock(sch);
@@ -345,19 +489,39 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
qdisc_class_hash_grow(sch, &q->clhash);
+set_change_agg:
+ sch_tree_lock(sch);
+ new_agg = qfq_find_agg(q, lmax, weight);
+ if (new_agg == NULL) { /* create new aggregate */
+ sch_tree_unlock(sch);
+ new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL);
+ if (new_agg == NULL) {
+ err = -ENOBUFS;
+ gen_kill_estimator(&cl->bstats, &cl->rate_est);
+ goto destroy_class;
+ }
+ sch_tree_lock(sch);
+ qfq_init_agg(q, new_agg, lmax, weight);
+ }
+ if (existing)
+ qfq_deact_rm_from_agg(q, cl);
+ qfq_add_to_agg(q, new_agg, cl);
+ sch_tree_unlock(sch);
+
*arg = (unsigned long)cl;
return 0;
+
+destroy_class:
+ qdisc_destroy(cl->qdisc);
+ kfree(cl);
+ return err;
}
static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
{
struct qfq_sched *q = qdisc_priv(sch);
- if (cl->inv_w) {
- q->wsum -= ONE_FP / cl->inv_w;
- cl->inv_w = 0;
- }
-
+ qfq_rm_from_agg(q, cl);
gen_kill_estimator(&cl->bstats, &cl->rate_est);
qdisc_destroy(cl->qdisc);
kfree(cl);
@@ -472,8 +636,8 @@ static int qfq_dump_class(struct Qdisc *sch, unsigned long arg,
nest = nla_nest_start(skb, TCA_OPTIONS);
if (nest == NULL)
goto nla_put_failure;
- if (nla_put_u32(skb, TCA_QFQ_WEIGHT, ONE_FP/cl->inv_w) ||
- nla_put_u32(skb, TCA_QFQ_LMAX, cl->lmax))
+ if (nla_put_u32(skb, TCA_QFQ_WEIGHT, cl->agg->class_weight) ||
+ nla_put_u32(skb, TCA_QFQ_LMAX, cl->agg->lmax))
goto nla_put_failure;
return nla_nest_end(skb, nest);
@@ -491,8 +655,8 @@ static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
memset(&xstats, 0, sizeof(xstats));
cl->qdisc->qstats.qlen = cl->qdisc->q.qlen;
- xstats.weight = ONE_FP/cl->inv_w;
- xstats.lmax = cl->lmax;
+ xstats.weight = cl->agg->class_weight;
+ xstats.lmax = cl->agg->lmax;
if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
@@ -643,16 +807,16 @@ static void qfq_unblock_groups(struct qfq_sched *q, int index, u64 old_F)
* perhaps
*
old_V ^= q->V;
- old_V >>= QFQ_MIN_SLOT_SHIFT;
+ old_V >>= q->min_slot_shift;
if (old_V) {
...
}
*
*/
-static void qfq_make_eligible(struct qfq_sched *q, u64 old_V)
+static void qfq_make_eligible(struct qfq_sched *q)
{
- unsigned long vslot = q->V >> QFQ_MIN_SLOT_SHIFT;
- unsigned long old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT;
+ unsigned long vslot = q->V >> q->min_slot_shift;
+ unsigned long old_vslot = q->oldV >> q->min_slot_shift;
if (vslot != old_vslot) {
unsigned long mask = (1UL << fls(vslot ^ old_vslot)) - 1;
@@ -663,25 +827,62 @@ static void qfq_make_eligible(struct qfq_sched *q, u64 old_V)
/*
- * XXX we should make sure that slot becomes less than 32.
- * This is guaranteed by the input values.
- * roundedS is always cl->S rounded on grp->slot_shift bits.
+ * The index of the slot in which the aggregate is to be inserted must
+ * not be higher than QFQ_MAX_SLOTS-2. There is a '-2' and not a '-1'
+ * because the start time of the group may be moved backward by one
+ * slot after the aggregate has been inserted, and this would cause
+ * non-empty slots to be right-shifted by one position.
+ *
+ * If the weight and lmax (max_pkt_size) of the classes do not change,
+ * then QFQ+ does meet the above contraint according to the current
+ * values of its parameters. In fact, if the weight and lmax of the
+ * classes do not change, then, from the theory, QFQ+ guarantees that
+ * the slot index is never higher than
+ * 2 + QFQ_MAX_AGG_CLASSES * ((1<<QFQ_MTU_SHIFT)/QFQ_MIN_LMAX) *
+ * (QFQ_MAX_WEIGHT/QFQ_MAX_WSUM) = 2 + 8 * 128 * (1 / 64) = 18
+ *
+ * When the weight of a class is increased or the lmax of the class is
+ * decreased, a new aggregate with smaller slot size than the original
+ * parent aggregate of the class may happen to be activated. The
+ * activation of this aggregate should be properly delayed to when the
+ * service of the class has finished in the ideal system tracked by
+ * QFQ+. If the activation of the aggregate is not delayed to this
+ * reference time instant, then this aggregate may be unjustly served
+ * before other aggregates waiting for service. This may cause the
+ * above bound to the slot index to be violated for some of these
+ * unlucky aggregates.
+ *
+ * Instead of delaying the activation of the new aggregate, which is
+ * quite complex, the following inaccurate but simple solution is used:
+ * if the slot index is higher than QFQ_MAX_SLOTS-2, then the
+ * timestamps of the aggregate are shifted backward so as to let the
+ * slot index become equal to QFQ_MAX_SLOTS-2.
*/
-static void qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl,
+static void qfq_slot_insert(struct qfq_group *grp, struct qfq_aggregate *agg,
u64 roundedS)
{
u64 slot = (roundedS - grp->S) >> grp->slot_shift;
- unsigned int i = (grp->front + slot) % QFQ_MAX_SLOTS;
+ unsigned int i; /* slot index in the bucket list */
+
+ if (unlikely(slot > QFQ_MAX_SLOTS - 2)) {
+ u64 deltaS = roundedS - grp->S -
+ ((u64)(QFQ_MAX_SLOTS - 2)<<grp->slot_shift);
+ agg->S -= deltaS;
+ agg->F -= deltaS;
+ slot = QFQ_MAX_SLOTS - 2;
+ }
- hlist_add_head(&cl->next, &grp->slots[i]);
+ i = (grp->front + slot) % QFQ_MAX_SLOTS;
+
+ hlist_add_head(&agg->next, &grp->slots[i]);
__set_bit(slot, &grp->full_slots);
}
/* Maybe introduce hlist_first_entry?? */
-static struct qfq_class *qfq_slot_head(struct qfq_group *grp)
+static struct qfq_aggregate *qfq_slot_head(struct qfq_group *grp)
{
return hlist_entry(grp->slots[grp->front].first,
- struct qfq_class, next);
+ struct qfq_aggregate, next);
}
/*
@@ -689,20 +890,20 @@ static struct qfq_class *qfq_slot_head(struct qfq_group *grp)
*/
static void qfq_front_slot_remove(struct qfq_group *grp)
{
- struct qfq_class *cl = qfq_slot_head(grp);
+ struct qfq_aggregate *agg = qfq_slot_head(grp);
- BUG_ON(!cl);
- hlist_del(&cl->next);
+ BUG_ON(!agg);
+ hlist_del(&agg->next);
if (hlist_empty(&grp->slots[grp->front]))
__clear_bit(0, &grp->full_slots);
}
/*
- * Returns the first full queue in a group. As a side effect,
- * adjust the bucket list so the first non-empty bucket is at
- * position 0 in full_slots.
+ * Returns the first aggregate in the first non-empty bucket of the
+ * group. As a side effect, adjusts the bucket list so the first
+ * non-empty bucket is at position 0 in full_slots.
*/
-static struct qfq_class *qfq_slot_scan(struct qfq_group *grp)
+static struct qfq_aggregate *qfq_slot_scan(struct qfq_group *grp)
{
unsigned int i;
@@ -738,7 +939,7 @@ static void qfq_slot_rotate(struct qfq_group *grp, u64 roundedS)
grp->front = (grp->front - i) % QFQ_MAX_SLOTS;
}
-static void qfq_update_eligible(struct qfq_sched *q, u64 old_V)
+static void qfq_update_eligible(struct qfq_sched *q)
{
struct qfq_group *grp;
unsigned long ineligible;
@@ -750,137 +951,226 @@ static void qfq_update_eligible(struct qfq_sched *q, u64 old_V)
if (qfq_gt(grp->S, q->V))
q->V = grp->S;
}
- qfq_make_eligible(q, old_V);
+ qfq_make_eligible(q);
}
}
-/*
- * Updates the class, returns true if also the group needs to be updated.
- */
-static bool qfq_update_class(struct qfq_group *grp, struct qfq_class *cl)
+/* Dequeue head packet of the head class in the DRR queue of the aggregate. */
+static void agg_dequeue(struct qfq_aggregate *agg,
+ struct qfq_class *cl, unsigned int len)
{
- unsigned int len = qdisc_peek_len(cl->qdisc);
+ qdisc_dequeue_peeked(cl->qdisc);
- cl->S = cl->F;
- if (!len)
- qfq_front_slot_remove(grp); /* queue is empty */
- else {
- u64 roundedS;
-
- cl->F = cl->S + (u64)len * cl->inv_w;
- roundedS = qfq_round_down(cl->S, grp->slot_shift);
- if (roundedS == grp->S)
- return false;
+ cl->deficit -= (int) len;
- qfq_front_slot_remove(grp);
- qfq_slot_insert(grp, cl, roundedS);
+ if (cl->qdisc->q.qlen == 0) /* no more packets, remove from list */
+ list_del(&cl->alist);
+ else if (cl->deficit < qdisc_pkt_len(cl->qdisc->ops->peek(cl->qdisc))) {
+ cl->deficit += agg->lmax;
+ list_move_tail(&cl->alist, &agg->active);
}
+}
- return true;
+static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg,
+ struct qfq_class **cl,
+ unsigned int *len)
+{
+ struct sk_buff *skb;
+
+ *cl = list_first_entry(&agg->active, struct qfq_class, alist);
+ skb = (*cl)->qdisc->ops->peek((*cl)->qdisc);
+ if (skb == NULL)
+ WARN_ONCE(1, "qfq_dequeue: non-workconserving leaf\n");
+ else
+ *len = qdisc_pkt_len(skb);
+
+ return skb;
+}
+
+/* Update F according to the actual service received by the aggregate. */
+static inline void charge_actual_service(struct qfq_aggregate *agg)
+{
+ /* compute the service received by the aggregate */
+ u32 service_received = agg->initial_budget - agg->budget;
+
+ agg->F = agg->S + (u64)service_received * agg->inv_w;
}
static struct sk_buff *qfq_dequeue(struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
- struct qfq_group *grp;
+ struct qfq_aggregate *in_serv_agg = q->in_serv_agg;
struct qfq_class *cl;
- struct sk_buff *skb;
- unsigned int len;
- u64 old_V;
+ struct sk_buff *skb = NULL;
+ /* next-packet len, 0 means no more active classes in in-service agg */
+ unsigned int len = 0;
- if (!q->bitmaps[ER])
+ if (in_serv_agg == NULL)
return NULL;
- grp = qfq_ffs(q, q->bitmaps[ER]);
+ if (!list_empty(&in_serv_agg->active))
+ skb = qfq_peek_skb(in_serv_agg, &cl, &len);
- cl = qfq_slot_head(grp);
- skb = qdisc_dequeue_peeked(cl->qdisc);
- if (!skb) {
- WARN_ONCE(1, "qfq_dequeue: non-workconserving leaf\n");
- return NULL;
+ /*
+ * If there are no active classes in the in-service aggregate,
+ * or if the aggregate has not enough budget to serve its next
+ * class, then choose the next aggregate to serve.
+ */
+ if (len == 0 || in_serv_agg->budget < len) {
+ charge_actual_service(in_serv_agg);
+
+ /* recharge the budget of the aggregate */
+ in_serv_agg->initial_budget = in_serv_agg->budget =
+ in_serv_agg->budgetmax;
+
+ if (!list_empty(&in_serv_agg->active))
+ /*
+ * Still active: reschedule for
+ * service. Possible optimization: if no other
+ * aggregate is active, then there is no point
+ * in rescheduling this aggregate, and we can
+ * just keep it as the in-service one. This
+ * should be however a corner case, and to
+ * handle it, we would need to maintain an
+ * extra num_active_aggs field.
+ */
+ qfq_activate_agg(q, in_serv_agg, requeue);
+ else if (sch->q.qlen == 0) { /* no aggregate to serve */
+ q->in_serv_agg = NULL;
+ return NULL;
+ }
+
+ /*
+ * If we get here, there are other aggregates queued:
+ * choose the new aggregate to serve.
+ */
+ in_serv_agg = q->in_serv_agg = qfq_choose_next_agg(q);
+ skb = qfq_peek_skb(in_serv_agg, &cl, &len);
}
+ if (!skb)
+ return NULL;
sch->q.qlen--;
qdisc_bstats_update(sch, skb);
- old_V = q->V;
- len = qdisc_pkt_len(skb);
+ agg_dequeue(in_serv_agg, cl, len);
+ in_serv_agg->budget -= len;
q->V += (u64)len * IWSUM;
pr_debug("qfq dequeue: len %u F %lld now %lld\n",
- len, (unsigned long long) cl->F, (unsigned long long) q->V);
+ len, (unsigned long long) in_serv_agg->F,
+ (unsigned long long) q->V);
- if (qfq_update_class(grp, cl)) {
- u64 old_F = grp->F;
+ return skb;
+}
- cl = qfq_slot_scan(grp);
- if (!cl)
- __clear_bit(grp->index, &q->bitmaps[ER]);
- else {
- u64 roundedS = qfq_round_down(cl->S, grp->slot_shift);
- unsigned int s;
+static struct qfq_aggregate *qfq_choose_next_agg(struct qfq_sched *q)
+{
+ struct qfq_group *grp;
+ struct qfq_aggregate *agg, *new_front_agg;
+ u64 old_F;
- if (grp->S == roundedS)
- goto skip_unblock;
- grp->S = roundedS;
- grp->F = roundedS + (2ULL << grp->slot_shift);
- __clear_bit(grp->index, &q->bitmaps[ER]);
- s = qfq_calc_state(q, grp);
- __set_bit(grp->index, &q->bitmaps[s]);
- }
+ qfq_update_eligible(q);
+ q->oldV = q->V;
- qfq_unblock_groups(q, grp->index, old_F);
+ if (!q->bitmaps[ER])
+ return NULL;
+
+ grp = qfq_ffs(q, q->bitmaps[ER]);
+ old_F = grp->F;
+
+ agg = qfq_slot_head(grp);
+
+ /* agg starts to be served, remove it from schedule */
+ qfq_front_slot_remove(grp);
+
+ new_front_agg = qfq_slot_scan(grp);
+
+ if (new_front_agg == NULL) /* group is now inactive, remove from ER */
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ else {
+ u64 roundedS = qfq_round_down(new_front_agg->S,
+ grp->slot_shift);
+ unsigned int s;
+
+ if (grp->S == roundedS)
+ return agg;
+ grp->S = roundedS;
+ grp->F = roundedS + (2ULL << grp->slot_shift);
+ __clear_bit(grp->index, &q->bitmaps[ER]);
+ s = qfq_calc_state(q, grp);
+ __set_bit(grp->index, &q->bitmaps[s]);
}
-skip_unblock:
- qfq_update_eligible(q, old_V);
+ qfq_unblock_groups(q, grp->index, old_F);
- return skb;
+ return agg;
}
/*
- * Assign a reasonable start time for a new flow k in group i.
+ * Assign a reasonable start time for a new aggregate in group i.
* Admissible values for \hat(F) are multiples of \sigma_i
* no greater than V+\sigma_i . Larger values mean that
* we had a wraparound so we consider the timestamp to be stale.
*
* If F is not stale and F >= V then we set S = F.
* Otherwise we should assign S = V, but this may violate
- * the ordering in ER. So, if we have groups in ER, set S to
- * the F_j of the first group j which would be blocking us.
+ * the ordering in EB (see [2]). So, if we have groups in ER,
+ * set S to the F_j of the first group j which would be blocking us.
* We are guaranteed not to move S backward because
* otherwise our group i would still be blocked.
*/
-static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
+static void qfq_update_start(struct qfq_sched *q, struct qfq_aggregate *agg)
{
unsigned long mask;
u64 limit, roundedF;
- int slot_shift = cl->grp->slot_shift;
+ int slot_shift = agg->grp->slot_shift;
- roundedF = qfq_round_down(cl->F, slot_shift);
+ roundedF = qfq_round_down(agg->F, slot_shift);
limit = qfq_round_down(q->V, slot_shift) + (1ULL << slot_shift);
- if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) {
+ if (!qfq_gt(agg->F, q->V) || qfq_gt(roundedF, limit)) {
/* timestamp was stale */
- mask = mask_from(q->bitmaps[ER], cl->grp->index);
+ mask = mask_from(q->bitmaps[ER], agg->grp->index);
if (mask) {
struct qfq_group *next = qfq_ffs(q, mask);
if (qfq_gt(roundedF, next->F)) {
if (qfq_gt(limit, next->F))
- cl->S = next->F;
+ agg->S = next->F;
else /* preserve timestamp correctness */
- cl->S = limit;
+ agg->S = limit;
return;
}
}
- cl->S = q->V;
+ agg->S = q->V;
} else /* timestamp is not stale */
- cl->S = cl->F;
+ agg->S = agg->F;
+}
+
+/*
+ * Update the timestamps of agg before scheduling/rescheduling it for
+ * service. In particular, assign to agg->F its maximum possible
+ * value, i.e., the virtual finish time with which the aggregate
+ * should be labeled if it used all its budget once in service.
+ */
+static inline void
+qfq_update_agg_ts(struct qfq_sched *q,
+ struct qfq_aggregate *agg, enum update_reason reason)
+{
+ if (reason != requeue)
+ qfq_update_start(q, agg);
+ else /* just charge agg for the service received */
+ agg->S = agg->F;
+
+ agg->F = agg->S + (u64)agg->budgetmax * agg->inv_w;
}
+static void qfq_schedule_agg(struct qfq_sched *, struct qfq_aggregate *);
+
static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
struct qfq_class *cl;
+ struct qfq_aggregate *agg;
int err = 0;
cl = qfq_classify(skb, sch, &err);
@@ -892,6 +1182,15 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
}
pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid);
+ if (unlikely(cl->agg->lmax < qdisc_pkt_len(skb))) {
+ pr_debug("qfq: increasing maxpkt from %u to %u for class %u",
+ cl->agg->lmax, qdisc_pkt_len(skb), cl->common.classid);
+ err = qfq_change_agg(sch, cl, cl->agg->class_weight,
+ qdisc_pkt_len(skb));
+ if (err)
+ return err;
+ }
+
err = qdisc_enqueue(skb, cl->qdisc);
if (unlikely(err != NET_XMIT_SUCCESS)) {
pr_debug("qfq_enqueue: enqueue failed %d\n", err);
@@ -905,35 +1204,50 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
bstats_update(&cl->bstats, skb);
++sch->q.qlen;
- /* If the new skb is not the head of queue, then done here. */
- if (cl->qdisc->q.qlen != 1)
+ agg = cl->agg;
+ /* if the queue was not empty, then done here */
+ if (cl->qdisc->q.qlen != 1) {
+ if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) &&
+ list_first_entry(&agg->active, struct qfq_class, alist)
+ == cl && cl->deficit < qdisc_pkt_len(skb))
+ list_move_tail(&cl->alist, &agg->active);
+
return err;
+ }
- /* If reach this point, queue q was idle */
- qfq_activate_class(q, cl, qdisc_pkt_len(skb));
+ /* schedule class for service within the aggregate */
+ cl->deficit = agg->lmax;
+ list_add_tail(&cl->alist, &agg->active);
+
+ if (list_first_entry(&agg->active, struct qfq_class, alist) != cl)
+ return err; /* aggregate was not empty, nothing else to do */
+
+ /* recharge budget */
+ agg->initial_budget = agg->budget = agg->budgetmax;
+
+ qfq_update_agg_ts(q, agg, enqueue);
+ if (q->in_serv_agg == NULL)
+ q->in_serv_agg = agg;
+ else if (agg != q->in_serv_agg)
+ qfq_schedule_agg(q, agg);
return err;
}
/*
- * Handle class switch from idle to backlogged.
+ * Schedule aggregate according to its timestamps.
*/
-static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
- unsigned int pkt_len)
+static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg)
{
- struct qfq_group *grp = cl->grp;
+ struct qfq_group *grp = agg->grp;
u64 roundedS;
int s;
- qfq_update_start(q, cl);
-
- /* compute new finish time and rounded start. */
- cl->F = cl->S + (u64)pkt_len * cl->inv_w;
- roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ roundedS = qfq_round_down(agg->S, grp->slot_shift);
/*
- * insert cl in the correct bucket.
- * If cl->S >= grp->S we don't need to adjust the
+ * Insert agg in the correct bucket.
+ * If agg->S >= grp->S we don't need to adjust the
* bucket list and simply go to the insertion phase.
* Otherwise grp->S is decreasing, we must make room
* in the bucket list, and also recompute the group state.
@@ -941,10 +1255,10 @@ static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
* was in ER make sure to adjust V.
*/
if (grp->full_slots) {
- if (!qfq_gt(grp->S, cl->S))
+ if (!qfq_gt(grp->S, agg->S))
goto skip_update;
- /* create a slot for this cl->S */
+ /* create a slot for this agg->S */
qfq_slot_rotate(grp, roundedS);
/* group was surely ineligible, remove */
__clear_bit(grp->index, &q->bitmaps[IR]);
@@ -959,46 +1273,61 @@ static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
pr_debug("qfq enqueue: new state %d %#lx S %lld F %lld V %lld\n",
s, q->bitmaps[s],
- (unsigned long long) cl->S,
- (unsigned long long) cl->F,
+ (unsigned long long) agg->S,
+ (unsigned long long) agg->F,
(unsigned long long) q->V);
skip_update:
- qfq_slot_insert(grp, cl, roundedS);
+ qfq_slot_insert(grp, agg, roundedS);
}
+/* Update agg ts and schedule agg for service */
+static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg,
+ enum update_reason reason)
+{
+ qfq_update_agg_ts(q, agg, reason);
+ qfq_schedule_agg(q, agg);
+}
+
static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp,
- struct qfq_class *cl)
+ struct qfq_aggregate *agg)
{
unsigned int i, offset;
u64 roundedS;
- roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ roundedS = qfq_round_down(agg->S, grp->slot_shift);
offset = (roundedS - grp->S) >> grp->slot_shift;
+
i = (grp->front + offset) % QFQ_MAX_SLOTS;
- hlist_del(&cl->next);
+ hlist_del(&agg->next);
if (hlist_empty(&grp->slots[i]))
__clear_bit(offset, &grp->full_slots);
}
/*
- * called to forcibly destroy a queue.
- * If the queue is not in the front bucket, or if it has
- * other queues in the front bucket, we can simply remove
- * the queue with no other side effects.
+ * Called to forcibly deschedule an aggregate. If the aggregate is
+ * not in the front bucket, or if the latter has other aggregates in
+ * the front bucket, we can simply remove the aggregate with no other
+ * side effects.
* Otherwise we must propagate the event up.
*/
-static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl)
+static void qfq_deactivate_agg(struct qfq_sched *q, struct qfq_aggregate *agg)
{
- struct qfq_group *grp = cl->grp;
+ struct qfq_group *grp = agg->grp;
unsigned long mask;
u64 roundedS;
int s;
- cl->F = cl->S;
- qfq_slot_remove(q, grp, cl);
+ if (agg == q->in_serv_agg) {
+ charge_actual_service(agg);
+ q->in_serv_agg = qfq_choose_next_agg(q);
+ return;
+ }
+
+ agg->F = agg->S;
+ qfq_slot_remove(q, grp, agg);
if (!grp->full_slots) {
__clear_bit(grp->index, &q->bitmaps[IR]);
@@ -1017,8 +1346,8 @@ static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl)
}
__clear_bit(grp->index, &q->bitmaps[ER]);
} else if (hlist_empty(&grp->slots[grp->front])) {
- cl = qfq_slot_scan(grp);
- roundedS = qfq_round_down(cl->S, grp->slot_shift);
+ agg = qfq_slot_scan(grp);
+ roundedS = qfq_round_down(agg->S, grp->slot_shift);
if (grp->S != roundedS) {
__clear_bit(grp->index, &q->bitmaps[ER]);
__clear_bit(grp->index, &q->bitmaps[IR]);
@@ -1031,7 +1360,7 @@ static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl)
}
}
- qfq_update_eligible(q, q->V);
+ qfq_update_eligible(q);
}
static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg)
@@ -1043,6 +1372,32 @@ static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg)
qfq_deactivate_class(q, cl);
}
+static unsigned int qfq_drop_from_slot(struct qfq_sched *q,
+ struct hlist_head *slot)
+{
+ struct qfq_aggregate *agg;
+ struct hlist_node *n;
+ struct qfq_class *cl;
+ unsigned int len;
+
+ hlist_for_each_entry(agg, n, slot, next) {
+ list_for_each_entry(cl, &agg->active, alist) {
+
+ if (!cl->qdisc->ops->drop)
+ continue;
+
+ len = cl->qdisc->ops->drop(cl->qdisc);
+ if (len > 0) {
+ if (cl->qdisc->q.qlen == 0)
+ qfq_deactivate_class(q, cl);
+
+ return len;
+ }
+ }
+ }
+ return 0;
+}
+
static unsigned int qfq_drop(struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
@@ -1052,24 +1407,13 @@ static unsigned int qfq_drop(struct Qdisc *sch)
for (i = 0; i <= QFQ_MAX_INDEX; i++) {
grp = &q->groups[i];
for (j = 0; j < QFQ_MAX_SLOTS; j++) {
- struct qfq_class *cl;
- struct hlist_node *n;
-
- hlist_for_each_entry(cl, n, &grp->slots[j], next) {
-
- if (!cl->qdisc->ops->drop)
- continue;
-
- len = cl->qdisc->ops->drop(cl->qdisc);
- if (len > 0) {
- sch->q.qlen--;
- if (!cl->qdisc->q.qlen)
- qfq_deactivate_class(q, cl);
-
- return len;
- }
+ len = qfq_drop_from_slot(q, &grp->slots[j]);
+ if (len > 0) {
+ sch->q.qlen--;
+ return len;
}
}
+
}
return 0;
@@ -1080,44 +1424,51 @@ static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt)
struct qfq_sched *q = qdisc_priv(sch);
struct qfq_group *grp;
int i, j, err;
+ u32 max_cl_shift, maxbudg_shift, max_classes;
err = qdisc_class_hash_init(&q->clhash);
if (err < 0)
return err;
+ if (qdisc_dev(sch)->tx_queue_len + 1 > QFQ_MAX_AGG_CLASSES)
+ max_classes = QFQ_MAX_AGG_CLASSES;
+ else
+ max_classes = qdisc_dev(sch)->tx_queue_len + 1;
+ /* max_cl_shift = floor(log_2(max_classes)) */
+ max_cl_shift = __fls(max_classes);
+ q->max_agg_classes = 1<<max_cl_shift;
+
+ /* maxbudg_shift = log2(max_len * max_classes_per_agg) */
+ maxbudg_shift = QFQ_MTU_SHIFT + max_cl_shift;
+ q->min_slot_shift = FRAC_BITS + maxbudg_shift - QFQ_MAX_INDEX;
+
for (i = 0; i <= QFQ_MAX_INDEX; i++) {
grp = &q->groups[i];
grp->index = i;
- grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS
- - (QFQ_MAX_INDEX - i);
+ grp->slot_shift = q->min_slot_shift + i;
for (j = 0; j < QFQ_MAX_SLOTS; j++)
INIT_HLIST_HEAD(&grp->slots[j]);
}
+ INIT_HLIST_HEAD(&q->nonfull_aggs);
+
return 0;
}
static void qfq_reset_qdisc(struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
- struct qfq_group *grp;
struct qfq_class *cl;
- struct hlist_node *n, *tmp;
- unsigned int i, j;
+ struct hlist_node *n;
+ unsigned int i;
- for (i = 0; i <= QFQ_MAX_INDEX; i++) {
- grp = &q->groups[i];
- for (j = 0; j < QFQ_MAX_SLOTS; j++) {
- hlist_for_each_entry_safe(cl, n, tmp,
- &grp->slots[j], next) {
+ for (i = 0; i < q->clhash.hashsize; i++) {
+ hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) {
+ if (cl->qdisc->q.qlen > 0)
qfq_deactivate_class(q, cl);
- }
- }
- }
- for (i = 0; i < q->clhash.hashsize; i++) {
- hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode)
qdisc_reset(cl->qdisc);
+ }
}
sch->q.qlen = 0;
}
diff --git a/net/sctp/Kconfig b/net/sctp/Kconfig
index 126b014eb79..7521d944c0f 100644
--- a/net/sctp/Kconfig
+++ b/net/sctp/Kconfig
@@ -9,7 +9,6 @@ menuconfig IP_SCTP
select CRYPTO
select CRYPTO_HMAC
select CRYPTO_SHA1
- select CRYPTO_MD5 if SCTP_HMAC_MD5
select LIBCRC32C
---help---
Stream Control Transmission Protocol
@@ -67,34 +66,45 @@ config SCTP_DBG_OBJCNT
'cat /proc/net/sctp/sctp_dbg_objcnt'
If unsure, say N
-
choice
- prompt "SCTP: Cookie HMAC Algorithm"
- default SCTP_HMAC_MD5
+ prompt "Default SCTP cookie HMAC encoding"
+ default SCTP_DEFAULT_COOKIE_HMAC_MD5
+ help
+ This option sets the default sctp cookie hmac algorithm
+ when in doubt select 'md5'
+
+config SCTP_DEFAULT_COOKIE_HMAC_MD5
+ bool "Enable optional MD5 hmac cookie generation"
help
- HMAC algorithm to be used during association initialization. It
- is strongly recommended to use HMAC-SHA1 or HMAC-MD5. See
- configuration for Cryptographic API and enable those algorithms
- to make usable by SCTP.
-
-config SCTP_HMAC_NONE
- bool "None"
- help
- Choosing this disables the use of an HMAC during association
- establishment. It is advised to use either HMAC-MD5 or HMAC-SHA1.
-
-config SCTP_HMAC_SHA1
- bool "HMAC-SHA1"
- help
- Enable the use of HMAC-SHA1 during association establishment. It
- is advised to use either HMAC-MD5 or HMAC-SHA1.
-
-config SCTP_HMAC_MD5
- bool "HMAC-MD5"
+ Enable optional MD5 hmac based SCTP cookie generation
+ select SCTP_COOKIE_HMAC_MD5
+
+config SCTP_DEFAULT_COOKIE_HMAC_SHA1
+ bool "Enable optional SHA1 hmac cookie generation"
+ help
+ Enable optional SHA1 hmac based SCTP cookie generation
+ select SCTP_COOKIE_HMAC_SHA1
+
+config SCTP_DEFAULT_COOKIE_HMAC_NONE
+ bool "Use no hmac alg in SCTP cookie generation"
help
- Enable the use of HMAC-MD5 during association establishment. It is
- advised to use either HMAC-MD5 or HMAC-SHA1.
+ Use no hmac algorithm in SCTP cookie generation
endchoice
+config SCTP_COOKIE_HMAC_MD5
+ bool "Enable optional MD5 hmac cookie generation"
+ help
+ Enable optional MD5 hmac based SCTP cookie generation
+ select CRYPTO_HMAC if SCTP_COOKIE_HMAC_MD5
+ select CRYPTO_MD5 if SCTP_COOKIE_HMAC_MD5
+
+config SCTP_COOKIE_HMAC_SHA1
+ bool "Enable optional SHA1 hmac cookie generation"
+ help
+ Enable optional SHA1 hmac based SCTP cookie generation
+ select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1
+ select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1
+
+
endif # IP_SCTP
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index b1ef3bc301a..b45ed1f9692 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -321,6 +321,9 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->default_timetolive = sp->default_timetolive;
asoc->default_rcv_context = sp->default_rcv_context;
+ /* SCTP_GET_ASSOC_STATS COUNTERS */
+ memset(&asoc->stats, 0, sizeof(struct sctp_priv_assoc_stats));
+
/* AUTH related initializations */
INIT_LIST_HEAD(&asoc->endpoint_shared_keys);
err = sctp_auth_asoc_copy_shkeys(ep, asoc, gfp);
@@ -445,7 +448,7 @@ void sctp_association_free(struct sctp_association *asoc)
/* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transports);
- list_del(pos);
+ list_del_rcu(pos);
sctp_transport_free(transport);
}
@@ -565,7 +568,7 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc,
sctp_assoc_update_retran_path(asoc);
/* Remove this peer from the list. */
- list_del(&peer->transports);
+ list_del_rcu(&peer->transports);
/* Get the first transport of asoc. */
pos = asoc->peer.transport_addr_list.next;
@@ -760,12 +763,13 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
/* Set the transport's RTO.initial value */
peer->rto = asoc->rto_initial;
+ sctp_max_rto(asoc, peer);
/* Set the peer's active state. */
peer->state = peer_state;
/* Attach the remote transport to our asoc. */
- list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);
+ list_add_tail_rcu(&peer->transports, &asoc->peer.transport_addr_list);
asoc->peer.transport_count++;
/* If we do not yet have a primary path, set one. */
@@ -1152,8 +1156,12 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
*/
if (sctp_chunk_is_data(chunk))
asoc->peer.last_data_from = chunk->transport;
- else
+ else {
SCTP_INC_STATS(net, SCTP_MIB_INCTRLCHUNKS);
+ asoc->stats.ictrlchunks++;
+ if (chunk->chunk_hdr->type == SCTP_CID_SACK)
+ asoc->stats.isacks++;
+ }
if (chunk->transport)
chunk->transport->last_time_heard = jiffies;
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index 7c2df9c33df..69ce21e3716 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -183,7 +183,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
msg = sctp_datamsg_new(GFP_KERNEL);
if (!msg)
- return NULL;
+ return ERR_PTR(-ENOMEM);
/* Note: Calculate this outside of the loop, so that all fragments
* have the same expiration.
@@ -280,11 +280,14 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
- if (!chunk)
+ if (!chunk) {
+ err = -ENOMEM;
goto errout;
+ }
+
err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov);
if (err < 0)
- goto errout;
+ goto errout_chunk_free;
offset += len;
@@ -315,8 +318,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
- if (!chunk)
+ if (!chunk) {
+ err = -ENOMEM;
goto errout;
+ }
err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov);
@@ -324,7 +329,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
if (err < 0)
- goto errout;
+ goto errout_chunk_free;
sctp_datamsg_assign(msg, chunk);
list_add_tail(&chunk->frag_list, &msg->chunks);
@@ -332,6 +337,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
return msg;
+errout_chunk_free:
+ sctp_chunk_free(chunk);
+
errout:
list_for_each_safe(pos, temp, &msg->chunks) {
list_del_init(pos);
@@ -339,7 +347,7 @@ errout:
sctp_chunk_free(chunk);
}
sctp_datamsg_put(msg);
- return NULL;
+ return ERR_PTR(err);
}
/* Check whether this message has expired. */
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index bd7e6a6a815..17a001bac2c 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -480,8 +480,11 @@ normal:
*/
if (asoc && sctp_chunk_is_data(chunk))
asoc->peer.last_data_from = chunk->transport;
- else
+ else {
SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_INCTRLCHUNKS);
+ if (asoc)
+ asoc->stats.ictrlchunks++;
+ }
if (chunk->transport)
chunk->transport->last_time_heard = jiffies;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 397296fb156..2d5ad280de3 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -104,6 +104,8 @@ void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
* on the BH related data structures.
*/
list_add_tail(&chunk->list, &q->in_chunk_list);
+ if (chunk->asoc)
+ chunk->asoc->stats.ipackets++;
q->immediate.func(&q->immediate);
}
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index ea14cb44529..f3f0f4dc31d 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -345,7 +345,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr,
}
out:
- if (!IS_ERR(dst)) {
+ if (!IS_ERR_OR_NULL(dst)) {
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
t->dst = dst;
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 4e90188bf48..f5200a2ad85 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -311,6 +311,8 @@ static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet,
case SCTP_CID_SACK:
packet->has_sack = 1;
+ if (chunk->asoc)
+ chunk->asoc->stats.osacks++;
break;
case SCTP_CID_AUTH:
@@ -584,11 +586,13 @@ int sctp_packet_transmit(struct sctp_packet *packet)
*/
/* Dump that on IP! */
- if (asoc && asoc->peer.last_sent_to != tp) {
- /* Considering the multiple CPU scenario, this is a
- * "correcter" place for last_sent_to. --xguo
- */
- asoc->peer.last_sent_to = tp;
+ if (asoc) {
+ asoc->stats.opackets++;
+ if (asoc->peer.last_sent_to != tp)
+ /* Considering the multiple CPU scenario, this is a
+ * "correcter" place for last_sent_to. --xguo
+ */
+ asoc->peer.last_sent_to = tp;
}
if (has_data) {
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1b4a7f8ec3f..9bcdbd02d77 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -224,7 +224,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
/* Free the outqueue structure and any related pending chunks.
*/
-void sctp_outq_teardown(struct sctp_outq *q)
+static void __sctp_outq_teardown(struct sctp_outq *q)
{
struct sctp_transport *transport;
struct list_head *lchunk, *temp;
@@ -277,8 +277,6 @@ void sctp_outq_teardown(struct sctp_outq *q)
sctp_chunk_free(chunk);
}
- q->error = 0;
-
/* Throw away any leftover control chunks. */
list_for_each_entry_safe(chunk, tmp, &q->control_chunk_list, list) {
list_del_init(&chunk->list);
@@ -286,11 +284,17 @@ void sctp_outq_teardown(struct sctp_outq *q)
}
}
+void sctp_outq_teardown(struct sctp_outq *q)
+{
+ __sctp_outq_teardown(q);
+ sctp_outq_init(q->asoc, q);
+}
+
/* Free the outqueue structure and any related pending chunks. */
void sctp_outq_free(struct sctp_outq *q)
{
/* Throw away leftover chunks. */
- sctp_outq_teardown(q);
+ __sctp_outq_teardown(q);
/* If we were kmalloc()'d, free the memory. */
if (q->malloced)
@@ -667,6 +671,7 @@ redo:
chunk->fast_retransmit = SCTP_DONT_FRTX;
q->empty = 0;
+ q->asoc->stats.rtxchunks++;
break;
}
@@ -876,12 +881,14 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
if (status != SCTP_XMIT_OK) {
/* put the chunk back */
list_add(&chunk->list, &q->control_chunk_list);
- } else if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) {
+ } else {
+ asoc->stats.octrlchunks++;
/* PR-SCTP C5) If a FORWARD TSN is sent, the
* sender MUST assure that at least one T3-rtx
* timer is running.
*/
- sctp_transport_reset_timers(transport);
+ if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN)
+ sctp_transport_reset_timers(transport);
}
break;
@@ -1055,6 +1062,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
*/
if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM;
+ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+ asoc->stats.ouodchunks++;
+ else
+ asoc->stats.oodchunks++;
break;
@@ -1162,6 +1173,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
sack_ctsn = ntohl(sack->cum_tsn_ack);
gap_ack_blocks = ntohs(sack->num_gap_ack_blocks);
+ asoc->stats.gapcnt += gap_ack_blocks;
/*
* SFR-CACC algorithm:
* On receipt of a SACK the sender SHOULD execute the
diff --git a/net/sctp/probe.c b/net/sctp/probe.c
index bc6cd75cc1d..5f7518de2fd 100644
--- a/net/sctp/probe.c
+++ b/net/sctp/probe.c
@@ -122,7 +122,8 @@ static const struct file_operations sctpprobe_fops = {
.llseek = noop_llseek,
};
-sctp_disposition_t jsctp_sf_eat_sack(const struct sctp_endpoint *ep,
+sctp_disposition_t jsctp_sf_eat_sack(struct net *net,
+ const struct sctp_endpoint *ep,
const struct sctp_association *asoc,
const sctp_subtype_t type,
void *arg,
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index c3bea269faf..8c19e97262c 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -102,7 +102,7 @@ static const struct file_operations sctp_snmp_seq_fops = {
.open = sctp_snmp_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = single_release,
+ .release = single_release_net,
};
/* Set up the proc fs entry for 'snmp' object. */
@@ -139,7 +139,11 @@ static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_commo
primary = &peer->saddr;
}
- list_for_each_entry(laddr, &epb->bind_addr.address_list, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(laddr, &epb->bind_addr.address_list, list) {
+ if (!laddr->valid)
+ continue;
+
addr = &laddr->a;
af = sctp_get_af_specific(addr->sa.sa_family);
if (primary && af->cmp_addr(addr, primary)) {
@@ -147,6 +151,7 @@ static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_commo
}
af->seq_dump_addr(seq, addr);
}
+ rcu_read_unlock();
}
/* Dump remote addresses of an association. */
@@ -157,15 +162,20 @@ static void sctp_seq_dump_remote_addrs(struct seq_file *seq, struct sctp_associa
struct sctp_af *af;
primary = &assoc->peer.primary_addr;
- list_for_each_entry(transport, &assoc->peer.transport_addr_list,
+ rcu_read_lock();
+ list_for_each_entry_rcu(transport, &assoc->peer.transport_addr_list,
transports) {
addr = &transport->ipaddr;
+ if (transport->dead)
+ continue;
+
af = sctp_get_af_specific(addr->sa.sa_family);
if (af->cmp_addr(addr, primary)) {
seq_printf(seq, "*");
}
af->seq_dump_addr(seq, addr);
}
+ rcu_read_unlock();
}
static void * sctp_eps_seq_start(struct seq_file *seq, loff_t *pos)
@@ -251,7 +261,7 @@ static const struct file_operations sctp_eps_seq_fops = {
.open = sctp_eps_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
/* Set up the proc fs entry for 'eps' object. */
@@ -372,7 +382,7 @@ static const struct file_operations sctp_assocs_seq_fops = {
.open = sctp_assocs_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
/* Set up the proc fs entry for 'assocs' object. */
@@ -436,12 +446,16 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
head = &sctp_assoc_hashtable[hash];
sctp_local_bh_disable();
read_lock(&head->lock);
+ rcu_read_lock();
sctp_for_each_hentry(epb, node, &head->chain) {
if (!net_eq(sock_net(epb->sk), seq_file_net(seq)))
continue;
assoc = sctp_assoc(epb);
- list_for_each_entry(tsp, &assoc->peer.transport_addr_list,
+ list_for_each_entry_rcu(tsp, &assoc->peer.transport_addr_list,
transports) {
+ if (tsp->dead)
+ continue;
+
/*
* The remote address (ADDR)
*/
@@ -487,6 +501,7 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
}
}
+ rcu_read_unlock();
read_unlock(&head->lock);
sctp_local_bh_enable();
@@ -517,7 +532,7 @@ static const struct file_operations sctp_remaddr_seq_fops = {
.open = sctp_remaddr_seq_open,
.read = seq_read,
.llseek = seq_lseek,
- .release = seq_release,
+ .release = seq_release_net,
};
int __net_init sctp_remaddr_proc_init(struct net *net)
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 2d518425d59..f898b1c58bd 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -86,7 +86,7 @@ int sysctl_sctp_rmem[3];
int sysctl_sctp_wmem[3];
/* Set up the proc fs entry for the SCTP protocol. */
-static __net_init int sctp_proc_init(struct net *net)
+static int __net_init sctp_proc_init(struct net *net)
{
#ifdef CONFIG_PROC_FS
net->sctp.proc_net_sctp = proc_net_mkdir(net, "sctp", net->proc_net);
@@ -1165,7 +1165,7 @@ static void sctp_v4_del_protocol(void)
unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
}
-static int sctp_net_init(struct net *net)
+static int __net_init sctp_net_init(struct net *net)
{
int status;
@@ -1190,6 +1190,15 @@ static int sctp_net_init(struct net *net)
/* Whether Cookie Preservative is enabled(1) or not(0) */
net->sctp.cookie_preserve_enable = 1;
+ /* Default sctp sockets to use md5 as their hmac alg */
+#if defined (CONFIG_SCTP_DEFAULT_COOKIE_HMAC_MD5)
+ net->sctp.sctp_hmac_alg = "md5";
+#elif defined (CONFIG_SCTP_DEFAULT_COOKIE_HMAC_SHA1)
+ net->sctp.sctp_hmac_alg = "sha1";
+#else
+ net->sctp.sctp_hmac_alg = NULL;
+#endif
+
/* Max.Burst - 4 */
net->sctp.max_burst = SCTP_DEFAULT_MAX_BURST;
@@ -1281,7 +1290,7 @@ err_sysctl_register:
return status;
}
-static void sctp_net_exit(struct net *net)
+static void __net_exit sctp_net_exit(struct net *net)
{
/* Free the local address list */
sctp_free_addr_wq(net);
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index 16a10850ed3..04df5301df0 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -804,10 +804,11 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
gabs);
/* Add the duplicate TSN information. */
- if (num_dup_tsns)
+ if (num_dup_tsns) {
+ aptr->stats.idupchunks += num_dup_tsns;
sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
sctp_tsnmap_get_dups(map));
-
+ }
/* Once we have a sack generated, check to see what our sack
* generation is, if its 0, reset the transports to 0, and reset
* the association generation to 1
@@ -1090,6 +1091,25 @@ nodata:
return retval;
}
+struct sctp_chunk *sctp_make_violation_max_retrans(
+ const struct sctp_association *asoc,
+ const struct sctp_chunk *chunk)
+{
+ struct sctp_chunk *retval;
+ static const char error[] = "Association exceeded its max_retans count";
+ size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t);
+
+ retval = sctp_make_abort(asoc, chunk, payload_len);
+ if (!retval)
+ goto nodata;
+
+ sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, sizeof(error));
+ sctp_addto_chunk(retval, sizeof(error), error);
+
+nodata:
+ return retval;
+}
+
/* Make a HEARTBEAT chunk. */
struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc,
const struct sctp_transport *transport)
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 6773d780362..c9577754a70 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -542,6 +542,7 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
*/
if (!is_hb || transport->hb_sent) {
transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
+ sctp_max_rto(asoc, transport);
}
}
@@ -577,7 +578,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
unsigned int error)
{
struct sctp_ulpevent *event;
-
+ struct sctp_chunk *abort;
/* Cancel any partial delivery in progress. */
sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
@@ -593,6 +594,13 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(event));
+ if (asoc->overall_error_count >= asoc->max_retrans) {
+ abort = sctp_make_violation_max_retrans(asoc, chunk);
+ if (abort)
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+ SCTP_CHUNK(abort));
+ }
+
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_CLOSED));
@@ -1268,14 +1276,14 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
sctp_outq_uncork(&asoc->outqueue);
local_cork = 0;
}
- asoc = cmd->obj.ptr;
+ asoc = cmd->obj.asoc;
/* Register with the endpoint. */
sctp_endpoint_add_asoc(ep, asoc);
sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
- sctp_assoc_update(asoc, cmd->obj.ptr);
+ sctp_assoc_update(asoc, cmd->obj.asoc);
break;
case SCTP_CMD_PURGE_OUTQUEUE:
@@ -1315,7 +1323,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
break;
case SCTP_CMD_PROCESS_FWDTSN:
- sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.ptr);
+ sctp_cmd_process_fwdtsn(&asoc->ulpq, cmd->obj.chunk);
break;
case SCTP_CMD_GEN_SACK:
@@ -1331,7 +1339,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_PROCESS_SACK:
/* Process an inbound SACK. */
error = sctp_cmd_process_sack(commands, asoc,
- cmd->obj.ptr);
+ cmd->obj.chunk);
break;
case SCTP_CMD_GEN_INIT_ACK:
@@ -1352,15 +1360,15 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
* layer which will bail.
*/
error = sctp_cmd_process_init(commands, asoc, chunk,
- cmd->obj.ptr, gfp);
+ cmd->obj.init, gfp);
break;
case SCTP_CMD_GEN_COOKIE_ECHO:
/* Generate a COOKIE ECHO chunk. */
new_obj = sctp_make_cookie_echo(asoc, chunk);
if (!new_obj) {
- if (cmd->obj.ptr)
- sctp_chunk_free(cmd->obj.ptr);
+ if (cmd->obj.chunk)
+ sctp_chunk_free(cmd->obj.chunk);
goto nomem;
}
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
@@ -1369,9 +1377,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
/* If there is an ERROR chunk to be sent along with
* the COOKIE_ECHO, send it, too.
*/
- if (cmd->obj.ptr)
+ if (cmd->obj.chunk)
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
- SCTP_CHUNK(cmd->obj.ptr));
+ SCTP_CHUNK(cmd->obj.chunk));
if (new_obj->transport) {
new_obj->transport->init_sent_count++;
@@ -1417,18 +1425,18 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
case SCTP_CMD_CHUNK_ULP:
/* Send a chunk to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
- "chunk_up:", cmd->obj.ptr,
+ "chunk_up:", cmd->obj.chunk,
"ulpq:", &asoc->ulpq);
- sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr,
+ sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.chunk,
GFP_ATOMIC);
break;
case SCTP_CMD_EVENT_ULP:
/* Send a notification to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
- "event_up:",cmd->obj.ptr,
+ "event_up:",cmd->obj.ulpevent,
"ulpq:",&asoc->ulpq);
- sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr);
+ sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ulpevent);
break;
case SCTP_CMD_REPLY:
@@ -1438,12 +1446,12 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
local_cork = 1;
}
/* Send a chunk to our peer. */
- error = sctp_outq_tail(&asoc->outqueue, cmd->obj.ptr);
+ error = sctp_outq_tail(&asoc->outqueue, cmd->obj.chunk);
break;
case SCTP_CMD_SEND_PKT:
/* Send a full packet to our peer. */
- packet = cmd->obj.ptr;
+ packet = cmd->obj.packet;
sctp_packet_transmit(packet);
sctp_ootb_pkt_free(packet);
break;
@@ -1480,7 +1488,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
break;
case SCTP_CMD_SETUP_T2:
- sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr);
+ sctp_cmd_setup_t2(commands, asoc, cmd->obj.chunk);
break;
case SCTP_CMD_TIMER_START_ONCE:
@@ -1514,7 +1522,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
break;
case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
- chunk = cmd->obj.ptr;
+ chunk = cmd->obj.chunk;
t = sctp_assoc_choose_alter_transport(asoc,
asoc->init_last_sent_to);
asoc->init_last_sent_to = t;
@@ -1665,17 +1673,16 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
break;
case SCTP_CMD_PART_DELIVER:
- sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr,
- GFP_ATOMIC);
+ sctp_ulpq_partial_delivery(&asoc->ulpq, GFP_ATOMIC);
break;
case SCTP_CMD_RENEGE:
- sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr,
+ sctp_ulpq_renege(&asoc->ulpq, cmd->obj.chunk,
GFP_ATOMIC);
break;
case SCTP_CMD_SETUP_T4:
- sctp_cmd_setup_t4(commands, asoc, cmd->obj.ptr);
+ sctp_cmd_setup_t4(commands, asoc, cmd->obj.chunk);
break;
case SCTP_CMD_PROCESS_OPERR:
@@ -1734,8 +1741,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
break;
default:
- pr_warn("Impossible command: %u, %p\n",
- cmd->verb, cmd->obj.ptr);
+ pr_warn("Impossible command: %u\n",
+ cmd->verb);
break;
}
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index 49493f33586..5131fcfedb0 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -1055,6 +1055,7 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net,
void *arg,
sctp_cmd_seq_t *commands)
{
+ sctp_paramhdr_t *param_hdr;
struct sctp_chunk *chunk = arg;
struct sctp_chunk *reply;
size_t paylen = 0;
@@ -1072,12 +1073,17 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net,
* Information field copied from the received HEARTBEAT chunk.
*/
chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data;
+ param_hdr = (sctp_paramhdr_t *) chunk->subh.hb_hdr;
paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
+
+ if (ntohs(param_hdr->length) > paylen)
+ return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
+ param_hdr, commands);
+
if (!pskb_pull(chunk->skb, paylen))
goto nomem;
- reply = sctp_make_heartbeat_ack(asoc, chunk,
- chunk->subh.hb_hdr, paylen);
+ reply = sctp_make_heartbeat_ack(asoc, chunk, param_hdr, paylen);
if (!reply)
goto nomem;
@@ -1773,8 +1779,10 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
/* Update the content of current association. */
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
- sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev));
+ sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+ SCTP_STATE(SCTP_STATE_ESTABLISHED));
+ sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
return SCTP_DISPOSITION_CONSUME;
nomem_ev:
@@ -6127,6 +6135,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
/* The TSN is too high--silently discard the chunk and
* count on it getting retransmitted later.
*/
+ if (chunk->asoc)
+ chunk->asoc->stats.outofseqtsns++;
return SCTP_IERROR_HIGH_TSN;
} else if (tmp > 0) {
/* This is a duplicate. Record it. */
@@ -6226,10 +6236,14 @@ static int sctp_eat_data(const struct sctp_association *asoc,
/* Note: Some chunks may get overcounted (if we drop) or overcounted
* if we renege and the chunk arrives again.
*/
- if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
+ if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
SCTP_INC_STATS(net, SCTP_MIB_INUNORDERCHUNKS);
- else {
+ if (chunk->asoc)
+ chunk->asoc->stats.iuodchunks++;
+ } else {
SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS);
+ if (chunk->asoc)
+ chunk->asoc->stats.iodchunks++;
ordered = 1;
}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 59d16ea927f..9e65758cb03 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -110,7 +110,6 @@ static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk);
static void sctp_sock_migrate(struct sock *, struct sock *,
struct sctp_association *, sctp_socket_type_t);
-static char *sctp_hmac_alg = SCTP_COOKIE_HMAC_ALG;
extern struct kmem_cache *sctp_bucket_cachep;
extern long sysctl_sctp_mem[3];
@@ -336,6 +335,7 @@ static struct sctp_af *sctp_sockaddr_af(struct sctp_sock *opt,
/* Bind a local address either to an endpoint or to an association. */
SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
{
+ struct net *net = sock_net(sk);
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
struct sctp_bind_addr *bp = &ep->base.bind_addr;
@@ -379,7 +379,8 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
}
}
- if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
+ if (snum && snum < PROT_SOCK &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
return -EACCES;
/* See if the address matches any of the addresses we may have
@@ -610,6 +611,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
2*asoc->pathmtu, 4380));
trans->ssthresh = asoc->peer.i.a_rwnd;
trans->rto = asoc->rto_initial;
+ sctp_max_rto(asoc, trans);
trans->rtt = trans->srtt = trans->rttvar = 0;
sctp_transport_route(trans, NULL,
sctp_sk(asoc->base.sk));
@@ -974,7 +976,7 @@ SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
void *addr_buf;
struct sctp_af *af;
- SCTP_DEBUG_PRINTK("sctp_setsocktopt_bindx: sk %p addrs %p"
+ SCTP_DEBUG_PRINTK("sctp_setsockopt_bindx: sk %p addrs %p"
" addrs_size %d opt %d\n", sk, addrs, addrs_size, op);
if (unlikely(addrs_size <= 0))
@@ -1162,7 +1164,7 @@ static int __sctp_connect(struct sock* sk,
* be permitted to open new associations.
*/
if (ep->base.bind_addr.port < PROT_SOCK &&
- !capable(CAP_NET_BIND_SERVICE)) {
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_free;
}
@@ -1791,7 +1793,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* associations.
*/
if (ep->base.bind_addr.port < PROT_SOCK &&
- !capable(CAP_NET_BIND_SERVICE)) {
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) {
err = -EACCES;
goto out_unlock;
}
@@ -1915,8 +1917,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Break the message into multiple chunks of maximum size. */
datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len);
- if (!datamsg) {
- err = -ENOMEM;
+ if (IS_ERR(datamsg)) {
+ err = PTR_ERR(datamsg);
goto out_free;
}
@@ -3890,6 +3892,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->default_rcv_context = 0;
sp->max_burst = net->sctp.max_burst;
+ sp->sctp_hmac_alg = net->sctp.sctp_hmac_alg;
+
/* Initialize default setup parameters. These parameters
* can be modified with the SCTP_INITMSG socket option or
* overridden by the SCTP_INIT CMSG.
@@ -5632,6 +5636,71 @@ static int sctp_getsockopt_paddr_thresholds(struct sock *sk,
return 0;
}
+/*
+ * SCTP_GET_ASSOC_STATS
+ *
+ * This option retrieves local per endpoint statistics. It is modeled
+ * after OpenSolaris' implementation
+ */
+static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
+ char __user *optval,
+ int __user *optlen)
+{
+ struct sctp_assoc_stats sas;
+ struct sctp_association *asoc = NULL;
+
+ /* User must provide at least the assoc id */
+ if (len < sizeof(sctp_assoc_t))
+ return -EINVAL;
+
+ if (copy_from_user(&sas, optval, len))
+ return -EFAULT;
+
+ asoc = sctp_id2assoc(sk, sas.sas_assoc_id);
+ if (!asoc)
+ return -EINVAL;
+
+ sas.sas_rtxchunks = asoc->stats.rtxchunks;
+ sas.sas_gapcnt = asoc->stats.gapcnt;
+ sas.sas_outofseqtsns = asoc->stats.outofseqtsns;
+ sas.sas_osacks = asoc->stats.osacks;
+ sas.sas_isacks = asoc->stats.isacks;
+ sas.sas_octrlchunks = asoc->stats.octrlchunks;
+ sas.sas_ictrlchunks = asoc->stats.ictrlchunks;
+ sas.sas_oodchunks = asoc->stats.oodchunks;
+ sas.sas_iodchunks = asoc->stats.iodchunks;
+ sas.sas_ouodchunks = asoc->stats.ouodchunks;
+ sas.sas_iuodchunks = asoc->stats.iuodchunks;
+ sas.sas_idupchunks = asoc->stats.idupchunks;
+ sas.sas_opackets = asoc->stats.opackets;
+ sas.sas_ipackets = asoc->stats.ipackets;
+
+ /* New high max rto observed, will return 0 if not a single
+ * RTO update took place. obs_rto_ipaddr will be bogus
+ * in such a case
+ */
+ sas.sas_maxrto = asoc->stats.max_obs_rto;
+ memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr,
+ sizeof(struct sockaddr_storage));
+
+ /* Mark beginning of a new observation period */
+ asoc->stats.max_obs_rto = asoc->rto_min;
+
+ /* Allow the struct to grow and fill in as much as possible */
+ len = min_t(size_t, len, sizeof(sas));
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+
+ SCTP_DEBUG_PRINTK("sctp_getsockopt_assoc_stat(%d): %d\n",
+ len, sas.sas_assoc_id);
+
+ if (copy_to_user(optval, &sas, len))
+ return -EFAULT;
+
+ return 0;
+}
+
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
@@ -5773,6 +5842,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
case SCTP_PEER_ADDR_THLDS:
retval = sctp_getsockopt_paddr_thresholds(sk, optval, len, optlen);
break;
+ case SCTP_GET_ASSOC_STATS:
+ retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
+ break;
default:
retval = -ENOPROTOOPT;
break;
@@ -5981,13 +6053,15 @@ SCTP_STATIC int sctp_listen_start(struct sock *sk, int backlog)
struct sctp_sock *sp = sctp_sk(sk);
struct sctp_endpoint *ep = sp->ep;
struct crypto_hash *tfm = NULL;
+ char alg[32];
/* Allocate HMAC for generating cookie. */
- if (!sctp_sk(sk)->hmac && sctp_hmac_alg) {
- tfm = crypto_alloc_hash(sctp_hmac_alg, 0, CRYPTO_ALG_ASYNC);
+ if (!sp->hmac && sp->sctp_hmac_alg) {
+ sprintf(alg, "hmac(%s)", sp->sctp_hmac_alg);
+ tfm = crypto_alloc_hash(alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm)) {
net_info_ratelimited("failed to load transform for %s: %ld\n",
- sctp_hmac_alg, PTR_ERR(tfm));
+ sp->sctp_hmac_alg, PTR_ERR(tfm));
return -ENOSYS;
}
sctp_sk(sk)->hmac = tfm;
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index 70e3ba5cb50..bf3c6e8fc40 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -62,6 +62,11 @@ extern long sysctl_sctp_mem[3];
extern int sysctl_sctp_rmem[3];
extern int sysctl_sctp_wmem[3];
+static int proc_sctp_do_hmac_alg(ctl_table *ctl,
+ int write,
+ void __user *buffer, size_t *lenp,
+
+ loff_t *ppos);
static ctl_table sctp_table[] = {
{
.procname = "sctp_mem",
@@ -147,6 +152,12 @@ static ctl_table sctp_net_table[] = {
.proc_handler = proc_dointvec,
},
{
+ .procname = "cookie_hmac_alg",
+ .maxlen = 8,
+ .mode = 0644,
+ .proc_handler = proc_sctp_do_hmac_alg,
+ },
+ {
.procname = "valid_cookie_life",
.data = &init_net.sctp.valid_cookie_life,
.maxlen = sizeof(unsigned int),
@@ -289,6 +300,54 @@ static ctl_table sctp_net_table[] = {
{ /* sentinel */ }
};
+static int proc_sctp_do_hmac_alg(ctl_table *ctl,
+ int write,
+ void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ struct net *net = current->nsproxy->net_ns;
+ char tmp[8];
+ ctl_table tbl;
+ int ret;
+ int changed = 0;
+ char *none = "none";
+
+ memset(&tbl, 0, sizeof(struct ctl_table));
+
+ if (write) {
+ tbl.data = tmp;
+ tbl.maxlen = 8;
+ } else {
+ tbl.data = net->sctp.sctp_hmac_alg ? : none;
+ tbl.maxlen = strlen(tbl.data);
+ }
+ ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
+
+ if (write) {
+#ifdef CONFIG_CRYPTO_MD5
+ if (!strncmp(tmp, "md5", 3)) {
+ net->sctp.sctp_hmac_alg = "md5";
+ changed = 1;
+ }
+#endif
+#ifdef CONFIG_CRYPTO_SHA1
+ if (!strncmp(tmp, "sha1", 4)) {
+ net->sctp.sctp_hmac_alg = "sha1";
+ changed = 1;
+ }
+#endif
+ if (!strncmp(tmp, "none", 4)) {
+ net->sctp.sctp_hmac_alg = NULL;
+ changed = 1;
+ }
+
+ if (!changed)
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
int sctp_sysctl_net_register(struct net *net)
{
struct ctl_table *table;
@@ -307,7 +366,11 @@ int sctp_sysctl_net_register(struct net *net)
void sctp_sysctl_net_unregister(struct net *net)
{
+ struct ctl_table *table;
+
+ table = net->sctp.sysctl_header->ctl_table_arg;
unregister_net_sysctl_table(net->sctp.sysctl_header);
+ kfree(table);
}
static struct ctl_table_header * sctp_sysctl_header;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 953c21e4af9..4e45bb68aef 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -163,13 +163,11 @@ void sctp_transport_free(struct sctp_transport *transport)
sctp_transport_put(transport);
}
-/* Destroy the transport data structure.
- * Assumes there are no more users of this structure.
- */
-static void sctp_transport_destroy(struct sctp_transport *transport)
+static void sctp_transport_destroy_rcu(struct rcu_head *head)
{
- SCTP_ASSERT(transport->dead, "Transport is not dead", return);
+ struct sctp_transport *transport;
+ transport = container_of(head, struct sctp_transport, rcu);
if (transport->asoc)
sctp_association_put(transport->asoc);
@@ -180,6 +178,16 @@ static void sctp_transport_destroy(struct sctp_transport *transport)
SCTP_DBG_OBJCNT_DEC(transport);
}
+/* Destroy the transport data structure.
+ * Assumes there are no more users of this structure.
+ */
+static void sctp_transport_destroy(struct sctp_transport *transport)
+{
+ SCTP_ASSERT(transport->dead, "Transport is not dead", return);
+
+ call_rcu(&transport->rcu, sctp_transport_destroy_rcu);
+}
+
/* Start T3_rtx timer if it is not already running and update the heartbeat
* timer. This routine is called every time a DATA chunk is sent.
*/
@@ -331,7 +339,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
* 1/8, rto_alpha would be expressed as 3.
*/
tp->rttvar = tp->rttvar - (tp->rttvar >> net->sctp.rto_beta)
- + ((abs(tp->srtt - rtt)) >> net->sctp.rto_beta);
+ + (((__u32)abs64((__s64)tp->srtt - (__s64)rtt)) >> net->sctp.rto_beta);
tp->srtt = tp->srtt - (tp->srtt >> net->sctp.rto_alpha)
+ (rtt >> net->sctp.rto_alpha);
} else {
@@ -363,6 +371,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
if (tp->rto > tp->asoc->rto_max)
tp->rto = tp->asoc->rto_max;
+ sctp_max_rto(tp->asoc, tp);
tp->rtt = rtt;
/* Reset rto_pending so that a new RTT measurement is started when a
@@ -620,6 +629,7 @@ void sctp_transport_reset(struct sctp_transport *t)
t->burst_limited = 0;
t->ssthresh = asoc->peer.i.a_rwnd;
t->rto = asoc->rto_initial;
+ sctp_max_rto(asoc, t);
t->rtt = 0;
t->srtt = 0;
t->rttvar = 0;
diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c
index b5fb7c40902..5f25e0c92c3 100644
--- a/net/sctp/tsnmap.c
+++ b/net/sctp/tsnmap.c
@@ -272,7 +272,7 @@ __u16 sctp_tsnmap_pending(struct sctp_tsnmap *map)
__u32 max_tsn = map->max_tsn_seen;
__u32 base_tsn = map->base_tsn;
__u16 pending_data;
- u32 gap, i;
+ u32 gap;
pending_data = max_tsn - cum_tsn;
gap = max_tsn - base_tsn;
@@ -280,11 +280,7 @@ __u16 sctp_tsnmap_pending(struct sctp_tsnmap *map)
if (gap == 0 || gap >= map->len)
goto out;
- for (i = 0; i < gap+1; i++) {
- if (test_bit(i, map->tsn_map))
- pending_data--;
- }
-
+ pending_data -= bitmap_weight(map->tsn_map, gap + 1);
out:
return pending_data;
}
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 360d8697b95..ada17464b65 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -997,7 +997,6 @@ static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
/* Partial deliver the first message as there is pressure on rwnd. */
void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
- struct sctp_chunk *chunk,
gfp_t gfp)
{
struct sctp_ulpevent *event;
@@ -1060,7 +1059,7 @@ void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn, chunk->transport);
sctp_ulpq_tail_data(ulpq, chunk, gfp);
- sctp_ulpq_partial_delivery(ulpq, chunk, gfp);
+ sctp_ulpq_partial_delivery(ulpq, gfp);
}
sk_mem_reclaim(asoc->base.sk);
diff --git a/net/socket.c b/net/socket.c
index d92c490e66f..2ca51c719ef 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -620,8 +620,6 @@ static inline int __sock_sendmsg_nosec(struct kiocb *iocb, struct socket *sock,
{
struct sock_iocb *si = kiocb_to_siocb(iocb);
- sock_update_classid(sock->sk);
-
si->sock = sock;
si->scm = NULL;
si->msg = msg;
@@ -784,8 +782,6 @@ static inline int __sock_recvmsg_nosec(struct kiocb *iocb, struct socket *sock,
{
struct sock_iocb *si = kiocb_to_siocb(iocb);
- sock_update_classid(sock->sk);
-
si->sock = sock;
si->scm = NULL;
si->msg = msg;
@@ -896,8 +892,6 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
if (unlikely(!sock->ops->splice_read))
return -EINVAL;
- sock_update_classid(sock->sk);
-
return sock->ops->splice_read(sock, ppos, pipe, len, flags);
}
@@ -3437,8 +3431,6 @@ EXPORT_SYMBOL(kernel_setsockopt);
int kernel_sendpage(struct socket *sock, struct page *page, int offset,
size_t size, int flags)
{
- sock_update_classid(sock->sk);
-
if (sock->ops->sendpage)
return sock->ops->sendpage(sock, page, offset, size, flags);
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 909dc0c31aa..6e5c824b040 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -192,17 +192,23 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
const void *q;
unsigned int seclen;
unsigned int timeout;
+ unsigned long now = jiffies;
u32 window_size;
int ret;
- /* First unsigned int gives the lifetime (in seconds) of the cred */
+ /* First unsigned int gives the remaining lifetime in seconds of the
+ * credential - e.g. the remaining TGT lifetime for Kerberos or
+ * the -t value passed to GSSD.
+ */
p = simple_get_bytes(p, end, &timeout, sizeof(timeout));
if (IS_ERR(p))
goto err;
if (timeout == 0)
timeout = GSSD_MIN_TIMEOUT;
- ctx->gc_expiry = jiffies + (unsigned long)timeout * HZ * 3 / 4;
- /* Sequence number window. Determines the maximum number of simultaneous requests */
+ ctx->gc_expiry = now + ((unsigned long)timeout * HZ);
+ /* Sequence number window. Determines the maximum number of
+ * simultaneous requests
+ */
p = simple_get_bytes(p, end, &window_size, sizeof(window_size));
if (IS_ERR(p))
goto err;
@@ -237,9 +243,12 @@ gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct
p = ERR_PTR(ret);
goto err;
}
+ dprintk("RPC: %s Success. gc_expiry %lu now %lu timeout %u\n",
+ __func__, ctx->gc_expiry, now, timeout);
return q;
err:
- dprintk("RPC: %s returning %ld\n", __func__, -PTR_ERR(p));
+ dprintk("RPC: %s returns %ld gc_expiry %lu now %lu timeout %u\n",
+ __func__, -PTR_ERR(p), ctx->gc_expiry, now, timeout);
return p;
}
diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
index 5a3d675d2f2..890a29912d5 100644
--- a/net/sunrpc/backchannel_rqst.c
+++ b/net/sunrpc/backchannel_rqst.c
@@ -59,7 +59,7 @@ static void xprt_free_allocation(struct rpc_rqst *req)
struct xdr_buf *xbufp;
dprintk("RPC: free allocations for req= %p\n", req);
- BUG_ON(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
+ WARN_ON_ONCE(test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
xbufp = &req->rq_private_buf;
free_page((unsigned long)xbufp->head[0].iov_base);
xbufp = &req->rq_snd_buf;
@@ -172,7 +172,7 @@ out_free:
xprt_free_allocation(req);
dprintk("RPC: setup backchannel transport failed\n");
- return -1;
+ return -ENOMEM;
}
EXPORT_SYMBOL_GPL(xprt_setup_backchannel);
@@ -191,7 +191,9 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs)
dprintk("RPC: destroy backchannel transport\n");
- BUG_ON(max_reqs == 0);
+ if (max_reqs == 0)
+ goto out;
+
spin_lock_bh(&xprt->bc_pa_lock);
xprt_dec_alloc_count(xprt, max_reqs);
list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) {
@@ -202,6 +204,7 @@ void xprt_destroy_backchannel(struct rpc_xprt *xprt, unsigned int max_reqs)
}
spin_unlock_bh(&xprt->bc_pa_lock);
+out:
dprintk("RPC: backchannel list empty= %s\n",
list_empty(&xprt->bc_pa_list) ? "true" : "false");
}
@@ -255,7 +258,7 @@ void xprt_free_bc_request(struct rpc_rqst *req)
dprintk("RPC: free backchannel req=%p\n", req);
smp_mb__before_clear_bit();
- BUG_ON(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
+ WARN_ON_ONCE(!test_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state));
clear_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
smp_mb__after_clear_bit();
diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c
index 0b2eb388cbd..15c7a8a1c24 100644
--- a/net/sunrpc/bc_svc.c
+++ b/net/sunrpc/bc_svc.c
@@ -53,7 +53,7 @@ int bc_send(struct rpc_rqst *req)
if (IS_ERR(task))
ret = PTR_ERR(task);
else {
- BUG_ON(atomic_read(&task->tk_count) != 1);
+ WARN_ON_ONCE(atomic_read(&task->tk_count) != 1);
ret = task->tk_status;
rpc_put_task(task);
}
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index fc2f7aa4dca..9afa4393c21 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -775,11 +775,11 @@ static ssize_t cache_read(struct file *filp, char __user *buf, size_t count,
if (rp->q.list.next == &cd->queue) {
spin_unlock(&queue_lock);
mutex_unlock(&inode->i_mutex);
- BUG_ON(rp->offset);
+ WARN_ON_ONCE(rp->offset);
return 0;
}
rq = container_of(rp->q.list.next, struct cache_request, q.list);
- BUG_ON(rq->q.reader);
+ WARN_ON_ONCE(rq->q.reader);
if (rp->offset == 0)
rq->readers++;
spin_unlock(&queue_lock);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index cdc7564b451..507b5e84fbd 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -132,8 +132,10 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,
int error;
dir = rpc_d_lookup_sb(sb, dir_name);
- if (dir == NULL)
+ if (dir == NULL) {
+ pr_info("RPC: pipefs directory doesn't exist: %s\n", dir_name);
return dir;
+ }
for (;;) {
q.len = snprintf(name, sizeof(name), "clnt%x", (unsigned int)clntid++);
name[sizeof(name) - 1] = '\0';
@@ -192,7 +194,8 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
case RPC_PIPEFS_MOUNT:
dentry = rpc_setup_pipedir_sb(sb, clnt,
clnt->cl_program->pipe_dir_name);
- BUG_ON(dentry == NULL);
+ if (!dentry)
+ return -ENOENT;
if (IS_ERR(dentry))
return PTR_ERR(dentry);
clnt->cl_dentry = dentry;
@@ -234,7 +237,7 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)
spin_lock(&sn->rpc_client_lock);
list_for_each_entry(clnt, &sn->all_clients, cl_clients) {
if (clnt->cl_program->pipe_dir_name == NULL)
- break;
+ continue;
if (rpc_clnt_skip_event(clnt, event))
continue;
if (atomic_inc_not_zero(&clnt->cl_count) == 0)
@@ -552,7 +555,7 @@ EXPORT_SYMBOL_GPL(rpc_clone_client);
* rpc_clone_client_set_auth - Clone an RPC client structure and set its auth
*
* @clnt: RPC client whose parameters are copied
- * @auth: security flavor for new client
+ * @flavor: security flavor for new client
*
* Returns a fresh RPC client or an ERR_PTR.
*/
@@ -607,6 +610,8 @@ EXPORT_SYMBOL_GPL(rpc_killall_tasks);
*/
void rpc_shutdown_client(struct rpc_clnt *clnt)
{
+ might_sleep();
+
dprintk_rcu("RPC: shutting down %s client for %s\n",
clnt->cl_protname,
rcu_dereference(clnt->cl_xprt)->servername);
@@ -693,21 +698,19 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,
const struct rpc_program *program,
u32 vers)
{
+ struct rpc_create_args args = {
+ .program = program,
+ .prognumber = program->number,
+ .version = vers,
+ .authflavor = old->cl_auth->au_flavor,
+ .client_name = old->cl_principal,
+ };
struct rpc_clnt *clnt;
- const struct rpc_version *version;
int err;
- BUG_ON(vers >= program->nrvers || !program->version[vers]);
- version = program->version[vers];
- clnt = rpc_clone_client(old);
+ clnt = __rpc_clone_client(&args, old);
if (IS_ERR(clnt))
goto out;
- clnt->cl_procinfo = version->procs;
- clnt->cl_maxproc = version->nrprocs;
- clnt->cl_protname = program->name;
- clnt->cl_prog = program->number;
- clnt->cl_vers = version->number;
- clnt->cl_stats = program->stats;
err = rpc_ping(clnt);
if (err != 0) {
rpc_shutdown_client(clnt);
@@ -832,7 +835,12 @@ int rpc_call_sync(struct rpc_clnt *clnt, const struct rpc_message *msg, int flag
};
int status;
- BUG_ON(flags & RPC_TASK_ASYNC);
+ WARN_ON_ONCE(flags & RPC_TASK_ASYNC);
+ if (flags & RPC_TASK_ASYNC) {
+ rpc_release_calldata(task_setup_data.callback_ops,
+ task_setup_data.callback_data);
+ return -EINVAL;
+ }
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
@@ -908,7 +916,7 @@ struct rpc_task *rpc_run_bc_task(struct rpc_rqst *req,
task->tk_action = call_bc_transmit;
atomic_inc(&task->tk_count);
- BUG_ON(atomic_read(&task->tk_count) != 2);
+ WARN_ON_ONCE(atomic_read(&task->tk_count) != 2);
rpc_execute(task);
out:
@@ -1368,6 +1376,7 @@ call_refreshresult(struct rpc_task *task)
return;
case -ETIMEDOUT:
rpc_delay(task, 3*HZ);
+ case -EKEYEXPIRED:
case -EAGAIN:
status = -EACCES;
if (!task->tk_cred_retry)
@@ -1654,7 +1663,6 @@ call_transmit(struct rpc_task *task)
task->tk_action = call_transmit_status;
/* Encode here so that rpcsec_gss can use correct sequence number. */
if (rpc_task_need_encode(task)) {
- BUG_ON(task->tk_rqstp->rq_bytes_sent != 0);
rpc_xdr_encode(task);
/* Did the encode result in an error condition? */
if (task->tk_status != 0) {
@@ -1738,7 +1746,6 @@ call_bc_transmit(struct rpc_task *task)
{
struct rpc_rqst *req = task->tk_rqstp;
- BUG_ON(task->tk_status != 0);
task->tk_status = xprt_prepare_transmit(task);
if (task->tk_status == -EAGAIN) {
/*
@@ -1785,7 +1792,7 @@ call_bc_transmit(struct rpc_task *task)
* We were unable to reply and will have to drop the
* request. The server should reconnect and retransmit.
*/
- BUG_ON(task->tk_status == -EAGAIN);
+ WARN_ON_ONCE(task->tk_status == -EAGAIN);
printk(KERN_NOTICE "RPC: Could not send backchannel reply "
"error: %d\n", task->tk_status);
break;
diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c
index 80f5dd23417..fd10981ea79 100644
--- a/net/sunrpc/rpc_pipe.c
+++ b/net/sunrpc/rpc_pipe.c
@@ -1093,7 +1093,7 @@ void rpc_put_sb_net(const struct net *net)
{
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
- BUG_ON(sn->pipefs_sb == NULL);
+ WARN_ON(sn->pipefs_sb == NULL);
mutex_unlock(&sn->pipefs_sb_lock);
}
EXPORT_SYMBOL_GPL(rpc_put_sb_net);
@@ -1152,14 +1152,19 @@ static void rpc_kill_sb(struct super_block *sb)
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
mutex_lock(&sn->pipefs_sb_lock);
+ if (sn->pipefs_sb != sb) {
+ mutex_unlock(&sn->pipefs_sb_lock);
+ goto out;
+ }
sn->pipefs_sb = NULL;
mutex_unlock(&sn->pipefs_sb_lock);
- put_net(net);
dprintk("RPC: sending pipefs UMOUNT notification for net %p%s\n",
net, NET_NAME(net));
blocking_notifier_call_chain(&rpc_pipefs_notifier_list,
RPC_PIPEFS_UMOUNT,
sb);
+ put_net(net);
+out:
kill_litter_super(sb);
}
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index a70acae496e..795a0f4e920 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -23,7 +23,6 @@
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/nsproxy.h>
#include <net/ipv6.h>
#include <linux/sunrpc/clnt.h>
@@ -884,7 +883,10 @@ static void encode_rpcb_string(struct xdr_stream *xdr, const char *string,
u32 len;
len = strlen(string);
- BUG_ON(len > maxstrlen);
+ WARN_ON_ONCE(len > maxstrlen);
+ if (len > maxstrlen)
+ /* truncate and hope for the best */
+ len = maxstrlen;
p = xdr_reserve_space(xdr, 4 + len);
xdr_encode_opaque(p, string, len);
}
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 6357fcb00c7..bfa31714581 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -98,6 +98,23 @@ __rpc_add_timer(struct rpc_wait_queue *queue, struct rpc_task *task)
list_add(&task->u.tk_wait.timer_list, &queue->timer_list.list);
}
+static void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority)
+{
+ queue->priority = priority;
+}
+
+static void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid)
+{
+ queue->owner = pid;
+ queue->nr = RPC_BATCH_COUNT;
+}
+
+static void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue)
+{
+ rpc_set_waitqueue_priority(queue, queue->maxpriority);
+ rpc_set_waitqueue_owner(queue, 0);
+}
+
/*
* Add new request to a priority queue.
*/
@@ -109,9 +126,11 @@ static void __rpc_add_wait_queue_priority(struct rpc_wait_queue *queue,
struct rpc_task *t;
INIT_LIST_HEAD(&task->u.tk_wait.links);
- q = &queue->tasks[queue_priority];
if (unlikely(queue_priority > queue->maxpriority))
- q = &queue->tasks[queue->maxpriority];
+ queue_priority = queue->maxpriority;
+ if (queue_priority > queue->priority)
+ rpc_set_waitqueue_priority(queue, queue_priority);
+ q = &queue->tasks[queue_priority];
list_for_each_entry(t, q, u.tk_wait.list) {
if (t->tk_owner == task->tk_owner) {
list_add_tail(&task->u.tk_wait.list, &t->u.tk_wait.links);
@@ -133,7 +152,9 @@ static void __rpc_add_wait_queue(struct rpc_wait_queue *queue,
struct rpc_task *task,
unsigned char queue_priority)
{
- BUG_ON (RPC_IS_QUEUED(task));
+ WARN_ON_ONCE(RPC_IS_QUEUED(task));
+ if (RPC_IS_QUEUED(task))
+ return;
if (RPC_IS_PRIORITY(queue))
__rpc_add_wait_queue_priority(queue, task, queue_priority);
@@ -178,24 +199,6 @@ static void __rpc_remove_wait_queue(struct rpc_wait_queue *queue, struct rpc_tas
task->tk_pid, queue, rpc_qname(queue));
}
-static inline void rpc_set_waitqueue_priority(struct rpc_wait_queue *queue, int priority)
-{
- queue->priority = priority;
- queue->count = 1 << (priority * 2);
-}
-
-static inline void rpc_set_waitqueue_owner(struct rpc_wait_queue *queue, pid_t pid)
-{
- queue->owner = pid;
- queue->nr = RPC_BATCH_COUNT;
-}
-
-static inline void rpc_reset_waitqueue_priority(struct rpc_wait_queue *queue)
-{
- rpc_set_waitqueue_priority(queue, queue->maxpriority);
- rpc_set_waitqueue_owner(queue, 0);
-}
-
static void __rpc_init_priority_wait_queue(struct rpc_wait_queue *queue, const char *qname, unsigned char nr_queues)
{
int i;
@@ -334,7 +337,7 @@ static void __rpc_sleep_on_priority(struct rpc_wait_queue *q,
__rpc_add_wait_queue(q, task, queue_priority);
- BUG_ON(task->tk_callback != NULL);
+ WARN_ON_ONCE(task->tk_callback != NULL);
task->tk_callback = action;
__rpc_add_timer(q, task);
}
@@ -343,7 +346,12 @@ void rpc_sleep_on(struct rpc_wait_queue *q, struct rpc_task *task,
rpc_action action)
{
/* We shouldn't ever put an inactive task to sleep */
- BUG_ON(!RPC_IS_ACTIVATED(task));
+ WARN_ON_ONCE(!RPC_IS_ACTIVATED(task));
+ if (!RPC_IS_ACTIVATED(task)) {
+ task->tk_status = -EIO;
+ rpc_put_task_async(task);
+ return;
+ }
/*
* Protect the queue operations.
@@ -358,7 +366,12 @@ void rpc_sleep_on_priority(struct rpc_wait_queue *q, struct rpc_task *task,
rpc_action action, int priority)
{
/* We shouldn't ever put an inactive task to sleep */
- BUG_ON(!RPC_IS_ACTIVATED(task));
+ WARN_ON_ONCE(!RPC_IS_ACTIVATED(task));
+ if (!RPC_IS_ACTIVATED(task)) {
+ task->tk_status = -EIO;
+ rpc_put_task_async(task);
+ return;
+ }
/*
* Protect the queue operations.
@@ -367,6 +380,7 @@ void rpc_sleep_on_priority(struct rpc_wait_queue *q, struct rpc_task *task,
__rpc_sleep_on_priority(q, task, action, priority - RPC_PRIORITY_LOW);
spin_unlock_bh(&q->lock);
}
+EXPORT_SYMBOL_GPL(rpc_sleep_on_priority);
/**
* __rpc_do_wake_up_task - wake up a single rpc_task
@@ -451,8 +465,7 @@ static struct rpc_task *__rpc_find_next_queued_priority(struct rpc_wait_queue *q
/*
* Check if we need to switch queues.
*/
- if (--queue->count)
- goto new_owner;
+ goto new_owner;
}
/*
@@ -697,7 +710,9 @@ static void __rpc_execute(struct rpc_task *task)
dprintk("RPC: %5u __rpc_execute flags=0x%x\n",
task->tk_pid, task->tk_flags);
- BUG_ON(RPC_IS_QUEUED(task));
+ WARN_ON_ONCE(RPC_IS_QUEUED(task));
+ if (RPC_IS_QUEUED(task))
+ return;
for (;;) {
void (*do_action)(struct rpc_task *);
@@ -919,16 +934,35 @@ struct rpc_task *rpc_new_task(const struct rpc_task_setup *setup_data)
return task;
}
+/*
+ * rpc_free_task - release rpc task and perform cleanups
+ *
+ * Note that we free up the rpc_task _after_ rpc_release_calldata()
+ * in order to work around a workqueue dependency issue.
+ *
+ * Tejun Heo states:
+ * "Workqueue currently considers two work items to be the same if they're
+ * on the same address and won't execute them concurrently - ie. it
+ * makes a work item which is queued again while being executed wait
+ * for the previous execution to complete.
+ *
+ * If a work function frees the work item, and then waits for an event
+ * which should be performed by another work item and *that* work item
+ * recycles the freed work item, it can create a false dependency loop.
+ * There really is no reliable way to detect this short of verifying
+ * every memory free."
+ *
+ */
static void rpc_free_task(struct rpc_task *task)
{
- const struct rpc_call_ops *tk_ops = task->tk_ops;
- void *calldata = task->tk_calldata;
+ unsigned short tk_flags = task->tk_flags;
+
+ rpc_release_calldata(task->tk_ops, task->tk_calldata);
- if (task->tk_flags & RPC_TASK_DYNAMIC) {
+ if (tk_flags & RPC_TASK_DYNAMIC) {
dprintk("RPC: %5u freeing task\n", task->tk_pid);
mempool_free(task, rpc_task_mempool);
}
- rpc_release_calldata(tk_ops, calldata);
}
static void rpc_async_release(struct work_struct *work)
@@ -938,8 +972,7 @@ static void rpc_async_release(struct work_struct *work)
static void rpc_release_resources_task(struct rpc_task *task)
{
- if (task->tk_rqstp)
- xprt_release(task);
+ xprt_release(task);
if (task->tk_msg.rpc_cred) {
put_rpccred(task->tk_msg.rpc_cred);
task->tk_msg.rpc_cred = NULL;
@@ -981,7 +1014,7 @@ static void rpc_release_task(struct rpc_task *task)
{
dprintk("RPC: %5u release task\n", task->tk_pid);
- BUG_ON (RPC_IS_QUEUED(task));
+ WARN_ON_ONCE(RPC_IS_QUEUED(task));
rpc_release_resources_task(task);
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 3ee7461926d..dbf12ac5ecb 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -20,7 +20,6 @@
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/slab.h>
-#include <linux/nsproxy.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
@@ -324,7 +323,9 @@ svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx)
* The caller checks for sv_nrpools > 1, which
* implies that we've been initialized.
*/
- BUG_ON(m->count == 0);
+ WARN_ON_ONCE(m->count == 0);
+ if (m->count == 0)
+ return;
switch (m->mode) {
case SVC_POOL_PERCPU:
@@ -585,7 +586,9 @@ svc_init_buffer(struct svc_rqst *rqstp, unsigned int size, int node)
* We assume one is at most one page
*/
arghi = 0;
- BUG_ON(pages > RPCSVC_MAXPAGES);
+ WARN_ON_ONCE(pages > RPCSVC_MAXPAGES);
+ if (pages > RPCSVC_MAXPAGES)
+ pages = RPCSVC_MAXPAGES;
while (pages) {
struct page *p = alloc_pages_node(node, GFP_KERNEL, 0);
if (!p)
@@ -946,7 +949,9 @@ int svc_register(const struct svc_serv *serv, struct net *net,
unsigned int i;
int error = 0;
- BUG_ON(proto == 0 && port == 0);
+ WARN_ON_ONCE(proto == 0 && port == 0);
+ if (proto == 0 && port == 0)
+ return -EINVAL;
for (progp = serv->sv_program; progp; progp = progp->pg_next) {
for (i = 0; i < progp->pg_nvers; i++) {
@@ -1035,7 +1040,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net)
}
/*
- * Printk the given error with the address of the client that caused it.
+ * dprintk the given error with the address of the client that caused it.
*/
static __printf(2, 3)
void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
@@ -1049,8 +1054,7 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;
- net_warn_ratelimited("svc: %s: %pV",
- svc_print_addr(rqstp, buf, sizeof(buf)), &vaf);
+ dprintk("svc: %s: %pV", svc_print_addr(rqstp, buf, sizeof(buf)), &vaf);
va_end(args);
}
@@ -1299,7 +1303,7 @@ svc_process(struct svc_rqst *rqstp)
* Setup response xdr_buf.
* Initially it has just one page
*/
- rqstp->rq_resused = 1;
+ rqstp->rq_next_page = &rqstp->rq_respages[1];
resv->iov_base = page_address(rqstp->rq_respages[0]);
resv->iov_len = 0;
rqstp->rq_res.pages = rqstp->rq_respages + 1;
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 194d865fae7..b8e47fac731 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -218,7 +218,9 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
*/
static void svc_xprt_received(struct svc_xprt *xprt)
{
- BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags));
+ WARN_ON_ONCE(!test_bit(XPT_BUSY, &xprt->xpt_flags));
+ if (!test_bit(XPT_BUSY, &xprt->xpt_flags))
+ return;
/* As soon as we clear busy, the xprt could be closed and
* 'put', so we need a reference to call svc_xprt_enqueue with:
*/
@@ -577,7 +579,10 @@ int svc_alloc_arg(struct svc_rqst *rqstp)
/* now allocate needed pages. If we get a failure, sleep briefly */
pages = (serv->sv_max_mesg + PAGE_SIZE) / PAGE_SIZE;
- BUG_ON(pages >= RPCSVC_MAXPAGES);
+ WARN_ON_ONCE(pages >= RPCSVC_MAXPAGES);
+ if (pages >= RPCSVC_MAXPAGES)
+ /* use as many pages as possible */
+ pages = RPCSVC_MAXPAGES - 1;
for (i = 0; i < pages ; i++)
while (rqstp->rq_pages[i] == NULL) {
struct page *p = alloc_page(GFP_KERNEL);
@@ -926,7 +931,7 @@ static void svc_delete_xprt(struct svc_xprt *xprt)
spin_lock_bh(&serv->sv_lock);
if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags))
list_del_init(&xprt->xpt_list);
- BUG_ON(!list_empty(&xprt->xpt_ready));
+ WARN_ON_ONCE(!list_empty(&xprt->xpt_ready));
if (test_bit(XPT_TEMP, &xprt->xpt_flags))
serv->sv_tmpcnt--;
spin_unlock_bh(&serv->sv_lock);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 03827cef1fa..0a148c9d2a5 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -84,7 +84,11 @@ static struct lock_class_key svc_slock_key[2];
static void svc_reclassify_socket(struct socket *sock)
{
struct sock *sk = sock->sk;
- BUG_ON(sock_owned_by_user(sk));
+
+ WARN_ON_ONCE(sock_owned_by_user(sk));
+ if (sock_owned_by_user(sk))
+ return;
+
switch (sk->sk_family) {
case AF_INET:
sock_lock_init_class_and_name(sk, "slock-AF_INET-NFSD",
@@ -601,6 +605,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
rqstp->rq_respages = rqstp->rq_pages + 1 +
DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE);
}
+ rqstp->rq_next_page = rqstp->rq_respages+1;
if (serv->sv_stats)
serv->sv_stats->netudpcnt++;
@@ -874,9 +879,9 @@ static unsigned int svc_tcp_restore_pages(struct svc_sock *svsk, struct svc_rqst
{
unsigned int i, len, npages;
- if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+ if (svsk->sk_datalen == 0)
return 0;
- len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+ len = svsk->sk_datalen;
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
if (rqstp->rq_pages[i] != NULL)
@@ -893,9 +898,9 @@ static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp)
{
unsigned int i, len, npages;
- if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+ if (svsk->sk_datalen == 0)
return;
- len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+ len = svsk->sk_datalen;
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
svsk->sk_pages[i] = rqstp->rq_pages[i];
@@ -907,9 +912,9 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
{
unsigned int i, len, npages;
- if (svsk->sk_tcplen <= sizeof(rpc_fraghdr))
+ if (svsk->sk_datalen == 0)
goto out;
- len = svsk->sk_tcplen - sizeof(rpc_fraghdr);
+ len = svsk->sk_datalen;
npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
for (i = 0; i < npages; i++) {
BUG_ON(svsk->sk_pages[i] == NULL);
@@ -918,13 +923,12 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk)
}
out:
svsk->sk_tcplen = 0;
+ svsk->sk_datalen = 0;
}
/*
- * Receive data.
+ * Receive fragment record header.
* If we haven't gotten the record length yet, get the next four bytes.
- * Otherwise try to gobble up as much as possible up to the complete
- * record length.
*/
static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp)
{
@@ -950,32 +954,16 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp)
return -EAGAIN;
}
- svsk->sk_reclen = ntohl(svsk->sk_reclen);
- if (!(svsk->sk_reclen & RPC_LAST_STREAM_FRAGMENT)) {
- /* FIXME: technically, a record can be fragmented,
- * and non-terminal fragments will not have the top
- * bit set in the fragment length header.
- * But apparently no known nfs clients send fragmented
- * records. */
- net_notice_ratelimited("RPC: multiple fragments per record not supported\n");
- goto err_delete;
- }
-
- svsk->sk_reclen &= RPC_FRAGMENT_SIZE_MASK;
- dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen);
- if (svsk->sk_reclen > serv->sv_max_mesg) {
- net_notice_ratelimited("RPC: fragment too large: 0x%08lx\n",
- (unsigned long)svsk->sk_reclen);
+ dprintk("svc: TCP record, %d bytes\n", svc_sock_reclen(svsk));
+ if (svc_sock_reclen(svsk) + svsk->sk_datalen >
+ serv->sv_max_mesg) {
+ net_notice_ratelimited("RPC: fragment too large: %d\n",
+ svc_sock_reclen(svsk));
goto err_delete;
}
}
- if (svsk->sk_reclen < 8)
- goto err_delete; /* client is nuts. */
-
- len = svsk->sk_reclen;
-
- return len;
+ return svc_sock_reclen(svsk);
error:
dprintk("RPC: TCP recv_record got %d\n", len);
return len;
@@ -1019,7 +1007,7 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
if (dst->iov_len < src->iov_len)
return -EAGAIN; /* whatever; just giving up. */
memcpy(dst->iov_base, src->iov_base, src->iov_len);
- xprt_complete_rqst(req->rq_task, svsk->sk_reclen);
+ xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len);
rqstp->rq_arg.len = 0;
return 0;
}
@@ -1038,6 +1026,17 @@ static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len)
return i;
}
+static void svc_tcp_fragment_received(struct svc_sock *svsk)
+{
+ /* If we have more data, signal svc_xprt_enqueue() to try again */
+ if (svc_recv_available(svsk) > sizeof(rpc_fraghdr))
+ set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ dprintk("svc: TCP %s record (%d bytes)\n",
+ svc_sock_final_rec(svsk) ? "final" : "nonfinal",
+ svc_sock_reclen(svsk));
+ svsk->sk_tcplen = 0;
+ svsk->sk_reclen = 0;
+}
/*
* Receive data from a TCP socket.
@@ -1064,29 +1063,39 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
goto error;
base = svc_tcp_restore_pages(svsk, rqstp);
- want = svsk->sk_reclen - base;
+ want = svc_sock_reclen(svsk) - (svsk->sk_tcplen - sizeof(rpc_fraghdr));
vec = rqstp->rq_vec;
pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0],
- svsk->sk_reclen);
+ svsk->sk_datalen + want);
rqstp->rq_respages = &rqstp->rq_pages[pnum];
+ rqstp->rq_next_page = rqstp->rq_respages + 1;
/* Now receive data */
len = svc_partial_recvfrom(rqstp, vec, pnum, want, base);
- if (len >= 0)
+ if (len >= 0) {
svsk->sk_tcplen += len;
- if (len != want) {
+ svsk->sk_datalen += len;
+ }
+ if (len != want || !svc_sock_final_rec(svsk)) {
svc_tcp_save_pages(svsk, rqstp);
if (len < 0 && len != -EAGAIN)
- goto err_other;
- dprintk("svc: incomplete TCP record (%d of %d)\n",
- svsk->sk_tcplen, svsk->sk_reclen);
+ goto err_delete;
+ if (len == want)
+ svc_tcp_fragment_received(svsk);
+ else
+ dprintk("svc: incomplete TCP record (%d of %d)\n",
+ (int)(svsk->sk_tcplen - sizeof(rpc_fraghdr)),
+ svc_sock_reclen(svsk));
goto err_noclose;
}
- rqstp->rq_arg.len = svsk->sk_reclen;
+ if (svc_sock_reclen(svsk) < 8)
+ goto err_delete; /* client is nuts. */
+
+ rqstp->rq_arg.len = svsk->sk_datalen;
rqstp->rq_arg.page_base = 0;
if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) {
rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len;
@@ -1103,11 +1112,8 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
len = receive_cb_reply(svsk, rqstp);
/* Reset TCP read info */
- svsk->sk_reclen = 0;
- svsk->sk_tcplen = 0;
- /* If we have more data, signal svc_xprt_enqueue() to try again */
- if (svc_recv_available(svsk) > sizeof(rpc_fraghdr))
- set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags);
+ svsk->sk_datalen = 0;
+ svc_tcp_fragment_received(svsk);
if (len < 0)
goto error;
@@ -1116,15 +1122,14 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
if (serv->sv_stats)
serv->sv_stats->nettcpcnt++;
- dprintk("svc: TCP complete record (%d bytes)\n", rqstp->rq_arg.len);
return rqstp->rq_arg.len;
error:
if (len != -EAGAIN)
- goto err_other;
+ goto err_delete;
dprintk("RPC: TCP recvfrom got EAGAIN\n");
return 0;
-err_other:
+err_delete:
printk(KERN_NOTICE "%s: recvfrom returned errno %d\n",
svsk->sk_xprt.xpt_server->sv_name, -len);
set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags);
@@ -1301,6 +1306,7 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv)
svsk->sk_reclen = 0;
svsk->sk_tcplen = 0;
+ svsk->sk_datalen = 0;
memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages));
tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 08f50afd5f2..56055632f15 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -318,7 +318,10 @@ xdr_shrink_bufhead(struct xdr_buf *buf, size_t len)
tail = buf->tail;
head = buf->head;
- BUG_ON (len > head->iov_len);
+
+ WARN_ON_ONCE(len > head->iov_len);
+ if (len > head->iov_len)
+ len = head->iov_len;
/* Shift the tail first */
if (tail->iov_len != 0) {
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index bd462a532ac..33811db8788 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -1136,10 +1136,18 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt)
void xprt_release(struct rpc_task *task)
{
struct rpc_xprt *xprt;
- struct rpc_rqst *req;
+ struct rpc_rqst *req = task->tk_rqstp;
- if (!(req = task->tk_rqstp))
+ if (req == NULL) {
+ if (task->tk_client) {
+ rcu_read_lock();
+ xprt = rcu_dereference(task->tk_client->cl_xprt);
+ if (xprt->snd_task == task)
+ xprt_release_write(xprt, task);
+ rcu_read_unlock();
+ }
return;
+ }
xprt = req->rq_xprt;
if (task->tk_ops->rpc_count_stats != NULL)
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index 41cb63b623d..0ce75524ed2 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -521,11 +521,11 @@ next_sge:
rqstp->rq_pages[ch_no] = NULL;
/*
- * Detach res pages. svc_release must see a resused count of
- * zero or it will attempt to put them.
+ * Detach res pages. If svc_release sees any it will attempt to
+ * put them.
*/
- while (rqstp->rq_resused)
- rqstp->rq_respages[--rqstp->rq_resused] = NULL;
+ while (rqstp->rq_next_page != rqstp->rq_respages)
+ *(--rqstp->rq_next_page) = NULL;
return err;
}
@@ -550,7 +550,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp,
/* rq_respages starts after the last arg page */
rqstp->rq_respages = &rqstp->rq_arg.pages[page_no];
- rqstp->rq_resused = 0;
+ rqstp->rq_next_page = &rqstp->rq_arg.pages[page_no];
/* Rebuild rq_arg head and tail. */
rqstp->rq_arg.head[0] = head->arg.head[0];
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index 42eb7ba0b90..c1d124dc772 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -548,6 +548,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
int sge_no;
int sge_bytes;
int page_no;
+ int pages;
int ret;
/* Post a recv buffer to handle another request. */
@@ -611,7 +612,8 @@ static int send_reply(struct svcxprt_rdma *rdma,
* respages array. They are our pages until the I/O
* completes.
*/
- for (page_no = 0; page_no < rqstp->rq_resused; page_no++) {
+ pages = rqstp->rq_next_page - rqstp->rq_respages;
+ for (page_no = 0; page_no < pages; page_no++) {
ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
ctxt->count++;
rqstp->rq_respages[page_no] = NULL;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 75853cabf4c..68b0a81c31d 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1746,7 +1746,6 @@ static inline void xs_reclassify_socketu(struct socket *sock)
{
struct sock *sk = sock->sk;
- BUG_ON(sock_owned_by_user(sk));
sock_lock_init_class_and_name(sk, "slock-AF_LOCAL-RPC",
&xs_slock_key[1], "sk_lock-AF_LOCAL-RPC", &xs_key[1]);
}
@@ -1755,7 +1754,6 @@ static inline void xs_reclassify_socket4(struct socket *sock)
{
struct sock *sk = sock->sk;
- BUG_ON(sock_owned_by_user(sk));
sock_lock_init_class_and_name(sk, "slock-AF_INET-RPC",
&xs_slock_key[0], "sk_lock-AF_INET-RPC", &xs_key[0]);
}
@@ -1764,13 +1762,16 @@ static inline void xs_reclassify_socket6(struct socket *sock)
{
struct sock *sk = sock->sk;
- BUG_ON(sock_owned_by_user(sk));
sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
&xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]);
}
static inline void xs_reclassify_socket(int family, struct socket *sock)
{
+ WARN_ON_ONCE(sock_owned_by_user(sock->sk));
+ if (sock_owned_by_user(sock->sk))
+ return;
+
switch (family) {
case AF_LOCAL:
xs_reclassify_socketu(sock);
@@ -1901,6 +1902,10 @@ static void xs_local_setup_socket(struct work_struct *work)
dprintk("RPC: xprt %p: socket %s does not exist\n",
xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
break;
+ case -ECONNREFUSED:
+ dprintk("RPC: xprt %p: connection refused for %s\n",
+ xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);
+ break;
default:
printk(KERN_ERR "%s: unhandled error (%d) connecting to %s\n",
__func__, -status,
@@ -2329,9 +2334,11 @@ static void *bc_malloc(struct rpc_task *task, size_t size)
struct page *page;
struct rpc_buffer *buf;
- BUG_ON(size > PAGE_SIZE - sizeof(struct rpc_buffer));
- page = alloc_page(GFP_KERNEL);
+ WARN_ON_ONCE(size > PAGE_SIZE - sizeof(struct rpc_buffer));
+ if (size > PAGE_SIZE - sizeof(struct rpc_buffer))
+ return NULL;
+ page = alloc_page(GFP_KERNEL);
if (!page)
return NULL;
@@ -2393,7 +2400,6 @@ static int bc_send_request(struct rpc_task *task)
{
struct rpc_rqst *req = task->tk_rqstp;
struct svc_xprt *xprt;
- struct svc_sock *svsk;
u32 len;
dprintk("sending request with xid: %08x\n", ntohl(req->rq_xid));
@@ -2401,7 +2407,6 @@ static int bc_send_request(struct rpc_task *task)
* Get the server socket associated with this callback xprt
*/
xprt = req->rq_xprt->bc_xprt;
- svsk = container_of(xprt, struct svc_sock, sk_xprt);
/*
* Grab the mutex to serialize data as the connection is shared
diff --git a/net/sysctl_net.c b/net/sysctl_net.c
index e3a6e37cd1c..9bc6db04be3 100644
--- a/net/sysctl_net.c
+++ b/net/sysctl_net.c
@@ -38,15 +38,24 @@ static int is_seen(struct ctl_table_set *set)
}
/* Return standard mode bits for table entry. */
-static int net_ctl_permissions(struct ctl_table_root *root,
- struct nsproxy *nsproxy,
+static int net_ctl_permissions(struct ctl_table_header *head,
struct ctl_table *table)
{
+ struct net *net = container_of(head->set, struct net, sysctls);
+ kuid_t root_uid = make_kuid(net->user_ns, 0);
+ kgid_t root_gid = make_kgid(net->user_ns, 0);
+
/* Allow network administrator to have same access as root. */
- if (capable(CAP_NET_ADMIN)) {
+ if (ns_capable(net->user_ns, CAP_NET_ADMIN) ||
+ uid_eq(root_uid, current_uid())) {
int mode = (table->mode >> 6) & 7;
return (mode << 6) | (mode << 3) | mode;
}
+ /* Allow netns root group to have the same access as the root group */
+ if (gid_eq(root_gid, current_gid())) {
+ int mode = (table->mode >> 3) & 7;
+ return (mode << 3) | mode;
+ }
return table->mode;
}
diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig
index 585460180ff..bc41bd31ead 100644
--- a/net/tipc/Kconfig
+++ b/net/tipc/Kconfig
@@ -20,18 +20,9 @@ menuconfig TIPC
If in doubt, say N.
-if TIPC
-
-config TIPC_ADVANCED
- bool "Advanced TIPC configuration"
- default n
- help
- Saying Y here will open some advanced configuration for TIPC.
- Most users do not need to bother; if unsure, just say N.
-
config TIPC_PORTS
int "Maximum number of ports in a node"
- depends on TIPC_ADVANCED
+ depends on TIPC
range 127 65535
default "8191"
help
@@ -40,5 +31,3 @@ config TIPC_PORTS
Setting this to a smaller value saves some memory,
setting it to higher allows for more ports.
-
-endif # TIPC
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index e4e6d8cd47e..54f89f90ac3 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -347,7 +347,7 @@ static void bclink_peek_nack(struct tipc_msg *msg)
tipc_node_lock(n_ptr);
- if (n_ptr->bclink.supported &&
+ if (n_ptr->bclink.recv_permitted &&
(n_ptr->bclink.last_in != n_ptr->bclink.last_sent) &&
(n_ptr->bclink.last_in == msg_bcgap_after(msg)))
n_ptr->bclink.oos_state = 2;
@@ -429,7 +429,7 @@ void tipc_bclink_recv_pkt(struct sk_buff *buf)
goto exit;
tipc_node_lock(node);
- if (unlikely(!node->bclink.supported))
+ if (unlikely(!node->bclink.recv_permitted))
goto unlock;
/* Handle broadcast protocol message */
@@ -564,7 +564,7 @@ exit:
u32 tipc_bclink_acks_missing(struct tipc_node *n_ptr)
{
- return (n_ptr->bclink.supported &&
+ return (n_ptr->bclink.recv_permitted &&
(tipc_bclink_get_last_sent() != n_ptr->bclink.acked));
}
@@ -619,16 +619,14 @@ static int tipc_bcbearer_send(struct sk_buff *buf,
if (bcbearer->remains_new.count == bcbearer->remains.count)
continue; /* bearer pair doesn't add anything */
- if (p->blocked ||
- p->media->send_msg(buf, p, &p->media->bcast_addr)) {
+ if (!tipc_bearer_blocked(p))
+ tipc_bearer_send(p, buf, &p->media->bcast_addr);
+ else if (s && !tipc_bearer_blocked(s))
/* unable to send on primary bearer */
- if (!s || s->blocked ||
- s->media->send_msg(buf, s,
- &s->media->bcast_addr)) {
- /* unable to send on either bearer */
- continue;
- }
- }
+ tipc_bearer_send(s, buf, &s->media->bcast_addr);
+ else
+ /* unable to send on either bearer */
+ continue;
if (s) {
bcbearer->bpairs[bp_index].primary = s;
@@ -731,8 +729,8 @@ int tipc_bclink_stats(char *buf, const u32 buf_size)
" TX naks:%u acks:%u dups:%u\n",
s->sent_nacks, s->sent_acks, s->retransmitted);
ret += tipc_snprintf(buf + ret, buf_size - ret,
- " Congestion bearer:%u link:%u Send queue max:%u avg:%u\n",
- s->bearer_congs, s->link_congs, s->max_queue_sz,
+ " Congestion link:%u Send queue max:%u avg:%u\n",
+ s->link_congs, s->max_queue_sz,
s->queue_sz_counts ?
(s->accu_queue_sz / s->queue_sz_counts) : 0);
@@ -766,7 +764,6 @@ int tipc_bclink_set_queue_limits(u32 limit)
void tipc_bclink_init(void)
{
- INIT_LIST_HEAD(&bcbearer->bearer.cong_links);
bcbearer->bearer.media = &bcbearer->media;
bcbearer->media.send_msg = tipc_bcbearer_send;
sprintf(bcbearer->media.name, "tipc-broadcast");
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 4ec5c80e8a7..aa62f93a912 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -279,116 +279,31 @@ void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
}
/*
- * bearer_push(): Resolve bearer congestion. Force the waiting
- * links to push out their unsent packets, one packet per link
- * per iteration, until all packets are gone or congestion reoccurs.
- * 'tipc_net_lock' is read_locked when this function is called
- * bearer.lock must be taken before calling
- * Returns binary true(1) ore false(0)
- */
-static int bearer_push(struct tipc_bearer *b_ptr)
-{
- u32 res = 0;
- struct tipc_link *ln, *tln;
-
- if (b_ptr->blocked)
- return 0;
-
- while (!list_empty(&b_ptr->cong_links) && (res != PUSH_FAILED)) {
- list_for_each_entry_safe(ln, tln, &b_ptr->cong_links, link_list) {
- res = tipc_link_push_packet(ln);
- if (res == PUSH_FAILED)
- break;
- if (res == PUSH_FINISHED)
- list_move_tail(&ln->link_list, &b_ptr->links);
- }
- }
- return list_empty(&b_ptr->cong_links);
-}
-
-void tipc_bearer_lock_push(struct tipc_bearer *b_ptr)
-{
- spin_lock_bh(&b_ptr->lock);
- bearer_push(b_ptr);
- spin_unlock_bh(&b_ptr->lock);
-}
-
-
-/*
- * Interrupt enabling new requests after bearer congestion or blocking:
+ * Interrupt enabling new requests after bearer blocking:
* See bearer_send().
*/
-void tipc_continue(struct tipc_bearer *b_ptr)
+void tipc_continue(struct tipc_bearer *b)
{
- spin_lock_bh(&b_ptr->lock);
- if (!list_empty(&b_ptr->cong_links))
- tipc_k_signal((Handler)tipc_bearer_lock_push, (unsigned long)b_ptr);
- b_ptr->blocked = 0;
- spin_unlock_bh(&b_ptr->lock);
+ spin_lock_bh(&b->lock);
+ b->blocked = 0;
+ spin_unlock_bh(&b->lock);
}
/*
- * Schedule link for sending of messages after the bearer
- * has been deblocked by 'continue()'. This method is called
- * when somebody tries to send a message via this link while
- * the bearer is congested. 'tipc_net_lock' is in read_lock here
- * bearer.lock is busy
+ * tipc_bearer_blocked - determines if bearer is currently blocked
*/
-static void tipc_bearer_schedule_unlocked(struct tipc_bearer *b_ptr,
- struct tipc_link *l_ptr)
+int tipc_bearer_blocked(struct tipc_bearer *b)
{
- list_move_tail(&l_ptr->link_list, &b_ptr->cong_links);
-}
-
-/*
- * Schedule link for sending of messages after the bearer
- * has been deblocked by 'continue()'. This method is called
- * when somebody tries to send a message via this link while
- * the bearer is congested. 'tipc_net_lock' is in read_lock here,
- * bearer.lock is free
- */
-void tipc_bearer_schedule(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr)
-{
- spin_lock_bh(&b_ptr->lock);
- tipc_bearer_schedule_unlocked(b_ptr, l_ptr);
- spin_unlock_bh(&b_ptr->lock);
-}
-
+ int res;
-/*
- * tipc_bearer_resolve_congestion(): Check if there is bearer congestion,
- * and if there is, try to resolve it before returning.
- * 'tipc_net_lock' is read_locked when this function is called
- */
-int tipc_bearer_resolve_congestion(struct tipc_bearer *b_ptr,
- struct tipc_link *l_ptr)
-{
- int res = 1;
+ spin_lock_bh(&b->lock);
+ res = b->blocked;
+ spin_unlock_bh(&b->lock);
- if (list_empty(&b_ptr->cong_links))
- return 1;
- spin_lock_bh(&b_ptr->lock);
- if (!bearer_push(b_ptr)) {
- tipc_bearer_schedule_unlocked(b_ptr, l_ptr);
- res = 0;
- }
- spin_unlock_bh(&b_ptr->lock);
return res;
}
/**
- * tipc_bearer_congested - determines if bearer is currently congested
- */
-int tipc_bearer_congested(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr)
-{
- if (unlikely(b_ptr->blocked))
- return 1;
- if (likely(list_empty(&b_ptr->cong_links)))
- return 0;
- return !tipc_bearer_resolve_congestion(b_ptr, l_ptr);
-}
-
-/**
* tipc_enable_bearer - enable bearer with the given name
*/
int tipc_enable_bearer(const char *name, u32 disc_domain, u32 priority)
@@ -489,7 +404,6 @@ restart:
b_ptr->net_plane = bearer_id + 'A';
b_ptr->active = 1;
b_ptr->priority = priority;
- INIT_LIST_HEAD(&b_ptr->cong_links);
INIT_LIST_HEAD(&b_ptr->links);
spin_lock_init(&b_ptr->lock);
@@ -528,7 +442,6 @@ int tipc_block_bearer(const char *name)
pr_info("Blocking bearer <%s>\n", name);
spin_lock_bh(&b_ptr->lock);
b_ptr->blocked = 1;
- list_splice_init(&b_ptr->cong_links, &b_ptr->links);
list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
struct tipc_node *n_ptr = l_ptr->owner;
@@ -555,7 +468,6 @@ static void bearer_disable(struct tipc_bearer *b_ptr)
spin_lock_bh(&b_ptr->lock);
b_ptr->blocked = 1;
b_ptr->media->disable_bearer(b_ptr);
- list_splice_init(&b_ptr->cong_links, &b_ptr->links);
list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
tipc_link_delete(l_ptr);
}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index dd4c2abf08e..39f1192d04b 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -120,7 +120,6 @@ struct tipc_media {
* @identity: array index of this bearer within TIPC bearer array
* @link_req: ptr to (optional) structure making periodic link setup requests
* @links: list of non-congested links associated with bearer
- * @cong_links: list of congested links associated with bearer
* @active: non-zero if bearer structure is represents a bearer
* @net_plane: network plane ('A' through 'H') currently associated with bearer
* @nodes: indicates which nodes in cluster can be reached through bearer
@@ -143,7 +142,6 @@ struct tipc_bearer {
u32 identity;
struct tipc_link_req *link_req;
struct list_head links;
- struct list_head cong_links;
int active;
char net_plane;
struct tipc_node_map nodes;
@@ -185,39 +183,23 @@ struct sk_buff *tipc_media_get_names(void);
struct sk_buff *tipc_bearer_get_names(void);
void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest);
void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest);
-void tipc_bearer_schedule(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr);
struct tipc_bearer *tipc_bearer_find(const char *name);
struct tipc_bearer *tipc_bearer_find_interface(const char *if_name);
struct tipc_media *tipc_media_find(const char *name);
-int tipc_bearer_resolve_congestion(struct tipc_bearer *b_ptr,
- struct tipc_link *l_ptr);
-int tipc_bearer_congested(struct tipc_bearer *b_ptr, struct tipc_link *l_ptr);
+int tipc_bearer_blocked(struct tipc_bearer *b_ptr);
void tipc_bearer_stop(void);
-void tipc_bearer_lock_push(struct tipc_bearer *b_ptr);
-
/**
* tipc_bearer_send- sends buffer to destination over bearer
*
- * Returns true (1) if successful, or false (0) if unable to send
- *
* IMPORTANT:
* The media send routine must not alter the buffer being passed in
* as it may be needed for later retransmission!
- *
- * If the media send routine returns a non-zero value (indicating that
- * it was unable to send the buffer), it must:
- * 1) mark the bearer as blocked,
- * 2) call tipc_continue() once the bearer is able to send again.
- * Media types that are unable to meet these two critera must ensure their
- * send routine always returns success -- even if the buffer was not sent --
- * and let TIPC's link code deal with the undelivered message.
*/
-static inline int tipc_bearer_send(struct tipc_bearer *b_ptr,
- struct sk_buff *buf,
+static inline void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
struct tipc_media_addr *dest)
{
- return !b_ptr->media->send_msg(buf, b_ptr, dest);
+ b->media->send_msg(buf, b, dest);
}
#endif /* _TIPC_BEARER_H */
diff --git a/net/tipc/core.c b/net/tipc/core.c
index bfe8af88469..fc05cecd748 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -42,11 +42,6 @@
#include <linux/module.h>
-#ifndef CONFIG_TIPC_PORTS
-#define CONFIG_TIPC_PORTS 8191
-#endif
-
-
/* global variables used by multiple sub-systems within TIPC */
int tipc_random __read_mostly;
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index 50eaa403eb6..1074b9587e8 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -243,7 +243,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
if ((type == DSC_REQ_MSG) && !link_fully_up && !b_ptr->blocked) {
rbuf = tipc_disc_init_msg(DSC_RESP_MSG, orig, b_ptr);
if (rbuf) {
- b_ptr->media->send_msg(rbuf, b_ptr, &media_addr);
+ tipc_bearer_send(b_ptr, rbuf, &media_addr);
kfree_skb(rbuf);
}
}
diff --git a/net/tipc/handler.c b/net/tipc/handler.c
index 111ff8300ae..b36f0fcd9bd 100644
--- a/net/tipc/handler.c
+++ b/net/tipc/handler.c
@@ -116,7 +116,6 @@ void tipc_handler_stop(void)
return;
handler_enabled = 0;
- tasklet_disable(&tipc_tasklet);
tasklet_kill(&tipc_tasklet);
spin_lock_bh(&qitem_lock);
diff --git a/net/tipc/link.c b/net/tipc/link.c
index a79c755cb41..daa6080a2a0 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1,7 +1,7 @@
/*
* net/tipc/link.c: TIPC link code
*
- * Copyright (c) 1996-2007, Ericsson AB
+ * Copyright (c) 1996-2007, 2012, Ericsson AB
* Copyright (c) 2004-2007, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -97,12 +97,13 @@ static int link_send_sections_long(struct tipc_port *sender,
struct iovec const *msg_sect,
u32 num_sect, unsigned int total_len,
u32 destnode);
-static void link_check_defragm_bufs(struct tipc_link *l_ptr);
static void link_state_event(struct tipc_link *l_ptr, u32 event);
static void link_reset_statistics(struct tipc_link *l_ptr);
static void link_print(struct tipc_link *l_ptr, const char *str);
static void link_start(struct tipc_link *l_ptr);
static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf);
+static void tipc_link_send_sync(struct tipc_link *l);
+static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf);
/*
* Simple link routines
@@ -269,7 +270,6 @@ static void link_timeout(struct tipc_link *l_ptr)
}
/* do all other link processing performed on a periodic basis */
- link_check_defragm_bufs(l_ptr);
link_state_event(l_ptr, TIMEOUT_EVT);
@@ -712,6 +712,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
link_activate(l_ptr);
tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
+ if (l_ptr->owner->working_links == 1)
+ tipc_link_send_sync(l_ptr);
link_set_timer(l_ptr, cont_intv);
break;
case RESET_MSG:
@@ -745,6 +747,8 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
link_activate(l_ptr);
tipc_link_send_proto_msg(l_ptr, STATE_MSG, 1, 0, 0, 0, 0);
l_ptr->fsm_msg_cnt++;
+ if (l_ptr->owner->working_links == 1)
+ tipc_link_send_sync(l_ptr);
link_set_timer(l_ptr, cont_intv);
break;
case RESET_MSG:
@@ -872,17 +876,12 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
return link_send_long_buf(l_ptr, buf);
/* Packet can be queued or sent. */
- if (likely(!tipc_bearer_congested(l_ptr->b_ptr, l_ptr) &&
+ if (likely(!tipc_bearer_blocked(l_ptr->b_ptr) &&
!link_congested(l_ptr))) {
link_add_to_outqueue(l_ptr, buf, msg);
- if (likely(tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr))) {
- l_ptr->unacked_window = 0;
- } else {
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
- l_ptr->stats.bearer_congs++;
- l_ptr->next_out = buf;
- }
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+ l_ptr->unacked_window = 0;
return dsz;
}
/* Congestion: can message be bundled ? */
@@ -891,10 +890,8 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
/* Try adding message to an existing bundle */
if (l_ptr->next_out &&
- link_bundle_buf(l_ptr, l_ptr->last_out, buf)) {
- tipc_bearer_resolve_congestion(l_ptr->b_ptr, l_ptr);
+ link_bundle_buf(l_ptr, l_ptr->last_out, buf))
return dsz;
- }
/* Try creating a new bundle */
if (size <= max_packet * 2 / 3) {
@@ -917,7 +914,6 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
if (!l_ptr->next_out)
l_ptr->next_out = buf;
link_add_to_outqueue(l_ptr, buf, msg);
- tipc_bearer_resolve_congestion(l_ptr->b_ptr, l_ptr);
return dsz;
}
@@ -949,7 +945,48 @@ int tipc_link_send(struct sk_buff *buf, u32 dest, u32 selector)
return res;
}
-/**
+/*
+ * tipc_link_send_sync - synchronize broadcast link endpoints.
+ *
+ * Give a newly added peer node the sequence number where it should
+ * start receiving and acking broadcast packets.
+ *
+ * Called with node locked
+ */
+static void tipc_link_send_sync(struct tipc_link *l)
+{
+ struct sk_buff *buf;
+ struct tipc_msg *msg;
+
+ buf = tipc_buf_acquire(INT_H_SIZE);
+ if (!buf)
+ return;
+
+ msg = buf_msg(buf);
+ tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, l->addr);
+ msg_set_last_bcast(msg, l->owner->bclink.acked);
+ link_add_chain_to_outqueue(l, buf, 0);
+ tipc_link_push_queue(l);
+}
+
+/*
+ * tipc_link_recv_sync - synchronize broadcast link endpoints.
+ * Receive the sequence number where we should start receiving and
+ * acking broadcast packets from a newly added peer node, and open
+ * up for reception of such packets.
+ *
+ * Called with node locked
+ */
+static void tipc_link_recv_sync(struct tipc_node *n, struct sk_buff *buf)
+{
+ struct tipc_msg *msg = buf_msg(buf);
+
+ n->bclink.last_sent = n->bclink.last_in = msg_last_bcast(msg);
+ n->bclink.recv_permitted = true;
+ kfree_skb(buf);
+}
+
+/*
* tipc_link_send_names - send name table entries to new neighbor
*
* Send routine for bulk delivery of name table messages when contact
@@ -1006,16 +1043,11 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
if (likely(!link_congested(l_ptr))) {
if (likely(msg_size(msg) <= l_ptr->max_pkt)) {
- if (likely(list_empty(&l_ptr->b_ptr->cong_links))) {
+ if (likely(!tipc_bearer_blocked(l_ptr->b_ptr))) {
link_add_to_outqueue(l_ptr, buf, msg);
- if (likely(tipc_bearer_send(l_ptr->b_ptr, buf,
- &l_ptr->media_addr))) {
- l_ptr->unacked_window = 0;
- return res;
- }
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
- l_ptr->stats.bearer_congs++;
- l_ptr->next_out = buf;
+ tipc_bearer_send(l_ptr->b_ptr, buf,
+ &l_ptr->media_addr);
+ l_ptr->unacked_window = 0;
return res;
}
} else
@@ -1106,7 +1138,7 @@ exit:
/* Exit if link (or bearer) is congested */
if (link_congested(l_ptr) ||
- !list_empty(&l_ptr->b_ptr->cong_links)) {
+ tipc_bearer_blocked(l_ptr->b_ptr)) {
res = link_schedule_port(l_ptr,
sender->ref, res);
goto exit;
@@ -1329,15 +1361,11 @@ u32 tipc_link_push_packet(struct tipc_link *l_ptr)
if (r_q_size && buf) {
msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
- if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- l_ptr->retransm_queue_head = mod(++r_q_head);
- l_ptr->retransm_queue_size = --r_q_size;
- l_ptr->stats.retransmitted++;
- return 0;
- } else {
- l_ptr->stats.bearer_congs++;
- return PUSH_FAILED;
- }
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+ l_ptr->retransm_queue_head = mod(++r_q_head);
+ l_ptr->retransm_queue_size = --r_q_size;
+ l_ptr->stats.retransmitted++;
+ return 0;
}
/* Send deferred protocol message, if any: */
@@ -1345,15 +1373,11 @@ u32 tipc_link_push_packet(struct tipc_link *l_ptr)
if (buf) {
msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
- if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- l_ptr->unacked_window = 0;
- kfree_skb(buf);
- l_ptr->proto_msg_queue = NULL;
- return 0;
- } else {
- l_ptr->stats.bearer_congs++;
- return PUSH_FAILED;
- }
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+ l_ptr->unacked_window = 0;
+ kfree_skb(buf);
+ l_ptr->proto_msg_queue = NULL;
+ return 0;
}
/* Send one deferred data message, if send window not full: */
@@ -1366,18 +1390,14 @@ u32 tipc_link_push_packet(struct tipc_link *l_ptr)
if (mod(next - first) < l_ptr->queue_limit[0]) {
msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
- if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- if (msg_user(msg) == MSG_BUNDLER)
- msg_set_type(msg, CLOSED_MSG);
- l_ptr->next_out = buf->next;
- return 0;
- } else {
- l_ptr->stats.bearer_congs++;
- return PUSH_FAILED;
- }
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+ if (msg_user(msg) == MSG_BUNDLER)
+ msg_set_type(msg, CLOSED_MSG);
+ l_ptr->next_out = buf->next;
+ return 0;
}
}
- return PUSH_FINISHED;
+ return 1;
}
/*
@@ -1388,15 +1408,12 @@ void tipc_link_push_queue(struct tipc_link *l_ptr)
{
u32 res;
- if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr))
+ if (tipc_bearer_blocked(l_ptr->b_ptr))
return;
do {
res = tipc_link_push_packet(l_ptr);
} while (!res);
-
- if (res == PUSH_FAILED)
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
}
static void link_reset_all(unsigned long addr)
@@ -1454,9 +1471,8 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,
tipc_addr_string_fill(addr_string, n_ptr->addr);
pr_info("Broadcast link info for %s\n", addr_string);
- pr_info("Supportable: %d, Supported: %d, Acked: %u\n",
- n_ptr->bclink.supportable,
- n_ptr->bclink.supported,
+ pr_info("Reception permitted: %d, Acked: %u\n",
+ n_ptr->bclink.recv_permitted,
n_ptr->bclink.acked);
pr_info("Last in: %u, Oos state: %u, Last sent: %u\n",
n_ptr->bclink.last_in,
@@ -1481,7 +1497,7 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
msg = buf_msg(buf);
- if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) {
+ if (tipc_bearer_blocked(l_ptr->b_ptr)) {
if (l_ptr->retransm_queue_size == 0) {
l_ptr->retransm_queue_head = msg_seqno(msg);
l_ptr->retransm_queue_size = retransmits;
@@ -1491,7 +1507,7 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
}
return;
} else {
- /* Detect repeated retransmit failures on uncongested bearer */
+ /* Detect repeated retransmit failures on unblocked bearer */
if (l_ptr->last_retransmitted == msg_seqno(msg)) {
if (++l_ptr->stale_count > 100) {
link_retransmit_failure(l_ptr, buf);
@@ -1507,17 +1523,10 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
msg = buf_msg(buf);
msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
- if (tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- buf = buf->next;
- retransmits--;
- l_ptr->stats.retransmitted++;
- } else {
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
- l_ptr->stats.bearer_congs++;
- l_ptr->retransm_queue_head = buf_seqno(buf);
- l_ptr->retransm_queue_size = retransmits;
- return;
- }
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
+ buf = buf->next;
+ retransmits--;
+ l_ptr->stats.retransmitted++;
}
l_ptr->retransm_queue_head = l_ptr->retransm_queue_size = 0;
@@ -1676,7 +1685,7 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr)
ackd = msg_ack(msg);
/* Release acked messages */
- if (n_ptr->bclink.supported)
+ if (n_ptr->bclink.recv_permitted)
tipc_bclink_acknowledge(n_ptr, msg_bcast_ack(msg));
crs = l_ptr->first_out;
@@ -1727,9 +1736,14 @@ deliver:
tipc_link_recv_bundle(buf);
continue;
case NAME_DISTRIBUTOR:
+ n_ptr->bclink.recv_permitted = true;
tipc_node_unlock(n_ptr);
tipc_named_recv(buf);
continue;
+ case BCAST_PROTOCOL:
+ tipc_link_recv_sync(n_ptr, buf);
+ tipc_node_unlock(n_ptr);
+ continue;
case CONN_MANAGER:
tipc_node_unlock(n_ptr);
tipc_port_recv_proto_msg(buf);
@@ -1772,16 +1786,19 @@ deliver:
continue;
}
+ /* Link is not in state WORKING_WORKING */
if (msg_user(msg) == LINK_PROTOCOL) {
link_recv_proto_msg(l_ptr, buf);
head = link_insert_deferred_queue(l_ptr, head);
tipc_node_unlock(n_ptr);
continue;
}
+
+ /* Traffic message. Conditionally activate link */
link_state_event(l_ptr, TRAFFIC_MSG_EVT);
if (link_working_working(l_ptr)) {
- /* Re-insert in front of queue */
+ /* Re-insert buffer in front of queue */
buf->next = head;
head = buf;
tipc_node_unlock(n_ptr);
@@ -1972,21 +1989,13 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
- /* Defer message if bearer is already congested */
- if (tipc_bearer_congested(l_ptr->b_ptr, l_ptr)) {
- l_ptr->proto_msg_queue = buf;
- return;
- }
-
- /* Defer message if attempting to send results in bearer congestion */
- if (!tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr)) {
- tipc_bearer_schedule(l_ptr->b_ptr, l_ptr);
+ /* Defer message if bearer is already blocked */
+ if (tipc_bearer_blocked(l_ptr->b_ptr)) {
l_ptr->proto_msg_queue = buf;
- l_ptr->stats.bearer_congs++;
return;
}
- /* Discard message if it was sent successfully */
+ tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
l_ptr->unacked_window = 0;
kfree_skb(buf);
}
@@ -2057,7 +2066,6 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
} else {
l_ptr->max_pkt = l_ptr->max_pkt_target;
}
- l_ptr->owner->bclink.supportable = (max_pkt_info != 0);
/* Synchronize broadcast link info, if not done previously */
if (!tipc_node_is_up(l_ptr->owner)) {
@@ -2112,7 +2120,7 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
}
/* Protocol message before retransmits, reduce loss risk */
- if (l_ptr->owner->bclink.supported)
+ if (l_ptr->owner->bclink.recv_permitted)
tipc_bclink_update_link_state(l_ptr->owner,
msg_last_bcast(msg));
@@ -2487,16 +2495,6 @@ static void set_expected_frags(struct sk_buff *buf, u32 exp)
msg_set_bcast_ack(buf_msg(buf), exp);
}
-static u32 get_timer_cnt(struct sk_buff *buf)
-{
- return msg_reroute_cnt(buf_msg(buf));
-}
-
-static void incr_timer_cnt(struct sk_buff *buf)
-{
- msg_incr_reroute_cnt(buf_msg(buf));
-}
-
/*
* tipc_link_recv_fragment(): Called with node lock on. Returns
* the reassembled buffer if message is complete.
@@ -2575,38 +2573,6 @@ int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb,
return 0;
}
-/**
- * link_check_defragm_bufs - flush stale incoming message fragments
- * @l_ptr: pointer to link
- */
-static void link_check_defragm_bufs(struct tipc_link *l_ptr)
-{
- struct sk_buff *prev = NULL;
- struct sk_buff *next = NULL;
- struct sk_buff *buf = l_ptr->defragm_buf;
-
- if (!buf)
- return;
- if (!link_working_working(l_ptr))
- return;
- while (buf) {
- u32 cnt = get_timer_cnt(buf);
-
- next = buf->next;
- if (cnt < 4) {
- incr_timer_cnt(buf);
- prev = buf;
- } else {
- if (prev)
- prev->next = buf->next;
- else
- l_ptr->defragm_buf = buf->next;
- kfree_skb(buf);
- }
- buf = next;
- }
-}
-
static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance)
{
if ((tolerance < TIPC_MIN_LINK_TOL) || (tolerance > TIPC_MAX_LINK_TOL))
@@ -2937,8 +2903,8 @@ static int tipc_link_stats(const char *name, char *buf, const u32 buf_size)
s->sent_nacks, s->sent_acks, s->retransmitted);
ret += tipc_snprintf(buf + ret, buf_size - ret,
- " Congestion bearer:%u link:%u Send queue"
- " max:%u avg:%u\n", s->bearer_congs, s->link_congs,
+ " Congestion link:%u Send queue"
+ " max:%u avg:%u\n", s->link_congs,
s->max_queue_sz, s->queue_sz_counts ?
(s->accu_queue_sz / s->queue_sz_counts) : 0);
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 6e921121be0..c048ed1cbd7 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -40,9 +40,6 @@
#include "msg.h"
#include "node.h"
-#define PUSH_FAILED 1
-#define PUSH_FINISHED 2
-
/*
* Out-of-range value for link sequence numbers
*/
@@ -82,7 +79,6 @@ struct tipc_stats {
u32 recv_fragmented;
u32 recv_fragments;
u32 link_congs; /* # port sends blocked by congestion */
- u32 bearer_congs;
u32 deferred_recv;
u32 duplicates;
u32 max_queue_sz; /* send queue size high water mark */
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 55d3928dfd6..e0d08055754 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -262,7 +262,7 @@ void tipc_named_node_up(unsigned long nodearg)
named_distribute(&message_list, node, &publ_zone, max_item_buf);
read_unlock_bh(&tipc_nametbl_lock);
- tipc_link_send_names(&message_list, (u32)node);
+ tipc_link_send_names(&message_list, node);
}
/**
diff --git a/net/tipc/node.c b/net/tipc/node.c
index d21db204e25..48f39dd3eae 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1,7 +1,7 @@
/*
* net/tipc/node.c: TIPC node management routines
*
- * Copyright (c) 2000-2006, Ericsson AB
+ * Copyright (c) 2000-2006, 2012 Ericsson AB
* Copyright (c) 2005-2006, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -263,12 +263,9 @@ void tipc_node_detach_link(struct tipc_node *n_ptr, struct tipc_link *l_ptr)
static void node_established_contact(struct tipc_node *n_ptr)
{
tipc_k_signal((Handler)tipc_named_node_up, n_ptr->addr);
-
- if (n_ptr->bclink.supportable) {
- n_ptr->bclink.acked = tipc_bclink_get_last_sent();
- tipc_bclink_add_node(n_ptr->addr);
- n_ptr->bclink.supported = 1;
- }
+ n_ptr->bclink.oos_state = 0;
+ n_ptr->bclink.acked = tipc_bclink_get_last_sent();
+ tipc_bclink_add_node(n_ptr->addr);
}
static void node_name_purge_complete(unsigned long node_addr)
@@ -294,7 +291,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)
tipc_addr_string_fill(addr_string, n_ptr->addr));
/* Flush broadcast link info associated with lost node */
- if (n_ptr->bclink.supported) {
+ if (n_ptr->bclink.recv_permitted) {
while (n_ptr->bclink.deferred_head) {
struct sk_buff *buf = n_ptr->bclink.deferred_head;
n_ptr->bclink.deferred_head = buf->next;
@@ -310,7 +307,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)
tipc_bclink_remove_node(n_ptr->addr);
tipc_bclink_acknowledge(n_ptr, INVALID_LINK_SEQ);
- n_ptr->bclink.supported = 0;
+ n_ptr->bclink.recv_permitted = false;
}
/* Abort link changeover */
diff --git a/net/tipc/node.h b/net/tipc/node.h
index cfcaf4d6e48..3c189b35b10 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -67,8 +67,6 @@
* @permit_changeover: non-zero if node has redundant links to this system
* @signature: node instance identifier
* @bclink: broadcast-related info
- * @supportable: non-zero if node supports TIPC b'cast link capability
- * @supported: non-zero if node supports TIPC b'cast capability
* @acked: sequence # of last outbound b'cast message acknowledged by node
* @last_in: sequence # of last in-sequence b'cast message received from node
* @last_sent: sequence # of last b'cast message sent by node
@@ -77,6 +75,7 @@
* @deferred_head: oldest OOS b'cast message received from node
* @deferred_tail: newest OOS b'cast message received from node
* @defragm: list of partially reassembled b'cast message fragments from node
+ * @recv_permitted: true if node is allowed to receive b'cast messages
*/
struct tipc_node {
u32 addr;
@@ -92,8 +91,6 @@ struct tipc_node {
int permit_changeover;
u32 signature;
struct {
- u8 supportable;
- u8 supported;
u32 acked;
u32 last_in;
u32 last_sent;
@@ -102,6 +99,7 @@ struct tipc_node {
struct sk_buff *deferred_head;
struct sk_buff *deferred_tail;
struct sk_buff *defragm;
+ bool recv_permitted;
} bclink;
};
diff --git a/net/tipc/port.c b/net/tipc/port.c
index 07c42fba672..18098cac62f 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -726,7 +726,7 @@ static void port_dispatcher_sigh(void *dummy)
if (unlikely(!cb))
goto reject;
if (unlikely(!connected)) {
- if (tipc_connect2port(dref, &orig))
+ if (tipc_connect(dref, &orig))
goto reject;
} else if (peer_invalid)
goto reject;
@@ -1036,15 +1036,30 @@ int tipc_withdraw(u32 ref, unsigned int scope, struct tipc_name_seq const *seq)
return res;
}
-int tipc_connect2port(u32 ref, struct tipc_portid const *peer)
+int tipc_connect(u32 ref, struct tipc_portid const *peer)
{
struct tipc_port *p_ptr;
- struct tipc_msg *msg;
- int res = -EINVAL;
+ int res;
p_ptr = tipc_port_lock(ref);
if (!p_ptr)
return -EINVAL;
+ res = __tipc_connect(ref, p_ptr, peer);
+ tipc_port_unlock(p_ptr);
+ return res;
+}
+
+/*
+ * __tipc_connect - connect to a remote peer
+ *
+ * Port must be locked.
+ */
+int __tipc_connect(u32 ref, struct tipc_port *p_ptr,
+ struct tipc_portid const *peer)
+{
+ struct tipc_msg *msg;
+ int res = -EINVAL;
+
if (p_ptr->published || p_ptr->connected)
goto exit;
if (!peer->ref)
@@ -1067,17 +1082,16 @@ int tipc_connect2port(u32 ref, struct tipc_portid const *peer)
(net_ev_handler)port_handle_node_down);
res = 0;
exit:
- tipc_port_unlock(p_ptr);
p_ptr->max_pkt = tipc_link_get_max_pkt(peer->node, ref);
return res;
}
-/**
- * tipc_disconnect_port - disconnect port from peer
+/*
+ * __tipc_disconnect - disconnect port from peer
*
* Port must be locked.
*/
-int tipc_disconnect_port(struct tipc_port *tp_ptr)
+int __tipc_disconnect(struct tipc_port *tp_ptr)
{
int res;
@@ -1104,7 +1118,7 @@ int tipc_disconnect(u32 ref)
p_ptr = tipc_port_lock(ref);
if (!p_ptr)
return -EINVAL;
- res = tipc_disconnect_port(p_ptr);
+ res = __tipc_disconnect(p_ptr);
tipc_port_unlock(p_ptr);
return res;
}
diff --git a/net/tipc/port.h b/net/tipc/port.h
index 4660e306579..fb66e2e5f4d 100644
--- a/net/tipc/port.h
+++ b/net/tipc/port.h
@@ -190,7 +190,7 @@ int tipc_publish(u32 portref, unsigned int scope,
int tipc_withdraw(u32 portref, unsigned int scope,
struct tipc_name_seq const *name_seq);
-int tipc_connect2port(u32 portref, struct tipc_portid const *port);
+int tipc_connect(u32 portref, struct tipc_portid const *port);
int tipc_disconnect(u32 portref);
@@ -200,7 +200,9 @@ int tipc_shutdown(u32 ref);
/*
* The following routines require that the port be locked on entry
*/
-int tipc_disconnect_port(struct tipc_port *tp_ptr);
+int __tipc_disconnect(struct tipc_port *tp_ptr);
+int __tipc_connect(u32 ref, struct tipc_port *p_ptr,
+ struct tipc_portid const *peer);
int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg);
/*
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index fd5f042dbff..9b4e4833a48 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -1,8 +1,8 @@
/*
* net/tipc/socket.c: TIPC socket API
*
- * Copyright (c) 2001-2007, Ericsson AB
- * Copyright (c) 2004-2008, 2010-2011, Wind River Systems
+ * Copyright (c) 2001-2007, 2012 Ericsson AB
+ * Copyright (c) 2004-2008, 2010-2012, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,7 +43,7 @@
#define SS_LISTENING -1 /* socket is listening */
#define SS_READY -2 /* socket is connectionless */
-#define OVERLOAD_LIMIT_BASE 5000
+#define OVERLOAD_LIMIT_BASE 10000
#define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */
struct tipc_sock {
@@ -62,6 +62,8 @@ struct tipc_sock {
static int backlog_rcv(struct sock *sk, struct sk_buff *skb);
static u32 dispatch(struct tipc_port *tport, struct sk_buff *buf);
static void wakeupdispatch(struct tipc_port *tport);
+static void tipc_data_ready(struct sock *sk, int len);
+static void tipc_write_space(struct sock *sk);
static const struct proto_ops packet_ops;
static const struct proto_ops stream_ops;
@@ -71,8 +73,6 @@ static struct proto tipc_proto;
static int sockets_enabled;
-static atomic_t tipc_queue_size = ATOMIC_INIT(0);
-
/*
* Revised TIPC socket locking policy:
*
@@ -126,7 +126,6 @@ static atomic_t tipc_queue_size = ATOMIC_INIT(0);
static void advance_rx_queue(struct sock *sk)
{
kfree_skb(__skb_dequeue(&sk->sk_receive_queue));
- atomic_dec(&tipc_queue_size);
}
/**
@@ -138,10 +137,8 @@ static void discard_rx_queue(struct sock *sk)
{
struct sk_buff *buf;
- while ((buf = __skb_dequeue(&sk->sk_receive_queue))) {
- atomic_dec(&tipc_queue_size);
+ while ((buf = __skb_dequeue(&sk->sk_receive_queue)))
kfree_skb(buf);
- }
}
/**
@@ -153,10 +150,8 @@ static void reject_rx_queue(struct sock *sk)
{
struct sk_buff *buf;
- while ((buf = __skb_dequeue(&sk->sk_receive_queue))) {
+ while ((buf = __skb_dequeue(&sk->sk_receive_queue)))
tipc_reject_msg(buf, TIPC_ERR_NO_PORT);
- atomic_dec(&tipc_queue_size);
- }
}
/**
@@ -221,6 +216,8 @@ static int tipc_create(struct net *net, struct socket *sock, int protocol,
sock_init_data(sock, sk);
sk->sk_backlog_rcv = backlog_rcv;
sk->sk_rcvbuf = TIPC_FLOW_CONTROL_WIN * 2 * TIPC_MAX_USER_MSG_SIZE * 2;
+ sk->sk_data_ready = tipc_data_ready;
+ sk->sk_write_space = tipc_write_space;
tipc_sk(sk)->p = tp_ptr;
tipc_sk(sk)->conn_timeout = CONN_TIMEOUT_DEFAULT;
@@ -276,7 +273,6 @@ static int release(struct socket *sock)
buf = __skb_dequeue(&sk->sk_receive_queue);
if (buf == NULL)
break;
- atomic_dec(&tipc_queue_size);
if (TIPC_SKB_CB(buf)->handle != 0)
kfree_skb(buf);
else {
@@ -408,7 +404,7 @@ static int get_name(struct socket *sock, struct sockaddr *uaddr,
* socket state flags set
* ------------ ---------
* unconnected no read flags
- * no write flags
+ * POLLOUT if port is not congested
*
* connecting POLLIN/POLLRDNORM if ACK/NACK in rx queue
* no write flags
@@ -435,9 +431,13 @@ static unsigned int poll(struct file *file, struct socket *sock,
struct sock *sk = sock->sk;
u32 mask = 0;
- poll_wait(file, sk_sleep(sk), wait);
+ sock_poll_wait(file, sk_sleep(sk), wait);
switch ((int)sock->state) {
+ case SS_UNCONNECTED:
+ if (!tipc_sk_port(sk)->congested)
+ mask |= POLLOUT;
+ break;
case SS_READY:
case SS_CONNECTED:
if (!tipc_sk_port(sk)->congested)
@@ -775,16 +775,19 @@ exit:
static int auto_connect(struct socket *sock, struct tipc_msg *msg)
{
struct tipc_sock *tsock = tipc_sk(sock->sk);
-
- if (msg_errcode(msg)) {
- sock->state = SS_DISCONNECTING;
- return -ECONNREFUSED;
- }
+ struct tipc_port *p_ptr;
tsock->peer_name.ref = msg_origport(msg);
tsock->peer_name.node = msg_orignode(msg);
- tipc_connect2port(tsock->p->ref, &tsock->peer_name);
- tipc_set_portimportance(tsock->p->ref, msg_importance(msg));
+ p_ptr = tipc_port_deref(tsock->p->ref);
+ if (!p_ptr)
+ return -EINVAL;
+
+ __tipc_connect(tsock->p->ref, p_ptr, &tsock->peer_name);
+
+ if (msg_importance(msg) > TIPC_CRITICAL_IMPORTANCE)
+ return -EINVAL;
+ msg_set_importance(&p_ptr->phdr, (u32)msg_importance(msg));
sock->state = SS_CONNECTED;
return 0;
}
@@ -943,13 +946,6 @@ restart:
sz = msg_data_sz(msg);
err = msg_errcode(msg);
- /* Complete connection setup for an implied connect */
- if (unlikely(sock->state == SS_CONNECTING)) {
- res = auto_connect(sock, msg);
- if (res)
- goto exit;
- }
-
/* Discard an empty non-errored message & try again */
if ((!sz) && (!err)) {
advance_rx_queue(sk);
@@ -1126,6 +1122,39 @@ exit:
}
/**
+ * tipc_write_space - wake up thread if port congestion is released
+ * @sk: socket
+ */
+static void tipc_write_space(struct sock *sk)
+{
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLOUT |
+ POLLWRNORM | POLLWRBAND);
+ rcu_read_unlock();
+}
+
+/**
+ * tipc_data_ready - wake up threads to indicate messages have been received
+ * @sk: socket
+ * @len: the length of messages
+ */
+static void tipc_data_ready(struct sock *sk, int len)
+{
+ struct socket_wq *wq;
+
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (wq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, POLLIN |
+ POLLRDNORM | POLLRDBAND);
+ rcu_read_unlock();
+}
+
+/**
* rx_queue_full - determine if receive queue can accept another message
* @msg: message to be added to queue
* @queue_size: current size of queue
@@ -1154,6 +1183,83 @@ static int rx_queue_full(struct tipc_msg *msg, u32 queue_size, u32 base)
}
/**
+ * filter_connect - Handle all incoming messages for a connection-based socket
+ * @tsock: TIPC socket
+ * @msg: message
+ *
+ * Returns TIPC error status code and socket error status code
+ * once it encounters some errors
+ */
+static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
+{
+ struct socket *sock = tsock->sk.sk_socket;
+ struct tipc_msg *msg = buf_msg(*buf);
+ struct sock *sk = &tsock->sk;
+ u32 retval = TIPC_ERR_NO_PORT;
+ int res;
+
+ if (msg_mcast(msg))
+ return retval;
+
+ switch ((int)sock->state) {
+ case SS_CONNECTED:
+ /* Accept only connection-based messages sent by peer */
+ if (msg_connected(msg) && tipc_port_peer_msg(tsock->p, msg)) {
+ if (unlikely(msg_errcode(msg))) {
+ sock->state = SS_DISCONNECTING;
+ __tipc_disconnect(tsock->p);
+ }
+ retval = TIPC_OK;
+ }
+ break;
+ case SS_CONNECTING:
+ /* Accept only ACK or NACK message */
+ if (unlikely(msg_errcode(msg))) {
+ sock->state = SS_DISCONNECTING;
+ sk->sk_err = -ECONNREFUSED;
+ retval = TIPC_OK;
+ break;
+ }
+
+ if (unlikely(!msg_connected(msg)))
+ break;
+
+ res = auto_connect(sock, msg);
+ if (res) {
+ sock->state = SS_DISCONNECTING;
+ sk->sk_err = res;
+ retval = TIPC_OK;
+ break;
+ }
+
+ /* If an incoming message is an 'ACK-', it should be
+ * discarded here because it doesn't contain useful
+ * data. In addition, we should try to wake up
+ * connect() routine if sleeping.
+ */
+ if (msg_data_sz(msg) == 0) {
+ kfree_skb(*buf);
+ *buf = NULL;
+ if (waitqueue_active(sk_sleep(sk)))
+ wake_up_interruptible(sk_sleep(sk));
+ }
+ retval = TIPC_OK;
+ break;
+ case SS_LISTENING:
+ case SS_UNCONNECTED:
+ /* Accept only SYN message */
+ if (!msg_connected(msg) && !(msg_errcode(msg)))
+ retval = TIPC_OK;
+ break;
+ case SS_DISCONNECTING:
+ break;
+ default:
+ pr_err("Unknown socket state %u\n", sock->state);
+ }
+ return retval;
+}
+
+/**
* filter_rcv - validate incoming message
* @sk: socket
* @buf: message
@@ -1170,6 +1276,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
struct socket *sock = sk->sk_socket;
struct tipc_msg *msg = buf_msg(buf);
u32 recv_q_len;
+ u32 res = TIPC_OK;
/* Reject message if it is wrong sort of message for socket */
if (msg_type(msg) > TIPC_DIRECT_MSG)
@@ -1179,32 +1286,12 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
if (msg_connected(msg))
return TIPC_ERR_NO_PORT;
} else {
- if (msg_mcast(msg))
- return TIPC_ERR_NO_PORT;
- if (sock->state == SS_CONNECTED) {
- if (!msg_connected(msg) ||
- !tipc_port_peer_msg(tipc_sk_port(sk), msg))
- return TIPC_ERR_NO_PORT;
- } else if (sock->state == SS_CONNECTING) {
- if (!msg_connected(msg) && (msg_errcode(msg) == 0))
- return TIPC_ERR_NO_PORT;
- } else if (sock->state == SS_LISTENING) {
- if (msg_connected(msg) || msg_errcode(msg))
- return TIPC_ERR_NO_PORT;
- } else if (sock->state == SS_DISCONNECTING) {
- return TIPC_ERR_NO_PORT;
- } else /* (sock->state == SS_UNCONNECTED) */ {
- if (msg_connected(msg) || msg_errcode(msg))
- return TIPC_ERR_NO_PORT;
- }
+ res = filter_connect(tipc_sk(sk), &buf);
+ if (res != TIPC_OK || buf == NULL)
+ return res;
}
/* Reject message if there isn't room to queue it */
- recv_q_len = (u32)atomic_read(&tipc_queue_size);
- if (unlikely(recv_q_len >= OVERLOAD_LIMIT_BASE)) {
- if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE))
- return TIPC_ERR_OVERLOAD;
- }
recv_q_len = skb_queue_len(&sk->sk_receive_queue);
if (unlikely(recv_q_len >= (OVERLOAD_LIMIT_BASE / 2))) {
if (rx_queue_full(msg, recv_q_len, OVERLOAD_LIMIT_BASE / 2))
@@ -1213,17 +1300,9 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf)
/* Enqueue message (finally!) */
TIPC_SKB_CB(buf)->handle = 0;
- atomic_inc(&tipc_queue_size);
__skb_queue_tail(&sk->sk_receive_queue, buf);
- /* Initiate connection termination for an incoming 'FIN' */
- if (unlikely(msg_errcode(msg) && (sock->state == SS_CONNECTED))) {
- sock->state = SS_DISCONNECTING;
- tipc_disconnect_port(tipc_sk_port(sk));
- }
-
- if (waitqueue_active(sk_sleep(sk)))
- wake_up_interruptible(sk_sleep(sk));
+ sk->sk_data_ready(sk, 0);
return TIPC_OK;
}
@@ -1290,8 +1369,7 @@ static void wakeupdispatch(struct tipc_port *tport)
{
struct sock *sk = (struct sock *)tport->usr_handle;
- if (waitqueue_active(sk_sleep(sk)))
- wake_up_interruptible(sk_sleep(sk));
+ sk->sk_write_space(sk);
}
/**
@@ -1309,8 +1387,6 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
struct sock *sk = sock->sk;
struct sockaddr_tipc *dst = (struct sockaddr_tipc *)dest;
struct msghdr m = {NULL,};
- struct sk_buff *buf;
- struct tipc_msg *msg;
unsigned int timeout;
int res;
@@ -1322,26 +1398,6 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
goto exit;
}
- /* For now, TIPC does not support the non-blocking form of connect() */
- if (flags & O_NONBLOCK) {
- res = -EOPNOTSUPP;
- goto exit;
- }
-
- /* Issue Posix-compliant error code if socket is in the wrong state */
- if (sock->state == SS_LISTENING) {
- res = -EOPNOTSUPP;
- goto exit;
- }
- if (sock->state == SS_CONNECTING) {
- res = -EALREADY;
- goto exit;
- }
- if (sock->state != SS_UNCONNECTED) {
- res = -EISCONN;
- goto exit;
- }
-
/*
* Reject connection attempt using multicast address
*
@@ -1353,49 +1409,66 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
goto exit;
}
- /* Reject any messages already in receive queue (very unlikely) */
- reject_rx_queue(sk);
+ timeout = (flags & O_NONBLOCK) ? 0 : tipc_sk(sk)->conn_timeout;
- /* Send a 'SYN-' to destination */
- m.msg_name = dest;
- m.msg_namelen = destlen;
- res = send_msg(NULL, sock, &m, 0);
- if (res < 0)
+ switch (sock->state) {
+ case SS_UNCONNECTED:
+ /* Send a 'SYN-' to destination */
+ m.msg_name = dest;
+ m.msg_namelen = destlen;
+
+ /* If connect is in non-blocking case, set MSG_DONTWAIT to
+ * indicate send_msg() is never blocked.
+ */
+ if (!timeout)
+ m.msg_flags = MSG_DONTWAIT;
+
+ res = send_msg(NULL, sock, &m, 0);
+ if ((res < 0) && (res != -EWOULDBLOCK))
+ goto exit;
+
+ /* Just entered SS_CONNECTING state; the only
+ * difference is that return value in non-blocking
+ * case is EINPROGRESS, rather than EALREADY.
+ */
+ res = -EINPROGRESS;
+ break;
+ case SS_CONNECTING:
+ res = -EALREADY;
+ break;
+ case SS_CONNECTED:
+ res = -EISCONN;
+ break;
+ default:
+ res = -EINVAL;
goto exit;
+ }
- /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
- timeout = tipc_sk(sk)->conn_timeout;
- release_sock(sk);
- res = wait_event_interruptible_timeout(*sk_sleep(sk),
- (!skb_queue_empty(&sk->sk_receive_queue) ||
- (sock->state != SS_CONNECTING)),
- timeout ? (long)msecs_to_jiffies(timeout)
- : MAX_SCHEDULE_TIMEOUT);
- lock_sock(sk);
+ if (sock->state == SS_CONNECTING) {
+ if (!timeout)
+ goto exit;
- if (res > 0) {
- buf = skb_peek(&sk->sk_receive_queue);
- if (buf != NULL) {
- msg = buf_msg(buf);
- res = auto_connect(sock, msg);
- if (!res) {
- if (!msg_data_sz(msg))
- advance_rx_queue(sk);
- }
- } else {
- if (sock->state == SS_CONNECTED)
- res = -EISCONN;
+ /* Wait until an 'ACK' or 'RST' arrives, or a timeout occurs */
+ release_sock(sk);
+ res = wait_event_interruptible_timeout(*sk_sleep(sk),
+ sock->state != SS_CONNECTING,
+ timeout ? (long)msecs_to_jiffies(timeout)
+ : MAX_SCHEDULE_TIMEOUT);
+ lock_sock(sk);
+ if (res <= 0) {
+ if (res == 0)
+ res = -ETIMEDOUT;
else
- res = -ECONNREFUSED;
+ ; /* leave "res" unchanged */
+ goto exit;
}
- } else {
- if (res == 0)
- res = -ETIMEDOUT;
- else
- ; /* leave "res" unchanged */
- sock->state = SS_DISCONNECTING;
}
+ if (unlikely(sock->state == SS_DISCONNECTING))
+ res = sock_error(sk);
+ else
+ res = 0;
+
exit:
release_sock(sk);
return res;
@@ -1436,8 +1509,13 @@ static int listen(struct socket *sock, int len)
*/
static int accept(struct socket *sock, struct socket *new_sock, int flags)
{
- struct sock *sk = sock->sk;
+ struct sock *new_sk, *sk = sock->sk;
struct sk_buff *buf;
+ struct tipc_sock *new_tsock;
+ struct tipc_port *new_tport;
+ struct tipc_msg *msg;
+ u32 new_ref;
+
int res;
lock_sock(sk);
@@ -1463,48 +1541,51 @@ static int accept(struct socket *sock, struct socket *new_sock, int flags)
buf = skb_peek(&sk->sk_receive_queue);
res = tipc_create(sock_net(sock->sk), new_sock, 0, 0);
- if (!res) {
- struct sock *new_sk = new_sock->sk;
- struct tipc_sock *new_tsock = tipc_sk(new_sk);
- struct tipc_port *new_tport = new_tsock->p;
- u32 new_ref = new_tport->ref;
- struct tipc_msg *msg = buf_msg(buf);
-
- lock_sock(new_sk);
-
- /*
- * Reject any stray messages received by new socket
- * before the socket lock was taken (very, very unlikely)
- */
- reject_rx_queue(new_sk);
-
- /* Connect new socket to it's peer */
- new_tsock->peer_name.ref = msg_origport(msg);
- new_tsock->peer_name.node = msg_orignode(msg);
- tipc_connect2port(new_ref, &new_tsock->peer_name);
- new_sock->state = SS_CONNECTED;
-
- tipc_set_portimportance(new_ref, msg_importance(msg));
- if (msg_named(msg)) {
- new_tport->conn_type = msg_nametype(msg);
- new_tport->conn_instance = msg_nameinst(msg);
- }
+ if (res)
+ goto exit;
- /*
- * Respond to 'SYN-' by discarding it & returning 'ACK'-.
- * Respond to 'SYN+' by queuing it on new socket.
- */
- if (!msg_data_sz(msg)) {
- struct msghdr m = {NULL,};
+ new_sk = new_sock->sk;
+ new_tsock = tipc_sk(new_sk);
+ new_tport = new_tsock->p;
+ new_ref = new_tport->ref;
+ msg = buf_msg(buf);
- advance_rx_queue(sk);
- send_packet(NULL, new_sock, &m, 0);
- } else {
- __skb_dequeue(&sk->sk_receive_queue);
- __skb_queue_head(&new_sk->sk_receive_queue, buf);
- }
- release_sock(new_sk);
+ /* we lock on new_sk; but lockdep sees the lock on sk */
+ lock_sock_nested(new_sk, SINGLE_DEPTH_NESTING);
+
+ /*
+ * Reject any stray messages received by new socket
+ * before the socket lock was taken (very, very unlikely)
+ */
+ reject_rx_queue(new_sk);
+
+ /* Connect new socket to it's peer */
+ new_tsock->peer_name.ref = msg_origport(msg);
+ new_tsock->peer_name.node = msg_orignode(msg);
+ tipc_connect(new_ref, &new_tsock->peer_name);
+ new_sock->state = SS_CONNECTED;
+
+ tipc_set_portimportance(new_ref, msg_importance(msg));
+ if (msg_named(msg)) {
+ new_tport->conn_type = msg_nametype(msg);
+ new_tport->conn_instance = msg_nameinst(msg);
}
+
+ /*
+ * Respond to 'SYN-' by discarding it & returning 'ACK'-.
+ * Respond to 'SYN+' by queuing it on new socket.
+ */
+ if (!msg_data_sz(msg)) {
+ struct msghdr m = {NULL,};
+
+ advance_rx_queue(sk);
+ send_packet(NULL, new_sock, &m, 0);
+ } else {
+ __skb_dequeue(&sk->sk_receive_queue);
+ __skb_queue_head(&new_sk->sk_receive_queue, buf);
+ }
+ release_sock(new_sk);
+
exit:
release_sock(sk);
return res;
@@ -1539,7 +1620,6 @@ restart:
/* Disconnect and send a 'FIN+' or 'FIN-' message to peer */
buf = __skb_dequeue(&sk->sk_receive_queue);
if (buf) {
- atomic_dec(&tipc_queue_size);
if (TIPC_SKB_CB(buf)->handle != 0) {
kfree_skb(buf);
goto restart;
@@ -1556,10 +1636,11 @@ restart:
case SS_DISCONNECTING:
- /* Discard any unreceived messages; wake up sleeping tasks */
+ /* Discard any unreceived messages */
discard_rx_queue(sk);
- if (waitqueue_active(sk_sleep(sk)))
- wake_up_interruptible(sk_sleep(sk));
+
+ /* Wake up anyone sleeping in poll */
+ sk->sk_state_change(sk);
res = 0;
break;
@@ -1677,7 +1758,7 @@ static int getsockopt(struct socket *sock,
/* no need to set "res", since already 0 at this point */
break;
case TIPC_NODE_RECVQ_DEPTH:
- value = (u32)atomic_read(&tipc_queue_size);
+ value = 0; /* was tipc_queue_size, now obsolete */
break;
case TIPC_SOCK_RECVQ_DEPTH:
value = skb_queue_len(&sk->sk_receive_queue);
diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c
index 0f7d0d007e2..6b42d47029a 100644
--- a/net/tipc/subscr.c
+++ b/net/tipc/subscr.c
@@ -462,7 +462,7 @@ static void subscr_named_msg_event(void *usr_handle,
kfree(subscriber);
return;
}
- tipc_connect2port(subscriber->port_ref, orig);
+ tipc_connect(subscriber->port_ref, orig);
/* Lock server port (& save lock address for future use) */
subscriber->lock = tipc_port_lock(subscriber->port_ref)->lock;
diff --git a/net/unix/diag.c b/net/unix/diag.c
index 06748f108a5..5ac19dc1d5e 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -151,6 +151,9 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r
sock_diag_put_meminfo(sk, skb, UNIX_DIAG_MEMINFO))
goto out_nlmsg_trim;
+ if (nla_put_u8(skb, UNIX_DIAG_SHUTDOWN, sk->sk_shutdown))
+ goto out_nlmsg_trim;
+
return nlmsg_end(skb, nlh);
out_nlmsg_trim:
diff --git a/net/unix/sysctl_net_unix.c b/net/unix/sysctl_net_unix.c
index b34b5b9792f..8800604c93f 100644
--- a/net/unix/sysctl_net_unix.c
+++ b/net/unix/sysctl_net_unix.c
@@ -34,6 +34,10 @@ int __net_init unix_sysctl_register(struct net *net)
if (table == NULL)
goto err_alloc;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
table[0].data = &net->unx.sysctl_max_dgram_qlen;
net->unx.ctl = register_net_sysctl(net, "net/unix", table);
if (net->unx.ctl == NULL)
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index fe4adb12b3e..16d08b39921 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -140,14 +140,13 @@ config CFG80211_WEXT
extensions with cfg80211-based drivers.
config LIB80211
- tristate "Common routines for IEEE802.11 drivers"
+ tristate
default n
help
This options enables a library of common routines used
by IEEE802.11 wireless LAN drivers.
- Drivers should select this themselves if needed. Say Y if
- you want this built into your kernel.
+ Drivers should select this themselves if needed.
config LIB80211_CRYPT_WEP
tristate
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index 0f7e0d621ab..a761670af31 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -10,11 +10,13 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
-cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
+CFLAGS_trace.o := -I$(src)
+
ccflags-y += -D__CHECK_ENDIAN__
$(obj)/regdb.c: $(src)/db.txt $(src)/genregdb.awk
diff --git a/net/wireless/ap.c b/net/wireless/ap.c
index fcc60d8dbef..324e8d851dc 100644
--- a/net/wireless/ap.c
+++ b/net/wireless/ap.c
@@ -3,6 +3,7 @@
#include <net/cfg80211.h>
#include "nl80211.h"
#include "core.h"
+#include "rdev-ops.h"
static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
@@ -23,10 +24,11 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
if (!wdev->beacon_interval)
return -ENOENT;
- err = rdev->ops->stop_ap(&rdev->wiphy, dev);
+ err = rdev_stop_ap(rdev, dev);
if (!err) {
wdev->beacon_interval = 0;
wdev->channel = NULL;
+ wdev->ssid_len = 0;
}
return err;
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 2f876b9ee34..a7990bb1652 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -9,90 +9,303 @@
#include <linux/export.h>
#include <net/cfg80211.h>
#include "core.h"
+#include "rdev-ops.h"
-struct ieee80211_channel *
-rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type channel_type)
+void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type chan_type)
{
- struct ieee80211_channel *chan;
- struct ieee80211_sta_ht_cap *ht_cap;
+ if (WARN_ON(!chan))
+ return;
+
+ chandef->chan = chan;
+ chandef->center_freq2 = 0;
+
+ switch (chan_type) {
+ case NL80211_CHAN_NO_HT:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT20:
+ chandef->width = NL80211_CHAN_WIDTH_20;
+ chandef->center_freq1 = chan->center_freq;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq + 10;
+ break;
+ case NL80211_CHAN_HT40MINUS:
+ chandef->width = NL80211_CHAN_WIDTH_40;
+ chandef->center_freq1 = chan->center_freq - 10;
+ break;
+ default:
+ WARN_ON(1);
+ }
+}
+EXPORT_SYMBOL(cfg80211_chandef_create);
+
+bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
+{
+ u32 control_freq;
+
+ if (!chandef->chan)
+ return false;
- chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ control_freq = chandef->chan->center_freq;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ if (chandef->center_freq1 != control_freq)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (!chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ if (chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (chandef->center_freq1 != control_freq + 70 &&
+ chandef->center_freq1 != control_freq + 50 &&
+ chandef->center_freq1 != control_freq + 30 &&
+ chandef->center_freq1 != control_freq + 10 &&
+ chandef->center_freq1 != control_freq - 10 &&
+ chandef->center_freq1 != control_freq - 30 &&
+ chandef->center_freq1 != control_freq - 50 &&
+ chandef->center_freq1 != control_freq - 70)
+ return false;
+ if (chandef->center_freq2)
+ return false;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(cfg80211_chandef_valid);
+
+static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
+ int *pri40, int *pri80)
+{
+ int tmp;
+
+ switch (c->width) {
+ case NL80211_CHAN_WIDTH_40:
+ *pri40 = c->center_freq1;
+ *pri80 = 0;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ case NL80211_CHAN_WIDTH_80P80:
+ *pri80 = c->center_freq1;
+ /* n_P20 */
+ tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 20 + 40 * tmp;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ /* n_P20 */
+ tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
+ /* n_P40 */
+ tmp /= 2;
+ /* freq_P40 */
+ *pri40 = c->center_freq1 - 60 + 40 * tmp;
+ /* n_P80 */
+ tmp /= 2;
+ *pri80 = c->center_freq1 - 40 + 80 * tmp;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+}
- /* Primary channel not allowed */
- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+const struct cfg80211_chan_def *
+cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
+ const struct cfg80211_chan_def *c2)
+{
+ u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
+
+ /* If they are identical, return */
+ if (cfg80211_chandef_identical(c1, c2))
+ return c1;
+
+ /* otherwise, must have same control channel */
+ if (c1->chan != c2->chan)
return NULL;
- if (channel_type == NL80211_CHAN_HT40MINUS &&
- chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ /*
+ * If they have the same width, but aren't identical,
+ * then they can't be compatible.
+ */
+ if (c1->width == c2->width)
return NULL;
- else if (channel_type == NL80211_CHAN_HT40PLUS &&
- chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+
+ if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c1->width == NL80211_CHAN_WIDTH_20)
+ return c2;
+
+ if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
+ c2->width == NL80211_CHAN_WIDTH_20)
+ return c1;
+
+ chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
+ chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
+
+ if (c1_pri40 != c2_pri40)
return NULL;
- ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+ WARN_ON(!c1_pri80 && !c2_pri80);
+ if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
+ return NULL;
- if (channel_type != NL80211_CHAN_NO_HT) {
- if (!ht_cap->ht_supported)
- return NULL;
+ if (c1->width > c2->width)
+ return c1;
+ return c2;
+}
+EXPORT_SYMBOL(cfg80211_chandef_compatible);
- if (channel_type != NL80211_CHAN_HT20 &&
- (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
- ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
- return NULL;
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq;
+
+ for (freq = center_freq - bandwidth/2 + 10;
+ freq <= center_freq + bandwidth/2 - 10;
+ freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
+ return false;
}
- return chan;
+ return true;
}
-bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+bool cfg80211_chandef_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef,
+ u32 prohibited_flags)
{
- struct ieee80211_channel *sec_chan;
- int diff;
+ struct ieee80211_sta_ht_cap *ht_cap;
+ struct ieee80211_sta_vht_cap *vht_cap;
+ u32 width, control_freq;
- switch (channel_type) {
- case NL80211_CHAN_HT40PLUS:
- diff = 20;
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
+ vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
+
+ control_freq = chandef->chan->center_freq;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ if (!ht_cap->ht_supported)
+ return false;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ width = 20;
break;
- case NL80211_CHAN_HT40MINUS:
- diff = -20;
+ case NL80211_CHAN_WIDTH_40:
+ width = 40;
+ if (!ht_cap->ht_supported)
+ return false;
+ if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+ ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
+ return false;
+ if (chandef->center_freq1 < control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+ return false;
+ if (chandef->center_freq1 > control_freq &&
+ chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
+ return false;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
+ return false;
+ case NL80211_CHAN_WIDTH_80:
+ if (!vht_cap->vht_supported)
+ return false;
+ width = 80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ if (!vht_cap->vht_supported)
+ return false;
+ if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
+ return false;
+ width = 160;
break;
default:
- return true;
+ WARN_ON_ONCE(1);
+ return false;
}
- sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff);
- if (!sec_chan)
- return false;
+ /* TODO: missing regulatory check on 80/160 bandwidth */
- /* we'll need a DFS capability later */
- if (sec_chan->flags & (IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR))
+ if (width > 20)
+ prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
+
+ if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
+ width, prohibited_flags))
return false;
- return true;
+ if (!chandef->center_freq2)
+ return true;
+ return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
+ width, prohibited_flags);
}
-EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan);
+EXPORT_SYMBOL(cfg80211_chandef_usable);
-int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type chantype)
+bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_channel *chan;
+ bool res;
+
+ trace_cfg80211_reg_can_beacon(wiphy, chandef);
+ res = cfg80211_chandef_usable(wiphy, chandef,
+ IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_PASSIVE_SCAN |
+ IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_RADAR);
+
+ trace_cfg80211_return_bool(res);
+ return res;
+}
+EXPORT_SYMBOL(cfg80211_reg_can_beacon);
+
+int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef)
+{
if (!rdev->ops->set_monitor_channel)
return -EOPNOTSUPP;
if (!cfg80211_has_monitors_only(rdev))
return -EBUSY;
- chan = rdev_freq_to_chan(rdev, freq, chantype);
- if (!chan)
- return -EINVAL;
-
- return rdev->ops->set_monitor_channel(&rdev->wiphy, chan, chantype);
+ return rdev_set_monitor_channel(rdev, chandef);
}
void
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 443d4d7deea..b677eab55b6 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -26,6 +26,7 @@
#include "debugfs.h"
#include "wext-compat.h"
#include "ethtool.h"
+#include "rdev-ops.h"
/* name for sysfs, %d is appended */
#define PHY_NAME "phy"
@@ -216,7 +217,7 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
{
struct cfg80211_registered_device *rdev = data;
- rdev->ops->rfkill_poll(&rdev->wiphy);
+ rdev_rfkill_poll(rdev);
}
static int cfg80211_rfkill_set_block(void *data, bool blocked)
@@ -240,7 +241,7 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
case NL80211_IFTYPE_P2P_DEVICE:
if (!wdev->p2p_started)
break;
- rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+ rdev_stop_p2p_device(rdev, wdev);
wdev->p2p_started = false;
rdev->opencount--;
break;
@@ -325,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
mutex_init(&rdev->devlist_mtx);
mutex_init(&rdev->sched_scan_mtx);
INIT_LIST_HEAD(&rdev->wdev_list);
+ INIT_LIST_HEAD(&rdev->beacon_registrations);
+ spin_lock_init(&rdev->beacon_registrations_lock);
spin_lock_init(&rdev->bss_lock);
INIT_LIST_HEAD(&rdev->bss_list);
INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done);
@@ -370,6 +373,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
+ rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
+
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
@@ -526,8 +531,7 @@ int wiphy_register(struct wiphy *wiphy)
for (i = 0; i < sband->n_channels; i++) {
sband->channels[i].orig_flags =
sband->channels[i].flags;
- sband->channels[i].orig_mag =
- sband->channels[i].max_antenna_gain;
+ sband->channels[i].orig_mag = INT_MAX;
sband->channels[i].orig_mpwr =
sband->channels[i].max_power;
sband->channels[i].band = band;
@@ -688,7 +692,7 @@ void wiphy_unregister(struct wiphy *wiphy)
flush_work(&rdev->event_work);
if (rdev->wowlan && rdev->ops->set_wakeup)
- rdev->ops->set_wakeup(&rdev->wiphy, false);
+ rdev_set_wakeup(rdev, false);
cfg80211_rdev_free_wowlan(rdev);
}
EXPORT_SYMBOL(wiphy_unregister);
@@ -696,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister);
void cfg80211_dev_free(struct cfg80211_registered_device *rdev)
{
struct cfg80211_internal_bss *scan, *tmp;
+ struct cfg80211_beacon_registration *reg, *treg;
rfkill_destroy(rdev->rfkill);
mutex_destroy(&rdev->mtx);
mutex_destroy(&rdev->devlist_mtx);
mutex_destroy(&rdev->sched_scan_mtx);
+ list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) {
+ list_del(&reg->list);
+ kfree(reg);
+ }
list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list)
cfg80211_put_bss(&scan->pub);
kfree(rdev);
@@ -771,7 +780,7 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
case NL80211_IFTYPE_P2P_DEVICE:
if (!wdev->p2p_started)
break;
- rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+ rdev_stop_p2p_device(rdev, wdev);
wdev->p2p_started = false;
rdev->opencount--;
break;
@@ -857,8 +866,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
/* allow mac80211 to determine the timeout */
wdev->ps_timeout = -1;
- if (!dev->ethtool_ops)
- dev->ethtool_ops = &cfg80211_ethtool_ops;
+ netdev_set_default_ethtool_ops(dev, &cfg80211_ethtool_ops);
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT ||
@@ -962,9 +970,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
if ((wdev->iftype == NL80211_IFTYPE_STATION ||
wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) &&
rdev->ops->set_power_mgmt)
- if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
- wdev->ps,
- wdev->ps_timeout)) {
+ if (rdev_set_power_mgmt(rdev, dev, wdev->ps,
+ wdev->ps_timeout)) {
/* assume this means it's off */
wdev->ps = false;
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index a343be4a52b..3563097169c 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -55,7 +55,8 @@ struct cfg80211_registered_device {
int opencount; /* also protected by devlist_mtx */
wait_queue_head_t dev_wait;
- u32 ap_beacons_nlportid;
+ struct list_head beacon_registrations;
+ spinlock_t beacon_registrations_lock;
/* protected by RTNL only */
int num_running_ifaces;
@@ -137,8 +138,6 @@ struct cfg80211_internal_bss {
unsigned long ts;
struct kref ref;
atomic_t hold;
- bool beacon_ies_allocated;
- bool proberesp_ies_allocated;
/* must be last because of priv member */
struct cfg80211_bss pub;
@@ -260,6 +259,10 @@ enum cfg80211_chan_mode {
CHAN_MODE_EXCLUSIVE,
};
+struct cfg80211_beacon_registration {
+ struct list_head list;
+ u32 nlportid;
+};
/* free object */
extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev);
@@ -304,9 +307,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
-int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, int freq,
- enum nl80211_channel_type channel_type);
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef);
/* AP */
int cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
@@ -320,13 +323,15 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
const u8 *bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx);
+ const u8 *key, int key_len, int key_idx,
+ const u8 *sae_data, int sae_data_len);
int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
struct net_device *dev, struct ieee80211_channel *chan,
enum nl80211_auth_type auth_type, const u8 *bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx);
+ const u8 *key, int key_len, int key_idx,
+ const u8 *sae_data, int sae_data_len);
int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct ieee80211_channel *chan,
@@ -371,10 +376,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie);
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
@@ -465,11 +468,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
struct ieee80211_channel **chan,
enum cfg80211_chan_mode *chanmode);
-struct ieee80211_channel *
-rdev_freq_to_chan(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type channel_type);
int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
- int freq, enum nl80211_channel_type chantype);
+ struct cfg80211_chan_def *chandef);
int ieee80211_get_ratemask(struct ieee80211_supported_band *sband,
const u8 *rates, unsigned int n_rates,
diff --git a/net/wireless/ethtool.c b/net/wireless/ethtool.c
index 7eecdf40cf8..48c48ffafa1 100644
--- a/net/wireless/ethtool.c
+++ b/net/wireless/ethtool.c
@@ -2,6 +2,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "ethtool.h"
+#include "rdev-ops.h"
static void cfg80211_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
@@ -47,9 +48,8 @@ static void cfg80211_get_ringparam(struct net_device *dev,
memset(rp, 0, sizeof(*rp));
if (rdev->ops->get_ringparam)
- rdev->ops->get_ringparam(wdev->wiphy,
- &rp->tx_pending, &rp->tx_max_pending,
- &rp->rx_pending, &rp->rx_max_pending);
+ rdev_get_ringparam(rdev, &rp->tx_pending, &rp->tx_max_pending,
+ &rp->rx_pending, &rp->rx_max_pending);
}
static int cfg80211_set_ringparam(struct net_device *dev,
@@ -62,8 +62,7 @@ static int cfg80211_set_ringparam(struct net_device *dev,
return -EINVAL;
if (rdev->ops->set_ringparam)
- return rdev->ops->set_ringparam(wdev->wiphy,
- rp->tx_pending, rp->rx_pending);
+ return rdev_set_ringparam(rdev, rp->tx_pending, rp->rx_pending);
return -ENOTSUPP;
}
@@ -73,7 +72,7 @@ static int cfg80211_get_sset_count(struct net_device *dev, int sset)
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
if (rdev->ops->get_et_sset_count)
- return rdev->ops->get_et_sset_count(wdev->wiphy, dev, sset);
+ return rdev_get_et_sset_count(rdev, dev, sset);
return -EOPNOTSUPP;
}
@@ -83,7 +82,7 @@ static void cfg80211_get_stats(struct net_device *dev,
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
if (rdev->ops->get_et_stats)
- rdev->ops->get_et_stats(wdev->wiphy, dev, stats, data);
+ rdev_get_et_stats(rdev, dev, stats, data);
}
static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
@@ -91,7 +90,7 @@ static void cfg80211_get_strings(struct net_device *dev, u32 sset, u8 *data)
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
if (rdev->ops->get_et_strings)
- rdev->ops->get_et_strings(wdev->wiphy, dev, sset, data);
+ rdev_get_et_strings(rdev, dev, sset, data);
}
const struct ethtool_ops cfg80211_ethtool_ops = {
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index ca5672f6ee2..9b9551e4a6f 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -11,6 +11,7 @@
#include <net/cfg80211.h>
#include "wext-compat.h"
#include "nl80211.h"
+#include "rdev-ops.h"
void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid)
@@ -61,6 +62,8 @@ void cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid, gfp_t gfp)
struct cfg80211_event *ev;
unsigned long flags;
+ trace_cfg80211_ibss_joined(dev, bssid);
+
CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING);
ev = kzalloc(sizeof(*ev), gfp);
@@ -97,9 +100,9 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
* 11a for maximum compatibility.
*/
struct ieee80211_supported_band *sband =
- rdev->wiphy.bands[params->channel->band];
+ rdev->wiphy.bands[params->chandef.chan->band];
int j;
- u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ?
+ u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ?
IEEE80211_RATE_MANDATORY_A :
IEEE80211_RATE_MANDATORY_B;
@@ -115,11 +118,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
wdev->ibss_fixed = params->channel_fixed;
#ifdef CONFIG_CFG80211_WEXT
- wdev->wext.ibss.channel = params->channel;
+ wdev->wext.ibss.chandef = params->chandef;
#endif
wdev->sme_state = CFG80211_SME_CONNECTING;
- err = cfg80211_can_use_chan(rdev, wdev, params->channel,
+ err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan,
params->channel_fixed
? CHAN_MODE_SHARED
: CHAN_MODE_EXCLUSIVE);
@@ -128,7 +131,7 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev,
return err;
}
- err = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
+ err = rdev_join_ibss(rdev, dev, params);
if (err) {
wdev->connect_keys = NULL;
wdev->sme_state = CFG80211_SME_IDLE;
@@ -175,7 +178,7 @@ static void __cfg80211_clear_ibss(struct net_device *dev, bool nowext)
*/
if (rdev->ops->del_key)
for (i = 0; i < 6; i++)
- rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
+ rdev_del_key(rdev, dev, i, false, NULL);
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
@@ -211,7 +214,7 @@ int __cfg80211_leave_ibss(struct cfg80211_registered_device *rdev,
if (!wdev->ssid_len)
return -ENOLINK;
- err = rdev->ops->leave_ibss(&rdev->wiphy, dev);
+ err = rdev_leave_ibss(rdev, dev);
if (err)
return err;
@@ -248,7 +251,9 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
wdev->wext.ibss.beacon_interval = 100;
/* try to find an IBSS channel if none requested ... */
- if (!wdev->wext.ibss.channel) {
+ if (!wdev->wext.ibss.chandef.chan) {
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
@@ -263,15 +268,15 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
continue;
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
- wdev->wext.ibss.channel = chan;
+ wdev->wext.ibss.chandef.chan = chan;
break;
}
- if (wdev->wext.ibss.channel)
+ if (wdev->wext.ibss.chandef.chan)
break;
}
- if (!wdev->wext.ibss.channel)
+ if (!wdev->wext.ibss.chandef.chan)
return -EINVAL;
}
@@ -333,7 +338,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
return -EINVAL;
}
- if (wdev->wext.ibss.channel == chan)
+ if (wdev->wext.ibss.chandef.chan == chan)
return 0;
wdev_lock(wdev);
@@ -346,7 +351,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
return err;
if (chan) {
- wdev->wext.ibss.channel = chan;
+ wdev->wext.ibss.chandef.chan = chan;
+ wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
wdev->wext.ibss.channel_fixed = true;
} else {
/* cfg80211_ibss_wext_join will pick one if needed */
@@ -376,8 +382,8 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev,
wdev_lock(wdev);
if (wdev->current_bss)
chan = wdev->current_bss->pub.channel;
- else if (wdev->wext.ibss.channel)
- chan = wdev->wext.ibss.channel;
+ else if (wdev->wext.ibss.chandef.chan)
+ chan = wdev->wext.ibss.chandef.chan;
wdev_unlock(wdev);
if (chan) {
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index c384e77ff77..f9d6ce5cfab 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -3,6 +3,7 @@
#include <net/cfg80211.h>
#include "nl80211.h"
#include "core.h"
+#include "rdev-ops.h"
/* Default values, timeouts in ms */
#define MESH_TTL 31
@@ -72,8 +73,6 @@ const struct mesh_config default_mesh_config = {
const struct mesh_setup default_mesh_setup = {
/* cfg80211_join_mesh() will pick a channel if needed */
- .channel = NULL,
- .channel_type = NL80211_CHAN_NO_HT,
.sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET,
.path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
.path_metric = IEEE80211_PATH_METRIC_AIRTIME,
@@ -110,13 +109,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (!rdev->ops->join_mesh)
return -EOPNOTSUPP;
- if (!setup->channel) {
+ if (!setup->chandef.chan) {
/* if no channel explicitly given, use preset channel */
- setup->channel = wdev->preset_chan;
- setup->channel_type = wdev->preset_chantype;
+ setup->chandef = wdev->preset_chandef;
}
- if (!setup->channel) {
+ if (!setup->chandef.chan) {
/* if we don't have that either, use the first usable channel */
enum ieee80211_band band;
@@ -136,35 +134,35 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR))
continue;
- setup->channel = chan;
+ setup->chandef.chan = chan;
break;
}
- if (setup->channel)
+ if (setup->chandef.chan)
break;
}
/* no usable channel ... */
- if (!setup->channel)
+ if (!setup->chandef.chan)
return -EINVAL;
- setup->channel_type = NL80211_CHAN_NO_HT;
+ setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ setup->chandef.center_freq1 = setup->chandef.chan->center_freq;
}
- if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel,
- setup->channel_type))
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
return -EINVAL;
- err = cfg80211_can_use_chan(rdev, wdev, setup->channel,
+ err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan,
CHAN_MODE_SHARED);
if (err)
return err;
- err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
+ err = rdev_join_mesh(rdev, dev, conf, setup);
if (!err) {
memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
wdev->mesh_id_len = setup->mesh_id_len;
- wdev->channel = setup->channel;
+ wdev->channel = setup->chandef.chan;
}
return err;
@@ -187,20 +185,12 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
return err;
}
-int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev, int freq,
- enum nl80211_channel_type channel_type)
+int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_channel *channel;
int err;
- channel = rdev_freq_to_chan(rdev, freq, channel_type);
- if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
- channel,
- channel_type)) {
- return -EINVAL;
- }
-
/*
* Workaround for libertas (only!), it puts the interface
* into mesh mode but doesn't implement join_mesh. Instead,
@@ -209,22 +199,21 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
* compatible with 802.11 mesh.
*/
if (rdev->ops->libertas_set_mesh_channel) {
- if (channel_type != NL80211_CHAN_NO_HT)
+ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT)
return -EINVAL;
if (!netif_running(wdev->netdev))
return -ENETDOWN;
- err = cfg80211_can_use_chan(rdev, wdev, channel,
+ err = cfg80211_can_use_chan(rdev, wdev, chandef->chan,
CHAN_MODE_SHARED);
if (err)
return err;
- err = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy,
- wdev->netdev,
- channel);
+ err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev,
+ chandef->chan);
if (!err)
- wdev->channel = channel;
+ wdev->channel = chandef->chan;
return err;
}
@@ -232,8 +221,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev,
if (wdev->mesh_id_len)
return -EBUSY;
- wdev->preset_chan = channel;
- wdev->preset_chantype = channel_type;
+ wdev->preset_chandef = *chandef;
return 0;
}
@@ -242,6 +230,7 @@ void cfg80211_notify_new_peer_candidate(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ trace_cfg80211_notify_new_peer_candidate(dev, macaddr);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT))
return;
@@ -267,7 +256,7 @@ static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
if (!wdev->mesh_id_len)
return -ENOTCONN;
- err = rdev->ops->leave_mesh(&rdev->wiphy, dev);
+ err = rdev_leave_mesh(rdev, dev);
if (!err) {
wdev->mesh_id_len = 0;
wdev->channel = NULL;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 904a7f36832..5e8123ee63f 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -15,6 +15,8 @@
#include <net/iw_handler.h>
#include "core.h"
#include "nl80211.h"
+#include "rdev-ops.h"
+
void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
{
@@ -22,6 +24,7 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len)
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_send_rx_auth(dev);
wdev_lock(wdev);
nl80211_send_rx_auth(rdev, dev, buf, len, GFP_KERNEL);
@@ -42,6 +45,7 @@ void cfg80211_send_rx_assoc(struct net_device *dev, struct cfg80211_bss *bss,
u8 *ie = mgmt->u.assoc_resp.variable;
int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable);
+ trace_cfg80211_send_rx_assoc(dev, bss);
wdev_lock(wdev);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -98,6 +102,7 @@ void __cfg80211_send_deauth(struct net_device *dev,
const u8 *bssid = mgmt->bssid;
bool was_current = false;
+ trace___cfg80211_send_deauth(dev);
ASSERT_WDEV_LOCK(wdev);
if (wdev->current_bss &&
@@ -147,6 +152,7 @@ void __cfg80211_send_disassoc(struct net_device *dev,
u16 reason_code;
bool from_ap;
+ trace___cfg80211_send_disassoc(dev);
ASSERT_WDEV_LOCK(wdev);
nl80211_send_disassoc(rdev, dev, buf, len, GFP_KERNEL);
@@ -188,6 +194,7 @@ void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_send_unprot_deauth(dev);
nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC);
}
EXPORT_SYMBOL(cfg80211_send_unprot_deauth);
@@ -199,6 +206,7 @@ void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_send_unprot_disassoc(dev);
nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC);
}
EXPORT_SYMBOL(cfg80211_send_unprot_disassoc);
@@ -209,6 +217,7 @@ void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr)
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_send_auth_timeout(dev, addr);
wdev_lock(wdev);
nl80211_send_auth_timeout(rdev, dev, addr, GFP_KERNEL);
@@ -227,6 +236,7 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr)
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_send_assoc_timeout(dev, addr);
wdev_lock(wdev);
nl80211_send_assoc_timeout(rdev, dev, addr, GFP_KERNEL);
@@ -261,6 +271,7 @@ void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr,
}
#endif
+ trace_cfg80211_michael_mic_failure(dev, addr, key_type, key_id, tsc);
nl80211_michael_mic_failure(rdev, dev, addr, key_type, key_id, tsc, gfp);
}
EXPORT_SYMBOL(cfg80211_michael_mic_failure);
@@ -273,7 +284,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
const u8 *bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx)
+ const u8 *key, int key_len, int key_idx,
+ const u8 *sae_data, int sae_data_len)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_auth_request req;
@@ -293,6 +305,8 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
req.ie = ie;
req.ie_len = ie_len;
+ req.sae_data = sae_data;
+ req.sae_data_len = sae_data_len;
req.auth_type = auth_type;
req.bss = cfg80211_get_bss(&rdev->wiphy, chan, bssid, ssid, ssid_len,
WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
@@ -307,7 +321,7 @@ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
if (err)
goto out;
- err = rdev->ops->auth(&rdev->wiphy, dev, &req);
+ err = rdev_auth(rdev, dev, &req);
out:
cfg80211_put_bss(req.bss);
@@ -319,7 +333,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
enum nl80211_auth_type auth_type, const u8 *bssid,
const u8 *ssid, int ssid_len,
const u8 *ie, int ie_len,
- const u8 *key, int key_len, int key_idx)
+ const u8 *key, int key_len, int key_idx,
+ const u8 *sae_data, int sae_data_len)
{
int err;
@@ -327,7 +342,8 @@ int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev,
wdev_lock(dev->ieee80211_ptr);
err = __cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
ssid, ssid_len, ie, ie_len,
- key, key_len, key_idx);
+ key, key_len, key_idx,
+ sae_data, sae_data_len);
wdev_unlock(dev->ieee80211_ptr);
mutex_unlock(&rdev->devlist_mtx);
@@ -410,7 +426,7 @@ int __cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev,
if (err)
goto out;
- err = rdev->ops->assoc(&rdev->wiphy, dev, &req);
+ err = rdev_assoc(rdev, dev, &req);
out:
if (err) {
@@ -466,7 +482,7 @@ int __cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
return 0;
- return rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ return rdev_deauth(rdev, dev, &req);
}
int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev,
@@ -511,7 +527,7 @@ static int __cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
else
return -ENOTCONN;
- return rdev->ops->disassoc(&rdev->wiphy, dev, &req);
+ return rdev_disassoc(rdev, dev, &req);
}
int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev,
@@ -552,7 +568,7 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
req.bssid = bssid;
- rdev->ops->deauth(&rdev->wiphy, dev, &req);
+ rdev_deauth(rdev, dev, &req);
if (wdev->current_bss) {
cfg80211_unhold_bss(wdev->current_bss);
@@ -563,27 +579,25 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev,
void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type,
- duration, gfp);
+ trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration);
+ nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp);
}
EXPORT_SYMBOL(cfg80211_ready_on_channel);
void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
gfp_t gfp)
{
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan,
- channel_type, gfp);
+ trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan);
+ nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp);
}
EXPORT_SYMBOL(cfg80211_remain_on_channel_expired);
@@ -593,6 +607,7 @@ void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr,
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_new_sta(dev, mac_addr, sinfo);
nl80211_send_sta_event(rdev, dev, mac_addr, sinfo, gfp);
}
EXPORT_SYMBOL(cfg80211_new_sta);
@@ -602,6 +617,7 @@ void cfg80211_del_sta(struct net_device *dev, const u8 *mac_addr, gfp_t gfp)
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_del_sta(dev, mac_addr);
nl80211_send_sta_del_event(rdev, dev, mac_addr, gfp);
}
EXPORT_SYMBOL(cfg80211_del_sta);
@@ -682,7 +698,7 @@ int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid,
list_add(&nreg->list, &wdev->mgmt_registrations);
if (rdev->ops->mgmt_frame_register)
- rdev->ops->mgmt_frame_register(wiphy, wdev, frame_type, true);
+ rdev_mgmt_frame_register(rdev, wdev, frame_type, true);
out:
spin_unlock_bh(&wdev->mgmt_registrations_lock);
@@ -705,8 +721,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid)
if (rdev->ops->mgmt_frame_register) {
u16 frame_type = le16_to_cpu(reg->frame_type);
- rdev->ops->mgmt_frame_register(wiphy, wdev,
- frame_type, false);
+ rdev_mgmt_frame_register(rdev, wdev,
+ frame_type, false);
}
list_del(&reg->list);
@@ -736,10 +752,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
- enum nl80211_channel_type channel_type,
- bool channel_type_valid, unsigned int wait,
- const u8 *buf, size_t len, bool no_cck,
- bool dont_wait_for_ack, u64 *cookie)
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
{
const struct ieee80211_mgmt *mgmt;
u16 stype;
@@ -832,10 +846,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
return -EINVAL;
/* Transmit the Action frame as requested by user space */
- return rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- channel_type, channel_type_valid,
- wait, buf, len, no_cck, dont_wait_for_ack,
- cookie);
+ return rdev_mgmt_tx(rdev, wdev, chan, offchan,
+ wait, buf, len, no_cck, dont_wait_for_ack,
+ cookie);
}
bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
@@ -854,10 +867,13 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE);
u16 stype;
+ trace_cfg80211_rx_mgmt(wdev, freq, sig_mbm);
stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4;
- if (!(stypes->rx & BIT(stype)))
+ if (!(stypes->rx & BIT(stype))) {
+ trace_cfg80211_return_bool(false);
return false;
+ }
data = buf + ieee80211_hdrlen(mgmt->frame_control);
data_len = len - ieee80211_hdrlen(mgmt->frame_control);
@@ -888,6 +904,7 @@ bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
spin_unlock_bh(&wdev->mgmt_registrations_lock);
+ trace_cfg80211_return_bool(result);
return result;
}
EXPORT_SYMBOL(cfg80211_rx_mgmt);
@@ -898,6 +915,8 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_mgmt_tx_status(wdev, cookie, ack);
+
/* Indicate TX status of the Action frame to user space */
nl80211_send_mgmt_tx_status(rdev, wdev, cookie, buf, len, ack, gfp);
}
@@ -911,6 +930,8 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
/* Indicate roaming trigger event to user space */
nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
}
@@ -923,6 +944,8 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
/* Indicate roaming trigger event to user space */
nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp);
}
@@ -948,6 +971,7 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_gtk_rekey_notify(dev, bssid);
nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp);
}
EXPORT_SYMBOL(cfg80211_gtk_rekey_notify);
@@ -959,17 +983,19 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_pmksa_candidate_notify(dev, index, bssid, preauth);
nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp);
}
EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
-void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
- enum nl80211_channel_type type)
+void cfg80211_ch_switch_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- struct ieee80211_channel *chan;
+
+ trace_cfg80211_ch_switch_notify(dev, chandef);
wdev_lock(wdev);
@@ -977,12 +1003,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq,
wdev->iftype != NL80211_IFTYPE_P2P_GO))
goto out;
- chan = rdev_freq_to_chan(rdev, freq, type);
- if (WARN_ON(!chan))
- goto out;
-
- wdev->channel = chan;
- nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL);
+ wdev->channel = chandef->chan;
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
out:
wdev_unlock(wdev);
return;
@@ -993,12 +1015,18 @@ bool cfg80211_rx_spurious_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_spurious_frame(dev, addr);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO))
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)) {
+ trace_cfg80211_return_bool(false);
return false;
-
- return nl80211_unexpected_frame(dev, addr, gfp);
+ }
+ ret = nl80211_unexpected_frame(dev, addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
}
EXPORT_SYMBOL(cfg80211_rx_spurious_frame);
@@ -1006,12 +1034,18 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
+ bool ret;
+
+ trace_cfg80211_rx_unexpected_4addr_frame(dev, addr);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
- wdev->iftype != NL80211_IFTYPE_AP_VLAN))
+ wdev->iftype != NL80211_IFTYPE_AP_VLAN)) {
+ trace_cfg80211_return_bool(false);
return false;
-
- return nl80211_unexpected_4addr_frame(dev, addr, gfp);
+ }
+ ret = nl80211_unexpected_4addr_frame(dev, addr, gfp);
+ trace_cfg80211_return_bool(ret);
+ return ret;
}
EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 0418a6d5c1a..f45706adaf3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -22,8 +22,8 @@
#include "core.h"
#include "nl80211.h"
#include "reg.h"
+#include "rdev-ops.h"
-static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
struct genl_info *info,
struct cfg80211_crypto_settings *settings,
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
[NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
[NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+ [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
[NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -355,6 +360,11 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
[NL80211_ATTR_WDEV] = { .type = NLA_U64 },
[NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
+ [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
+ [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
+ [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
+ [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
};
/* policy for the key attributes */
@@ -690,7 +700,7 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
static struct cfg80211_cached_keys *
nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
- struct nlattr *keys)
+ struct nlattr *keys, bool *no_ht)
{
struct key_parse parse;
struct nlattr *key;
@@ -733,6 +743,12 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
result->params[parse.idx].key_len = parse.p.key_len;
result->params[parse.idx].key = result->data[parse.idx];
memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
+
+ if (parse.p.cipher == WLAN_CIPHER_SUITE_WEP40 ||
+ parse.p.cipher == WLAN_CIPHER_SUITE_WEP104) {
+ if (no_ht)
+ *no_ht = true;
+ }
}
return result;
@@ -943,7 +959,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
u32 tx_ant = 0, rx_ant = 0;
int res;
- res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
+ res = rdev_get_antenna(dev, &tx_ant, &rx_ant);
if (!res) {
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
tx_ant) ||
@@ -1101,6 +1117,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure;
}
CMD(start_p2p_device, START_P2P_DEVICE);
+ CMD(set_mcast_rate, SET_MCAST_RATE);
#ifdef CONFIG_NL80211_TESTMODE
CMD(testmode_cmd, TESTMODE);
@@ -1350,51 +1367,83 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
wdev->iftype == NL80211_IFTYPE_P2P_GO;
}
-static bool nl80211_valid_channel_type(struct genl_info *info,
- enum nl80211_channel_type *channel_type)
+static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
+ struct genl_info *info,
+ struct cfg80211_chan_def *chandef)
{
- enum nl80211_channel_type tmp;
+ u32 control_freq;
- if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
- return false;
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ return -EINVAL;
- tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
- if (tmp != NL80211_CHAN_NO_HT &&
- tmp != NL80211_CHAN_HT20 &&
- tmp != NL80211_CHAN_HT40PLUS &&
- tmp != NL80211_CHAN_HT40MINUS)
- return false;
+ control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- if (channel_type)
- *channel_type = tmp;
+ chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ chandef->center_freq1 = control_freq;
+ chandef->center_freq2 = 0;
- return true;
+ /* Primary channel not allowed */
+ if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type chantype;
+
+ chantype = nla_get_u32(
+ info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (chantype) {
+ case NL80211_CHAN_NO_HT:
+ case NL80211_CHAN_HT20:
+ case NL80211_CHAN_HT40PLUS:
+ case NL80211_CHAN_HT40MINUS:
+ cfg80211_chandef_create(chandef, chandef->chan,
+ chantype);
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+ chandef->width =
+ nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+ chandef->center_freq1 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+ if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+ chandef->center_freq2 =
+ nla_get_u32(
+ info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ if (!cfg80211_chandef_valid(chandef))
+ return -EINVAL;
+
+ if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ return 0;
}
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct genl_info *info)
{
- struct ieee80211_channel *channel;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- u32 freq;
+ struct cfg80211_chan_def chandef;
int result;
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
if (wdev)
iftype = wdev->iftype;
- if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
- return -EINVAL;
-
if (!nl80211_can_set_dev_channel(wdev))
return -EOPNOTSUPP;
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+ result = nl80211_parse_chandef(rdev, info, &chandef);
+ if (result)
+ return result;
mutex_lock(&rdev->devlist_mtx);
switch (iftype) {
@@ -1404,22 +1453,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
result = -EBUSY;
break;
}
- channel = rdev_freq_to_chan(rdev, freq, channel_type);
- if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
- channel,
- channel_type)) {
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) {
result = -EINVAL;
break;
}
- wdev->preset_chan = channel;
- wdev->preset_chantype = channel_type;
+ wdev->preset_chandef = chandef;
result = 0;
break;
case NL80211_IFTYPE_MESH_POINT:
- result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
+ result = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
break;
case NL80211_IFTYPE_MONITOR:
- result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
+ result = cfg80211_set_monitor_channel(rdev, &chandef);
break;
default:
result = -EINVAL;
@@ -1457,7 +1502,7 @@ static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
return -EOPNOTSUPP;
bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
- return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
+ return rdev_set_wds_peer(rdev, dev, bssid);
}
@@ -1507,10 +1552,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
result = 0;
mutex_lock(&rdev->mtx);
- } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
+ } else
wdev = netdev->ieee80211_ptr;
- else
- wdev = NULL;
/*
* end workaround code, by now the rdev is available
@@ -1562,24 +1605,29 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (result)
goto bad_res;
- result = rdev->ops->set_txq_params(&rdev->wiphy,
- netdev,
- &txq_params);
+ result = rdev_set_txq_params(rdev, netdev,
+ &txq_params);
if (result)
goto bad_res;
}
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- result = __nl80211_set_channel(rdev, wdev, info);
+ result = __nl80211_set_channel(rdev,
+ nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
+ info);
if (result)
goto bad_res;
}
if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
+ struct wireless_dev *txp_wdev = wdev;
enum nl80211_tx_power_setting type;
int idx, mbm = 0;
+ if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
+ txp_wdev = NULL;
+
if (!rdev->ops->set_tx_power) {
result = -EOPNOTSUPP;
goto bad_res;
@@ -1599,7 +1647,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
mbm = nla_get_u32(info->attrs[idx]);
}
- result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
+ result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
if (result)
goto bad_res;
}
@@ -1628,7 +1676,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
- result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ result = rdev_set_antenna(rdev, tx_ant, rx_ant);
if (result)
goto bad_res;
}
@@ -1713,7 +1761,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (changed & WIPHY_PARAM_COVERAGE_CLASS)
rdev->wiphy.coverage_class = coverage_class;
- result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
+ result = rdev_set_wiphy_params(rdev, changed);
if (result) {
rdev->wiphy.retry_short = old_retry_short;
rdev->wiphy.retry_long = old_retry_long;
@@ -1736,6 +1784,35 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32);
}
+static int nl80211_send_chandef(struct sk_buff *msg,
+ struct cfg80211_chan_def *chandef)
+{
+ WARN_ON(!cfg80211_chandef_valid(chandef));
+
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+ chandef->chan->center_freq))
+ return -ENOBUFS;
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ cfg80211_get_chandef_type(chandef)))
+ return -ENOBUFS;
+ break;
+ default:
+ break;
+ }
+ if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+ return -ENOBUFS;
+ if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+ return -ENOBUFS;
+ if (chandef->center_freq2 &&
+ nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
+ return -ENOBUFS;
+ return 0;
+}
+
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev)
@@ -1762,16 +1839,18 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
goto nla_put_failure;
if (rdev->ops->get_channel) {
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type;
-
- chan = rdev->ops->get_channel(&rdev->wiphy, wdev,
- &channel_type);
- if (chan &&
- (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
- chan->center_freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
- channel_type)))
+ int ret;
+ struct cfg80211_chan_def chandef;
+
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret == 0) {
+ if (nl80211_send_chandef(msg, &chandef))
+ goto nla_put_failure;
+ }
+ }
+
+ if (wdev->ssid_len) {
+ if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid))
goto nla_put_failure;
}
@@ -2014,9 +2093,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
&flags);
- wdev = rdev->ops->add_virtual_intf(&rdev->wiphy,
- nla_data(info->attrs[NL80211_ATTR_IFNAME]),
- type, err ? NULL : &flags, &params);
+ wdev = rdev_add_virtual_intf(rdev,
+ nla_data(info->attrs[NL80211_ATTR_IFNAME]),
+ type, err ? NULL : &flags, &params);
if (IS_ERR(wdev)) {
nlmsg_free(msg);
return PTR_ERR(wdev);
@@ -2083,7 +2162,7 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
if (!wdev->netdev)
info->user_ptr[1] = NULL;
- return rdev->ops->del_virtual_intf(&rdev->wiphy, wdev);
+ return rdev_del_virtual_intf(rdev, wdev);
}
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -2100,7 +2179,7 @@ static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
- return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
+ return rdev_set_noack_map(rdev, dev, noack_map);
}
struct get_key_cookie {
@@ -2210,8 +2289,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
return -ENOENT;
- err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
- mac_addr, &cookie, get_key_callback);
+ err = rdev_get_key(rdev, dev, key_idx, pairwise, mac_addr, &cookie,
+ get_key_callback);
if (err)
goto free_msg;
@@ -2259,7 +2338,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto out;
- err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
+ err = rdev_set_default_key(rdev, dev, key.idx,
key.def_uni, key.def_multi);
if (err)
@@ -2283,8 +2362,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
if (err)
goto out;
- err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
- dev, key.idx);
+ err = rdev_set_default_mgmt_key(rdev, dev, key.idx);
if (err)
goto out;
@@ -2340,9 +2418,9 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
wdev_lock(dev->ieee80211_ptr);
err = nl80211_key_allowed(dev->ieee80211_ptr);
if (!err)
- err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
- mac_addr, &key.p);
+ err = rdev_add_key(rdev, dev, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr, &key.p);
wdev_unlock(dev->ieee80211_ptr);
return err;
@@ -2386,9 +2464,9 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
err = -ENOENT;
if (!err)
- err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
- key.type == NL80211_KEYTYPE_PAIRWISE,
- mac_addr);
+ err = rdev_del_key(rdev, dev, key.idx,
+ key.type == NL80211_KEYTYPE_PAIRWISE,
+ mac_addr);
#ifdef CONFIG_CFG80211_WEXT
if (!err) {
@@ -2476,11 +2554,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
wdev->iftype != NL80211_IFTYPE_P2P_GO)
continue;
- if (!wdev->preset_chan)
+ if (!wdev->preset_chandef.chan)
continue;
- params->channel = wdev->preset_chan;
- params->channel_type = wdev->preset_chantype;
+ params->chandef = wdev->preset_chandef;
ret = true;
break;
}
@@ -2490,6 +2567,30 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev,
return ret;
}
+static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
+ enum nl80211_auth_type auth_type,
+ enum nl80211_commands cmd)
+{
+ if (auth_type > NL80211_AUTHTYPE_MAX)
+ return false;
+
+ switch (cmd) {
+ case NL80211_CMD_AUTHENTICATE:
+ if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
+ auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ return true;
+ case NL80211_CMD_CONNECT:
+ case NL80211_CMD_START_AP:
+ /* SAE not supported yet */
+ if (auth_type == NL80211_AUTHTYPE_SAE)
+ return false;
+ return true;
+ default:
+ return false;
+ }
+}
+
static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -2559,7 +2660,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
params.auth_type = nla_get_u32(
info->attrs[NL80211_ATTR_AUTH_TYPE]);
- if (!nl80211_valid_auth_type(params.auth_type))
+ if (!nl80211_valid_auth_type(rdev, params.auth_type,
+ NL80211_CMD_START_AP))
return -EINVAL;
} else
params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
@@ -2576,43 +2678,59 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
}
- if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
+ if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
+ params.p2p_ctwindow =
+ nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+ if (params.p2p_ctwindow > 127)
+ return -EINVAL;
+ if (params.p2p_ctwindow != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+ u8 tmp;
- params.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- channel_type);
- if (!params.channel)
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EINVAL;
- params.channel_type = channel_type;
- } else if (wdev->preset_chan) {
- params.channel = wdev->preset_chan;
- params.channel_type = wdev->preset_chantype;
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+ if (tmp > 1)
+ return -EINVAL;
+ params.p2p_opp_ps = tmp;
+ if (params.p2p_opp_ps != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+ err = nl80211_parse_chandef(rdev, info, &params.chandef);
+ if (err)
+ return err;
+ } else if (wdev->preset_chandef.chan) {
+ params.chandef = wdev->preset_chandef;
} else if (!nl80211_get_ap_channel(rdev, &params))
return -EINVAL;
- if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
- params.channel_type))
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_can_use_chan(rdev, wdev, params.channel,
+ err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan,
CHAN_MODE_SHARED);
mutex_unlock(&rdev->devlist_mtx);
if (err)
return err;
- err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
+ err = rdev_start_ap(rdev, dev, &params);
if (!err) {
- wdev->preset_chan = params.channel;
- wdev->preset_chantype = params.channel_type;
+ wdev->preset_chandef = params.chandef;
wdev->beacon_interval = params.beacon_interval;
- wdev->channel = params.channel;
+ wdev->channel = params.chandef.chan;
+ wdev->ssid_len = params.ssid_len;
+ memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
return err;
}
@@ -2639,7 +2757,7 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
+ return rdev_change_beacon(rdev, dev, &params);
}
static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -2744,29 +2862,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
rate = nla_nest_start(msg, attr);
if (!rate)
- goto nla_put_failure;
+ return false;
/* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
bitrate = cfg80211_calculate_bitrate(info);
/* report 16-bit bitrate only if we can */
bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
- if ((bitrate > 0 &&
- nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) ||
- (bitrate_compat > 0 &&
- nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) ||
- ((info->flags & RATE_INFO_FLAGS_MCS) &&
- nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
- ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
- nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
- ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
- nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
- goto nla_put_failure;
+ if (bitrate > 0 &&
+ nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
+ return false;
+ if (bitrate_compat > 0 &&
+ nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
+ return false;
+
+ if (info->flags & RATE_INFO_FLAGS_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
+ return false;
+ if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
+ nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
+ return false;
+ if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+ nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+ return false;
+ }
nla_nest_end(msg, rate);
return true;
-
-nla_put_failure:
- return false;
}
static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
@@ -2923,8 +3064,8 @@ static int nl80211_dump_station(struct sk_buff *skb,
while (1) {
memset(&sinfo, 0, sizeof(sinfo));
- err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
- mac_addr, &sinfo);
+ err = rdev_dump_station(dev, netdev, sta_idx,
+ mac_addr, &sinfo);
if (err == -ENOENT)
break;
if (err)
@@ -2969,7 +3110,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->get_station)
return -EOPNOTSUPP;
- err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
+ err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
if (err)
return err;
@@ -3146,7 +3287,7 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
/* be aware of params.vlan when changing code here */
- err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
+ err = rdev_change_station(rdev, dev, mac_addr, &params);
if (params.vlan)
dev_put(params.vlan);
@@ -3198,6 +3339,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
params.ht_capa =
nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
+ if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
+ params.vht_capa =
+ nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
+
if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
@@ -3275,7 +3420,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
/* be aware of params.vlan when changing code here */
- err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
+ err = rdev_add_station(rdev, dev, mac_addr, &params);
if (params.vlan)
dev_put(params.vlan);
@@ -3300,7 +3445,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->del_station)
return -EOPNOTSUPP;
- return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
+ return rdev_del_station(rdev, dev, mac_addr);
}
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@ -3382,8 +3527,8 @@ static int nl80211_dump_mpath(struct sk_buff *skb,
}
while (1) {
- err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
- dst, next_hop, &pinfo);
+ err = rdev_dump_mpath(dev, netdev, path_idx, dst, next_hop,
+ &pinfo);
if (err == -ENOENT)
break;
if (err)
@@ -3430,7 +3575,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
- err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
+ err = rdev_get_mpath(rdev, dev, dst, next_hop, &pinfo);
if (err)
return err;
@@ -3469,7 +3614,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
- return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
+ return rdev_change_mpath(rdev, dev, dst, next_hop);
}
static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
@@ -3494,7 +3639,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
return -EOPNOTSUPP;
- return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
+ return rdev_add_mpath(rdev, dev, dst, next_hop);
}
static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
@@ -3509,7 +3654,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->del_mpath)
return -EOPNOTSUPP;
- return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
+ return rdev_del_mpath(rdev, dev, dst);
}
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
@@ -3525,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.use_short_slot_time = -1;
params.ap_isolate = -1;
params.ht_opmode = -1;
+ params.p2p_ctwindow = -1;
+ params.p2p_opp_ps = -1;
if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
params.use_cts_prot =
@@ -3547,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.ht_opmode =
nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
+ if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ params.p2p_ctwindow =
+ nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+ if (params.p2p_ctwindow < 0)
+ return -EINVAL;
+ if (params.p2p_ctwindow != 0 &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+ return -EINVAL;
+ }
+
+ if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+ u8 tmp;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EINVAL;
+ tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+ if (tmp > 1)
+ return -EINVAL;
+ params.p2p_opp_ps = tmp;
+ if (params.p2p_opp_ps &&
+ !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+ return -EINVAL;
+ }
+
if (!rdev->ops->change_bss)
return -EOPNOTSUPP;
@@ -3554,7 +3727,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
- return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
+ return rdev_change_bss(rdev, dev, &params);
}
static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -3668,8 +3841,7 @@ static int nl80211_get_mesh_config(struct sk_buff *skb,
if (!wdev->mesh_id_len)
memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
else
- err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
- &cur_params);
+ err = rdev_get_mesh_config(rdev, dev, &cur_params);
wdev_unlock(wdev);
if (err)
@@ -3971,8 +4143,7 @@ static int nl80211_update_mesh_config(struct sk_buff *skb,
err = -ENOLINK;
if (!err)
- err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
- mask, &cfg);
+ err = rdev_update_mesh_config(rdev, dev, mask, &cfg);
wdev_unlock(wdev);
@@ -4337,14 +4508,27 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
}
}
+ if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ request->flags = nla_get_u32(
+ info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
+ !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+ }
+
request->no_cck =
nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
request->wdev = wdev;
request->wiphy = &rdev->wiphy;
+ request->scan_start = jiffies;
rdev->scan_req = request;
- err = rdev->ops->scan(&rdev->wiphy, request);
+ err = rdev_scan(rdev, request);
if (!err) {
nl80211_send_scan_start(rdev, wdev);
@@ -4568,11 +4752,24 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
request->ie_len);
}
+ if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ request->flags = nla_get_u32(
+ info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
+ ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
+ !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+ }
+
request->dev = dev;
request->wiphy = &rdev->wiphy;
request->interval = interval;
+ request->scan_start = jiffies;
- err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
+ err = rdev_sched_scan_start(rdev, dev, request);
if (!err) {
rdev->sched_scan_req = request;
nl80211_send_sched_scan(rdev, dev,
@@ -4611,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
struct cfg80211_internal_bss *intbss)
{
struct cfg80211_bss *res = &intbss->pub;
+ const struct cfg80211_bss_ies *ies;
void *hdr;
struct nlattr *bss;
@@ -4631,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
if (!bss)
goto nla_put_failure;
if ((!is_zero_ether_addr(res->bssid) &&
- nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
- (res->information_elements && res->len_information_elements &&
- nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
- res->len_information_elements,
- res->information_elements)) ||
- (res->beacon_ies && res->len_beacon_ies &&
- res->beacon_ies != res->information_elements &&
- nla_put(msg, NL80211_BSS_BEACON_IES,
- res->len_beacon_ies, res->beacon_ies)))
+ nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
+ goto nla_put_failure;
+
+ rcu_read_lock();
+ ies = rcu_dereference(res->ies);
+ if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
+ ies->len, ies->data)) {
+ rcu_read_unlock();
+ goto nla_put_failure;
+ }
+ ies = rcu_dereference(res->beacon_ies);
+ if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
+ ies->len, ies->data)) {
+ rcu_read_unlock();
goto nla_put_failure;
+ }
+ rcu_read_unlock();
+
if (res->tsf &&
nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
goto nla_put_failure;
@@ -4815,8 +5021,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
while (1) {
struct ieee80211_channel *chan;
- res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
- &survey);
+ res = rdev_dump_survey(dev, netdev, survey_idx, &survey);
if (res == -ENOENT)
break;
if (res)
@@ -4852,11 +5057,6 @@ static int nl80211_dump_survey(struct sk_buff *skb,
return res;
}
-static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
-{
- return auth_type <= NL80211_AUTHTYPE_MAX;
-}
-
static bool nl80211_valid_wpa_versions(u32 wpa_versions)
{
return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
@@ -4868,8 +5068,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct ieee80211_channel *chan;
- const u8 *bssid, *ssid, *ie = NULL;
- int err, ssid_len, ie_len = 0;
+ const u8 *bssid, *ssid, *ie = NULL, *sae_data = NULL;
+ int err, ssid_len, ie_len = 0, sae_data_len = 0;
enum nl80211_auth_type auth_type;
struct key_parse key;
bool local_state_change;
@@ -4945,9 +5145,23 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
}
auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
- if (!nl80211_valid_auth_type(auth_type))
+ if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE))
+ return -EINVAL;
+
+ if (auth_type == NL80211_AUTHTYPE_SAE &&
+ !info->attrs[NL80211_ATTR_SAE_DATA])
return -EINVAL;
+ if (info->attrs[NL80211_ATTR_SAE_DATA]) {
+ if (auth_type != NL80211_AUTHTYPE_SAE)
+ return -EINVAL;
+ sae_data = nla_data(info->attrs[NL80211_ATTR_SAE_DATA]);
+ sae_data_len = nla_len(info->attrs[NL80211_ATTR_SAE_DATA]);
+ /* need to include at least Auth Transaction and Status Code */
+ if (sae_data_len < 4)
+ return -EINVAL;
+ }
+
local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
/*
@@ -4959,7 +5173,8 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
ssid, ssid_len, ie, ie_len,
- key.p.key, key.p.key_len, key.idx);
+ key.p.key, key.p.key_len, key.idx,
+ sae_data, sae_data_len);
}
static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
@@ -5250,8 +5465,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
return -EINVAL;
- if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
- !info->attrs[NL80211_ATTR_SSID] ||
+ if (!info->attrs[NL80211_ATTR_SSID] ||
!nla_len(info->attrs[NL80211_ATTR_SSID]))
return -EINVAL;
@@ -5286,34 +5500,17 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
}
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
- enum nl80211_channel_type channel_type;
-
- if (!nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- if (channel_type != NL80211_CHAN_NO_HT &&
- !(wiphy->features & NL80211_FEATURE_HT_IBSS))
- return -EINVAL;
-
- ibss.channel_type = channel_type;
- } else {
- ibss.channel_type = NL80211_CHAN_NO_HT;
- }
+ err = nl80211_parse_chandef(rdev, info, &ibss.chandef);
+ if (err)
+ return err;
- ibss.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- ibss.channel_type);
- if (!ibss.channel ||
- ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
- ibss.channel->flags & IEEE80211_CHAN_DISABLED)
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
return -EINVAL;
- /* Both channels should be able to initiate communication */
- if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
- ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
- !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
- ibss.channel_type))
+ if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
+ return -EINVAL;
+ if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
return -EINVAL;
ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
@@ -5325,7 +5522,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
int n_rates =
nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
struct ieee80211_supported_band *sband =
- wiphy->bands[ibss.channel->band];
+ wiphy->bands[ibss.chandef.chan->band];
err = ieee80211_get_ratemask(sband, rates, n_rates,
&ibss.basic_rates);
@@ -5339,10 +5536,19 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
+ bool no_ht = false;
+
connkeys = nl80211_parse_connkeys(rdev,
- info->attrs[NL80211_ATTR_KEYS]);
+ info->attrs[NL80211_ATTR_KEYS],
+ &no_ht);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
+
+ if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+ no_ht) {
+ kfree(connkeys);
+ return -EINVAL;
+ }
}
ibss.control_port =
@@ -5368,6 +5574,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
return cfg80211_leave_ibss(rdev, dev, false);
}
+static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ int mcast_rate[IEEE80211_NUM_BANDS];
+ u32 nla_rate;
+ int err;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->set_mcast_rate)
+ return -EOPNOTSUPP;
+
+ memset(mcast_rate, 0, sizeof(mcast_rate));
+
+ if (!info->attrs[NL80211_ATTR_MCAST_RATE])
+ return -EINVAL;
+
+ nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]);
+ if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate))
+ return -EINVAL;
+
+ err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate);
+
+ return err;
+}
+
+
#ifdef CONFIG_NL80211_TESTMODE
static struct genl_multicast_group nl80211_testmode_mcgrp = {
.name = "testmode",
@@ -5384,7 +5620,7 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
err = -EOPNOTSUPP;
if (rdev->ops->testmode_cmd) {
rdev->testmode_info = info;
- err = rdev->ops->testmode_cmd(&rdev->wiphy,
+ err = rdev_testmode_cmd(rdev,
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
rdev->testmode_info = NULL;
@@ -5466,8 +5702,7 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
genlmsg_cancel(skb, hdr);
break;
}
- err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
- data, data_len);
+ err = rdev_testmode_dump(rdev, skb, cb, data, data_len);
nla_nest_end(skb, tmdata);
if (err == -ENOBUFS || err == -ENOENT) {
@@ -5596,7 +5831,8 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
connect.auth_type =
nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
- if (!nl80211_valid_auth_type(connect.auth_type))
+ if (!nl80211_valid_auth_type(rdev, connect.auth_type,
+ NL80211_CMD_CONNECT))
return -EINVAL;
} else
connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
@@ -5642,7 +5878,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
connkeys = nl80211_parse_connkeys(rdev,
- info->attrs[NL80211_ATTR_KEYS]);
+ info->attrs[NL80211_ATTR_KEYS], NULL);
if (IS_ERR(connkeys))
return PTR_ERR(connkeys);
}
@@ -5771,7 +6007,7 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->flush_pmksa)
return -EOPNOTSUPP;
- return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+ return rdev_flush_pmksa(rdev, dev);
}
static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
@@ -5798,10 +6034,10 @@ static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
- return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
- dialog_token, status_code,
- nla_data(info->attrs[NL80211_ATTR_IE]),
- nla_len(info->attrs[NL80211_ATTR_IE]));
+ return rdev_tdls_mgmt(rdev, dev, peer, action_code,
+ dialog_token, status_code,
+ nla_data(info->attrs[NL80211_ATTR_IE]),
+ nla_len(info->attrs[NL80211_ATTR_IE]));
}
static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
@@ -5822,7 +6058,7 @@ static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
- return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
+ return rdev_tdls_oper(rdev, dev, peer, operation);
}
static int nl80211_remain_on_channel(struct sk_buff *skb,
@@ -5830,12 +6066,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
- struct ieee80211_channel *chan;
+ struct cfg80211_chan_def chandef;
struct sk_buff *msg;
void *hdr;
u64 cookie;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- u32 freq, duration;
+ u32 duration;
int err;
if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
@@ -5856,14 +6091,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
duration > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- chan = rdev_freq_to_chan(rdev, freq, channel_type);
- if (chan == NULL)
- return -EINVAL;
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
@@ -5877,8 +6107,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
goto free_msg;
}
- err = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
- channel_type, duration, &cookie);
+ err = rdev_remain_on_channel(rdev, wdev, chandef.chan,
+ duration, &cookie);
if (err)
goto free_msg;
@@ -5912,7 +6142,7 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
- return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
+ return rdev_cancel_remain_on_channel(rdev, wdev, cookie);
}
static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
@@ -6055,7 +6285,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
}
}
- return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
+ return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
}
static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
@@ -6097,10 +6327,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
- bool channel_type_valid = false;
- u32 freq;
+ struct cfg80211_chan_def chandef;
int err;
void *hdr = NULL;
u64 cookie;
@@ -6110,8 +6337,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
- if (!info->attrs[NL80211_ATTR_FRAME] ||
- !info->attrs[NL80211_ATTR_WIPHY_FREQ])
+ if (!info->attrs[NL80211_ATTR_FRAME])
return -EINVAL;
if (!rdev->ops->mgmt_tx)
@@ -6146,12 +6372,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
- if (!nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
- channel_type_valid = true;
- }
-
offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
@@ -6159,10 +6379,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
- freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
- chan = rdev_freq_to_chan(rdev, freq, channel_type);
- if (chan == NULL)
- return -EINVAL;
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
if (!dont_wait_for_ack) {
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
@@ -6178,8 +6397,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
}
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type,
- channel_type_valid, wait,
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
nla_data(info->attrs[NL80211_ATTR_FRAME]),
nla_len(info->attrs[NL80211_ATTR_FRAME]),
no_cck, dont_wait_for_ack, &cookie);
@@ -6230,7 +6448,7 @@ static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *in
cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
- return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
+ return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie);
}
static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
@@ -6260,8 +6478,7 @@ static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
if (state == wdev->ps)
return 0;
- err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
- wdev->ps_timeout);
+ err = rdev_set_power_mgmt(rdev, dev, state, wdev->ps_timeout);
if (!err)
wdev->ps = state;
return err;
@@ -6322,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
};
static int nl80211_set_cqm_txe(struct genl_info *info,
- u32 rate, u32 pkts, u32 intvl)
+ u32 rate, u32 pkts, u32 intvl)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
- if ((rate < 0 || rate > 100) ||
- (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
+ if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
return -EINVAL;
wdev = dev->ieee80211_ptr;
@@ -6341,8 +6557,7 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;
- return rdev->ops->set_cqm_txe_config(wdev->wiphy, dev,
- rate, pkts, intvl);
+ return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
}
static int nl80211_set_cqm_rssi(struct genl_info *info,
@@ -6364,8 +6579,7 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
return -EOPNOTSUPP;
- return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
- threshold, hysteresis);
+ return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
}
static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
@@ -6446,21 +6660,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
}
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
- enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
-
- if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
- !nl80211_valid_channel_type(info, &channel_type))
- return -EINVAL;
-
- setup.channel = rdev_freq_to_chan(rdev,
- nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
- channel_type);
- if (!setup.channel)
- return -EINVAL;
- setup.channel_type = channel_type;
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
} else {
/* cfg80211_join_mesh() will sort it out */
- setup.channel = NULL;
+ setup.chandef.chan = NULL;
}
return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
@@ -6690,7 +6895,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
set_wakeup:
if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
- rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
+ rdev_set_wakeup(rdev, rdev->wowlan);
return 0;
error:
@@ -6746,7 +6951,7 @@ static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
goto out;
}
- err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
+ err = rdev_set_rekey_data(rdev, dev, &rekey_data);
out:
wdev_unlock(wdev);
return err;
@@ -6805,7 +7010,7 @@ static int nl80211_probe_client(struct sk_buff *skb,
addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
- err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
+ err = rdev_probe_client(rdev, dev, addr, &cookie);
if (err)
goto free_msg;
@@ -6826,16 +7031,35 @@ static int nl80211_probe_client(struct sk_buff *skb,
static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct cfg80211_beacon_registration *reg, *nreg;
+ int rv;
if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
return -EOPNOTSUPP;
- if (rdev->ap_beacons_nlportid)
- return -EBUSY;
+ nreg = kzalloc(sizeof(*nreg), GFP_KERNEL);
+ if (!nreg)
+ return -ENOMEM;
+
+ /* First, check if already registered. */
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ if (reg->nlportid == info->snd_portid) {
+ rv = -EALREADY;
+ goto out_err;
+ }
+ }
+ /* Add it to the list */
+ nreg->nlportid = info->snd_portid;
+ list_add(&nreg->list, &rdev->beacon_registrations);
- rdev->ap_beacons_nlportid = info->snd_portid;
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
return 0;
+out_err:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ kfree(nreg);
+ return rv;
}
static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
@@ -6859,7 +7083,7 @@ static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- err = rdev->ops->start_p2p_device(&rdev->wiphy, wdev);
+ err = rdev_start_p2p_device(rdev, wdev);
if (err)
return err;
@@ -6885,7 +7109,7 @@ static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info)
if (!wdev->p2p_started)
return 0;
- rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+ rdev_stop_p2p_device(rdev, wdev);
wdev->p2p_started = false;
mutex_lock(&rdev->devlist_mtx);
@@ -7552,6 +7776,14 @@ static struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_MCAST_RATE,
+ .doit = nl80211_set_mcast_rate,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -7622,6 +7854,9 @@ static int nl80211_add_scan_req(struct sk_buff *msg,
nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
goto nla_put_failure;
+ if (req->flags)
+ nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, req->flags);
+
return 0;
nla_put_failure:
return -ENOBUFS;
@@ -8250,7 +8485,6 @@ static void nl80211_send_remain_on_chan_event(
int cmd, struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
struct sk_buff *msg;
@@ -8271,7 +8505,8 @@ static void nl80211_send_remain_on_chan_event(
wdev->netdev->ifindex)) ||
nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) ||
nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+ NL80211_CHAN_NO_HT) ||
nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
goto nla_put_failure;
@@ -8293,23 +8528,20 @@ static void nl80211_send_remain_on_chan_event(
void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp)
{
nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
rdev, wdev, cookie, chan,
- channel_type, duration, gfp);
+ duration, gfp);
}
void nl80211_send_remain_on_channel_cancel(
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, gfp_t gfp)
+ u64 cookie, struct ieee80211_channel *chan, gfp_t gfp)
{
nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
- rdev, wdev, cookie, chan,
- channel_type, 0, gfp);
+ rdev, wdev, cookie, chan, 0, gfp);
}
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
@@ -8665,8 +8897,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
}
void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *netdev, int freq,
- enum nl80211_channel_type type, gfp_t gfp)
+ struct net_device *netdev,
+ struct cfg80211_chan_def *chandef, gfp_t gfp)
{
struct sk_buff *msg;
void *hdr;
@@ -8681,9 +8913,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
return;
}
- if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
- nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
+ if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
+ goto nla_put_failure;
+
+ if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
genlmsg_end(msg, hdr);
@@ -8800,7 +9033,10 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
void *hdr;
int err;
+ trace_cfg80211_probe_status(dev, addr, cookie, acked);
+
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+
if (!msg)
return;
@@ -8835,44 +9071,96 @@ EXPORT_SYMBOL(cfg80211_probe_status);
void cfg80211_report_obss_beacon(struct wiphy *wiphy,
const u8 *frame, size_t len,
- int freq, int sig_dbm, gfp_t gfp)
+ int freq, int sig_dbm)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
struct sk_buff *msg;
void *hdr;
- u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid);
+ struct cfg80211_beacon_registration *reg;
- if (!nlportid)
- return;
+ trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm);
+
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry(reg, &rdev->beacon_registrations, list) {
+ msg = nlmsg_new(len + 100, GFP_ATOMIC);
+ if (!msg) {
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+ }
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ if (!hdr)
+ goto nla_put_failure;
- msg = nlmsg_new(len + 100, gfp);
+ if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+ (freq &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+ (sig_dbm &&
+ nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
+ nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+
+ genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid);
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ return;
+
+ nla_put_failure:
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
+ if (hdr)
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+
+void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
+ enum nl80211_tdls_operation oper,
+ u16 reason_code, gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct sk_buff *msg;
+ void *hdr;
+ int err;
+
+ trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper,
+ reason_code);
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
return;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
if (!hdr) {
nlmsg_free(msg);
return;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- (freq &&
- nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
- (sig_dbm &&
- nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
- nla_put(msg, NL80211_ATTR_FRAME, len, frame))
+ nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
+ (reason_code > 0 &&
+ nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
goto nla_put_failure;
- genlmsg_end(msg, hdr);
+ err = genlmsg_end(msg, hdr);
+ if (err < 0) {
+ nlmsg_free(msg);
+ return;
+ }
- genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
return;
nla_put_failure:
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
-EXPORT_SYMBOL(cfg80211_report_obss_beacon);
+EXPORT_SYMBOL(cfg80211_tdls_oper_request);
static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state,
@@ -8881,6 +9169,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
struct netlink_notify *notify = _notify;
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
+ struct cfg80211_beacon_registration *reg, *tmp;
if (state != NETLINK_URELEASE)
return NOTIFY_DONE;
@@ -8890,8 +9179,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
list_for_each_entry_rcu(wdev, &rdev->wdev_list, list)
cfg80211_mlme_unregister_socket(wdev, notify->portid);
- if (rdev->ap_beacons_nlportid == notify->portid)
- rdev->ap_beacons_nlportid = 0;
+
+ spin_lock_bh(&rdev->beacon_registrations_lock);
+ list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations,
+ list) {
+ if (reg->nlportid == notify->portid) {
+ list_del(&reg->list);
+ kfree(reg);
+ break;
+ }
+ }
+ spin_unlock_bh(&rdev->beacon_registrations_lock);
}
rcu_read_unlock();
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index f6153516068..2acba8477e9 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -76,13 +76,11 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev, u64 cookie,
struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type,
unsigned int duration, gfp_t gfp);
void nl80211_send_remain_on_channel_cancel(
struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- u64 cookie, struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type, gfp_t gfp);
+ u64 cookie, struct ieee80211_channel *chan, gfp_t gfp);
void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
struct net_device *dev, const u8 *mac_addr,
@@ -129,8 +127,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
const u8 *bssid, bool preauth, gfp_t gfp);
void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
- struct net_device *dev, int freq,
- enum nl80211_channel_type type, gfp_t gfp);
+ struct net_device *dev,
+ struct cfg80211_chan_def *chandef, gfp_t gfp);
bool nl80211_unexpected_frame(struct net_device *dev,
const u8 *addr, gfp_t gfp);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
new file mode 100644
index 00000000000..6c0c8191f83
--- /dev/null
+++ b/net/wireless/rdev-ops.h
@@ -0,0 +1,878 @@
+#ifndef __CFG80211_RDEV_OPS
+#define __CFG80211_RDEV_OPS
+
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "core.h"
+#include "trace.h"
+
+static inline int rdev_suspend(struct cfg80211_registered_device *rdev)
+{
+ int ret;
+ trace_rdev_suspend(&rdev->wiphy, rdev->wowlan);
+ ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_resume(struct cfg80211_registered_device *rdev)
+{
+ int ret;
+ trace_rdev_resume(&rdev->wiphy);
+ ret = rdev->ops->resume(&rdev->wiphy);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_set_wakeup(struct cfg80211_registered_device *rdev,
+ bool enabled)
+{
+ trace_rdev_set_wakeup(&rdev->wiphy, enabled);
+ rdev->ops->set_wakeup(&rdev->wiphy, enabled);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline struct wireless_dev
+*rdev_add_virtual_intf(struct cfg80211_registered_device *rdev, char *name,
+ enum nl80211_iftype type, u32 *flags,
+ struct vif_params *params)
+{
+ struct wireless_dev *ret;
+ trace_rdev_add_virtual_intf(&rdev->wiphy, name, type);
+ ret = rdev->ops->add_virtual_intf(&rdev->wiphy, name, type, flags,
+ params);
+ trace_rdev_return_wdev(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ int ret;
+ trace_rdev_del_virtual_intf(&rdev->wiphy, wdev);
+ ret = rdev->ops->del_virtual_intf(&rdev->wiphy, wdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_change_virtual_intf(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, enum nl80211_iftype type,
+ u32 *flags, struct vif_params *params)
+{
+ int ret;
+ trace_rdev_change_virtual_intf(&rdev->wiphy, dev, type);
+ ret = rdev->ops->change_virtual_intf(&rdev->wiphy, dev, type, flags,
+ params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_add_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr,
+ struct key_params *params)
+{
+ int ret;
+ trace_rdev_add_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->add_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_get_key(struct cfg80211_registered_device *rdev, struct net_device *netdev,
+ u8 key_index, bool pairwise, const u8 *mac_addr, void *cookie,
+ void (*callback)(void *cookie, struct key_params*))
+{
+ int ret;
+ trace_rdev_get_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->get_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr, cookie, callback);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr)
+{
+ int ret;
+ trace_rdev_del_key(&rdev->wiphy, netdev, key_index, pairwise, mac_addr);
+ ret = rdev->ops->del_key(&rdev->wiphy, netdev, key_index, pairwise,
+ mac_addr);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_default_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index, bool unicast,
+ bool multicast)
+{
+ int ret;
+ trace_rdev_set_default_key(&rdev->wiphy, netdev, key_index,
+ unicast, multicast);
+ ret = rdev->ops->set_default_key(&rdev->wiphy, netdev, key_index,
+ unicast, multicast);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, u8 key_index)
+{
+ int ret;
+ trace_rdev_set_default_mgmt_key(&rdev->wiphy, netdev, key_index);
+ ret = rdev->ops->set_default_mgmt_key(&rdev->wiphy, netdev,
+ key_index);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_start_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ap_settings *settings)
+{
+ int ret;
+ trace_rdev_start_ap(&rdev->wiphy, dev, settings);
+ ret = rdev->ops->start_ap(&rdev->wiphy, dev, settings);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_beacon(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_beacon_data *info)
+{
+ int ret;
+ trace_rdev_change_beacon(&rdev->wiphy, dev, info);
+ ret = rdev->ops->change_beacon(&rdev->wiphy, dev, info);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_stop_ap(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_stop_ap(&rdev->wiphy, dev);
+ ret = rdev->ops->stop_ap(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac,
+ struct station_parameters *params)
+{
+ int ret;
+ trace_rdev_add_station(&rdev->wiphy, dev, mac, params);
+ ret = rdev->ops->add_station(&rdev->wiphy, dev, mac, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac)
+{
+ int ret;
+ trace_rdev_del_station(&rdev->wiphy, dev, mac);
+ ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac,
+ struct station_parameters *params)
+{
+ int ret;
+ trace_rdev_change_station(&rdev->wiphy, dev, mac, params);
+ ret = rdev->ops->change_station(&rdev->wiphy, dev, mac, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *mac,
+ struct station_info *sinfo)
+{
+ int ret;
+ trace_rdev_get_station(&rdev->wiphy, dev, mac);
+ ret = rdev->ops->get_station(&rdev->wiphy, dev, mac, sinfo);
+ trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
+ return ret;
+}
+
+static inline int rdev_dump_station(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *mac,
+ struct station_info *sinfo)
+{
+ int ret;
+ trace_rdev_dump_station(&rdev->wiphy, dev, idx, mac);
+ ret = rdev->ops->dump_station(&rdev->wiphy, dev, idx, mac, sinfo);
+ trace_rdev_return_int_station_info(&rdev->wiphy, ret, sinfo);
+ return ret;
+}
+
+static inline int rdev_add_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *next_hop)
+{
+ int ret;
+ trace_rdev_add_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst)
+{
+ int ret;
+ trace_rdev_del_mpath(&rdev->wiphy, dev, dst);
+ ret = rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst,
+ u8 *next_hop)
+{
+ int ret;
+ trace_rdev_change_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *next_hop,
+ struct mpath_info *pinfo)
+{
+ int ret;
+ trace_rdev_get_mpath(&rdev->wiphy, dev, dst, next_hop);
+ ret = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+
+}
+
+static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *dst,
+ u8 *next_hop, struct mpath_info *pinfo)
+
+{
+ int ret;
+ trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
+ ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
+ pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int
+rdev_get_mesh_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, struct mesh_config *conf)
+{
+ int ret;
+ trace_rdev_get_mesh_config(&rdev->wiphy, dev);
+ ret = rdev->ops->get_mesh_config(&rdev->wiphy, dev, conf);
+ trace_rdev_return_int_mesh_config(&rdev->wiphy, ret, conf);
+ return ret;
+}
+
+static inline int
+rdev_update_mesh_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u32 mask,
+ const struct mesh_config *nconf)
+{
+ int ret;
+ trace_rdev_update_mesh_config(&rdev->wiphy, dev, mask, nconf);
+ ret = rdev->ops->update_mesh_config(&rdev->wiphy, dev, mask, nconf);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_join_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ const struct mesh_config *conf,
+ const struct mesh_setup *setup)
+{
+ int ret;
+ trace_rdev_join_mesh(&rdev->wiphy, dev, conf, setup);
+ ret = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+
+static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_mesh(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_mesh(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct bss_parameters *params)
+
+{
+ int ret;
+ trace_rdev_change_bss(&rdev->wiphy, dev, params);
+ ret = rdev->ops->change_bss(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_txq_params(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_txq_params *params)
+
+{
+ int ret;
+ trace_rdev_set_txq_params(&rdev->wiphy, dev, params);
+ ret = rdev->ops->set_txq_params(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ieee80211_channel *chan)
+{
+ int ret;
+ trace_rdev_libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
+ ret = rdev->ops->libertas_set_mesh_channel(&rdev->wiphy, dev, chan);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_monitor_channel(struct cfg80211_registered_device *rdev,
+ struct cfg80211_chan_def *chandef)
+{
+ int ret;
+ trace_rdev_set_monitor_channel(&rdev->wiphy, chandef);
+ ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_scan(struct cfg80211_registered_device *rdev,
+ struct cfg80211_scan_request *request)
+{
+ int ret;
+ trace_rdev_scan(&rdev->wiphy, request);
+ ret = rdev->ops->scan(&rdev->wiphy, request);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_auth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_auth_request *req)
+{
+ int ret;
+ trace_rdev_auth(&rdev->wiphy, dev, req);
+ ret = rdev->ops->auth(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_assoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_assoc_request *req)
+{
+ int ret;
+ trace_rdev_assoc(&rdev->wiphy, dev, req);
+ ret = rdev->ops->assoc(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_deauth(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_deauth_request *req)
+{
+ int ret;
+ trace_rdev_deauth(&rdev->wiphy, dev, req);
+ ret = rdev->ops->deauth(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_disassoc(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_disassoc_request *req)
+{
+ int ret;
+ trace_rdev_disassoc(&rdev->wiphy, dev, req);
+ ret = rdev->ops->disassoc(&rdev->wiphy, dev, req);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_connect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_connect_params *sme)
+{
+ int ret;
+ trace_rdev_connect(&rdev->wiphy, dev, sme);
+ ret = rdev->ops->connect(&rdev->wiphy, dev, sme);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_disconnect(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 reason_code)
+{
+ int ret;
+ trace_rdev_disconnect(&rdev->wiphy, dev, reason_code);
+ ret = rdev->ops->disconnect(&rdev->wiphy, dev, reason_code);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_join_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_ibss_params *params)
+{
+ int ret;
+ trace_rdev_join_ibss(&rdev->wiphy, dev, params);
+ ret = rdev->ops->join_ibss(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ibss(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ibss(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ibss(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed)
+{
+ int ret;
+ trace_rdev_set_wiphy_params(&rdev->wiphy, changed);
+ ret = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm)
+{
+ int ret;
+ trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm);
+ ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, int *dbm)
+{
+ int ret;
+ trace_rdev_get_tx_power(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm);
+ trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm);
+ return ret;
+}
+
+static inline int rdev_set_wds_peer(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr)
+{
+ int ret;
+ trace_rdev_set_wds_peer(&rdev->wiphy, dev, addr);
+ ret = rdev->ops->set_wds_peer(&rdev->wiphy, dev, addr);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
+{
+ trace_rdev_rfkill_poll(&rdev->wiphy);
+ rdev->ops->rfkill_poll(&rdev->wiphy);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+
+#ifdef CONFIG_NL80211_TESTMODE
+static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
+ void *data, int len)
+{
+ int ret;
+ trace_rdev_testmode_cmd(&rdev->wiphy);
+ ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_testmode_dump(struct cfg80211_registered_device *rdev,
+ struct sk_buff *skb,
+ struct netlink_callback *cb, void *data,
+ int len)
+{
+ int ret;
+ trace_rdev_testmode_dump(&rdev->wiphy);
+ ret = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb, data, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+#endif
+
+static inline int
+rdev_set_bitrate_mask(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *peer,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ int ret;
+ trace_rdev_set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
+ ret = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, peer, mask);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_dump_survey(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev, int idx,
+ struct survey_info *info)
+{
+ int ret;
+ trace_rdev_dump_survey(&rdev->wiphy, netdev, idx);
+ ret = rdev->ops->dump_survey(&rdev->wiphy, netdev, idx, info);
+ if (ret < 0)
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ else
+ trace_rdev_return_int_survey_info(&rdev->wiphy, ret, info);
+ return ret;
+}
+
+static inline int rdev_set_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ int ret;
+ trace_rdev_set_pmksa(&rdev->wiphy, netdev, pmksa);
+ ret = rdev->ops->set_pmksa(&rdev->wiphy, netdev, pmksa);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_del_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa)
+{
+ int ret;
+ trace_rdev_del_pmksa(&rdev->wiphy, netdev, pmksa);
+ ret = rdev->ops->del_pmksa(&rdev->wiphy, netdev, pmksa);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_flush_pmksa(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev)
+{
+ int ret;
+ trace_rdev_flush_pmksa(&rdev->wiphy, netdev);
+ ret = rdev->ops->flush_pmksa(&rdev->wiphy, netdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_remain_on_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration, u64 *cookie)
+{
+ int ret;
+ trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration);
+ ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan,
+ duration, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int
+rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ int ret;
+ trace_rdev_cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
+ ret = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct ieee80211_channel *chan, bool offchan,
+ unsigned int wait, const u8 *buf, size_t len,
+ bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+{
+ int ret;
+ trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
+ wait, no_cck, dont_wait_for_ack);
+ ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
+ wait, buf, len, no_cck,
+ dont_wait_for_ack, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int
+rdev_mgmt_tx_cancel_wait(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u64 cookie)
+{
+ int ret;
+ trace_rdev_mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
+ ret = rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, wdev, cookie);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_power_mgmt(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, bool enabled,
+ int timeout)
+{
+ int ret;
+ trace_rdev_set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
+ ret = rdev->ops->set_power_mgmt(&rdev->wiphy, dev, enabled, timeout);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, s32 rssi_thold, u32 rssi_hyst)
+{
+ int ret;
+ trace_rdev_set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
+ rssi_hyst);
+ ret = rdev->ops->set_cqm_rssi_config(&rdev->wiphy, dev, rssi_thold,
+ rssi_hyst);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u32 rate, u32 pkts, u32 intvl)
+{
+ int ret;
+ trace_rdev_set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts, intvl);
+ ret = rdev->ops->set_cqm_txe_config(&rdev->wiphy, dev, rate, pkts,
+ intvl);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void
+rdev_mgmt_frame_register(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev, u16 frame_type, bool reg)
+{
+ trace_rdev_mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
+ rdev->ops->mgmt_frame_register(&rdev->wiphy, wdev , frame_type, reg);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int rdev_set_antenna(struct cfg80211_registered_device *rdev,
+ u32 tx_ant, u32 rx_ant)
+{
+ int ret;
+ trace_rdev_set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ ret = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_get_antenna(struct cfg80211_registered_device *rdev,
+ u32 *tx_ant, u32 *rx_ant)
+{
+ int ret;
+ trace_rdev_get_antenna(&rdev->wiphy);
+ ret = rdev->ops->get_antenna(&rdev->wiphy, tx_ant, rx_ant);
+ if (ret)
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ else
+ trace_rdev_return_int_tx_rx(&rdev->wiphy, ret, *tx_ant,
+ *rx_ant);
+ return ret;
+}
+
+static inline int rdev_set_ringparam(struct cfg80211_registered_device *rdev,
+ u32 tx, u32 rx)
+{
+ int ret;
+ trace_rdev_set_ringparam(&rdev->wiphy, tx, rx);
+ ret = rdev->ops->set_ringparam(&rdev->wiphy, tx, rx);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_get_ringparam(struct cfg80211_registered_device *rdev,
+ u32 *tx, u32 *tx_max, u32 *rx,
+ u32 *rx_max)
+{
+ trace_rdev_get_ringparam(&rdev->wiphy);
+ rdev->ops->get_ringparam(&rdev->wiphy, tx, tx_max, rx, rx_max);
+ trace_rdev_return_void_tx_rx(&rdev->wiphy, *tx, *tx_max, *rx, *rx_max);
+}
+
+static inline int
+rdev_sched_scan_start(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_sched_scan_request *request)
+{
+ int ret;
+ trace_rdev_sched_scan_start(&rdev->wiphy, dev, request);
+ ret = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_sched_scan_stop(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_sched_scan_stop(&rdev->wiphy, dev);
+ ret = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_set_rekey_data(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_gtk_rekey_data *data)
+{
+ int ret;
+ trace_rdev_set_rekey_data(&rdev->wiphy, dev);
+ ret = rdev->ops->set_rekey_data(&rdev->wiphy, dev, data);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_tdls_mgmt(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *peer,
+ u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len)
+{
+ int ret;
+ trace_rdev_tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+ dialog_token, status_code, buf, len);
+ ret = rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
+ dialog_token, status_code, buf, len);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_tdls_oper(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ int ret;
+ trace_rdev_tdls_oper(&rdev->wiphy, dev, peer, oper);
+ ret = rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, oper);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_probe_client(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *peer,
+ u64 *cookie)
+{
+ int ret;
+ trace_rdev_probe_client(&rdev->wiphy, dev, peer);
+ ret = rdev->ops->probe_client(&rdev->wiphy, dev, peer, cookie);
+ trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
+ return ret;
+}
+
+static inline int rdev_set_noack_map(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u16 noack_map)
+{
+ int ret;
+ trace_rdev_set_noack_map(&rdev->wiphy, dev, noack_map);
+ ret = rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int
+rdev_get_et_sset_count(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int sset)
+{
+ int ret;
+ trace_rdev_get_et_sset_count(&rdev->wiphy, dev, sset);
+ ret = rdev->ops->get_et_sset_count(&rdev->wiphy, dev, sset);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_get_et_stats(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ethtool_stats *stats, u64 *data)
+{
+ trace_rdev_get_et_stats(&rdev->wiphy, dev);
+ rdev->ops->get_et_stats(&rdev->wiphy, dev, stats, data);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u32 sset,
+ u8 *data)
+{
+ trace_rdev_get_et_strings(&rdev->wiphy, dev, sset);
+ rdev->ops->get_et_strings(&rdev->wiphy, dev, sset, data);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
+static inline int
+rdev_get_channel(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
+ struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_get_channel(&rdev->wiphy, wdev);
+ ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef);
+ trace_rdev_return_chandef(&rdev->wiphy, ret, chandef);
+
+ return ret;
+}
+
+static inline int rdev_start_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ int ret;
+
+ trace_rdev_start_p2p_device(&rdev->wiphy, wdev);
+ ret = rdev->ops->start_p2p_device(&rdev->wiphy, wdev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void rdev_stop_p2p_device(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev)
+{
+ trace_rdev_stop_p2p_device(&rdev->wiphy, wdev);
+ rdev->ops->stop_p2p_device(&rdev->wiphy, wdev);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 3b8cbbc214d..82c4fc7c994 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -141,9 +141,8 @@ static const struct ieee80211_regdomain world_regdom = {
.reg_rules = {
/* IEEE 802.11b/g, channels 1..11 */
REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
- /* IEEE 802.11b/g, channels 12..13. No HT40
- * channel fits here. */
- REG_RULE(2467-10, 2472+10, 20, 6, 20,
+ /* IEEE 802.11b/g, channels 12..13. */
+ REG_RULE(2467-10, 2472+10, 40, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
/* IEEE 802.11 channel 14 - Only JP enables
@@ -908,7 +907,7 @@ static void handle_channel(struct wiphy *wiphy,
map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = chan->orig_mag =
(int) MBI_TO_DBI(power_rule->max_antenna_gain);
- chan->max_power = chan->orig_mpwr =
+ chan->max_reg_power = chan->max_power = chan->orig_mpwr =
(int) MBM_TO_DBM(power_rule->max_eirp);
return;
}
@@ -1331,7 +1330,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
- chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp);
+ chan->max_reg_power = chan->max_power =
+ (int) MBM_TO_DBM(power_rule->max_eirp);
}
static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band,
@@ -1796,7 +1796,7 @@ EXPORT_SYMBOL(regulatory_hint);
*/
void regulatory_hint_11d(struct wiphy *wiphy,
enum ieee80211_band band,
- u8 *country_ie,
+ const u8 *country_ie,
u8 country_ie_len)
{
char alpha2[2];
@@ -2365,7 +2365,6 @@ int set_regdom(const struct ieee80211_regdomain *rd)
return r;
}
-#ifdef CONFIG_HOTPLUG
int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
{
if (last_request && !last_request->processed) {
@@ -2377,12 +2376,6 @@ int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0;
}
-#else
-int reg_device_uevent(struct device *dev, struct kobj_uevent_env *env)
-{
- return -ENODEV;
-}
-#endif /* CONFIG_HOTPLUG */
void wiphy_regulatory_register(struct wiphy *wiphy)
{
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index f023c8a31c6..4c0a32ffd53 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -81,7 +81,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy,
*/
void regulatory_hint_11d(struct wiphy *wiphy,
enum ieee80211_band band,
- u8 *country_ie,
+ const u8 *country_ie,
u8 country_ie_len);
/**
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index 9730c9862bd..01592d7d478 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -17,9 +17,63 @@
#include "core.h"
#include "nl80211.h"
#include "wext-compat.h"
+#include "rdev-ops.h"
#define IEEE80211_SCAN_RESULT_EXPIRE (30 * HZ)
+static void bss_release(struct kref *ref)
+{
+ struct cfg80211_bss_ies *ies;
+ struct cfg80211_internal_bss *bss;
+
+ bss = container_of(ref, struct cfg80211_internal_bss, ref);
+
+ if (WARN_ON(atomic_read(&bss->hold)))
+ return;
+
+ if (bss->pub.free_priv)
+ bss->pub.free_priv(&bss->pub);
+
+ ies = (void *)rcu_access_pointer(bss->pub.beacon_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+ ies = (void *)rcu_access_pointer(bss->pub.proberesp_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+
+ kfree(bss);
+}
+
+/* must hold dev->bss_lock! */
+static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
+ struct cfg80211_internal_bss *bss)
+{
+ list_del_init(&bss->list);
+ rb_erase(&bss->rbn, &dev->bss_tree);
+ kref_put(&bss->ref, bss_release);
+}
+
+/* must hold dev->bss_lock! */
+static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
+ unsigned long expire_time)
+{
+ struct cfg80211_internal_bss *bss, *tmp;
+ bool expired = false;
+
+ list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
+ if (atomic_read(&bss->hold))
+ continue;
+ if (!time_after(expire_time, bss->ts))
+ continue;
+
+ __cfg80211_unlink_bss(dev, bss);
+ expired = true;
+ }
+
+ if (expired)
+ dev->bss_generation++;
+}
+
void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
{
struct cfg80211_scan_request *request;
@@ -45,10 +99,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
if (wdev->netdev)
cfg80211_sme_scan_done(wdev->netdev);
- if (request->aborted)
+ if (request->aborted) {
nl80211_send_scan_aborted(rdev, wdev);
- else
+ } else {
+ if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
+ /* flush entries from previous scans */
+ spin_lock_bh(&rdev->bss_lock);
+ __cfg80211_bss_expire(rdev, request->scan_start);
+ spin_unlock_bh(&rdev->bss_lock);
+ }
nl80211_send_scan_done(rdev, wdev);
+ }
#ifdef CONFIG_CFG80211_WEXT
if (wdev->netdev && !request->aborted) {
@@ -89,6 +150,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
{
+ trace_cfg80211_scan_done(request, aborted);
WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted;
@@ -99,22 +161,34 @@ EXPORT_SYMBOL(cfg80211_scan_done);
void __cfg80211_sched_scan_results(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
+ struct cfg80211_sched_scan_request *request;
rdev = container_of(wk, struct cfg80211_registered_device,
sched_scan_results_wk);
+ request = rdev->sched_scan_req;
+
mutex_lock(&rdev->sched_scan_mtx);
/* we don't have sched_scan_req anymore if the scan is stopping */
- if (rdev->sched_scan_req)
- nl80211_send_sched_scan_results(rdev,
- rdev->sched_scan_req->dev);
+ if (request) {
+ if (request->flags & NL80211_SCAN_FLAG_FLUSH) {
+ /* flush entries from previous scans */
+ spin_lock_bh(&rdev->bss_lock);
+ __cfg80211_bss_expire(rdev, request->scan_start);
+ spin_unlock_bh(&rdev->bss_lock);
+ request->scan_start =
+ jiffies + msecs_to_jiffies(request->interval);
+ }
+ nl80211_send_sched_scan_results(rdev, request->dev);
+ }
mutex_unlock(&rdev->sched_scan_mtx);
}
void cfg80211_sched_scan_results(struct wiphy *wiphy)
{
+ trace_cfg80211_sched_scan_results(wiphy);
/* ignore if we're not scanning */
if (wiphy_to_dev(wiphy)->sched_scan_req)
queue_work(cfg80211_wq,
@@ -126,6 +200,8 @@ void cfg80211_sched_scan_stopped(struct wiphy *wiphy)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ trace_cfg80211_sched_scan_stopped(wiphy);
+
mutex_lock(&rdev->sched_scan_mtx);
__cfg80211_stop_sched_scan(rdev, true);
mutex_unlock(&rdev->sched_scan_mtx);
@@ -145,7 +221,7 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
dev = rdev->sched_scan_req->dev;
if (!driver_initiated) {
- int err = rdev->ops->sched_scan_stop(&rdev->wiphy, dev);
+ int err = rdev_sched_scan_stop(rdev, dev);
if (err)
return err;
}
@@ -158,24 +234,6 @@ int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
return 0;
}
-static void bss_release(struct kref *ref)
-{
- struct cfg80211_internal_bss *bss;
-
- bss = container_of(ref, struct cfg80211_internal_bss, ref);
- if (bss->pub.free_priv)
- bss->pub.free_priv(&bss->pub);
-
- if (bss->beacon_ies_allocated)
- kfree(bss->pub.beacon_ies);
- if (bss->proberesp_ies_allocated)
- kfree(bss->pub.proberesp_ies);
-
- BUG_ON(atomic_read(&bss->hold));
-
- kfree(bss);
-}
-
/* must hold dev->bss_lock! */
void cfg80211_bss_age(struct cfg80211_registered_device *dev,
unsigned long age_secs)
@@ -183,37 +241,13 @@ void cfg80211_bss_age(struct cfg80211_registered_device *dev,
struct cfg80211_internal_bss *bss;
unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
- list_for_each_entry(bss, &dev->bss_list, list) {
+ list_for_each_entry(bss, &dev->bss_list, list)
bss->ts -= age_jiffies;
- }
-}
-
-/* must hold dev->bss_lock! */
-static void __cfg80211_unlink_bss(struct cfg80211_registered_device *dev,
- struct cfg80211_internal_bss *bss)
-{
- list_del_init(&bss->list);
- rb_erase(&bss->rbn, &dev->bss_tree);
- kref_put(&bss->ref, bss_release);
}
-/* must hold dev->bss_lock! */
void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
{
- struct cfg80211_internal_bss *bss, *tmp;
- bool expired = false;
-
- list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
- if (atomic_read(&bss->hold))
- continue;
- if (!time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
- continue;
- __cfg80211_unlink_bss(dev, bss);
- expired = true;
- }
-
- if (expired)
- dev->bss_generation++;
+ __cfg80211_bss_expire(dev, jiffies - IEEE80211_SCAN_RESULT_EXPIRE);
}
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
@@ -257,7 +291,7 @@ const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type,
}
EXPORT_SYMBOL(cfg80211_find_vendor_ie);
-static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
+static int cmp_ies(u8 num, const u8 *ies1, int len1, const u8 *ies2, int len2)
{
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
@@ -277,10 +311,10 @@ static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
}
-static bool is_bss(struct cfg80211_bss *a,
- const u8 *bssid,
+static bool is_bss(struct cfg80211_bss *a, const u8 *bssid,
const u8 *ssid, size_t ssid_len)
{
+ const struct cfg80211_bss_ies *ies;
const u8 *ssidie;
if (bssid && !ether_addr_equal(a->bssid, bssid))
@@ -289,9 +323,10 @@ static bool is_bss(struct cfg80211_bss *a,
if (!ssid)
return true;
- ssidie = cfg80211_find_ie(WLAN_EID_SSID,
- a->information_elements,
- a->len_information_elements);
+ ies = rcu_access_pointer(a->ies);
+ if (!ies)
+ return false;
+ ssidie = cfg80211_find_ie(WLAN_EID_SSID, ies->data, ies->len);
if (!ssidie)
return false;
if (ssidie[1] != ssid_len)
@@ -301,20 +336,21 @@ static bool is_bss(struct cfg80211_bss *a,
static bool is_mesh_bss(struct cfg80211_bss *a)
{
+ const struct cfg80211_bss_ies *ies;
const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
- ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
- a->information_elements,
- a->len_information_elements);
+ ies = rcu_access_pointer(a->ies);
+ if (!ies)
+ return false;
+
+ ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
- ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
- a->information_elements,
- a->len_information_elements);
+ ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
@@ -325,14 +361,17 @@ static bool is_mesh(struct cfg80211_bss *a,
const u8 *meshid, size_t meshidlen,
const u8 *meshcfg)
{
+ const struct cfg80211_bss_ies *ies;
const u8 *ie;
if (!WLAN_CAPABILITY_IS_STA_BSS(a->capability))
return false;
- ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
- a->information_elements,
- a->len_information_elements);
+ ies = rcu_access_pointer(a->ies);
+ if (!ies)
+ return false;
+
+ ie = cfg80211_find_ie(WLAN_EID_MESH_ID, ies->data, ies->len);
if (!ie)
return false;
if (ie[1] != meshidlen)
@@ -340,9 +379,7 @@ static bool is_mesh(struct cfg80211_bss *a,
if (memcmp(ie + 2, meshid, meshidlen))
return false;
- ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
- a->information_elements,
- a->len_information_elements);
+ ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG, ies->data, ies->len);
if (!ie)
return false;
if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@@ -354,30 +391,33 @@ static bool is_mesh(struct cfg80211_bss *a,
* part in the same mesh.
*/
return memcmp(ie + 2, meshcfg,
- sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
+ sizeof(struct ieee80211_meshconf_ie) - 2) == 0;
}
-static int cmp_bss_core(struct cfg80211_bss *a,
- struct cfg80211_bss *b)
+static int cmp_bss_core(struct cfg80211_bss *a, struct cfg80211_bss *b)
{
+ const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
if (a->channel != b->channel)
return b->channel->center_freq - a->channel->center_freq;
if (is_mesh_bss(a) && is_mesh_bss(b)) {
+ a_ies = rcu_access_pointer(a->ies);
+ if (!a_ies)
+ return -1;
+ b_ies = rcu_access_pointer(b->ies);
+ if (!b_ies)
+ return 1;
+
r = cmp_ies(WLAN_EID_MESH_ID,
- a->information_elements,
- a->len_information_elements,
- b->information_elements,
- b->len_information_elements);
+ a_ies->data, a_ies->len,
+ b_ies->data, b_ies->len);
if (r)
return r;
return cmp_ies(WLAN_EID_MESH_CONFIG,
- a->information_elements,
- a->len_information_elements,
- b->information_elements,
- b->len_information_elements);
+ a_ies->data, a_ies->len,
+ b_ies->data, b_ies->len);
}
/*
@@ -390,22 +430,28 @@ static int cmp_bss_core(struct cfg80211_bss *a,
static int cmp_bss(struct cfg80211_bss *a,
struct cfg80211_bss *b)
{
+ const struct cfg80211_bss_ies *a_ies, *b_ies;
int r;
r = cmp_bss_core(a, b);
if (r)
return r;
+ a_ies = rcu_access_pointer(a->ies);
+ if (!a_ies)
+ return -1;
+ b_ies = rcu_access_pointer(b->ies);
+ if (!b_ies)
+ return 1;
+
return cmp_ies(WLAN_EID_SSID,
- a->information_elements,
- a->len_information_elements,
- b->information_elements,
- b->len_information_elements);
+ a_ies->data, a_ies->len,
+ b_ies->data, b_ies->len);
}
-static int cmp_hidden_bss(struct cfg80211_bss *a,
- struct cfg80211_bss *b)
+static int cmp_hidden_bss(struct cfg80211_bss *a, struct cfg80211_bss *b)
{
+ const struct cfg80211_bss_ies *a_ies, *b_ies;
const u8 *ie1;
const u8 *ie2;
int i;
@@ -415,17 +461,26 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (r)
return r;
- ie1 = cfg80211_find_ie(WLAN_EID_SSID,
- a->information_elements,
- a->len_information_elements);
- ie2 = cfg80211_find_ie(WLAN_EID_SSID,
- b->information_elements,
- b->len_information_elements);
+ a_ies = rcu_access_pointer(a->ies);
+ if (!a_ies)
+ return -1;
+ b_ies = rcu_access_pointer(b->ies);
+ if (!b_ies)
+ return 1;
+
+ ie1 = cfg80211_find_ie(WLAN_EID_SSID, a_ies->data, a_ies->len);
+ ie2 = cfg80211_find_ie(WLAN_EID_SSID, b_ies->data, b_ies->len);
- /* Key comparator must use same algorithm in any rb-tree
+ /*
+ * Key comparator must use same algorithm in any rb-tree
* search function (order is important), otherwise ordering
* of items in the tree is broken and search gives incorrect
- * results. This code uses same order as cmp_ies() does. */
+ * results. This code uses same order as cmp_ies() does.
+ *
+ * Note that due to the differring behaviour with hidden SSIDs
+ * this function only works when "b" is the tree element and
+ * "a" is the key we're looking for.
+ */
/* sort missing IE before (left of) present IE */
if (!ie1)
@@ -441,10 +496,14 @@ static int cmp_hidden_bss(struct cfg80211_bss *a,
if (ie1[1] != ie2[1])
return ie2[1] - ie1[1];
- /* zeroed SSID ie is another indication of a hidden bss */
+ /*
+ * zeroed SSID ie is another indication of a hidden bss;
+ * if it isn't zeroed just return the regular sort value
+ * to find the next candidate
+ */
for (i = 0; i < ie2[1]; i++)
if (ie2[i + 2])
- return -1;
+ return memcmp(ie1 + 2, ie2 + 2, ie1[1]);
return 0;
}
@@ -459,6 +518,9 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct cfg80211_internal_bss *bss, *res = NULL;
unsigned long now = jiffies;
+ trace_cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, capa_mask,
+ capa_val);
+
spin_lock_bh(&dev->bss_lock);
list_for_each_entry(bss, &dev->bss_list, list) {
@@ -480,6 +542,7 @@ struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
spin_unlock_bh(&dev->bss_lock);
if (!res)
return NULL;
+ trace_cfg80211_return_bss(&res->pub);
return &res->pub;
}
EXPORT_SYMBOL(cfg80211_get_bss);
@@ -566,7 +629,7 @@ rb_find_bss(struct cfg80211_registered_device *dev,
static struct cfg80211_internal_bss *
rb_find_hidden_bss(struct cfg80211_registered_device *dev,
- struct cfg80211_internal_bss *res)
+ struct cfg80211_internal_bss *res)
{
struct rb_node *n = dev->bss_tree.rb_node;
struct cfg80211_internal_bss *bss;
@@ -589,127 +652,86 @@ rb_find_hidden_bss(struct cfg80211_registered_device *dev,
static void
copy_hidden_ies(struct cfg80211_internal_bss *res,
- struct cfg80211_internal_bss *hidden)
+ struct cfg80211_internal_bss *hidden)
{
- if (unlikely(res->pub.beacon_ies))
- return;
- if (WARN_ON(!hidden->pub.beacon_ies))
+ const struct cfg80211_bss_ies *ies;
+
+ if (rcu_access_pointer(res->pub.beacon_ies))
return;
- res->pub.beacon_ies = kmalloc(hidden->pub.len_beacon_ies, GFP_ATOMIC);
- if (unlikely(!res->pub.beacon_ies))
+ ies = rcu_access_pointer(hidden->pub.beacon_ies);
+ if (WARN_ON(!ies))
return;
- res->beacon_ies_allocated = true;
- res->pub.len_beacon_ies = hidden->pub.len_beacon_ies;
- memcpy(res->pub.beacon_ies, hidden->pub.beacon_ies,
- res->pub.len_beacon_ies);
+ ies = kmemdup(ies, sizeof(*ies) + ies->len, GFP_ATOMIC);
+ if (unlikely(!ies))
+ return;
+ rcu_assign_pointer(res->pub.beacon_ies, ies);
}
static struct cfg80211_internal_bss *
cfg80211_bss_update(struct cfg80211_registered_device *dev,
- struct cfg80211_internal_bss *res)
+ struct cfg80211_internal_bss *tmp)
{
struct cfg80211_internal_bss *found = NULL;
- /*
- * The reference to "res" is donated to this function.
- */
-
- if (WARN_ON(!res->pub.channel)) {
- kref_put(&res->ref, bss_release);
+ if (WARN_ON(!tmp->pub.channel))
return NULL;
- }
- res->ts = jiffies;
+ tmp->ts = jiffies;
spin_lock_bh(&dev->bss_lock);
- found = rb_find_bss(dev, res);
+ if (WARN_ON(!rcu_access_pointer(tmp->pub.ies))) {
+ spin_unlock_bh(&dev->bss_lock);
+ return NULL;
+ }
+
+ found = rb_find_bss(dev, tmp);
if (found) {
- found->pub.beacon_interval = res->pub.beacon_interval;
- found->pub.tsf = res->pub.tsf;
- found->pub.signal = res->pub.signal;
- found->pub.capability = res->pub.capability;
- found->ts = res->ts;
+ found->pub.beacon_interval = tmp->pub.beacon_interval;
+ found->pub.tsf = tmp->pub.tsf;
+ found->pub.signal = tmp->pub.signal;
+ found->pub.capability = tmp->pub.capability;
+ found->ts = tmp->ts;
/* Update IEs */
- if (res->pub.proberesp_ies) {
- size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
- size_t ielen = res->pub.len_proberesp_ies;
-
- if (found->pub.proberesp_ies &&
- !found->proberesp_ies_allocated &&
- ksize(found) >= used + ielen) {
- memcpy(found->pub.proberesp_ies,
- res->pub.proberesp_ies, ielen);
- found->pub.len_proberesp_ies = ielen;
- } else {
- u8 *ies = found->pub.proberesp_ies;
-
- if (found->proberesp_ies_allocated)
- ies = krealloc(ies, ielen, GFP_ATOMIC);
- else
- ies = kmalloc(ielen, GFP_ATOMIC);
-
- if (ies) {
- memcpy(ies, res->pub.proberesp_ies,
- ielen);
- found->proberesp_ies_allocated = true;
- found->pub.proberesp_ies = ies;
- found->pub.len_proberesp_ies = ielen;
- }
- }
+ if (rcu_access_pointer(tmp->pub.proberesp_ies)) {
+ const struct cfg80211_bss_ies *old;
+
+ old = rcu_access_pointer(found->pub.proberesp_ies);
+ rcu_assign_pointer(found->pub.proberesp_ies,
+ tmp->pub.proberesp_ies);
/* Override possible earlier Beacon frame IEs */
- found->pub.information_elements =
- found->pub.proberesp_ies;
- found->pub.len_information_elements =
- found->pub.len_proberesp_ies;
- }
- if (res->pub.beacon_ies) {
- size_t used = dev->wiphy.bss_priv_size + sizeof(*res);
- size_t ielen = res->pub.len_beacon_ies;
- bool information_elements_is_beacon_ies =
- (found->pub.information_elements ==
- found->pub.beacon_ies);
-
- if (found->pub.beacon_ies &&
- !found->beacon_ies_allocated &&
- ksize(found) >= used + ielen) {
- memcpy(found->pub.beacon_ies,
- res->pub.beacon_ies, ielen);
- found->pub.len_beacon_ies = ielen;
- } else {
- u8 *ies = found->pub.beacon_ies;
-
- if (found->beacon_ies_allocated)
- ies = krealloc(ies, ielen, GFP_ATOMIC);
- else
- ies = kmalloc(ielen, GFP_ATOMIC);
-
- if (ies) {
- memcpy(ies, res->pub.beacon_ies,
- ielen);
- found->beacon_ies_allocated = true;
- found->pub.beacon_ies = ies;
- found->pub.len_beacon_ies = ielen;
- }
- }
+ rcu_assign_pointer(found->pub.ies,
+ tmp->pub.proberesp_ies);
+ if (old)
+ kfree_rcu((struct cfg80211_bss_ies *)old,
+ rcu_head);
+ } else if (rcu_access_pointer(tmp->pub.beacon_ies)) {
+ const struct cfg80211_bss_ies *old, *ies;
+
+ old = rcu_access_pointer(found->pub.beacon_ies);
+ ies = rcu_access_pointer(found->pub.ies);
+
+ rcu_assign_pointer(found->pub.beacon_ies,
+ tmp->pub.beacon_ies);
/* Override IEs if they were from a beacon before */
- if (information_elements_is_beacon_ies) {
- found->pub.information_elements =
- found->pub.beacon_ies;
- found->pub.len_information_elements =
- found->pub.len_beacon_ies;
- }
- }
+ if (old == ies)
+ rcu_assign_pointer(found->pub.ies,
+ tmp->pub.beacon_ies);
- kref_put(&res->ref, bss_release);
+ if (old)
+ kfree_rcu((struct cfg80211_bss_ies *)old,
+ rcu_head);
+ }
} else {
+ struct cfg80211_internal_bss *new;
struct cfg80211_internal_bss *hidden;
+ struct cfg80211_bss_ies *ies;
/* First check if the beacon is a probe response from
* a hidden bss. If so, copy beacon ies (with nullified
@@ -720,14 +742,32 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
/* TODO: The code is not trying to update existing probe
* response bss entries when beacon ies are
* getting changed. */
- hidden = rb_find_hidden_bss(dev, res);
+ hidden = rb_find_hidden_bss(dev, tmp);
if (hidden)
- copy_hidden_ies(res, hidden);
-
- /* this "consumes" the reference */
- list_add_tail(&res->list, &dev->bss_list);
- rb_insert_bss(dev, res);
- found = res;
+ copy_hidden_ies(tmp, hidden);
+
+ /*
+ * create a copy -- the "res" variable that is passed in
+ * is allocated on the stack since it's not needed in the
+ * more common case of an update
+ */
+ new = kzalloc(sizeof(*new) + dev->wiphy.bss_priv_size,
+ GFP_ATOMIC);
+ if (!new) {
+ ies = (void *)rcu_dereference(tmp->pub.beacon_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+ ies = (void *)rcu_dereference(tmp->pub.proberesp_ies);
+ if (ies)
+ kfree_rcu(ies, rcu_head);
+ spin_unlock_bh(&dev->bss_lock);
+ return NULL;
+ }
+ memcpy(new, tmp, sizeof(*new));
+ kref_init(&new->ref);
+ list_add_tail(&new->list, &dev->bss_list);
+ rb_insert_bss(dev, new);
+ found = new;
}
dev->bss_generation++;
@@ -737,6 +777,38 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
return found;
}
+static struct ieee80211_channel *
+cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen,
+ struct ieee80211_channel *channel)
+{
+ const u8 *tmp;
+ u32 freq;
+ int channel_number = -1;
+
+ tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen);
+ if (tmp && tmp[1] == 1) {
+ channel_number = tmp[2];
+ } else {
+ tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen);
+ if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) {
+ struct ieee80211_ht_operation *htop = (void *)(tmp + 2);
+
+ channel_number = htop->primary_chan;
+ }
+ }
+
+ if (channel_number < 0)
+ return channel;
+
+ freq = ieee80211_channel_to_frequency(channel_number, channel->band);
+ channel = ieee80211_get_channel(wiphy, freq);
+ if (!channel)
+ return NULL;
+ if (channel->flags & IEEE80211_CHAN_DISABLED)
+ return NULL;
+ return channel;
+}
+
struct cfg80211_bss*
cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
@@ -744,54 +816,54 @@ cfg80211_inform_bss(struct wiphy *wiphy,
u16 beacon_interval, const u8 *ie, size_t ielen,
s32 signal, gfp_t gfp)
{
- struct cfg80211_internal_bss *res;
- size_t privsz;
+ struct cfg80211_bss_ies *ies;
+ struct cfg80211_internal_bss tmp = {}, *res;
if (WARN_ON(!wiphy))
return NULL;
- privsz = wiphy->bss_priv_size;
-
if (WARN_ON(wiphy->signal_type == CFG80211_SIGNAL_TYPE_UNSPEC &&
(signal < 0 || signal > 100)))
return NULL;
- res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
- if (!res)
+ channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel);
+ if (!channel)
return NULL;
- memcpy(res->pub.bssid, bssid, ETH_ALEN);
- res->pub.channel = channel;
- res->pub.signal = signal;
- res->pub.tsf = tsf;
- res->pub.beacon_interval = beacon_interval;
- res->pub.capability = capability;
+ memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
+ tmp.pub.channel = channel;
+ tmp.pub.signal = signal;
+ tmp.pub.tsf = tsf;
+ tmp.pub.beacon_interval = beacon_interval;
+ tmp.pub.capability = capability;
/*
* Since we do not know here whether the IEs are from a Beacon or Probe
* Response frame, we need to pick one of the options and only use it
* with the driver that does not provide the full Beacon/Probe Response
* frame. Use Beacon frame pointer to avoid indicating that this should
- * override the information_elements pointer should we have received an
- * earlier indication of Probe Response data.
+ * override the iies pointer should we have received an earlier
+ * indication of Probe Response data.
*
* The initial buffer for the IEs is allocated with the BSS entry and
* is located after the private area.
*/
- res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz;
- memcpy(res->pub.beacon_ies, ie, ielen);
- res->pub.len_beacon_ies = ielen;
- res->pub.information_elements = res->pub.beacon_ies;
- res->pub.len_information_elements = res->pub.len_beacon_ies;
+ ies = kmalloc(sizeof(*ies) + ielen, gfp);
+ if (!ies)
+ return NULL;
+ ies->len = ielen;
+ memcpy(ies->data, ie, ielen);
- kref_init(&res->ref);
+ rcu_assign_pointer(tmp.pub.beacon_ies, ies);
+ rcu_assign_pointer(tmp.pub.ies, ies);
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
if (!res)
return NULL;
if (res->pub.capability & WLAN_CAPABILITY_ESS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
+ trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
@@ -803,10 +875,15 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal, gfp_t gfp)
{
- struct cfg80211_internal_bss *res;
+ struct cfg80211_internal_bss tmp = {}, *res;
+ struct cfg80211_bss_ies *ies;
size_t ielen = len - offsetof(struct ieee80211_mgmt,
u.probe_resp.variable);
- size_t privsz;
+
+ BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
+ offsetof(struct ieee80211_mgmt, u.beacon.variable));
+
+ trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
if (WARN_ON(!mgmt))
return NULL;
@@ -821,46 +898,38 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy,
if (WARN_ON(len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
return NULL;
- privsz = wiphy->bss_priv_size;
-
- res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
- if (!res)
+ channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable,
+ ielen, channel);
+ if (!channel)
return NULL;
- memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
- res->pub.channel = channel;
- res->pub.signal = signal;
- res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
- res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
- res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
- /*
- * The initial buffer for the IEs is allocated with the BSS entry and
- * is located after the private area.
- */
- if (ieee80211_is_probe_resp(mgmt->frame_control)) {
- res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz;
- memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable,
- ielen);
- res->pub.len_proberesp_ies = ielen;
- res->pub.information_elements = res->pub.proberesp_ies;
- res->pub.len_information_elements = res->pub.len_proberesp_ies;
- } else {
- res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz;
- memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen);
- res->pub.len_beacon_ies = ielen;
- res->pub.information_elements = res->pub.beacon_ies;
- res->pub.len_information_elements = res->pub.len_beacon_ies;
- }
-
- kref_init(&res->ref);
+ ies = kmalloc(sizeof(*ies) + ielen, gfp);
+ if (!ies)
+ return NULL;
+ ies->len = ielen;
+ memcpy(ies->data, mgmt->u.probe_resp.variable, ielen);
- res = cfg80211_bss_update(wiphy_to_dev(wiphy), res);
+ if (ieee80211_is_probe_resp(mgmt->frame_control))
+ rcu_assign_pointer(tmp.pub.proberesp_ies, ies);
+ else
+ rcu_assign_pointer(tmp.pub.beacon_ies, ies);
+ rcu_assign_pointer(tmp.pub.ies, ies);
+
+ memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
+ tmp.pub.channel = channel;
+ tmp.pub.signal = signal;
+ tmp.pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
+ tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
+ tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
+
+ res = cfg80211_bss_update(wiphy_to_dev(wiphy), &tmp);
if (!res)
return NULL;
if (res->pub.capability & WLAN_CAPABILITY_ESS)
regulatory_hint_found_beacon(wiphy, channel, gfp);
+ trace_cfg80211_return_bss(&res->pub);
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
@@ -962,6 +1031,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
creq->ssids = (void *)&creq->channels[n_channels];
creq->n_channels = n_channels;
creq->n_ssids = 1;
+ creq->scan_start = jiffies;
/* translate "Scan on frequencies" request */
i = 0;
@@ -1026,7 +1096,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
creq->rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1;
rdev->scan_req = creq;
- err = rdev->ops->scan(wiphy, creq);
+ err = rdev_scan(rdev, creq);
if (err) {
rdev->scan_req = NULL;
/* creq will be freed below */
@@ -1044,22 +1114,21 @@ int cfg80211_wext_siwscan(struct net_device *dev,
EXPORT_SYMBOL_GPL(cfg80211_wext_siwscan);
static void ieee80211_scan_add_ies(struct iw_request_info *info,
- struct cfg80211_bss *bss,
+ const struct cfg80211_bss_ies *ies,
char **current_ev, char *end_buf)
{
- u8 *pos, *end, *next;
+ const u8 *pos, *end, *next;
struct iw_event iwe;
- if (!bss->information_elements ||
- !bss->len_information_elements)
+ if (!ies)
return;
/*
* If needed, fragment the IEs buffer (at IE boundaries) into short
* enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
*/
- pos = bss->information_elements;
- end = pos + bss->len_information_elements;
+ pos = ies->data;
+ end = pos + ies->len;
while (end - pos > IW_GENERIC_IE_MAX) {
next = pos + 2 + pos[1];
@@ -1070,7 +1139,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE;
iwe.u.data.length = next - pos;
*current_ev = iwe_stream_add_point(info, *current_ev,
- end_buf, &iwe, pos);
+ end_buf, &iwe,
+ (void *)pos);
pos = next;
}
@@ -1080,7 +1150,8 @@ static void ieee80211_scan_add_ies(struct iw_request_info *info,
iwe.cmd = IWEVGENIE;
iwe.u.data.length = end - pos;
*current_ev = iwe_stream_add_point(info, *current_ev,
- end_buf, &iwe, pos);
+ end_buf, &iwe,
+ (void *)pos);
}
}
@@ -1099,10 +1170,11 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
struct cfg80211_internal_bss *bss, char *current_ev,
char *end_buf)
{
+ const struct cfg80211_bss_ies *ies;
struct iw_event iwe;
+ const u8 *ie;
u8 *buf, *cfg, *p;
- u8 *ie = bss->pub.information_elements;
- int rem = bss->pub.len_information_elements, i, sig;
+ int rem, i, sig;
bool ismesh = false;
memset(&iwe, 0, sizeof(iwe));
@@ -1167,7 +1239,17 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
&iwe, "");
- while (rem >= 2) {
+ rcu_read_lock();
+ ies = rcu_dereference(bss->pub.ies);
+ if (ies) {
+ rem = ies->len;
+ ie = ies->data;
+ } else {
+ rem = 0;
+ ie = NULL;
+ }
+
+ while (ies && rem >= 2) {
/* invalid data */
if (ie[1] > rem - 2)
break;
@@ -1179,7 +1261,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.data.length = ie[1];
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, ie + 2);
+ &iwe, (u8 *)ie + 2);
break;
case WLAN_EID_MESH_ID:
memset(&iwe, 0, sizeof(iwe));
@@ -1187,7 +1269,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
iwe.u.data.length = ie[1];
iwe.u.data.flags = 1;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, ie + 2);
+ &iwe, (u8 *)ie + 2);
break;
case WLAN_EID_MESH_CONFIG:
ismesh = true;
@@ -1196,7 +1278,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
buf = kmalloc(50, GFP_ATOMIC);
if (!buf)
break;
- cfg = ie + 2;
+ cfg = (u8 *)ie + 2;
memset(&iwe, 0, sizeof(iwe));
iwe.cmd = IWEVCUSTOM;
sprintf(buf, "Mesh Network Path Selection Protocol ID: "
@@ -1294,7 +1376,8 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
kfree(buf);
}
- ieee80211_scan_add_ies(info, &bss->pub, &current_ev, end_buf);
+ ieee80211_scan_add_ies(info, ies, &current_ev, end_buf);
+ rcu_read_unlock();
return current_ev;
}
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 6f39cb80830..f2431e41a37 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -16,6 +16,7 @@
#include <net/rtnetlink.h>
#include "nl80211.h"
#include "reg.h"
+#include "rdev-ops.h"
struct cfg80211_conn {
struct cfg80211_connect_params params;
@@ -138,10 +139,11 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
request->wdev = wdev;
request->wiphy = &rdev->wiphy;
+ request->scan_start = jiffies;
rdev->scan_req = request;
- err = rdev->ops->scan(wdev->wiphy, request);
+ err = rdev_scan(rdev, request);
if (!err) {
wdev->conn->state = CFG80211_CONN_SCANNING;
nl80211_send_scan_start(rdev, wdev);
@@ -179,7 +181,7 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
params->ssid, params->ssid_len,
NULL, 0,
params->key, params->key_len,
- params->key_idx);
+ params->key_idx, NULL, 0);
case CFG80211_CONN_ASSOCIATE_NEXT:
BUG_ON(!rdev->ops->assoc);
wdev->conn->state = CFG80211_CONN_ASSOCIATING;
@@ -415,7 +417,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
struct cfg80211_bss *bss)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- u8 *country_ie;
+ const u8 *country_ie;
#ifdef CONFIG_CFG80211_WEXT
union iwreq_data wrqu;
#endif
@@ -499,7 +501,15 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
wdev->sme_state = CFG80211_SME_CONNECTED;
cfg80211_upload_connect_keys(wdev);
- country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ rcu_read_lock();
+ country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY);
+ if (!country_ie) {
+ rcu_read_unlock();
+ return;
+ }
+
+ country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC);
+ rcu_read_unlock();
if (!country_ie)
return;
@@ -513,6 +523,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
bss->channel->band,
country_ie + 2,
country_ie[1]);
+ kfree(country_ie);
}
void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
@@ -716,7 +727,7 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
*/
if (rdev->ops->del_key)
for (i = 0; i < 6; i++)
- rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL);
+ rdev_del_key(rdev, dev, i, false, NULL);
#ifdef CONFIG_CFG80211_WEXT
memset(&wrqu, 0, sizeof(wrqu));
@@ -892,7 +903,7 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
} else {
wdev->sme_state = CFG80211_SME_CONNECTING;
wdev->connect_keys = connkeys;
- err = rdev->ops->connect(&rdev->wiphy, dev, connect);
+ err = rdev_connect(rdev, dev, connect);
if (err) {
wdev->connect_keys = NULL;
wdev->sme_state = CFG80211_SME_IDLE;
@@ -964,7 +975,7 @@ int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
if (err)
return err;
} else {
- err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
+ err = rdev_disconnect(rdev, dev, reason);
if (err)
return err;
}
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c
index ff574597a85..1f6f01e2dc4 100644
--- a/net/wireless/sysfs.c
+++ b/net/wireless/sysfs.c
@@ -16,6 +16,7 @@
#include <net/cfg80211.h>
#include "sysfs.h"
#include "core.h"
+#include "rdev-ops.h"
static inline struct cfg80211_registered_device *dev_to_rdev(
struct device *dev)
@@ -76,13 +77,11 @@ static void wiphy_dev_release(struct device *dev)
cfg80211_dev_free(rdev);
}
-#ifdef CONFIG_HOTPLUG
static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env)
{
/* TODO, we probably need stuff here */
return 0;
}
-#endif
static int wiphy_suspend(struct device *dev, pm_message_t state)
{
@@ -94,7 +93,7 @@ static int wiphy_suspend(struct device *dev, pm_message_t state)
if (rdev->ops->suspend) {
rtnl_lock();
if (rdev->wiphy.registered)
- ret = rdev->ops->suspend(&rdev->wiphy, rdev->wowlan);
+ ret = rdev_suspend(rdev);
rtnl_unlock();
}
@@ -114,7 +113,7 @@ static int wiphy_resume(struct device *dev)
if (rdev->ops->resume) {
rtnl_lock();
if (rdev->wiphy.registered)
- ret = rdev->ops->resume(&rdev->wiphy);
+ ret = rdev_resume(rdev);
rtnl_unlock();
}
@@ -133,9 +132,7 @@ struct class ieee80211_class = {
.owner = THIS_MODULE,
.dev_release = wiphy_dev_release,
.dev_attrs = ieee80211_dev_attrs,
-#ifdef CONFIG_HOTPLUG
.dev_uevent = wiphy_uevent,
-#endif
.suspend = wiphy_suspend,
.resume = wiphy_resume,
.ns_type = &net_ns_type_operations,
diff --git a/net/wireless/trace.c b/net/wireless/trace.c
new file mode 100644
index 00000000000..95f997fad75
--- /dev/null
+++ b/net/wireless/trace.c
@@ -0,0 +1,7 @@
+#include <linux/module.h>
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+#endif
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
new file mode 100644
index 00000000000..2134576f426
--- /dev/null
+++ b/net/wireless/trace.h
@@ -0,0 +1,2324 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cfg80211
+
+#if !defined(__RDEV_OPS_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __RDEV_OPS_TRACE
+
+#include <linux/tracepoint.h>
+
+#include <linux/rtnetlink.h>
+#include <net/cfg80211.h>
+#include "core.h"
+
+#define MAC_ENTRY(entry_mac) __array(u8, entry_mac, ETH_ALEN)
+#define MAC_ASSIGN(entry_mac, given_mac) do { \
+ if (given_mac) \
+ memcpy(__entry->entry_mac, given_mac, ETH_ALEN); \
+ else \
+ memset(__entry->entry_mac, 0, ETH_ALEN); \
+ } while (0)
+#define MAC_PR_FMT "%pM"
+#define MAC_PR_ARG(entry_mac) (__entry->entry_mac)
+
+#define MAXNAME 32
+#define WIPHY_ENTRY __array(char, wiphy_name, 32)
+#define WIPHY_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(wiphy), MAXNAME)
+#define WIPHY_PR_FMT "%s"
+#define WIPHY_PR_ARG __entry->wiphy_name
+
+#define WDEV_ENTRY __field(u32, id)
+#define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0)
+#define WDEV_PR_FMT "wdev(%u)"
+#define WDEV_PR_ARG (__entry->id)
+
+#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \
+ __field(int, ifindex)
+#define NETDEV_ASSIGN \
+ do { \
+ memcpy(__entry->name, netdev->name, IFNAMSIZ); \
+ (__entry->ifindex) = (netdev->ifindex); \
+ } while (0)
+#define NETDEV_PR_FMT "netdev:%s(%d)"
+#define NETDEV_PR_ARG __entry->name, __entry->ifindex
+
+#define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout) \
+ __field(u16, dot11MeshConfirmTimeout) \
+ __field(u16, dot11MeshHoldingTimeout) \
+ __field(u16, dot11MeshMaxPeerLinks) \
+ __field(u8, dot11MeshMaxRetries) \
+ __field(u8, dot11MeshTTL) \
+ __field(u8, element_ttl) \
+ __field(bool, auto_open_plinks) \
+ __field(u32, dot11MeshNbrOffsetMaxNeighbor) \
+ __field(u8, dot11MeshHWMPmaxPREQretries) \
+ __field(u32, path_refresh_time) \
+ __field(u32, dot11MeshHWMPactivePathTimeout) \
+ __field(u16, min_discovery_timeout) \
+ __field(u16, dot11MeshHWMPpreqMinInterval) \
+ __field(u16, dot11MeshHWMPperrMinInterval) \
+ __field(u16, dot11MeshHWMPnetDiameterTraversalTime) \
+ __field(u8, dot11MeshHWMPRootMode) \
+ __field(u16, dot11MeshHWMPRannInterval) \
+ __field(bool, dot11MeshGateAnnouncementProtocol) \
+ __field(bool, dot11MeshForwarding) \
+ __field(s32, rssi_threshold) \
+ __field(u16, ht_opmode) \
+ __field(u32, dot11MeshHWMPactivePathToRootTimeout) \
+ __field(u16, dot11MeshHWMProotInterval) \
+ __field(u16, dot11MeshHWMPconfirmationInterval)
+#define MESH_CFG_ASSIGN \
+ do { \
+ __entry->dot11MeshRetryTimeout = conf->dot11MeshRetryTimeout; \
+ __entry->dot11MeshConfirmTimeout = \
+ conf->dot11MeshConfirmTimeout; \
+ __entry->dot11MeshHoldingTimeout = \
+ conf->dot11MeshHoldingTimeout; \
+ __entry->dot11MeshMaxPeerLinks = conf->dot11MeshMaxPeerLinks; \
+ __entry->dot11MeshMaxRetries = conf->dot11MeshMaxRetries; \
+ __entry->dot11MeshTTL = conf->dot11MeshTTL; \
+ __entry->element_ttl = conf->element_ttl; \
+ __entry->auto_open_plinks = conf->auto_open_plinks; \
+ __entry->dot11MeshNbrOffsetMaxNeighbor = \
+ conf->dot11MeshNbrOffsetMaxNeighbor; \
+ __entry->dot11MeshHWMPmaxPREQretries = \
+ conf->dot11MeshHWMPmaxPREQretries; \
+ __entry->path_refresh_time = conf->path_refresh_time; \
+ __entry->dot11MeshHWMPactivePathTimeout = \
+ conf->dot11MeshHWMPactivePathTimeout; \
+ __entry->min_discovery_timeout = conf->min_discovery_timeout; \
+ __entry->dot11MeshHWMPpreqMinInterval = \
+ conf->dot11MeshHWMPpreqMinInterval; \
+ __entry->dot11MeshHWMPperrMinInterval = \
+ conf->dot11MeshHWMPperrMinInterval; \
+ __entry->dot11MeshHWMPnetDiameterTraversalTime = \
+ conf->dot11MeshHWMPnetDiameterTraversalTime; \
+ __entry->dot11MeshHWMPRootMode = conf->dot11MeshHWMPRootMode; \
+ __entry->dot11MeshHWMPRannInterval = \
+ conf->dot11MeshHWMPRannInterval; \
+ __entry->dot11MeshGateAnnouncementProtocol = \
+ conf->dot11MeshGateAnnouncementProtocol; \
+ __entry->dot11MeshForwarding = conf->dot11MeshForwarding; \
+ __entry->rssi_threshold = conf->rssi_threshold; \
+ __entry->ht_opmode = conf->ht_opmode; \
+ __entry->dot11MeshHWMPactivePathToRootTimeout = \
+ conf->dot11MeshHWMPactivePathToRootTimeout; \
+ __entry->dot11MeshHWMProotInterval = \
+ conf->dot11MeshHWMProotInterval; \
+ __entry->dot11MeshHWMPconfirmationInterval = \
+ conf->dot11MeshHWMPconfirmationInterval; \
+ } while (0)
+
+#define CHAN_ENTRY __field(enum ieee80211_band, band) \
+ __field(u16, center_freq)
+#define CHAN_ASSIGN(chan) \
+ do { \
+ if (chan) { \
+ __entry->band = chan->band; \
+ __entry->center_freq = chan->center_freq; \
+ } else { \
+ __entry->band = 0; \
+ __entry->center_freq = 0; \
+ } \
+ } while (0)
+#define CHAN_PR_FMT "band: %d, freq: %u"
+#define CHAN_PR_ARG __entry->band, __entry->center_freq
+
+#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \
+ __field(u32, control_freq) \
+ __field(u32, width) \
+ __field(u32, center_freq1) \
+ __field(u32, center_freq2)
+#define CHAN_DEF_ASSIGN(chandef) \
+ do { \
+ if ((chandef) && (chandef)->chan) { \
+ __entry->band = (chandef)->chan->band; \
+ __entry->control_freq = \
+ (chandef)->chan->center_freq; \
+ __entry->width = (chandef)->width; \
+ __entry->center_freq1 = (chandef)->center_freq1;\
+ __entry->center_freq2 = (chandef)->center_freq2;\
+ } else { \
+ __entry->band = 0; \
+ __entry->control_freq = 0; \
+ __entry->width = 0; \
+ __entry->center_freq1 = 0; \
+ __entry->center_freq2 = 0; \
+ } \
+ } while (0)
+#define CHAN_DEF_PR_FMT \
+ "band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u"
+#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \
+ __entry->width, __entry->center_freq1, \
+ __entry->center_freq2
+
+#define SINFO_ENTRY __field(int, generation) \
+ __field(u32, connected_time) \
+ __field(u32, inactive_time) \
+ __field(u32, rx_bytes) \
+ __field(u32, tx_bytes) \
+ __field(u32, rx_packets) \
+ __field(u32, tx_packets) \
+ __field(u32, tx_retries) \
+ __field(u32, tx_failed) \
+ __field(u32, rx_dropped_misc) \
+ __field(u32, beacon_loss_count) \
+ __field(u16, llid) \
+ __field(u16, plid) \
+ __field(u8, plink_state)
+#define SINFO_ASSIGN \
+ do { \
+ __entry->generation = sinfo->generation; \
+ __entry->connected_time = sinfo->connected_time; \
+ __entry->inactive_time = sinfo->inactive_time; \
+ __entry->rx_bytes = sinfo->rx_bytes; \
+ __entry->tx_bytes = sinfo->tx_bytes; \
+ __entry->rx_packets = sinfo->rx_packets; \
+ __entry->tx_packets = sinfo->tx_packets; \
+ __entry->tx_retries = sinfo->tx_retries; \
+ __entry->tx_failed = sinfo->tx_failed; \
+ __entry->rx_dropped_misc = sinfo->rx_dropped_misc; \
+ __entry->beacon_loss_count = sinfo->beacon_loss_count; \
+ __entry->llid = sinfo->llid; \
+ __entry->plid = sinfo->plid; \
+ __entry->plink_state = sinfo->plink_state; \
+ } while (0)
+
+#define BOOL_TO_STR(bo) (bo) ? "true" : "false"
+
+/*************************************************************
+ * rdev->ops traces *
+ *************************************************************/
+
+TRACE_EVENT(rdev_suspend,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_wowlan *wow),
+ TP_ARGS(wiphy, wow),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(bool, any)
+ __field(bool, disconnect)
+ __field(bool, magic_pkt)
+ __field(bool, gtk_rekey_failure)
+ __field(bool, eap_identity_req)
+ __field(bool, four_way_handshake)
+ __field(bool, rfkill_release)
+ __field(bool, valid_wow)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ if (wow) {
+ __entry->any = wow->any;
+ __entry->disconnect = wow->disconnect;
+ __entry->magic_pkt = wow->magic_pkt;
+ __entry->gtk_rekey_failure = wow->gtk_rekey_failure;
+ __entry->eap_identity_req = wow->eap_identity_req;
+ __entry->four_way_handshake = wow->four_way_handshake;
+ __entry->rfkill_release = wow->rfkill_release;
+ __entry->valid_wow = true;
+ } else {
+ __entry->valid_wow = false;
+ }
+ ),
+ TP_printk(WIPHY_PR_FMT ", wow%s - any: %d, disconnect: %d, "
+ "magic pkt: %d, gtk rekey failure: %d, eap identify req: %d, "
+ "four way handshake: %d, rfkill release: %d.",
+ WIPHY_PR_ARG, __entry->valid_wow ? "" : "(Not configured!)",
+ __entry->any, __entry->disconnect, __entry->magic_pkt,
+ __entry->gtk_rekey_failure, __entry->eap_identity_req,
+ __entry->four_way_handshake, __entry->rfkill_release)
+);
+
+TRACE_EVENT(rdev_return_int,
+ TP_PROTO(struct wiphy *wiphy, int ret),
+ TP_ARGS(wiphy, ret),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d", WIPHY_PR_ARG, __entry->ret)
+);
+
+TRACE_EVENT(rdev_scan,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_scan_request *request),
+ TP_ARGS(wiphy, request),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(wiphy_only_evt,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_resume,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_return_void,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_get_ringparam,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DECLARE_EVENT_CLASS(wiphy_enabled_evt,
+ TP_PROTO(struct wiphy *wiphy, bool enabled),
+ TP_ARGS(wiphy, enabled),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(bool, enabled)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->enabled = enabled;
+ ),
+ TP_printk(WIPHY_PR_FMT ", %senabled ",
+ WIPHY_PR_ARG, __entry->enabled ? "" : "not ")
+);
+
+DEFINE_EVENT(wiphy_enabled_evt, rdev_set_wakeup,
+ TP_PROTO(struct wiphy *wiphy, bool enabled),
+ TP_ARGS(wiphy, enabled)
+);
+
+TRACE_EVENT(rdev_add_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, char *name, enum nl80211_iftype type),
+ TP_ARGS(wiphy, name, type),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __string(vir_intf_name, name ? name : "<noname>")
+ __field(enum nl80211_iftype, type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __assign_str(vir_intf_name, name ? name : "<noname>");
+ __entry->type = type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", virtual intf name: %s, type: %d",
+ WIPHY_PR_ARG, __get_str(vir_intf_name), __entry->type)
+);
+
+DECLARE_EVENT_CLASS(wiphy_wdev_evt,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_del_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_change_virtual_intf,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ enum nl80211_iftype type),
+ TP_ARGS(wiphy, netdev, type),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(enum nl80211_iftype, type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->type = type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", type: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type)
+);
+
+DECLARE_EVENT_CLASS(key_handle,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(mac_addr)
+ __field(u8, key_index)
+ __field(bool, pairwise)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac_addr, mac_addr);
+ __entry->key_index = key_index;
+ __entry->pairwise = pairwise;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
+ BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr))
+);
+
+DEFINE_EVENT(key_handle, rdev_add_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+DEFINE_EVENT(key_handle, rdev_get_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+DEFINE_EVENT(key_handle, rdev_del_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool pairwise, const u8 *mac_addr),
+ TP_ARGS(wiphy, netdev, key_index, pairwise, mac_addr)
+);
+
+TRACE_EVENT(rdev_set_default_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index,
+ bool unicast, bool multicast),
+ TP_ARGS(wiphy, netdev, key_index, unicast, multicast),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u8, key_index)
+ __field(bool, unicast)
+ __field(bool, multicast)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->key_index = key_index;
+ __entry->unicast = unicast;
+ __entry->multicast = multicast;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index,
+ BOOL_TO_STR(__entry->unicast),
+ BOOL_TO_STR(__entry->multicast))
+);
+
+TRACE_EVENT(rdev_set_default_mgmt_key,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index),
+ TP_ARGS(wiphy, netdev, key_index),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u8, key_index)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->key_index = key_index;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index)
+);
+
+TRACE_EVENT(rdev_start_ap,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ap_settings *settings),
+ TP_ARGS(wiphy, netdev, settings),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(int, beacon_interval)
+ __field(int, dtim_period)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ __field(enum nl80211_hidden_ssid, hidden_ssid)
+ __field(u32, wpa_ver)
+ __field(bool, privacy)
+ __field(enum nl80211_auth_type, auth_type)
+ __field(int, inactivity_timeout)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(&settings->chandef);
+ __entry->beacon_interval = settings->beacon_interval;
+ __entry->dtim_period = settings->dtim_period;
+ __entry->hidden_ssid = settings->hidden_ssid;
+ __entry->wpa_ver = settings->crypto.wpa_versions;
+ __entry->privacy = settings->privacy;
+ __entry->auth_type = settings->auth_type;
+ __entry->inactivity_timeout = settings->inactivity_timeout;
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, settings->ssid, settings->ssid_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", AP settings - ssid: %s, "
+ CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, "
+ "hidden ssid: %d, wpa versions: %u, privacy: %s, "
+ "auth type: %d, inactivity timeout: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG,
+ __entry->beacon_interval, __entry->dtim_period,
+ __entry->hidden_ssid, __entry->wpa_ver,
+ BOOL_TO_STR(__entry->privacy), __entry->auth_type,
+ __entry->inactivity_timeout)
+);
+
+TRACE_EVENT(rdev_change_beacon,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_beacon_data *info),
+ TP_ARGS(wiphy, netdev, info),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __dynamic_array(u8, head, info ? info->head_len : 0)
+ __dynamic_array(u8, tail, info ? info->tail_len : 0)
+ __dynamic_array(u8, beacon_ies, info ? info->beacon_ies_len : 0)
+ __dynamic_array(u8, proberesp_ies,
+ info ? info->proberesp_ies_len : 0)
+ __dynamic_array(u8, assocresp_ies,
+ info ? info->assocresp_ies_len : 0)
+ __dynamic_array(u8, probe_resp, info ? info->probe_resp_len : 0)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (info) {
+ if (info->head)
+ memcpy(__get_dynamic_array(head), info->head,
+ info->head_len);
+ if (info->tail)
+ memcpy(__get_dynamic_array(tail), info->tail,
+ info->tail_len);
+ if (info->beacon_ies)
+ memcpy(__get_dynamic_array(beacon_ies),
+ info->beacon_ies, info->beacon_ies_len);
+ if (info->proberesp_ies)
+ memcpy(__get_dynamic_array(proberesp_ies),
+ info->proberesp_ies,
+ info->proberesp_ies_len);
+ if (info->assocresp_ies)
+ memcpy(__get_dynamic_array(assocresp_ies),
+ info->assocresp_ies,
+ info->assocresp_ies_len);
+ if (info->probe_resp)
+ memcpy(__get_dynamic_array(probe_resp),
+ info->probe_resp, info->probe_resp_len);
+ }
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(wiphy_netdev_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_get_et_stats,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_sched_scan_stop,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_set_rekey_data,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_get_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_mesh,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
+DECLARE_EVENT_CLASS(station_add_change,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(u32, sta_flags_mask)
+ __field(u32, sta_flags_set)
+ __field(u32, sta_modify_mask)
+ __field(int, listen_interval)
+ __field(u16, aid)
+ __field(u8, plink_action)
+ __field(u8, plink_state)
+ __field(u8, uapsd_queues)
+ __array(u8, ht_capa, (int)sizeof(struct ieee80211_ht_cap))
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ __entry->sta_flags_mask = params->sta_flags_mask;
+ __entry->sta_flags_set = params->sta_flags_set;
+ __entry->sta_modify_mask = params->sta_modify_mask;
+ __entry->listen_interval = params->listen_interval;
+ __entry->aid = params->aid;
+ __entry->plink_action = params->plink_action;
+ __entry->plink_state = params->plink_state;
+ __entry->uapsd_queues = params->uapsd_queues;
+ memset(__entry->ht_capa, 0, sizeof(struct ieee80211_ht_cap));
+ if (params->ht_capa)
+ memcpy(__entry->ht_capa, params->ht_capa,
+ sizeof(struct ieee80211_ht_cap));
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", station flags mask: %u, station flags set: %u, "
+ "station modify mask: %u, listen interval: %d, aid: %u, "
+ "plink action: %u, plink state: %u, uapsd queues: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->sta_flags_mask, __entry->sta_flags_set,
+ __entry->sta_modify_mask, __entry->listen_interval,
+ __entry->aid, __entry->plink_action, __entry->plink_state,
+ __entry->uapsd_queues)
+);
+
+DEFINE_EVENT(station_add_change, rdev_add_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params)
+);
+
+DEFINE_EVENT(station_add_change, rdev_change_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *mac,
+ struct station_parameters *params),
+ TP_ARGS(wiphy, netdev, mac, params)
+);
+
+DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_set_wds_peer,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
+ TP_ARGS(wiphy, netdev, mac)
+);
+
+TRACE_EVENT(rdev_dump_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *mac),
+ TP_ARGS(wiphy, netdev, idx, mac),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, mac);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->idx)
+);
+
+TRACE_EVENT(rdev_return_int_station_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct station_info *sinfo),
+ TP_ARGS(wiphy, ret, sinfo),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ SINFO_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ SINFO_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d" ,
+ WIPHY_PR_ARG, __entry->ret)
+);
+
+DECLARE_EVENT_CLASS(mpath_evt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(next_hop)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(next_hop, next_hop);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst),
+ MAC_PR_ARG(next_hop))
+);
+
+DEFINE_EVENT(mpath_evt, rdev_add_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+DEFINE_EVENT(mpath_evt, rdev_change_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+DEFINE_EVENT(mpath_evt, rdev_get_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 *dst,
+ u8 *next_hop),
+ TP_ARGS(wiphy, netdev, dst, next_hop)
+);
+
+TRACE_EVENT(rdev_dump_mpath,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *dst, u8 *next_hop),
+ TP_ARGS(wiphy, netdev, idx, dst, next_hop),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(next_hop)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(next_hop, next_hop);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
+ MAC_PR_FMT ", next hop: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
+ MAC_PR_ARG(next_hop))
+);
+
+TRACE_EVENT(rdev_return_int_mpath_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
+ TP_ARGS(wiphy, ret, pinfo),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(int, generation)
+ __field(u32, filled)
+ __field(u32, frame_qlen)
+ __field(u32, sn)
+ __field(u32, metric)
+ __field(u32, exptime)
+ __field(u32, discovery_timeout)
+ __field(u8, discovery_retries)
+ __field(u8, flags)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->generation = pinfo->generation;
+ __entry->filled = pinfo->filled;
+ __entry->frame_qlen = pinfo->frame_qlen;
+ __entry->sn = pinfo->sn;
+ __entry->metric = pinfo->metric;
+ __entry->exptime = pinfo->exptime;
+ __entry->discovery_timeout = pinfo->discovery_timeout;
+ __entry->discovery_retries = pinfo->discovery_retries;
+ __entry->flags = pinfo->flags;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d. mpath info - generation: %d, "
+ "filled: %u, frame qlen: %u, sn: %u, metric: %u, exptime: %u,"
+ " discovery timeout: %u, discovery retries: %u, flags: %u",
+ WIPHY_PR_ARG, __entry->ret, __entry->generation,
+ __entry->filled, __entry->frame_qlen, __entry->sn,
+ __entry->metric, __entry->exptime, __entry->discovery_timeout,
+ __entry->discovery_retries, __entry->flags)
+);
+
+TRACE_EVENT(rdev_return_int_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct mesh_config *conf),
+ TP_ARGS(wiphy, ret, conf),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ MESH_CFG_ENTRY
+ __field(int, ret)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ MESH_CFG_ASSIGN;
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d",
+ WIPHY_PR_ARG, __entry->ret)
+);
+
+TRACE_EVENT(rdev_update_mesh_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 mask,
+ const struct mesh_config *conf),
+ TP_ARGS(wiphy, netdev, mask, conf),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MESH_CFG_ENTRY
+ __field(u32, mask)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MESH_CFG_ASSIGN;
+ __entry->mask = mask;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mask: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask)
+);
+
+TRACE_EVENT(rdev_join_mesh,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct mesh_config *conf,
+ const struct mesh_setup *setup),
+ TP_ARGS(wiphy, netdev, conf, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MESH_CFG_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MESH_CFG_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_change_bss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct bss_parameters *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(int, use_cts_prot)
+ __field(int, use_short_preamble)
+ __field(int, use_short_slot_time)
+ __field(int, ap_isolate)
+ __field(int, ht_opmode)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->use_cts_prot = params->use_cts_prot;
+ __entry->use_short_preamble = params->use_short_preamble;
+ __entry->use_short_slot_time = params->use_short_slot_time;
+ __entry->ap_isolate = params->ap_isolate;
+ __entry->ht_opmode = params->ht_opmode;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, "
+ "use short preamble: %d, use short slot time: %d, "
+ "ap isolate: %d, ht opmode: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot,
+ __entry->use_short_preamble, __entry->use_short_slot_time,
+ __entry->ap_isolate, __entry->ht_opmode)
+);
+
+TRACE_EVENT(rdev_set_txq_params,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct ieee80211_txq_params *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(enum nl80211_ac, ac)
+ __field(u16, txop)
+ __field(u16, cwmin)
+ __field(u16, cwmax)
+ __field(u8, aifs)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->ac = params->ac;
+ __entry->txop = params->txop;
+ __entry->cwmin = params->cwmin;
+ __entry->cwmax = params->cwmax;
+ __entry->aifs = params->aifs;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop,
+ __entry->cwmin, __entry->cwmax, __entry->aifs)
+);
+
+TRACE_EVENT(rdev_libertas_set_mesh_channel,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct ieee80211_channel *chan),
+ TP_ARGS(wiphy, netdev, chan),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_PR_FMT, WIPHY_PR_ARG,
+ NETDEV_PR_ARG, CHAN_PR_ARG)
+);
+
+TRACE_EVENT(rdev_set_monitor_channel,
+ TP_PROTO(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_auth,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_auth_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(enum nl80211_auth_type, auth_type)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ memset(__entry->bssid, 0, ETH_ALEN);
+ __entry->auth_type = req->auth_type;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type,
+ MAC_PR_ARG(bssid))
+);
+
+TRACE_EVENT(rdev_assoc,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_assoc_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ MAC_ENTRY(prev_bssid)
+ __field(bool, use_mfp)
+ __field(u32, flags)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ memset(__entry->bssid, 0, ETH_ALEN);
+ MAC_ASSIGN(prev_bssid, req->prev_bssid);
+ __entry->use_mfp = req->use_mfp;
+ __entry->flags = req->flags;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp),
+ __entry->flags)
+);
+
+TRACE_EVENT(rdev_deauth,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_deauth_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, req->bssid);
+ __entry->reason_code = req->reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ __entry->reason_code)
+);
+
+TRACE_EVENT(rdev_disassoc,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_disassoc_request *req),
+ TP_ARGS(wiphy, netdev, req),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __field(u16, reason_code)
+ __field(bool, local_state_change)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ if (req->bss)
+ MAC_ASSIGN(bssid, req->bss->bssid);
+ else
+ memset(__entry->bssid, 0, ETH_ALEN);
+ __entry->reason_code = req->reason_code;
+ __entry->local_state_change = req->local_state_change;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", reason: %u, local state change: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid),
+ __entry->reason_code,
+ BOOL_TO_STR(__entry->local_state_change))
+);
+
+TRACE_EVENT(rdev_mgmt_tx_cancel_wait,
+ TP_PROTO(struct wiphy *wiphy,
+ struct wireless_dev *wdev, u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu ",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_set_power_mgmt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ bool enabled, int timeout),
+ TP_ARGS(wiphy, netdev, enabled, timeout),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(bool, enabled)
+ __field(int, timeout)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->enabled = enabled;
+ __entry->timeout = timeout;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %senabled, timeout: %d ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->enabled ? "" : "not ", __entry->timeout)
+);
+
+TRACE_EVENT(rdev_connect,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_connect_params *sme),
+ TP_ARGS(wiphy, netdev, sme),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ __field(enum nl80211_auth_type, auth_type)
+ __field(bool, privacy)
+ __field(u32, wpa_versions)
+ __field(u32, flags)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, sme->bssid);
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, sme->ssid, sme->ssid_len);
+ __entry->auth_type = sme->auth_type;
+ __entry->privacy = sme->privacy;
+ __entry->wpa_versions = sme->crypto.wpa_versions;
+ __entry->flags = sme->flags;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT
+ ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, "
+ "flags: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid,
+ __entry->auth_type, BOOL_TO_STR(__entry->privacy),
+ __entry->wpa_versions, __entry->flags)
+);
+
+TRACE_EVENT(rdev_set_cqm_rssi_config,
+ TP_PROTO(struct wiphy *wiphy,
+ struct net_device *netdev, s32 rssi_thold,
+ u32 rssi_hyst),
+ TP_ARGS(wiphy, netdev, rssi_thold, rssi_hyst),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(s32, rssi_thold)
+ __field(u32, rssi_hyst)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rssi_thold = rssi_thold;
+ __entry->rssi_hyst = rssi_hyst;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+ ", rssi_thold: %d, rssi_hyst: %u ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG,
+ __entry->rssi_thold, __entry->rssi_hyst)
+);
+
+TRACE_EVENT(rdev_set_cqm_txe_config,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate,
+ u32 pkts, u32 intvl),
+ TP_ARGS(wiphy, netdev, rate, pkts, intvl),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, rate)
+ __field(u32, pkts)
+ __field(u32, intvl)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->rate = rate;
+ __entry->pkts = pkts;
+ __entry->intvl = intvl;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts,
+ __entry->intvl)
+);
+
+TRACE_EVENT(rdev_disconnect,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u16 reason_code),
+ TP_ARGS(wiphy, netdev, reason_code),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->reason_code = reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG,
+ NETDEV_PR_ARG, __entry->reason_code)
+);
+
+TRACE_EVENT(rdev_join_ibss,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_ibss_params *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, params->bssid);
+ memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1);
+ memcpy(__entry->ssid, params->ssid, params->ssid_len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
+);
+
+TRACE_EVENT(rdev_set_wiphy_params,
+ TP_PROTO(struct wiphy *wiphy, u32 changed),
+ TP_ARGS(wiphy, changed),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, changed)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->changed = changed;
+ ),
+ TP_printk(WIPHY_PR_FMT ", changed: %u",
+ WIPHY_PR_ARG, __entry->changed)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_set_tx_power,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ enum nl80211_tx_power_setting type, int mbm),
+ TP_ARGS(wiphy, wdev, type, mbm),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(enum nl80211_tx_power_setting, type)
+ __field(int, mbm)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->type = type;
+ __entry->mbm = mbm;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d",
+ WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm)
+);
+
+TRACE_EVENT(rdev_return_int_int,
+ TP_PROTO(struct wiphy *wiphy, int func_ret, int func_fill),
+ TP_ARGS(wiphy, func_ret, func_fill),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, func_ret)
+ __field(int, func_fill)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->func_ret = func_ret;
+ __entry->func_fill = func_fill;
+ ),
+ TP_printk(WIPHY_PR_FMT ", function returns: %d, function filled: %d",
+ WIPHY_PR_ARG, __entry->func_ret, __entry->func_fill)
+);
+
+#ifdef CONFIG_NL80211_TESTMODE
+TRACE_EVENT(rdev_testmode_cmd,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+
+TRACE_EVENT(rdev_testmode_dump,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+);
+#endif /* CONFIG_NL80211_TESTMODE */
+
+TRACE_EVENT(rdev_set_bitrate_mask,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *peer, const struct cfg80211_bitrate_mask *mask),
+ TP_ARGS(wiphy, netdev, peer, mask),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
+);
+
+TRACE_EVENT(rdev_mgmt_frame_register,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ u16 frame_type, bool reg),
+ TP_ARGS(wiphy, wdev, frame_type, reg),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u16, frame_type)
+ __field(bool, reg)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->frame_type = frame_type;
+ __entry->reg = reg;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type,
+ __entry->reg ? "true" : "false")
+);
+
+TRACE_EVENT(rdev_return_int_tx_rx,
+ TP_PROTO(struct wiphy *wiphy, int ret, u32 tx, u32 rx),
+ TP_ARGS(wiphy, ret, tx, rx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(u32, tx)
+ __field(u32, rx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->tx = tx;
+ __entry->rx = rx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d, tx: %u, rx: %u",
+ WIPHY_PR_ARG, __entry->ret, __entry->tx, __entry->rx)
+);
+
+TRACE_EVENT(rdev_return_void_tx_rx,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 tx_max,
+ u32 rx, u32 rx_max),
+ TP_ARGS(wiphy, tx, tx_max, rx, rx_max),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, tx)
+ __field(u32, tx_max)
+ __field(u32, rx)
+ __field(u32, rx_max)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->tx = tx;
+ __entry->tx_max = tx_max;
+ __entry->rx = rx;
+ __entry->rx_max = rx_max;
+ ),
+ TP_printk(WIPHY_PR_FMT ", tx: %u, tx_max: %u, rx: %u, rx_max: %u ",
+ WIPHY_PR_ARG, __entry->tx, __entry->tx_max, __entry->rx,
+ __entry->rx_max)
+);
+
+DECLARE_EVENT_CLASS(tx_rx_evt,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+ TP_ARGS(wiphy, rx, tx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(u32, tx)
+ __field(u32, rx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->tx = tx;
+ __entry->rx = rx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", tx: %u, rx: %u ",
+ WIPHY_PR_ARG, __entry->tx, __entry->rx)
+);
+
+DEFINE_EVENT(tx_rx_evt, rdev_set_ringparam,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+ TP_ARGS(wiphy, rx, tx)
+);
+
+DEFINE_EVENT(tx_rx_evt, rdev_set_antenna,
+ TP_PROTO(struct wiphy *wiphy, u32 tx, u32 rx),
+ TP_ARGS(wiphy, rx, tx)
+);
+
+TRACE_EVENT(rdev_sched_scan_start,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_sched_scan_request *request),
+ TP_ARGS(wiphy, netdev, request),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
+TRACE_EVENT(rdev_tdls_mgmt,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, const u8 *buf, size_t len),
+ TP_ARGS(wiphy, netdev, peer, action_code, dialog_token, status_code,
+ buf, len),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u8, action_code)
+ __field(u8, dialog_token)
+ __field(u16, status_code)
+ __dynamic_array(u8, buf, len)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->action_code = action_code;
+ __entry->dialog_token = dialog_token;
+ __entry->status_code = status_code;
+ memcpy(__get_dynamic_array(buf), buf, len);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, "
+ "dialog_token: %u, status_code: %u, buf: %#.2x ",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer),
+ __entry->action_code, __entry->dialog_token,
+ __entry->status_code, ((u8 *)__get_dynamic_array(buf))[0])
+);
+
+TRACE_EVENT(rdev_dump_survey,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx),
+ TP_ARGS(wiphy, netdev, idx),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx)
+);
+
+TRACE_EVENT(rdev_return_int_survey_info,
+ TP_PROTO(struct wiphy *wiphy, int ret, struct survey_info *info),
+ TP_ARGS(wiphy, ret, info),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ __field(int, ret)
+ __field(u64, channel_time)
+ __field(u64, channel_time_busy)
+ __field(u64, channel_time_ext_busy)
+ __field(u64, channel_time_rx)
+ __field(u64, channel_time_tx)
+ __field(u32, filled)
+ __field(s8, noise)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(info->channel);
+ __entry->ret = ret;
+ __entry->channel_time = info->channel_time;
+ __entry->channel_time_busy = info->channel_time_busy;
+ __entry->channel_time_ext_busy = info->channel_time_ext_busy;
+ __entry->channel_time_rx = info->channel_time_rx;
+ __entry->channel_time_tx = info->channel_time_tx;
+ __entry->filled = info->filled;
+ __entry->noise = info->noise;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned: %d, " CHAN_PR_FMT
+ ", channel time: %llu, channel time busy: %llu, "
+ "channel time extension busy: %llu, channel time rx: %llu, "
+ "channel time tx: %llu, filled: %u, noise: %d",
+ WIPHY_PR_ARG, __entry->ret, CHAN_PR_ARG,
+ __entry->channel_time, __entry->channel_time_busy,
+ __entry->channel_time_ext_busy, __entry->channel_time_rx,
+ __entry->channel_time_tx, __entry->filled, __entry->noise)
+);
+
+TRACE_EVENT(rdev_tdls_oper,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *peer, enum nl80211_tdls_operation oper),
+ TP_ARGS(wiphy, netdev, peer, oper),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(enum nl80211_tdls_operation, oper)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->oper = oper;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", oper: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper)
+);
+
+DECLARE_EVENT_CLASS(rdev_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, pmksa->bssid);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid))
+);
+
+TRACE_EVENT(rdev_probe_client,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *peer),
+ TP_ARGS(wiphy, netdev, peer),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
+);
+
+DEFINE_EVENT(rdev_pmksa, rdev_set_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa)
+);
+
+DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_pmksa *pmksa),
+ TP_ARGS(wiphy, netdev, pmksa)
+);
+
+TRACE_EVENT(rdev_remain_on_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
+ TP_ARGS(wiphy, wdev, chan, duration),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ CHAN_ENTRY
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ __entry->duration = duration;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration)
+);
+
+TRACE_EVENT(rdev_return_int_cookie,
+ TP_PROTO(struct wiphy *wiphy, int ret, u64 cookie),
+ TP_ARGS(wiphy, ret, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->ret = ret;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", returned %d, cookie: %llu",
+ WIPHY_PR_ARG, __entry->ret, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_cancel_remain_on_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie),
+ TP_ARGS(wiphy, wdev, cookie),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ __field(u64, cookie)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu",
+ WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie)
+);
+
+TRACE_EVENT(rdev_mgmt_tx,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct ieee80211_channel *chan, bool offchan,
+ unsigned int wait, bool no_cck, bool dont_wait_for_ack),
+ TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ WDEV_ENTRY
+ CHAN_ENTRY
+ __field(bool, offchan)
+ __field(unsigned int, wait)
+ __field(bool, no_cck)
+ __field(bool, dont_wait_for_ack)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ WDEV_ASSIGN;
+ CHAN_ASSIGN(chan);
+ __entry->offchan = offchan;
+ __entry->wait = wait;
+ __entry->no_cck = no_cck;
+ __entry->dont_wait_for_ack = dont_wait_for_ack;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"
+ " wait: %u, no cck: %s, dont wait for ack: %s",
+ WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG,
+ BOOL_TO_STR(__entry->offchan), __entry->wait,
+ BOOL_TO_STR(__entry->no_cck),
+ BOOL_TO_STR(__entry->dont_wait_for_ack))
+);
+
+TRACE_EVENT(rdev_set_noack_map,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u16 noack_map),
+ TP_ARGS(wiphy, netdev, noack_map),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u16, noack_map)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->noack_map = noack_map;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", noack_map: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map)
+);
+
+TRACE_EVENT(rdev_get_et_sset_count,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int sset),
+ TP_ARGS(wiphy, netdev, sset),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(int, sset)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->sset = sset;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
+);
+
+TRACE_EVENT(rdev_get_et_strings,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 sset),
+ TP_ARGS(wiphy, netdev, sset),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ __field(u32, sset)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ __entry->sset = sset;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+TRACE_EVENT(rdev_return_chandef,
+ TP_PROTO(struct wiphy *wiphy, int ret,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, ret, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, ret)
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ if (ret == 0)
+ CHAN_DEF_ASSIGN(chandef);
+ else
+ CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL);
+ __entry->ret = ret;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", ret: %d",
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+DEFINE_EVENT(wiphy_wdev_evt, rdev_stop_p2p_device,
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev)
+);
+
+/*************************************************************
+ * cfg80211 exported functions traces *
+ *************************************************************/
+
+TRACE_EVENT(cfg80211_return_bool,
+ TP_PROTO(bool ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(bool, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("returned %s", BOOL_TO_STR(__entry->ret))
+);
+
+DECLARE_EVENT_CLASS(cfg80211_netdev_mac_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(macaddr)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(macaddr, macaddr);
+ ),
+ TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(macaddr))
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_notify_new_peer_candidate,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+DECLARE_EVENT_CLASS(netdev_evt_only,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(NETDEV_PR_FMT , NETDEV_PR_ARG)
+);
+
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_rx_auth,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+TRACE_EVENT(cfg80211_send_rx_assoc,
+ TP_PROTO(struct net_device *netdev, struct cfg80211_bss *bss),
+ TP_ARGS(netdev, bss),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(bssid, bss->bssid);
+ CHAN_ASSIGN(bss->channel);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", " CHAN_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
+DEFINE_EVENT(netdev_evt_only, __cfg80211_send_deauth,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+DEFINE_EVENT(netdev_evt_only, __cfg80211_send_disassoc,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_deauth,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+DEFINE_EVENT(netdev_evt_only, cfg80211_send_unprot_disassoc,
+ TP_PROTO(struct net_device *netdev),
+ TP_ARGS(netdev)
+);
+
+DECLARE_EVENT_CLASS(netdev_mac_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(mac)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac, mac)
+ ),
+ TP_printk(NETDEV_PR_FMT ", mac: " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(mac))
+);
+
+DEFINE_EVENT(netdev_mac_evt, cfg80211_send_auth_timeout,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac)
+);
+
+DEFINE_EVENT(netdev_mac_evt, cfg80211_send_assoc_timeout,
+ TP_PROTO(struct net_device *netdev, const u8 *mac),
+ TP_ARGS(netdev, mac)
+);
+
+TRACE_EVENT(cfg80211_michael_mic_failure,
+ TP_PROTO(struct net_device *netdev, const u8 *addr,
+ enum nl80211_key_type key_type, int key_id, const u8 *tsc),
+ TP_ARGS(netdev, addr, key_type, key_id, tsc),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(enum nl80211_key_type, key_type)
+ __field(int, key_id)
+ __array(u8, tsc, 6)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->key_type = key_type;
+ __entry->key_id = key_id;
+ memcpy(__entry->tsc, tsc, 6);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm",
+ NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type,
+ __entry->key_id, __entry->tsc)
+);
+
+TRACE_EVENT(cfg80211_ready_on_channel,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan,
+ unsigned int duration),
+ TP_ARGS(wdev, cookie, chan, duration),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ CHAN_ENTRY
+ __field(unsigned int, duration)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ CHAN_ASSIGN(chan);
+ __entry->duration = duration;
+ ),
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u",
+ WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG,
+ __entry->duration)
+);
+
+TRACE_EVENT(cfg80211_ready_on_channel_expired,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie,
+ struct ieee80211_channel *chan),
+ TP_ARGS(wdev, cookie, chan),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ CHAN_ASSIGN(chan);
+ ),
+ TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT,
+ WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_new_sta,
+ TP_PROTO(struct net_device *netdev, const u8 *mac_addr,
+ struct station_info *sinfo),
+ TP_ARGS(netdev, mac_addr, sinfo),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(mac_addr)
+ SINFO_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(mac_addr, mac_addr);
+ SINFO_ASSIGN;
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT,
+ NETDEV_PR_ARG, MAC_PR_ARG(mac_addr))
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_del_sta,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+TRACE_EVENT(cfg80211_rx_mgmt,
+ TP_PROTO(struct wireless_dev *wdev, int freq, int sig_mbm),
+ TP_ARGS(wdev, freq, sig_mbm),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(int, freq)
+ __field(int, sig_mbm)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->freq = freq;
+ __entry->sig_mbm = sig_mbm;
+ ),
+ TP_printk(WDEV_PR_FMT ", freq: %d, sig mbm: %d",
+ WDEV_PR_ARG, __entry->freq, __entry->sig_mbm)
+);
+
+TRACE_EVENT(cfg80211_mgmt_tx_status,
+ TP_PROTO(struct wireless_dev *wdev, u64 cookie, bool ack),
+ TP_ARGS(wdev, cookie, ack),
+ TP_STRUCT__entry(
+ WDEV_ENTRY
+ __field(u64, cookie)
+ __field(bool, ack)
+ ),
+ TP_fast_assign(
+ WDEV_ASSIGN;
+ __entry->cookie = cookie;
+ __entry->ack = ack;
+ ),
+ TP_printk(WDEV_PR_FMT", cookie: %llu, ack: %s",
+ WDEV_PR_ARG, __entry->cookie, BOOL_TO_STR(__entry->ack))
+);
+
+TRACE_EVENT(cfg80211_cqm_rssi_notify,
+ TP_PROTO(struct net_device *netdev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event),
+ TP_ARGS(netdev, rssi_event),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(enum nl80211_cqm_rssi_threshold_event, rssi_event)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->rssi_event = rssi_event;
+ ),
+ TP_printk(NETDEV_PR_FMT ", rssi event: %d",
+ NETDEV_PR_ARG, __entry->rssi_event)
+);
+
+TRACE_EVENT(cfg80211_reg_can_beacon,
+ TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(cfg80211_ch_switch_notify,
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
+DECLARE_EVENT_CLASS(cfg80211_rx_evt,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+);
+
+DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr)
+);
+
+DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_spurious_frame,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr)
+);
+
+DEFINE_EVENT(cfg80211_rx_evt, cfg80211_rx_unexpected_4addr_frame,
+ TP_PROTO(struct net_device *netdev, const u8 *addr),
+ TP_ARGS(netdev, addr)
+);
+
+TRACE_EVENT(cfg80211_probe_status,
+ TP_PROTO(struct net_device *netdev, const u8 *addr, u64 cookie,
+ bool acked),
+ TP_ARGS(netdev, addr, cookie, acked),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u64, cookie)
+ __field(bool, acked)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->cookie = cookie;
+ __entry->acked = acked;
+ ),
+ TP_printk(NETDEV_PR_FMT " addr:" MAC_PR_FMT ", cookie: %llu, acked: %s",
+ NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie,
+ BOOL_TO_STR(__entry->acked))
+);
+
+TRACE_EVENT(cfg80211_cqm_pktloss_notify,
+ TP_PROTO(struct net_device *netdev, const u8 *peer, u32 num_packets),
+ TP_ARGS(netdev, peer, num_packets),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(u32, num_packets)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->num_packets = num_packets;
+ ),
+ TP_printk(NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", num of lost packets: %u",
+ NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->num_packets)
+);
+
+DEFINE_EVENT(cfg80211_netdev_mac_evt, cfg80211_gtk_rekey_notify,
+ TP_PROTO(struct net_device *netdev, const u8 *macaddr),
+ TP_ARGS(netdev, macaddr)
+);
+
+TRACE_EVENT(cfg80211_pmksa_candidate_notify,
+ TP_PROTO(struct net_device *netdev, int index, const u8 *bssid,
+ bool preauth),
+ TP_ARGS(netdev, index, bssid, preauth),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ __field(int, index)
+ MAC_ENTRY(bssid)
+ __field(bool, preauth)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ __entry->index = index;
+ MAC_ASSIGN(bssid, bssid);
+ __entry->preauth = preauth;
+ ),
+ TP_printk(NETDEV_PR_FMT ", index:%d, bssid: " MAC_PR_FMT ", pre auth: %s",
+ NETDEV_PR_ARG, __entry->index, MAC_PR_ARG(bssid),
+ BOOL_TO_STR(__entry->preauth))
+);
+
+TRACE_EVENT(cfg80211_report_obss_beacon,
+ TP_PROTO(struct wiphy *wiphy, const u8 *frame, size_t len,
+ int freq, int sig_dbm),
+ TP_ARGS(wiphy, frame, len, freq, sig_dbm),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ __field(int, freq)
+ __field(int, sig_dbm)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ __entry->freq = freq;
+ __entry->sig_dbm = sig_dbm;
+ ),
+ TP_printk(WIPHY_PR_FMT ", freq: %d, sig_dbm: %d",
+ WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm)
+);
+
+TRACE_EVENT(cfg80211_tdls_oper_request,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer,
+ enum nl80211_tdls_operation oper, u16 reason_code),
+ TP_ARGS(wiphy, netdev, peer, oper, reason_code),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(peer)
+ __field(enum nl80211_tdls_operation, oper)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(peer, peer);
+ __entry->oper = oper;
+ __entry->reason_code = reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", oper: %d, reason_code %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper,
+ __entry->reason_code)
+ );
+
+TRACE_EVENT(cfg80211_scan_done,
+ TP_PROTO(struct cfg80211_scan_request *request, bool aborted),
+ TP_ARGS(request, aborted),
+ TP_STRUCT__entry(
+ __field(u32, n_channels)
+ __dynamic_array(u8, ie, request ? request->ie_len : 0)
+ __array(u32, rates, IEEE80211_NUM_BANDS)
+ __field(u32, wdev_id)
+ MAC_ENTRY(wiphy_mac)
+ __field(bool, no_cck)
+ __field(bool, aborted)
+ ),
+ TP_fast_assign(
+ if (request) {
+ memcpy(__get_dynamic_array(ie), request->ie,
+ request->ie_len);
+ memcpy(__entry->rates, request->rates,
+ IEEE80211_NUM_BANDS);
+ __entry->wdev_id = request->wdev ?
+ request->wdev->identifier : 0;
+ if (request->wiphy)
+ MAC_ASSIGN(wiphy_mac,
+ request->wiphy->perm_addr);
+ __entry->no_cck = request->no_cck;
+ }
+ __entry->aborted = aborted;
+ ),
+ TP_printk("aborted: %s", BOOL_TO_STR(__entry->aborted))
+);
+
+DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_results,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+DEFINE_EVENT(wiphy_only_evt, cfg80211_sched_scan_stopped,
+ TP_PROTO(struct wiphy *wiphy),
+ TP_ARGS(wiphy)
+);
+
+TRACE_EVENT(cfg80211_get_bss,
+ TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+ const u8 *bssid, const u8 *ssid, size_t ssid_len,
+ u16 capa_mask, u16 capa_val),
+ TP_ARGS(wiphy, channel, bssid, ssid, ssid_len, capa_mask, capa_val),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ MAC_ENTRY(bssid)
+ __dynamic_array(u8, ssid, ssid_len)
+ __field(u16, capa_mask)
+ __field(u16, capa_val)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(channel);
+ MAC_ASSIGN(bssid, bssid);
+ memcpy(__get_dynamic_array(ssid), ssid, ssid_len);
+ __entry->capa_mask = capa_mask;
+ __entry->capa_val = capa_val;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, "
+ "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG,
+ MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0],
+ __entry->capa_mask, __entry->capa_val)
+);
+
+TRACE_EVENT(cfg80211_inform_bss_frame,
+ TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ s32 signal),
+ TP_ARGS(wiphy, channel, mgmt, len, signal),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ CHAN_ENTRY
+ __dynamic_array(u8, mgmt, len)
+ __field(s32, signal)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ CHAN_ASSIGN(channel);
+ if (mgmt)
+ memcpy(__get_dynamic_array(mgmt), mgmt, len);
+ __entry->signal = signal;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
+ WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+);
+
+DECLARE_EVENT_CLASS(cfg80211_bss_evt,
+ TP_PROTO(struct cfg80211_bss *pub),
+ TP_ARGS(pub),
+ TP_STRUCT__entry(
+ MAC_ENTRY(bssid)
+ CHAN_ENTRY
+ ),
+ TP_fast_assign(
+ MAC_ASSIGN(bssid, pub->bssid);
+ CHAN_ASSIGN(pub->channel);
+ ),
+ TP_printk(MAC_PR_FMT ", " CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG)
+);
+
+DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss,
+ TP_PROTO(struct cfg80211_bss *pub),
+ TP_ARGS(pub)
+);
+
+TRACE_EVENT(cfg80211_return_uint,
+ TP_PROTO(unsigned int ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(unsigned int, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret: %d", __entry->ret)
+);
+
+TRACE_EVENT(cfg80211_return_u32,
+ TP_PROTO(u32 ret),
+ TP_ARGS(ret),
+ TP_STRUCT__entry(
+ __field(u32, ret)
+ ),
+ TP_fast_assign(
+ __entry->ret = ret;
+ ),
+ TP_printk("ret: %u", __entry->ret)
+);
+
+#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/net/wireless/util.c b/net/wireless/util.c
index ef35f4ef2aa..16d76a807c2 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -11,6 +11,8 @@
#include <net/ip.h>
#include <net/dsfield.h>
#include "core.h"
+#include "rdev-ops.h"
+
struct ieee80211_rate *
ieee80211_get_response_rate(struct ieee80211_supported_band *sband,
@@ -309,23 +311,21 @@ unsigned int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
-static int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+unsigned int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
{
int ae = meshhdr->flags & MESH_FLAGS_AE;
- /* 7.1.3.5a.2 */
+ /* 802.11-2012, 8.2.4.7.3 */
switch (ae) {
+ default:
case 0:
return 6;
case MESH_FLAGS_AE_A4:
return 12;
case MESH_FLAGS_AE_A5_A6:
return 18;
- case (MESH_FLAGS_AE_A4 | MESH_FLAGS_AE_A5_A6):
- return 24;
- default:
- return 6;
}
}
+EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
enum nl80211_iftype iftype)
@@ -373,6 +373,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
/* make sure meshdr->flags is on the linear part */
if (!pskb_may_pull(skb, hdrlen + 1))
return -1;
+ if (meshdr->flags & MESH_FLAGS_AE_A4)
+ return -1;
if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr1),
@@ -397,6 +399,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
/* make sure meshdr->flags is on the linear part */
if (!pskb_may_pull(skb, hdrlen + 1))
return -1;
+ if (meshdr->flags & MESH_FLAGS_AE_A5_A6)
+ return -1;
if (meshdr->flags & MESH_FLAGS_AE_A4)
skb_copy_bits(skb, hdrlen +
offsetof(struct ieee80211s_hdr, eaddr1),
@@ -684,10 +688,13 @@ EXPORT_SYMBOL(cfg80211_classify8021d);
const u8 *ieee80211_bss_get_ie(struct cfg80211_bss *bss, u8 ie)
{
- if (bss->information_elements == NULL)
+ const struct cfg80211_bss_ies *ies;
+
+ ies = rcu_dereference(bss->ies);
+ if (!ies)
return NULL;
- return cfg80211_find_ie(ie, bss->information_elements,
- bss->len_information_elements);
+
+ return cfg80211_find_ie(ie, ies->data, ies->len);
}
EXPORT_SYMBOL(ieee80211_bss_get_ie);
@@ -703,19 +710,18 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev)
for (i = 0; i < 6; i++) {
if (!wdev->connect_keys->params[i].cipher)
continue;
- if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL,
- &wdev->connect_keys->params[i])) {
+ if (rdev_add_key(rdev, dev, i, false, NULL,
+ &wdev->connect_keys->params[i])) {
netdev_err(dev, "failed to set key %d\n", i);
continue;
}
if (wdev->connect_keys->def == i)
- if (rdev->ops->set_default_key(wdev->wiphy, dev,
- i, true, true)) {
+ if (rdev_set_default_key(rdev, dev, i, true, true)) {
netdev_err(dev, "failed to set defkey %d\n", i);
continue;
}
if (wdev->connect_keys->defmgmt == i)
- if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i))
+ if (rdev_set_default_mgmt_key(rdev, dev, i))
netdev_err(dev, "failed to set mgtdef %d\n", i);
}
@@ -848,8 +854,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
cfg80211_process_rdev_events(rdev);
}
- err = rdev->ops->change_virtual_intf(&rdev->wiphy, dev,
- ntype, flags, params);
+ err = rdev_change_virtual_intf(rdev, dev, ntype, flags, params);
WARN_ON(!err && dev->ieee80211_ptr->iftype != ntype);
@@ -942,14 +947,86 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate)
return __mcs2bitrate[rate->mcs];
}
+static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate)
+{
+ static const u32 base[4][10] = {
+ { 6500000,
+ 13000000,
+ 19500000,
+ 26000000,
+ 39000000,
+ 52000000,
+ 58500000,
+ 65000000,
+ 78000000,
+ 0,
+ },
+ { 13500000,
+ 27000000,
+ 40500000,
+ 54000000,
+ 81000000,
+ 108000000,
+ 121500000,
+ 135000000,
+ 162000000,
+ 180000000,
+ },
+ { 29300000,
+ 58500000,
+ 87800000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 263300000,
+ 292500000,
+ 351000000,
+ 390000000,
+ },
+ { 58500000,
+ 117000000,
+ 175500000,
+ 234000000,
+ 351000000,
+ 468000000,
+ 526500000,
+ 585000000,
+ 702000000,
+ 780000000,
+ },
+ };
+ u32 bitrate;
+ int idx;
+
+ if (WARN_ON_ONCE(rate->mcs > 9))
+ return 0;
+
+ idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH |
+ RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 :
+ rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 :
+ rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0;
+
+ bitrate = base[idx][rate->mcs];
+ bitrate *= rate->nss;
+
+ if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+ bitrate = (bitrate / 9) * 10;
+
+ /* do NOT round down here */
+ return (bitrate + 50000) / 100000;
+}
+
u32 cfg80211_calculate_bitrate(struct rate_info *rate)
{
int modulation, streams, bitrate;
- if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+ if (!(rate->flags & RATE_INFO_FLAGS_MCS) &&
+ !(rate->flags & RATE_INFO_FLAGS_VHT_MCS))
return rate->legacy;
if (rate->flags & RATE_INFO_FLAGS_60G)
return cfg80211_calculate_bitrate_60g(rate);
+ if (rate->flags & RATE_INFO_FLAGS_VHT_MCS)
+ return cfg80211_calculate_bitrate_vht(rate);
/* the formula below does only work for MCS values smaller than 32 */
if (WARN_ON_ONCE(rate->mcs >= 32))
@@ -978,6 +1055,106 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate)
}
EXPORT_SYMBOL(cfg80211_calculate_bitrate);
+int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len,
+ enum ieee80211_p2p_attr_id attr,
+ u8 *buf, unsigned int bufsize)
+{
+ u8 *out = buf;
+ u16 attr_remaining = 0;
+ bool desired_attr = false;
+ u16 desired_len = 0;
+
+ while (len > 0) {
+ unsigned int iedatalen;
+ unsigned int copy;
+ const u8 *iedata;
+
+ if (len < 2)
+ return -EILSEQ;
+ iedatalen = ies[1];
+ if (iedatalen + 2 > len)
+ return -EILSEQ;
+
+ if (ies[0] != WLAN_EID_VENDOR_SPECIFIC)
+ goto cont;
+
+ if (iedatalen < 4)
+ goto cont;
+
+ iedata = ies + 2;
+
+ /* check WFA OUI, P2P subtype */
+ if (iedata[0] != 0x50 || iedata[1] != 0x6f ||
+ iedata[2] != 0x9a || iedata[3] != 0x09)
+ goto cont;
+
+ iedatalen -= 4;
+ iedata += 4;
+
+ /* check attribute continuation into this IE */
+ copy = min_t(unsigned int, attr_remaining, iedatalen);
+ if (copy && desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+
+ if (copy == attr_remaining)
+ return desired_len;
+ }
+
+ attr_remaining -= copy;
+ if (attr_remaining)
+ goto cont;
+
+ iedatalen -= copy;
+ iedata += copy;
+
+ while (iedatalen > 0) {
+ u16 attr_len;
+
+ /* P2P attribute ID & size must fit */
+ if (iedatalen < 3)
+ return -EILSEQ;
+ desired_attr = iedata[0] == attr;
+ attr_len = get_unaligned_le16(iedata + 1);
+ iedatalen -= 3;
+ iedata += 3;
+
+ copy = min_t(unsigned int, attr_len, iedatalen);
+
+ if (desired_attr) {
+ desired_len += copy;
+ if (out) {
+ memcpy(out, iedata, min(bufsize, copy));
+ out += min(bufsize, copy);
+ bufsize -= min(bufsize, copy);
+ }
+
+ if (copy == attr_len)
+ return desired_len;
+ }
+
+ iedata += copy;
+ iedatalen -= copy;
+ attr_remaining = attr_len - copy;
+ }
+
+ cont:
+ len -= ies[1] + 2;
+ ies += ies[1] + 2;
+ }
+
+ if (attr_remaining && desired_attr)
+ return -EILSEQ;
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL(cfg80211_get_p2p_attr);
+
int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
u32 beacon_int)
{
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 494379eb464..d997d0f0c54 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -19,6 +19,7 @@
#include <net/cfg80211-wext.h>
#include "wext-compat.h"
#include "core.h"
+#include "rdev-ops.h"
int cfg80211_wext_giwname(struct net_device *dev,
struct iw_request_info *info,
@@ -175,7 +176,7 @@ int cfg80211_wext_giwrange(struct net_device *dev,
case CFG80211_SIGNAL_TYPE_NONE:
break;
case CFG80211_SIGNAL_TYPE_MBM:
- range->max_qual.level = -110;
+ range->max_qual.level = (u8)-110;
range->max_qual.qual = 70;
range->avg_qual.qual = 35;
range->max_qual.updated |= IW_QUAL_DBM;
@@ -301,8 +302,7 @@ int cfg80211_wext_siwrts(struct net_device *dev,
else
wdev->wiphy->rts_threshold = rts->value;
- err = rdev->ops->set_wiphy_params(wdev->wiphy,
- WIPHY_PARAM_RTS_THRESHOLD);
+ err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_RTS_THRESHOLD);
if (err)
wdev->wiphy->rts_threshold = orts;
@@ -342,8 +342,7 @@ int cfg80211_wext_siwfrag(struct net_device *dev,
wdev->wiphy->frag_threshold = frag->value & ~0x1;
}
- err = rdev->ops->set_wiphy_params(wdev->wiphy,
- WIPHY_PARAM_FRAG_THRESHOLD);
+ err = rdev_set_wiphy_params(rdev, WIPHY_PARAM_FRAG_THRESHOLD);
if (err)
wdev->wiphy->frag_threshold = ofrag;
@@ -396,7 +395,7 @@ static int cfg80211_wext_siwretry(struct net_device *dev,
if (!changed)
return 0;
- err = rdev->ops->set_wiphy_params(wdev->wiphy, changed);
+ err = rdev_set_wiphy_params(rdev, changed);
if (err) {
wdev->wiphy->retry_short = oshort;
wdev->wiphy->retry_long = olong;
@@ -490,8 +489,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT;
else
- err = rdev->ops->del_key(&rdev->wiphy, dev, idx,
- pairwise, addr);
+ err = rdev_del_key(rdev, dev, idx, pairwise,
+ addr);
}
wdev->wext.connect.privacy = false;
/*
@@ -525,8 +524,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
err = 0;
if (wdev->current_bss)
- err = rdev->ops->add_key(&rdev->wiphy, dev, idx,
- pairwise, addr, params);
+ err = rdev_add_key(rdev, dev, idx, pairwise, addr, params);
if (err)
return err;
@@ -552,8 +550,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
__cfg80211_leave_ibss(rdev, wdev->netdev, true);
rejoin = true;
}
- err = rdev->ops->set_default_key(&rdev->wiphy, dev,
- idx, true, true);
+ err = rdev_set_default_key(rdev, dev, idx, true, true);
}
if (!err) {
wdev->wext.default_key = idx;
@@ -566,8 +563,7 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev,
if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC &&
(tx_key || (!addr && wdev->wext.default_mgmt_key == -1))) {
if (wdev->current_bss)
- err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
- dev, idx);
+ err = rdev_set_default_mgmt_key(rdev, dev, idx);
if (!err)
wdev->wext.default_mgmt_key = idx;
return err;
@@ -631,8 +627,8 @@ static int cfg80211_wext_siwencode(struct net_device *dev,
err = 0;
wdev_lock(wdev);
if (wdev->current_bss)
- err = rdev->ops->set_default_key(&rdev->wiphy, dev,
- idx, true, true);
+ err = rdev_set_default_key(rdev, dev, idx, true,
+ true);
if (!err)
wdev->wext.default_key = idx;
wdev_unlock(wdev);
@@ -788,6 +784,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
+ struct cfg80211_chan_def chandef = {
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ };
int freq, err;
switch (wdev->iftype) {
@@ -801,8 +800,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
+ err = cfg80211_set_monitor_channel(rdev, &chandef);
mutex_unlock(&rdev->devlist_mtx);
return err;
case NL80211_IFTYPE_MESH_POINT:
@@ -811,9 +814,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
return freq;
if (freq == 0)
return -EINVAL;
+ chandef.center_freq1 = freq;
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (!chandef.chan)
+ return -EINVAL;
mutex_lock(&rdev->devlist_mtx);
- err = cfg80211_set_mesh_freq(rdev, wdev, freq,
- NL80211_CHAN_NO_HT);
+ err = cfg80211_set_mesh_channel(rdev, wdev, &chandef);
mutex_unlock(&rdev->devlist_mtx);
return err;
default:
@@ -827,8 +833,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
- struct ieee80211_channel *chan;
- enum nl80211_channel_type channel_type;
+ struct cfg80211_chan_def chandef;
+ int ret;
switch (wdev->iftype) {
case NL80211_IFTYPE_STATION:
@@ -839,10 +845,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev,
if (!rdev->ops->get_channel)
return -EINVAL;
- chan = rdev->ops->get_channel(wdev->wiphy, wdev, &channel_type);
- if (!chan)
- return -EINVAL;
- freq->m = chan->center_freq;
+ ret = rdev_get_channel(rdev, wdev, &chandef);
+ if (ret)
+ return ret;
+ freq->m = chandef.chan->center_freq;
freq->e = 6;
return 0;
default:
@@ -899,7 +905,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev,
return 0;
}
- return rdev->ops->set_tx_power(wdev->wiphy, type, DBM_TO_MBM(dbm));
+ return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm));
}
static int cfg80211_wext_giwtxpower(struct net_device *dev,
@@ -918,7 +924,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev,
if (!rdev->ops->get_tx_power)
return -EOPNOTSUPP;
- err = rdev->ops->get_tx_power(wdev->wiphy, &val);
+ err = rdev_get_tx_power(rdev, wdev, &val);
if (err)
return err;
@@ -1158,7 +1164,7 @@ static int cfg80211_wext_siwpower(struct net_device *dev,
timeout = wrq->value / 1000;
}
- err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, ps, timeout);
+ err = rdev_set_power_mgmt(rdev, dev, ps, timeout);
if (err)
return err;
@@ -1200,7 +1206,7 @@ static int cfg80211_wds_wext_siwap(struct net_device *dev,
if (!rdev->ops->set_wds_peer)
return -EOPNOTSUPP;
- err = rdev->ops->set_wds_peer(wdev->wiphy, dev, (u8 *) &addr->sa_data);
+ err = rdev_set_wds_peer(rdev, dev, (u8 *)&addr->sa_data);
if (err)
return err;
@@ -1272,7 +1278,7 @@ static int cfg80211_wext_siwrate(struct net_device *dev,
if (!match)
return -EINVAL;
- return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask);
+ return rdev_set_bitrate_mask(rdev, dev, NULL, &mask);
}
static int cfg80211_wext_giwrate(struct net_device *dev,
@@ -1302,7 +1308,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
if (err)
return err;
- err = rdev->ops->get_station(&rdev->wiphy, dev, addr, &sinfo);
+ err = rdev_get_station(rdev, dev, addr, &sinfo);
if (err)
return err;
@@ -1339,7 +1345,7 @@ static struct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
memcpy(bssid, wdev->current_bss->pub.bssid, ETH_ALEN);
wdev_unlock(wdev);
- if (rdev->ops->get_station(&rdev->wiphy, dev, bssid, &sinfo))
+ if (rdev_get_station(rdev, dev, bssid, &sinfo))
return NULL;
memset(&wstats, 0, sizeof(wstats));
@@ -1474,19 +1480,19 @@ static int cfg80211_wext_siwpmksa(struct net_device *dev,
if (!rdev->ops->set_pmksa)
return -EOPNOTSUPP;
- return rdev->ops->set_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+ return rdev_set_pmksa(rdev, dev, &cfg_pmksa);
case IW_PMKSA_REMOVE:
if (!rdev->ops->del_pmksa)
return -EOPNOTSUPP;
- return rdev->ops->del_pmksa(&rdev->wiphy, dev, &cfg_pmksa);
+ return rdev_del_pmksa(rdev, dev, &cfg_pmksa);
case IW_PMKSA_FLUSH:
if (!rdev->ops->flush_pmksa)
return -EOPNOTSUPP;
- return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
+ return rdev_flush_pmksa(rdev, dev);
default:
return -EOPNOTSUPP;
diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c
index 1f773f668d1..fb9622f6d99 100644
--- a/net/wireless/wext-sme.c
+++ b/net/wireless/wext-sme.c
@@ -119,7 +119,16 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev,
* channel we disconnected above and reconnect below.
*/
if (chan && !wdev->wext.connect.ssid_len) {
- err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT);
+ struct cfg80211_chan_def chandef = {
+ .width = NL80211_CHAN_WIDTH_20_NOHT,
+ .center_freq1 = freq,
+ };
+
+ chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
+ if (chandef.chan)
+ err = cfg80211_set_monitor_channel(rdev, &chandef);
+ else
+ err = -EINVAL;
goto out;
}
@@ -233,13 +242,17 @@ int cfg80211_mgd_wext_giwessid(struct net_device *dev,
wdev_lock(wdev);
if (wdev->current_bss) {
- const u8 *ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
- WLAN_EID_SSID);
+ const u8 *ie;
+
+ rcu_read_lock();
+ ie = ieee80211_bss_get_ie(&wdev->current_bss->pub,
+ WLAN_EID_SSID);
if (ie) {
data->flags = 1;
data->length = ie[1];
memcpy(ssid, ie + 2, data->length);
}
+ rcu_read_unlock();
} else if (wdev->wext.connect.ssid && wdev->wext.connect.ssid_len) {
data->flags = 1;
data->length = wdev->wext.connect.ssid_len;
diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c
index e5246fbe36c..2906d520eea 100644
--- a/net/xfrm/xfrm_ipcomp.c
+++ b/net/xfrm/xfrm_ipcomp.c
@@ -276,18 +276,16 @@ static struct crypto_comp * __percpu *ipcomp_alloc_tfms(const char *alg_name)
struct crypto_comp * __percpu *tfms;
int cpu;
- /* This can be any valid CPU ID so we don't need locking. */
- cpu = raw_smp_processor_id();
list_for_each_entry(pos, &ipcomp_tfms_list, list) {
struct crypto_comp *tfm;
- tfms = pos->tfms;
- tfm = *per_cpu_ptr(tfms, cpu);
+ /* This can be any valid CPU ID so we don't need locking. */
+ tfm = __this_cpu_read(*pos->tfms);
if (!strcmp(crypto_comp_name(tfm), alg_name)) {
pos->users++;
- return tfms;
+ return pos->tfms;
}
}
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 41eabc46f11..07c585756d2 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -2656,7 +2656,7 @@ static void xfrm_policy_fini(struct net *net)
WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
htab = &net->xfrm.policy_bydst[dir];
- sz = (htab->hmask + 1);
+ sz = (htab->hmask + 1) * sizeof(struct hlist_head);
WARN_ON(!hlist_empty(htab->table));
xfrm_hash_free(htab->table, sz);
}
diff --git a/net/xfrm/xfrm_replay.c b/net/xfrm/xfrm_replay.c
index 3efb07d3eb2..35754cc8a9e 100644
--- a/net/xfrm/xfrm_replay.c
+++ b/net/xfrm/xfrm_replay.c
@@ -242,11 +242,13 @@ static void xfrm_replay_advance_bmp(struct xfrm_state *x, __be32 net_seq)
u32 diff;
struct xfrm_replay_state_esn *replay_esn = x->replay_esn;
u32 seq = ntohl(net_seq);
- u32 pos = (replay_esn->seq - 1) % replay_esn->replay_window;
+ u32 pos;
if (!replay_esn->replay_window)
return;
+ pos = (replay_esn->seq - 1) % replay_esn->replay_window;
+
if (seq > replay_esn->seq) {
diff = seq - replay_esn->seq;
@@ -521,13 +523,12 @@ int xfrm_init_replay(struct xfrm_state *x)
replay_esn->bmp_len * sizeof(__u32) * 8)
return -EINVAL;
- if ((x->props.flags & XFRM_STATE_ESN) && replay_esn->replay_window == 0)
- return -EINVAL;
-
- if ((x->props.flags & XFRM_STATE_ESN) && x->replay_esn)
- x->repl = &xfrm_replay_esn;
- else
- x->repl = &xfrm_replay_bmp;
+ if (x->props.flags & XFRM_STATE_ESN) {
+ if (replay_esn->replay_window == 0)
+ return -EINVAL;
+ x->repl = &xfrm_replay_esn;
+ } else
+ x->repl = &xfrm_replay_bmp;
} else
x->repl = &xfrm_replay_legacy;
diff --git a/net/xfrm/xfrm_sysctl.c b/net/xfrm/xfrm_sysctl.c
index 380976f74c4..05a6e3d9c25 100644
--- a/net/xfrm/xfrm_sysctl.c
+++ b/net/xfrm/xfrm_sysctl.c
@@ -54,6 +54,10 @@ int __net_init xfrm_sysctl_init(struct net *net)
table[2].data = &net->xfrm.sysctl_larval_drop;
table[3].data = &net->xfrm.sysctl_acq_expires;
+ /* Don't export sysctls to unprivileged users */
+ if (net->user_ns != &init_user_ns)
+ table[0].procname = NULL;
+
net->xfrm.sysctl_hdr = register_net_sysctl(net, "net/core", table);
if (!net->xfrm.sysctl_hdr)
goto out_register;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 421f9844433..eb872b2e366 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -2349,7 +2349,7 @@ static int xfrm_user_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
link = &xfrm_dispatch[type];
/* All operations require privileges, even GET */
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if ((type == (XFRM_MSG_GETSA - XFRM_MSG_BASE) ||